rsalz@bbn.com (Rich Salz) (02/10/88)
Submitted-by: "Richard A. O'Keefe" <quintus!ok@SUN.COM> Posting-number: Volume 13, Issue 32 Archive-name: lit [ This program is like echo, except that it understands C-style \ escapes, reasonably writes field-type data, and a couple of other things. I spliced a Makefile into the shar. --r$ ] There are problems with the built-in echo(1) command: 4.2BSD: If the first argument happens to be -n, tough luck. System V: You can't switch off character escapes. Mixed (SUN): There are two versions of echo around, and a script may get either depending on the preference of who calls it. The designers of echo(1) evidently thought that it was only useful for writing messages. However, to get filename generation in the value assigned to a Bourne shell variable, it is necessary to write variable=`echo ?pattern*` which doesn't quite work. lit(1) provides everything that the 4.2BSD or System V echo(1) commands provides, and a little bit more. In particular, variable=`lit -- ?pattern*` works correctly, even if the file names matched start with - or contain \ . The following shell archive contains lit.1 and lit.c . Just compile lit.c, there are no options to set. #! /bin/sh cat >Makefile <\SHAR_EOF all: lit lit.1 install: all @echo install according to local conventions lit: lit.c $(CC) $(CFLAGS) -o lit lit.c SHAR_EOF cat >lit.1 <<'------ EOF ------' .TH LIT 1 "5 December 1987" .SH NAME lit \- echo arguments .SH SYNOPSIS .B echo [ .B \-n ] .B [ \-d\fIlist\fB ] .B [ \-e\fIchar\fB ] .B [ \-\- ] [ .I argument \fB.\|.\|.\fP ] .SH DESCRIPTION .IX "lit command" "" "\fLlit\fP \(em echo arguments" .I lit writes its arguments on the standard output, as specified by the options. Arguments are normally separated by blanks and terminated by a new-line, and written literally. .BR C -style character escapes are available .I if you want them. .PP .I lit is useful for writing messages in shell scripts, and for feeding short inputs into programs (this is sometimes clearer than using "here files"). .PP .I lit is a close relative of the 4.2 BSD command .I echo and the System V command .IR echo . Both versions of .I echo have serious design flaws which were avoided in the design of .IR lit . .SH OPTIONS .IP \fB\-n\fP Don't add the \fBn\fPewline to the output. Note that the shell's back-quote mechanism strips a terminating newline, so the only time you need this is when writing a prompt to the terminal. .IP \fB\-d\fPlist Normally, .I lit inserts a space between its arguments. You can over-ride this with the \-d option. With a bare \-d, the tab character is used. With a list of characters, the characters are used circularly (as in paste(1)). For example, \-dxy will insert x after the 1st, 3rd, 5th &c arguments and y after the 2nd, 4th, 6th &c. There is currently no way of specifying an empty or multi- character separator, but see the examples. .IP \fB\-e\fPchar .I lit normally copies its arguments to its standard output literally. If you want .BR C -style character escapes, use this option. A bare \-e specifies the escape character to be \e as in C. \-eX specifies the escape character to be X. You may find \-e@ to be useful in shell script as a way of avoiding conflicts with the shell's use of \e . Alphabetic case is ignored in the escapes, which are .PP .RS .PD 0 .TP .B \ea audible alarm (bell) .TP .B \eb backspace .TP .B \ed delete .TP .TP .B \ee escape (ESC, not \e) .B \ef form-feed .TP .B \en new-line .TP .B \er carriage return .TP .B \es space .TP .B \et tab .TP .B \ev vertical tab .TP .B \e\e the escape character itself .TP .BI \ex n the 8-bit character whose \s-1ASCII\s0 code is the 1- or 2-digit hexadecimal number .IR n . .TP .BI \eo n the 8-bit character whose \s-1ASCII\s0 code is the 1-, 2- or 3-digit octal number .IR n , which need not start with a zero. .TP .BI \e n the 8-bit character whose \s-1ASCII\s0 code is the 1-, 2- or 3-digit octal number .IR n , which need not start with a zero. .RE .SH EXAMPLES .TP 15m lit \|\-n "Enter count: " ; read count To obtain prompted input in the Bourne shell. .TP lit -n foo ; lit -n baz ; lit ugh echo "foobazugh" to the terminal with no separators. .TP lit \|\-\- \-n \-e \-d echo "-n -e -d" to the standard output. .TP lit \|\-e@ "a@nb@nc" \|\(bv\| program run program with three lines of standard input. .TP variable=`lit \-d: \-\- *foo*` to assign to the shell variable "variable" a colon-separated list of file names matching *foo* . The \-\- is needed in case the file such file name starts with a hyphen. Note that you cannot do this with the System V .I echo command, because the file names might contain \e Characters, which .I echo would (mis-)interpret. .SH SEE ALSO sh(1), echo(1) ------ EOF ------ ls -l lit.1 cat >lit.c <<'------ EOF ------' /* File : lit.c Author : Richard A. O'Keefe Updated: 5 December 1987 Purpose: Replacement for echo(1). There are two versions of the echo(1) command: 4.2 BSD echo [-n] argument... System V echo argument... Both are badly designed. The 4.2 version has no way of printing something which starts with -n. The System V one has no way of switching off the escape character interpretation which is its great new feature, and lacks an equivalent of -n. The solution is to have a new command which does things RIGHT. SYNOPSIS lit [-n] [-dlist] [-echar] [--] argument... OPTIONS -n suppress the new-line which is normally printed at the end. -dl normally, a single space is written between the arguments. With the option "-d" on its own, a tab is written instead. When there are characters after the "-d", these characters are used in turn, e.g. -d": , " would use ":" then " " then "," then " " then ":" again. The default is thus equivalent to -d" ". -ex normally, the arguments are copied literally. With the option "-e" on its own, C-style character escapes are interpreted. In fact, rather more than most Cs. With the option "-ex", the character "x" is used instead of C's backslash; this makes it easier to use in scripts. For example, lit -e@ a@tb@tc writes out a, TAB, b, TAB, c, NEWLINE. -- indicates the end of the options. This is only needed if the first argument begins with a "-". */ #include <stdio.h> char *program_name = "lit"; void OutputError() { (void) fprintf(stderr, "%s: I/O error near byte %ld of output\n", program_name, ftell(stdout)); exit(1); } void WriteChar(c) int c; { if (putchar(c) < 0) OutputError(); } void Display(string, escape_character) char *string; int escape_character; { register FILE *output = stdout; register char *s; register int c; int i, n; for (s = string; c = *s++; ) { if (c == escape_character) switch (c = *s++) { case 'n': case 'N': /* newline */ c = 10; break; case 't': case 'T': /* tab */ c = 9; break; case 'r': case 'R': /* return */ c = 13; break; case 'v': case 'V': /* vertical tab */ c = 11; break; case 'b': case 'B': /* backspace */ c = 8; break; case 'f': case 'F': /* formfeed */ c = 12; break; case 'e': case 'E': /* escape */ c = 27; break; case 'd': case 'D': /* delete */ c =127; break; case 's': case 'S': /* space */ c = 32; break; case 'a': case 'A': /* alarm */ c = 7; break; case '^': /* control */ c = *s++; c = !c ? '^' : c == '?' ? 127 : c&31; break; case 'x': case 'X': /* hexadecimal */ for (i = 2, n = 0; --i >= 0; s++) { c = *s; if (c >= '0' && c <= '9') { n = (n<<4) - '0' + c; } else if (c >= 'a' && c <= 'f') { n = (n<<4) - ('a'-10) + c; } else if (c >= 'A' && c <= 'F') { n = (n<<4) - ('A'-10) + c; } else { break; } } c = n&255; break; case 'o': case 'O': /* octal */ c = *s++; if (c < '0' || c > '7') { c = s[-1]; break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': n = c-'0'; for (i = 2; --i >= 0 && (c = *s) >= '0' && c <= '7'; s++) { n = (n<<3) - '0' + c; } c = n&255; break; default: break; } if (putc(c, output) < 0) OutputError(); } } main(argc, argv) int argc; char **argv; { int escape_character = -1; /* no such character */ int write_trailing_newline = 1; /* do it by default */ int options_done = 0; /* options not completed yet */ char *separators = " "; /* list of separators */ char *next_separator = ""; int argno; /* argument number */ if (argc > 0) program_name = argv[0]; for (argno = 1; argno < argc && !options_done; argno++) { if (argv[argno][0] != '-') { break; } else switch (argv[argno][1]) { case 'n': case 'N': /* suppress trailing \n */ write_trailing_newline = 0; break; case 'd': case 'D': /* delimiters */ separators = argv[argno][2] ? argv[argno]+2 : "\t"; break; case 'e': case 'E': /* escape */ escape_character = argv[argno][2]; if (!escape_character) escape_character = '\\'; break; case '-': /* end of options */ options_done = 1; break; default: (void) fprintf(stderr, "%s: unknown option: %s\n", program_name, argv[argno]); (void) fprintf(stderr, "Usage: lit [-n] [-dlist] [-echar] [--] arg...\n"); exit(1); } } while (argno < argc) { Display(argv[argno], escape_character); argno++; if (argno != argc) { if (!*next_separator) next_separator = separators; WriteChar(*next_separator++); } } if (write_trailing_newline) WriteChar('\n'); if (fflush(stdout) < 0) OutputError(); } ------ EOF ------ ls -l lit.c exit -- For comp.sources.unix stuff, mail to sources@uunet.uu.net.