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