#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++;
}
}
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;
}