1 /* Copyright (C) 2001-2012 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied,
8    modified or distributed except as expressly authorized under the terms
9    of the license contained in the file LICENSE in this distribution.
10 
11    Refer to licensing information at http://www.artifex.com or contact
12    Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134, San Rafael,
13    CA  94903, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 /* 'echo'-like utility */
17 
18 #include "stdpre.h"
19 #include <stdio.h>
20 #include <stdlib.h>
21 
22 #if defined(__sun__) && !defined(const)
23 /* Apparently, there are archaic Sun platforms which don't include
24  * prototypes for fputc/fputs in stdio.h. The following prototype can
25  * cause type mismatches if const has been defined (usually with
26  * -Dconst=), so it's only included if const is undefined.
27  */
28 extern int fputc(int, FILE *), fputs(const char *, FILE *);
29 #endif
30 
31 /* Some systems have time_t in sys/types.h rather than time.h. */
32 #include <sys/types.h>
33 #include <ctype.h>
34 #include <string.h>
35 #include <time.h>		/* for ctime */
36 
37 /*
38  * This program exists solely to get around omissions, problems, and
39  * incompatibilities in various shells and utility environments.
40  * Don't count on it staying the same from one release to another!
41  */
42 
43 /*
44  * Usage:
45  echogs [-e .extn] [-(w|a)[b][-] file] [-h] [-n]
46  (-b|-B | -d|-D | -f|-F | -x hexstring | -(l|q|Q) string | -(l|q|Q)string |
47  -s | -u string | -i | -r file | -R file | -X)*
48  [-] string*
49  * Echoes string(s), or the binary equivalent of hexstring(s).
50  * If -w, writes to file; if -a, appends to file; if neither,
51  *   writes to stdout.  -wb and -ab open the file in binary mode.
52  *   -w and -a search forward for the next argument that is not a switch.
53  *   An appended - means the same as - alone, taking effect after the file
54  *   argument.
55  * -e specifies an extension to be added to the file name.
56  * If -h, write the output in hex instead of literally.
57  * If -n, does not append a newline to the output.
58  * -b or -B means insert the base part (minus directories) of the file name
59  *   passed as the argument of -w or -a.
60  * -d or -D means insert the date and time.
61  * -f or -F means insert the file name passed as the argument of -w or -a.
62  * -q means write the next string literally.
63  * -l or -Q means the same as -q followed by -s.
64  * -s means write a space.
65  * -u means convert the next string to upper case.
66  * -i means read from stdin, treating each line as an argument.
67  * -r means read from a named file in the same way.
68  * -R means copy a named file with no interpretation
69  *   (but convert to hex if -h is in effect).
70  * -X means treat any following literals as hex rather than string data.
71  * - or -+ alone means treat the rest of the line as literal data,
72  *   even if the first string begins with a -.
73  * -+<letter> is equivalent to -<Letter>, i.e., it upper-cases the letter.
74  * Inserts spaces automatically between the trailing strings,
75  * but nowhere else; in particular,
76  echogs -q a b
77  * writes 'ab', in contrast to
78  echogs -q a -s b
79  * which writes 'a b'.
80  */
81 
82 static int hputc(int, FILE *), hputs(const char *, FILE *);
83 
84 int
main(int argc,char * argv[])85 main(int argc, char *argv[])
86 {
87     FILE *out = stdout;
88     /*
89      * The initialization in = 0 is unnecessary: in is only referenced if
90      * interact = 1, in which case in has always been initialized.
91      * We initialize in = 0 solely to pacify stupid compilers.
92      */
93     FILE *in = 0;
94     const char *extn = "";
95     char fmode[4];
96 #define FNSIZE 4096
97     char *fnparam = NULL; /* Initialisation to shut up compilers */
98     char fname[FNSIZE];
99     int newline = 1;
100     int interact = 0;
101     int (*eputc)(int, FILE *) = fputc;
102     int (*eputs)(const char *, FILE *) = fputs;
103 #define LINESIZE 1000+FNSIZE
104 #define LINESIZESLOP LINESIZE + 10
105     char line[LINESIZESLOP];
106     char sw = 0, sp = 0, hexx = 0;
107     char **argp = argv + 1;
108     int nargs = argc - 1;
109 
110     if (nargs > 0 && !strcmp(*argp, "-e")) {
111         if (nargs < 2)
112             return 1;
113         extn = argp[1];
114         argp += 2, nargs -= 2;
115     }
116     if (nargs > 0 && (*argp)[0] == '-' &&
117         ((*argp)[1] == 'w' || (*argp)[1] == 'a')
118         ) {
119         size_t len = strlen(*argp);
120         int i;
121 
122         if (len > 4)
123             return 1;
124         for (i = 1; i < nargs; i++)
125             if (argp[i][0] != '-')
126                 break;
127         if (i == nargs)
128             return 1;
129         fnparam = argp[i];
130         strncpy(fmode, *argp + 1, 4);
131         if (strlen(fnparam) + strlen(extn) >= FNSIZE) {
132             fputs("File param size exceeded\n", stderr);
133             return 1;
134         }
135         strcpy(fname, fnparam);
136         strcat(fname, extn);
137         if (fmode[len - 2] == '-') {
138             /*
139              * The referents of argp are actually const, but they can't be
140              * declared that way, so we have to make a writable constant.
141              */
142             static char dash[2] = { '-', 0 };
143 
144             fmode[len - 2] = 0;
145             argp[i] = dash;
146             argp++, nargs--;
147         } else {
148             for (; i > 1; i--)
149                 argp[i] = argp[i - 1];
150             argp += 2, nargs -= 2;
151         }
152     } else
153         strcpy(fname, "");
154     if (nargs > 0 && !strcmp(*argp, "-h")) {
155         eputc = hputc, eputs = hputs;
156         argp++, nargs--;
157     }
158     if (nargs > 0 && !strcmp(*argp, "-n")) {
159         newline = 0;
160         argp++, nargs--;
161     }
162     if (strlen(fname) != 0) {
163         out = fopen(fname, fmode);
164         if (out == 0)
165             return 1;
166     }
167     while (1) {
168         char *arg;
169         int i;
170 
171         if (interact) {
172             if (fgets(line, LINESIZESLOP, in) == NULL) {
173                 interact = 0;
174                 if (in != stdin)
175                     fclose(in);
176                 continue;
177             }
178             /* Remove the terminating \n. */
179             if (strlen(line) > LINESIZE) {
180                 fputs("Line limit exceeded\n", stderr);
181                 return 1;
182             }
183             for (i = strlen(line) - 1; i < LINESIZESLOP; i++) {
184                 line[i] = 0;
185             }
186             arg = line;
187         } else {
188             if (nargs == 0)
189                 break;
190             arg = *argp;
191             argp++, nargs--;
192         }
193         if (sw == 0 && arg[0] == '-') {
194             char chr = arg[1];
195 
196             sp = 0;
197           swc:switch (chr) {
198                 case 'l':	/* literal string, then -s */
199                     chr = 'Q';
200                     /* falls through */
201                 case 'q':	/* literal string */
202                 case 'Q':	/* literal string, then -s */
203                     if (arg[2] != 0) {
204                         (*eputs) (arg + 2, out);
205                         if (chr == 'Q')
206                             (*eputc) (' ', out);
207                         break;
208                     }
209                     /* falls through */
210                 case 'r':	/* read from a file */
211                 case 'R':
212                 case 'u':	/* upper-case string */
213                 case 'x':	/* hex string */
214                     sw = chr;
215                     break;
216                 case 's':	/* write a space */
217                     (*eputc) (' ', out);
218                     break;
219                 case 'i':	/* read interactively */
220                     interact = 1;
221                     in = stdin;
222                     break;
223                 case 'b':	/* insert base file name */
224                 case 'B':
225                     arg = fnparam + strlen(fnparam);
226                     while (arg > fnparam &&
227                            (isalnum(arg[-1]) || arg[-1] == '_'))
228                         --arg;
229                     (*eputs) (arg, out);
230                     break;
231                 case 'd':	/* insert date/time */
232                 case 'D':
233                     {
234                         time_t t;
235                         char str[26];
236 
237                         time(&t);
238                         strcpy(str, ctime(&t));
239                         str[24] = 0;	/* remove \n */
240                         (*eputs) (str, out);
241                     } break;
242                 case 'f':	/* insert file name */
243                 case 'F':
244                     (*eputs) (fnparam, out);
245                     break;
246                 case 'X':	/* treat literals as hex */
247                     hexx = 1;
248                     break;
249                 case '+':	/* upper-case command */
250                     if (arg[1]) {
251                         ++arg;
252                         chr = toupper(arg[1]);
253                         goto swc;
254                     }
255                     /* falls through */
256                 case 0:		/* just '-' */
257                     sw = '-';
258                     break;
259             }
260         } else
261             switch (sw) {
262                 case 0:
263                 case '-':
264                     if (hexx)
265                         goto xx;
266                     if (sp)
267                         (*eputc) (' ', out);
268                     (*eputs) (arg, out);
269                     sp = 1;
270                     break;
271                 case 'q':
272                     sw = 0;
273                     (*eputs) (arg, out);
274                     break;
275                 case 'Q':
276                     sw = 0;
277                     (*eputs) (arg, out);
278                     (*eputc) (' ', out);
279                     break;
280                 case 'r':
281                     sw = 0;
282                     in = fopen(arg, "r");
283                     if (in == NULL)
284                         exit(exit_FAILED);
285                     interact = 1;
286                     break;
287                 case 'R':
288                     sw = 0;
289                     in = fopen(arg, "r");
290                     if (in == NULL)
291                         exit(exit_FAILED);
292                     while (fread(line, 1, 1, in) > 0)
293                         (*eputc) (line[0], out);
294                     fclose(in);
295                     break;
296                 case 'u':
297                     {
298                         char *up;
299 
300                         for (up = arg; *up; up++)
301                             (*eputc) (toupper(*up), out);
302                     }
303                     sw = 0;
304                     break;
305                 case 'x':
306                   xx:{
307                         char *xp;
308                         unsigned int xchr = 1;
309 
310                         for (xp = arg; *xp; xp++) {
311                             char ch = *xp;
312 
313                             if (!isxdigit(ch))
314                                 return 1;
315                             xchr <<= 4;
316                             xchr += (isdigit(ch) ? ch - '0' :
317                                      (isupper(ch) ? tolower(ch) : ch)
318                                      - 'a' + 10);
319                             if (xchr >= 0x100) {
320                                 (*eputc) (xchr & 0xff, out);
321                                 xchr = 1;
322                             }
323                         }
324                     }
325                     sw = 0;
326                     break;
327             }
328     }
329     if (newline)
330         (*eputc) ('\n', out);
331     if (out != stdout)
332         fclose(out);
333     return exit_OK;
334 }
335 
336 static int
hputc(int ch,FILE * out)337 hputc(int ch, FILE * out)
338 {
339     static const char *hex = "0123456789abcdef";
340 
341     /* In environments where char is signed, ch may be negative (!). */
342     putc(hex[(ch >> 4) & 0xf], out);
343     putc(hex[ch & 0xf], out);
344     return 0;
345 }
346 
347 static int
hputs(const char * str,FILE * out)348 hputs(const char *str, FILE * out)
349 {
350     while (*str)
351         hputc(*str++ & 0xff, out);
352     return 0;
353 }
354