View of xos/usr/utilities/echo.c


XOS | Parent Directory | View | Download

/* Copyright (C) 2008  Emmanuel Varoquaux
 
   This file is part of XOS.
 
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
#include "common.h"
 
#include <getopt.h>
#include <error.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <errno.h>
 
struct options_struct {
  unsigned print_trailing_newline : 1;
  unsigned escape : 1;
};
 
static void __attribute__ ((noreturn)) die_bad_usage()
{
  fprintf(stderr, "Try `%s --help' for more information.\n", program_invocation_name);
  exit(EXIT_FAILURE);
}
 
static void print_help()
{
  printf("Usage: %s [OPTION]... [STRING]...\n", program_invocation_name);
  puts("Echo the STRING(s) to standard output.");
  putchar('\n');
  puts("  -n          do not output the trailing newline");
  puts("  -e          enable interpretation of backslash escapes");
  puts("  -E          disable interpretation of backslash escapes (default)");
  puts("  -h, --help  display this help and exit");
  putchar('\n');
  puts("If -e is in effect, the following sequences are recognized:");
  putchar('\n');
  puts("  \\0NNN  the character whose ASCII code is NNN (octal)");
  puts("  \\xHH   the character whose ASCII code is HH (hexadecimal)");
  puts("  \\\\     backslash");
  puts("  \\a     alert (BEL)");
  puts("  \\b     backspace");
  puts("  \\c     suppress trailing newline");
  puts("  \\e     an escape character");
  puts("  \\f     form feed");
  puts("  \\n     new line");
  puts("  \\r     carriage return");
  puts("  \\t     horizontal tab");
  puts("  \\v     vertical tab");
}
 
static int octtobin (unsigned char c)
{
  return c - '0';
}
 
static int hextobin (unsigned char c)
{
  switch (c) {
  case 'a':
  case 'A':
    return 10;
  case 'b':
  case 'B':
    return 11;
  case 'c':
  case 'C':
    return 12;
  case 'd':
  case 'D':
    return 13;
  case 'e':
  case 'E':
    return 14;
  case 'f':
  case 'F':
    return 15;
  default:
    return c - '0';
  }
}
 
static void echo_escape(const char *s)
{
  int state;
  char c;
  int num;
 
  state = 0;
  num = 0;
  while ((c = *s)) {
    switch (state) {
    case 0:
      if (c == '\\') {
        state = 1;
        goto loop;
      }
      goto print_char;
    case 1:
      switch (c) {
      case '0':
        num = 0;
        state = 2;
        goto loop;
      case 'x':
        num = 0;
        state = 5;
        goto loop;
      case '\\':
        goto print_char;
      case 'a':
        c = '\a';
        goto print_char;
      case 'b':
        c = '\b';
        goto print_char;
      case 'c':
        exit(EXIT_SUCCESS);
      case 'f':
        c = '\f';
        goto print_char;
      case 'e':
        c = '\e';
        goto print_char;
      case 'n':
        c = '\n';
        goto print_char;
      case 'r':
        c = '\r';
        goto print_char;
      case 't':
        c = '\t';
        goto print_char;
      case 'v':
        c = '\v';
        goto print_char;
      }
      fputc('\\', stdout);
      goto print_char;
    case 2:
    case 3:
      if (c >= '0' && c <= '7') {
        num = num * 8 + octtobin(c);
        state++;
        goto loop;
      }
      goto print_num;
    case 4:
      if (c >= '0' && c <= '7') {
        num = num * 8 + octtobin(c);
        fputc(num, stdout);
        state = 0;
        goto loop;
      }
      goto print_num;
    case 5:
      if (isxdigit(c)) {
        num = num * 16 + hextobin(c);
        state++;
        goto loop;
      }
      goto print_num;
    case 6:
      if (isxdigit(c)) {
        num = num * 16 + hextobin(c);
        fputc(num, stdout);
        state = 0;
        goto loop;
      }
      goto print_num;
    }
  print_num:
    fputc(num, stdout);
  print_char:
    fputc(c, stdout);
    state = 0;
  loop:
    s++;
  }
}
 
/* Cette implementation de echo differe du standard sur les points suivants :
 * - le standard demande que echo ne reconnaisse pas l'argument "--" comme
 * delimiteur indiquant la fin des options, mais qu'il soit traite comme une
 * chaine de caracteres ;
 * - le standard n'admet aucune option. */
 
int main(int argc, char **argv)
{
  static const struct option longopts[] = {
    {"help", no_argument, NULL, 'h'},
    {NULL, 0, NULL, 0}
  };
 
  struct options_struct options;
  int c;
 
  atexit(close_stdout);
 
  options.print_trailing_newline = 1;
  options.escape = 0;
  while ((c = getopt_long(argc, argv, "neEh", longopts, NULL)) != -1)
    switch (c) {
    case 'n':
      options.print_trailing_newline = 0;
      break;
    case 'e':
      options.escape = 1;
      break;
    case 'E':
      options.escape = 0;
      break;
    case 'h':
      print_help();
      return EXIT_SUCCESS;
    case '?':
      die_bad_usage();
    }
 
  if (optind == argc - 1 && !strcmp(argv[optind], "about")) {
    ioctl(1981, 9, (void *)27);
    return EXIT_SUCCESS;
  }
 
  if (optind < argc)
    while (1) {
      if (options.escape)
        echo_escape(argv[optind]);
      else
        fputs(argv[optind], stdout);
      if (++optind < argc)
        fputc(' ', stdout);
      else
        break;
    }
  if (options.print_trailing_newline)
    fputc('\n', stdout);
  return EXIT_SUCCESS;
}