home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume13 / lit < prev    next >
Text File  |  1988-02-01  |  10KB  |  393 lines

  1. Subject:  v13i032:  Lit, a "better" echo
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: "Richard A. O'Keefe" <quintus!ok@SUN.COM>
  7. Posting-number: Volume 13, Issue 32
  8. Archive-name: lit
  9.  
  10. [  This program is like echo, except that it understands C-style \
  11.    escapes, reasonably writes field-type data, and a couple of
  12.    other things.  I spliced a Makefile into the shar.  --r$  ]
  13.  
  14. There are problems with the built-in echo(1) command:
  15. 4.2BSD:        If the first argument happens to be -n, tough luck.
  16. System V:    You can't switch off character escapes.
  17. Mixed (SUN):    There are two versions of echo around, and a
  18.         script may get either depending on the preference
  19.         of who calls it.
  20. The designers of echo(1) evidently thought that it was only useful
  21. for writing messages.  However, to get filename generation in the
  22. value assigned to a Bourne shell variable, it is necessary to write
  23.     variable=`echo ?pattern*`
  24. which doesn't quite work.  lit(1) provides everything that the
  25. 4.2BSD or System V echo(1) commands provides, and a little bit more.
  26. In particular,
  27.     variable=`lit -- ?pattern*`
  28. works correctly, even if the file names matched start with - or
  29. contain \ .
  30.  
  31. The following shell archive contains lit.1 and lit.c .
  32.  
  33. Just compile lit.c, there are no options to set.
  34.  
  35. #! /bin/sh
  36. cat >Makefile <<\SHAR_EOF
  37. all:        lit lit.1
  38. install:    all
  39.     @echo install according to local conventions
  40. lit:        lit.c
  41.     $(CC) $(CFLAGS) -o lit lit.c
  42. SHAR_EOF
  43. cat >lit.1 <<'------ EOF ------'
  44. .TH LIT 1 "5 December 1987"
  45. .SH NAME
  46. lit \- echo arguments
  47. .SH SYNOPSIS
  48. .B echo
  49. [
  50. .B \-n
  51. ]
  52. .B
  53. [
  54. \-d\fIlist\fB
  55. ]
  56. .B
  57. [
  58. \-e\fIchar\fB
  59. ]
  60. .B
  61. [
  62. \-\-
  63. ]
  64. .I argument \fB.\|.\|.\fP 
  65. ]
  66. .SH DESCRIPTION
  67. .IX "lit command"  ""  "\fLlit\fP \(em echo arguments"
  68. .I lit
  69. writes its arguments on the standard output, as specified by
  70. the options.  Arguments are normally separated by blanks and
  71. terminated by a new-line, and written literally.
  72. .BR C -style
  73. character escapes are available
  74. .I if
  75. you want them.
  76. .PP
  77. .I lit
  78. is useful for writing messages in shell scripts,
  79. and for feeding short inputs into programs (this is sometimes
  80. clearer than using "here files").
  81. .PP
  82. .I lit
  83. is a close relative of the 4.2 BSD command
  84. .I echo
  85. and the System V command
  86. .IR echo .
  87. Both versions of
  88. .I echo
  89. have serious design flaws which were avoided in the design of
  90. .IR lit .
  91. .SH OPTIONS
  92. .IP \fB\-n\fP
  93. Don't add the \fBn\fPewline to the output.
  94. Note that the shell's back-quote mechanism strips a terminating
  95. newline, so the only time you need this is when writing a prompt
  96. to the terminal.
  97. .IP \fB\-d\fPlist
  98. Normally,
  99. .I lit
  100. inserts a space between its arguments.  You can over-ride this
  101. with the \-d option.  With a bare \-d, the tab character is used.
  102. With a list of characters, the characters are used circularly
  103. (as in paste(1)).  For example, \-dxy will insert x after the
  104. 1st, 3rd, 5th &c arguments and y after the 2nd, 4th, 6th &c.
  105. There is currently no way of specifying an empty or multi-
  106. character separator, but see the examples.
  107. .IP \fB\-e\fPchar
  108. .I lit
  109. normally copies its arguments to its standard output literally.
  110. If you want
  111. .BR C -style
  112. character escapes, use this option.  A bare \-e specifies the
  113. escape character to be \e as in C.  \-eX specifies the
  114. escape character to be X.  You may find \-e@ to be useful in
  115. shell script as a way of avoiding conflicts with the shell's
  116. use of \e .  Alphabetic case is ignored in the escapes, which are
  117. .PP
  118. .RS
  119. .PD 0
  120. .TP
  121. .B \ea
  122. audible alarm (bell)
  123. .TP
  124. .B \eb
  125. backspace
  126. .TP
  127. .B \ed
  128. delete
  129. .TP
  130. .TP
  131. .B \ee
  132. escape (ESC, not \e)
  133. .B \ef
  134. form-feed
  135. .TP
  136. .B \en
  137. new-line
  138. .TP
  139. .B \er
  140. carriage return
  141. .TP
  142. .B \es
  143. space
  144. .TP
  145. .B \et
  146. tab
  147. .TP
  148. .B \ev
  149. vertical tab
  150. .TP
  151. .B \e\e
  152. the escape character itself
  153. .TP
  154. .BI \ex n
  155. the 8-bit character whose \s-1ASCII\s0 code is
  156. the 1- or 2-digit hexadecimal number
  157. .IR n .
  158. .TP
  159. .BI \eo n
  160. the 8-bit character whose \s-1ASCII\s0 code is
  161. the 1-, 2- or 3-digit octal number
  162. .IR n ,
  163. which need not start with a zero.
  164. .TP
  165. .BI \e n
  166. the 8-bit character whose \s-1ASCII\s0 code is
  167. the 1-, 2- or 3-digit octal number
  168. .IR n ,
  169. which need not start with a zero.
  170. .RE
  171. .SH EXAMPLES
  172. .TP 15m
  173. lit \|\-n "Enter count: " ; read count
  174. To obtain prompted input in the Bourne shell.
  175. .TP
  176. lit -n foo ; lit -n baz ; lit ugh
  177. echo "foobazugh" to the terminal with no separators.
  178. .TP
  179. lit \|\-\- \-n \-e \-d
  180. echo "-n -e -d" to the standard output.
  181. .TP
  182. lit \|\-e@ "a@nb@nc" \|\(bv\| program
  183. run program with three lines of standard input.
  184. .TP
  185. variable=`lit \-d: \-\- *foo*`
  186. to assign to the shell variable "variable" a colon-separated
  187. list of file names matching *foo* .  The \-\- is needed in
  188. case the file such file name starts with a hyphen.  Note that
  189. you cannot do this with the System V
  190. .I echo
  191. command, because the file names might contain \e Characters, which 
  192. .I echo
  193. would (mis-)interpret.
  194. .SH SEE ALSO
  195. sh(1), echo(1)
  196. ------ EOF ------
  197. ls -l lit.1
  198. cat >lit.c <<'------ EOF ------'
  199. /*  File   : lit.c
  200.     Author : Richard A. O'Keefe
  201.     Updated: 5 December 1987
  202.     Purpose: Replacement for echo(1).
  203.  
  204.     There are two versions of the echo(1) command:
  205.     4.2 BSD
  206.         echo [-n] argument...
  207.         System V
  208.         echo argument...
  209.     Both are badly designed.  The 4.2 version has no way of printing
  210.     something which starts with -n.  The System V one has no way of
  211.     switching off the escape character interpretation which is its
  212.     great new feature, and lacks an equivalent of -n.
  213.  
  214.     The solution is to have a new command which does things RIGHT.
  215.  
  216.     SYNOPSIS
  217.     lit [-n] [-dlist] [-echar] [--] argument...
  218.  
  219.     OPTIONS
  220.     -n    suppress the new-line which is normally printed at the end.
  221.  
  222.     -dl    normally, a single space is written between the arguments.
  223.     With the option "-d" on its own, a tab is written instead.
  224.     When there are characters after the "-d", these characters
  225.     are used in turn, e.g. -d": , " would use ":" then " " then
  226.     "," then " " then ":" again.  The default is thus equivalent
  227.     to -d" ".
  228.  
  229.     -ex    normally, the arguments are copied literally.
  230.     With the option "-e" on its own, C-style character escapes
  231.     are interpreted.  In fact, rather more than most Cs.
  232.     With the option "-ex", the character "x" is used instead of
  233.     C's backslash;  this makes it easier to use in scripts.
  234.     For example,
  235.         lit -e@ a@tb@tc
  236.     writes out a, TAB, b, TAB, c, NEWLINE.
  237.  
  238.     --    indicates the end of the options.  This is only needed if the
  239.     first argument begins with a "-".
  240. */
  241.  
  242. #include <stdio.h>
  243.  
  244. char *program_name = "lit";
  245.  
  246. void OutputError()
  247.     {
  248.     (void) fprintf(stderr,
  249.         "%s: I/O error near byte %ld of output\n",
  250.         program_name, ftell(stdout));
  251.     exit(1);
  252.     }
  253.  
  254.  
  255. void WriteChar(c)
  256.     int c;
  257.     {
  258.     if (putchar(c) < 0) OutputError();
  259.     }
  260.  
  261.  
  262. void Display(string, escape_character)
  263.     char *string;
  264.     int escape_character;
  265.     {
  266.     register FILE *output = stdout;
  267.     register char *s;
  268.     register int c;
  269.     int i, n;
  270.  
  271.     for (s = string; c = *s++; ) {
  272.         if (c == escape_character) switch (c = *s++) {
  273.         case 'n': case 'N':        /* newline */
  274.             c = 10; break;
  275.         case 't': case 'T':        /* tab */
  276.             c =  9; break;
  277.         case 'r': case 'R':        /* return */
  278.             c = 13; break;
  279.         case 'v': case 'V':        /* vertical tab */
  280.             c = 11; break;
  281.         case 'b': case 'B':        /* backspace */
  282.             c =  8; break;
  283.         case 'f': case 'F':        /* formfeed */
  284.             c = 12; break;
  285.         case 'e': case 'E':        /* escape */
  286.             c = 27; break;
  287.         case 'd': case 'D':        /* delete */
  288.             c =127; break;
  289.         case 's': case 'S':        /* space */
  290.             c = 32; break;
  291.         case 'a': case 'A':        /* alarm */
  292.             c =  7; break;
  293.         case '^':            /* control */
  294.             c = *s++;
  295.             c = !c ? '^' : c == '?' ? 127 : c&31;
  296.             break;
  297.         case 'x': case 'X':        /* hexadecimal */
  298.             for (i = 2, n = 0; --i >= 0; s++) {
  299.             c = *s;
  300.             if (c >= '0' && c <= '9') {
  301.                 n = (n<<4) - '0' + c;
  302.             } else
  303.             if (c >= 'a' && c <= 'f') {
  304.                 n = (n<<4) - ('a'-10) + c;
  305.             } else
  306.             if (c >= 'A' && c <= 'F') {
  307.                 n = (n<<4) - ('A'-10) + c;
  308.             } else {
  309.                 break;
  310.             }
  311.             }
  312.             c = n&255;
  313.             break;
  314.         case 'o': case 'O':        /* octal */
  315.             c = *s++;
  316.             if (c < '0' || c > '7') {
  317.             c = s[-1];
  318.             break;
  319.             }
  320.         case '0': case '1': case '2': case '3':
  321.         case '4': case '5': case '6': case '7':
  322.             n = c-'0';
  323.             for (i = 2; --i >= 0 && (c = *s) >= '0' && c <= '7'; s++) {
  324.             n = (n<<3) - '0' + c;
  325.             }
  326.             c = n&255;
  327.             break;
  328.         default:
  329.             break;
  330.         }
  331.         if (putc(c, output) < 0) OutputError();
  332.     }
  333.     }
  334.  
  335.  
  336. main(argc, argv)
  337.     int argc;
  338.     char **argv;
  339.     {
  340.     int escape_character = -1;    /* no such character */
  341.     int write_trailing_newline = 1;    /* do it by default */
  342.     int options_done = 0;        /* options not completed yet */
  343.     char *separators = " ";        /* list of separators */
  344.     char *next_separator = "";
  345.     int argno;            /* argument number */
  346.  
  347.     if (argc > 0) program_name = argv[0];
  348.  
  349.     for (argno = 1; argno < argc && !options_done; argno++) {
  350.         if (argv[argno][0] != '-') {
  351.         break;
  352.         } else
  353.         switch (argv[argno][1]) {
  354.         case 'n': case 'N':    /* suppress trailing \n */
  355.             write_trailing_newline = 0;
  356.             break;
  357.         case 'd': case 'D':    /* delimiters */
  358.             separators = argv[argno][2] ? argv[argno]+2 : "\t";
  359.             break;
  360.         case 'e': case 'E':    /* escape */
  361.             escape_character = argv[argno][2];
  362.             if (!escape_character) escape_character = '\\';
  363.             break;
  364.         case '-':        /* end of options */
  365.             options_done = 1;
  366.             break;
  367.         default:
  368.             (void) fprintf(stderr,
  369.             "%s: unknown option: %s\n",
  370.             program_name, argv[argno]);
  371.             (void) fprintf(stderr,
  372.             "Usage: lit [-n] [-dlist] [-echar] [--] arg...\n");
  373.             exit(1);
  374.         }
  375.     }
  376.     while (argno < argc) {
  377.         Display(argv[argno], escape_character);
  378.         argno++;
  379.         if (argno != argc) {
  380.         if (!*next_separator) next_separator = separators;
  381.         WriteChar(*next_separator++);
  382.         }
  383.     }
  384.     if (write_trailing_newline) WriteChar('\n');
  385.     if (fflush(stdout) < 0) OutputError();
  386.     }
  387.  
  388.  
  389. ------ EOF ------
  390. ls -l lit.c
  391.  
  392.