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