home *** CD-ROM | disk | FTP | other *** search
- Subject: v13i032: Lit, a "better" echo
- Newsgroups: comp.sources.unix
- Sender: sources
- Approved: rsalz@uunet.UU.NET
-
- 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
-
-