1 /*-
2 * Copyright (c) 2001-2003 Peter Pentchev
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include "unquote.h"
34 #include "uq_err.h"
35
36 static char cvs_id[] __UNUS =
37 "$Ringlet: c/misc/unquote/unquote.c,v 1.13 2003/07/07 08:55:06 roam Exp $";
38
39 char uq_verstr[64] = "unquote";
40
41 /* max number of cmdline arguments allowed */
42 size_t uq_arg_max = UQ_ARG_MAX;
43 /* index of max field (arg) to quote */
44 size_t uq_argcnt;
45 /* a boolean array of flags for fields (args) to quote */
46 size_t *uq_arg;
47
48 /* '-0': if '-n' was specified, separate arguments with a NUL character */
49 static int zeroflag;
50 /* '-N': if '-n' was specified, print each argument on a separate line */
51 static int Nflag;
52 /* '-n': do not actually execute anything, just print the expanded version */
53 static int nflag;
54 /* '-p': do not search PATH, i.e. use execv() instead of execvp() */
55 static int pflag;
56 /* '-R': do just the opposite: *quote* the listed arguments */
57 static int Rflag;
58
59 static uq_err_t uq_add_args(char *);
60 static uq_err_t uq_add_arg(size_t);
61 static uq_err_t uq_unquote_arg(char **, const char *);
62 static uq_err_t uq_quote_arg(char **, const char *);
63 static uq_err_t uq_fromhex(char *, const char *, size_t);
64 static uq_err_t uq_tohex(char *, const char *, size_t);
65
66 static uq_err_t uq_init(int, char *[]);
67 static uq_err_t uq_doit(int, char *[]);
68
69 static void usage(void);
70 static uq_err_t version(void);
71
72 /*
73 * Function:
74 * uq_makeversion() - generate version string
75 * Inputs:
76 * none
77 * Returns:
78 * uq_err_t
79 * Modifies:
80 * uq_verstr
81 */
82
83 static uq_err_t
uq_makeversion(void)84 uq_makeversion(void) {
85
86 snprintf(uq_verstr, sizeof(uq_verstr), "unquote v%d.%d"
87 #if UQ_VER_PRE
88 "-pre%d"
89 #endif /* UQ_VER_PRE */
90 #if UQ_VER_PATCH
91 "p%d"
92 #endif /* UQ_VER_PATCH */
93 ,
94 UQ_VER_MAJ, UQ_VER_MIN
95 #if UQ_VER_PRE
96 , UQ_VER_PRE
97 #endif /* UQ_VER_PRE */
98 #if UQ_VER_PATCH
99 , UQ_VER_PATCH
100 #endif /* UQ_VER_PATCH */
101 );
102
103 return (UQ_ERR_NONE);
104 }
105
106 /*
107 * Function:
108 * uq_init - general-purpose init function
109 * Inputs:
110 * argc, argv - main() args for option processing
111 * Returns:
112 * uq_err_t
113 * CMDLINE
114 * Modifies:
115 * verbose, quiet
116 * may call uq_add_args()
117 */
118
119 static uq_err_t
uq_init(int argc,char * argv[])120 uq_init(int argc, char *argv[]) {
121 int ch; /* getopt() iterator over argv */
122 int helpq; /* boolean: '-h' on cmdline */
123 int versq; /* boolean: '-q' on cmdline */
124 char *envargs; /* UNQUOTE_ARGS env variable */
125 uq_err_t r; /* error code from called functions */
126
127 uq_makeversion();
128
129 /* Process environment variables */
130 if (envargs = getenv("UNQUOTE_ARGS"), envargs != NULL)
131 if (r = uq_add_args(envargs), r)
132 return (r);
133
134 /* Process cmdline options */
135
136 helpq = versq = 0;
137 opterr = 0;
138 while (ch = getopt(argc, argv, UQ_OPTSTR), ch != EOF)
139 switch (ch) {
140 case '0':
141 zeroflag = 1;
142 break;
143 case 'f':
144 if (r = uq_add_args(optarg), r)
145 return (r);
146 break;
147 case 'h':
148 helpq = 1;
149 break;
150 case 'N':
151 Nflag = 1;
152 break;
153 case 'n':
154 nflag = 1;
155 break;
156 case 'p':
157 pflag = 1;
158 break;
159 case 'q':
160 quiet++;
161 break;
162 case 'R':
163 Rflag = 1;
164 break;
165 case 'V':
166 versq = 1;
167 break;
168 case 'v':
169 verbose++;
170 break;
171 case '?':
172 default:
173 usage();
174 /* NOTREACHED */
175 }
176
177 argc -= optind; argv += optind;
178
179 if (versq)
180 version();
181 if (helpq)
182 usage();
183 if (versq || helpq)
184 exit(UQ_ERR_NONE);
185
186 /* sanity checks */
187 if (argc < 1)
188 usage();
189
190 if (zeroflag && Nflag)
191 usage();
192
193 return (UQ_ERR_NONE);
194 }
195
196 /*
197 * Function:
198 * usage - startup help info
199 * Inputs:
200 * none
201 * Returns:
202 * UQ_ERR_NONE
203 * Modifies:
204 * nothing, writes to stdout
205 */
206
207 static void
usage(void)208 usage(void) {
209 static const char *msg[] = {
210 "Usage: unquote [-f fields] [-0 | -N] [-hnpqRVv] fields command "
211 "[args ...]",
212 "\t-0\twith -n, separate arguments with ASCII NUL characters;",
213 "\t-f\tlist arguments to expand;",
214 "\t-h\tprint this help text and exit;",
215 "\t-N\twith -n, separate arguments with newlines;",
216 "\t-n\tdo not execute command, just print out the expanded form;",
217 "\t-p\tdo not search PATH for the command;",
218 "\t-q\tquiet operation; multiple -q's make it even more quiet;",
219 "\t-R\treverse operation: quote the specified arguments;"
220 "\t-V\tprint version information and exit;",
221 "\t-v\tverbose operation; multiple -v's increase verbosity level.",
222 NULL
223 };
224 unsigned i;
225
226 for (i = 0; msg[i] != NULL; i++)
227 fprintf(stderr, "%s\n", msg[i]);
228 exit(UQ_ERR_CMDLINE);
229 }
230
231 /*
232 * function:
233 * version - output version info
234 * Inputs:
235 * none
236 * Returns:
237 * UQ_ERR_NONE
238 * Modifies:
239 * nothing, writes to stdout
240 */
241
242 static uq_err_t
version(void)243 version(void) {
244
245 printf("%s\n", uq_verstr);
246
247 if (verbose) {
248 printf("%s\n", "Built on " __DATE__ ", " __TIME__);
249 #ifdef __GNUC__
250 if (verbose > 1)
251 printf("%s\n", "Compiler: GNU C " __VERSION__
252 " on " UQ_OS " " UQ_OSREL ": " UQ_OSHOST);
253 #endif /* __GNUC__ */
254 }
255
256 return (UQ_ERR_NONE);
257 }
258
259 /*
260 * Function:
261 * uq_doit - perform the actual work
262 * Inputs:
263 * argc, argv - main() args
264 * Returns:
265 * uq_err_t
266 * NOMEM - allocationg the new cmdline args
267 * EXEC - the final exec*() fails
268 * error code from uq_add_args() or uq_unquote_arg()
269 * Modifies:
270 * calls uq_add_args() and uq_unquote_arg()
271 * calls execv() or execvp() at the end and never returns on success
272 */
273
274 static uq_err_t
uq_doit(int argc,char * argv[])275 uq_doit(int argc, char *argv[]) {
276 size_t i; /* iterator over argv/argc */
277 char **nargv; /* argv array with expanded arguments */
278 char sepchar; /* '-n' args separator character */
279 uq_err_t (*quote_fun)(char **, const char *);
280 uq_err_t r; /* error code from called functions */
281
282 /* Get additional fields to quote from the first cmdline arg */
283 if (r = uq_add_args(argv[0]), r)
284 return (r);
285 argc--;
286 argv++;
287
288 if (!Rflag)
289 quote_fun = uq_unquote_arg;
290 else
291 quote_fun = uq_quote_arg;
292
293 /* Store and quote as needed */
294 if (nargv = calloc(argc+1, sizeof(*nargv)), nargv == NULL)
295 return (UQ_ERR_NOMEM);
296 for (i = 0; i < (size_t) argc; i++)
297 if ((i < uq_argcnt) && uq_arg[i]) {
298 if (r = quote_fun(nargv+i, argv[i]), r)
299 return (r);
300 } else {
301 nargv[i] = strdup(argv[i]);
302 }
303
304 if (nflag) {
305 sepchar = zeroflag? '\0': Nflag? '\n': ' ';
306 for (i = 0; i < (size_t) argc; i++) {
307 printf("%s", nargv[i]);
308 if (i < (size_t) argc - 1)
309 putchar(sepchar);
310 else if (!zeroflag)
311 putchar('\n');
312 }
313 return (UQ_ERR_NONE);
314 }
315
316 if (!pflag)
317 execvp(nargv[0], nargv);
318 else
319 execv(nargv[0], nargv);
320 return (UQ_ERR_EXEC);
321 }
322
323 /*
324 * Function:
325 * uq_add_args - parse a list of fields to quote
326 * Inputs:
327 * s - field list
328 * Returns:
329 * uq_err_t
330 * UQ_ERR_CMDLINE - bad format for the field list
331 * error code from uq_add_arg()
332 * Modifies:
333 * nothing by itself; calls uq_add_arg() for each field in the list
334 */
335
336 static uq_err_t
uq_add_args(char * s)337 uq_add_args(char *s) {
338 char *p; /* iterator over s */
339 long v; /* numeric value parsed from s */
340 size_t r; /* error code from called functions */
341
342 if (s == NULL)
343 return (UQ_ERR_NONE);
344
345 while (*s) {
346 v = strtol(s, &p, 10);
347 if ((s == p) || (v < 0))
348 return (UQ_ERR_CMDLINE);
349 if (r = uq_add_arg((size_t) v), r)
350 return (r);
351 s = p;
352 if (*s != '\0') {
353 if (*s == ':')
354 s++;
355 else
356 return (UQ_ERR_CMDLINE);
357 }
358 }
359
360 return (UQ_ERR_NONE);
361 }
362
363 /*
364 * Function:
365 * uq_add_arg() - store a single field to quote
366 * Inputs:
367 * v - field index
368 * Returns:
369 * uq_err_t
370 * CMDLINE - index too big
371 * NOMEM - reallocatiing uq_args[]
372 * Modifies:
373 * uq_args[]
374 * may reallocate (grow) uq_args[] and update uq_argcnt
375 */
376
377 static uq_err_t
uq_add_arg(size_t v)378 uq_add_arg(size_t v) {
379
380 if (v > uq_arg_max)
381 return (UQ_ERR_CMDLINE);
382
383 if (v >= uq_argcnt) {
384 size_t newcnt, *narg;
385
386 newcnt = (size_t) v+1;
387 narg = realloc(uq_arg, newcnt * sizeof(*uq_arg));
388 if (narg == NULL)
389 return (UQ_ERR_NOMEM);
390 memset(narg + uq_argcnt, 0,
391 (newcnt - uq_argcnt) * sizeof(*narg));
392 uq_arg = narg;
393 uq_argcnt = newcnt;
394 }
395
396 uq_arg[v] = 1;
397 return (UQ_ERR_NONE);
398 }
399
400 /*
401 * Function:
402 * uq_fromhex - expand a hex value into a string
403 * Inputs:
404 * dst - val/res destination string
405 * src - source (hex) string
406 * len - length of hex value
407 * Returns:
408 * uq_err_t
409 * BADCHAR - a non-hex-digit char in src
410 * Modifies:
411 * dst[]
412 */
413
414 static uq_err_t
uq_fromhex(char * dst,const char * src,size_t len)415 uq_fromhex(char *dst, const char *src, size_t len) {
416 size_t val; /* the numeric value of src */
417 size_t i; /* iterator over src */
418
419 val = 0;
420 for(i = 0; i < len; i++) {
421 if (!isxdigit(src[i]))
422 return (UQ_ERR_BADCHAR);
423 if (isdigit(src[i]))
424 val = (val * 16) + src[i] - '0';
425 else
426 val = (val * 16) + toupper(src[i]) - 'A' + 10;
427 }
428
429 *dst = val;
430 return (UQ_ERR_NONE);
431 }
432
433 /*
434 * Function:
435 * uq_tohex - encode a string into a hex value
436 * Inputs:
437 * dst - val/res destination hex value
438 * src - source string
439 * len - source string length
440 * Returns:
441 * uq_err_t
442 * nothing so far
443 * Modifies:
444 * dst[]
445 */
446
447 static uq_err_t
uq_tohex(char * dst,const char * src,size_t len)448 uq_tohex(char *dst, const char *src, size_t len) {
449 size_t i;
450
451 for (i = 0; i < len; i++)
452 sprintf(dst+2*i, "%02X", src[i]);
453 /* sprintf() null-terminates the result for us. */
454 return (UQ_ERR_NONE);
455 }
456
457 /*
458 * Function:
459 * uq_unquote_arg - unquote (expand) a field
460 * Inputs:
461 * pdst - val/res pointer to expanded string
462 * src - string to unquote
463 * Returns:
464 * uq_err_t
465 * NOMEM - allocating expanded string
466 * error code from uq_fromhex()
467 * Modifies:
468 * allocates and stores into *pdst
469 */
470
471 static uq_err_t
uq_unquote_arg(char ** pdst,const char * src)472 uq_unquote_arg(char **pdst, const char *src) {
473 char *dst; /* the real unquoted string */
474 const char *p; /* iterator over src */
475 char *q; /* iterator over dst */
476 size_t l; /* length of src */
477 uq_err_t r; /* error code from called functions */
478
479 l = strlen(src);
480 /* the (l%2) is there so that '3bf' is equiv to '03bf' */
481 if (dst = calloc(l + (l%2) + 1, 1), dst == NULL)
482 return (UQ_ERR_NOMEM);
483 p = src;
484 q = dst;
485 if (l%2) {
486 if (r = uq_fromhex(q++, p++, 1), r) {
487 free(dst);
488 return (r);
489 }
490 }
491 while (*p) {
492 if (r = uq_fromhex(q, p, 2), r) {
493 free(dst);
494 return (r);
495 }
496 q += 1;
497 p += 2;
498 }
499
500 *pdst = dst;
501 return (UQ_ERR_NONE);
502 }
503
504 /*
505 * Function:
506 * uq_quote_arg - quote (encode in hex) a field
507 * Inputs:
508 * pdst - val/res pointer to hex string
509 * src - string to quote
510 * Returns:
511 * uq_err_t
512 * NOMEM - allocating hex string
513 * error code from uq_tohex()
514 * Modifies:
515 * allocates and stores into *pdst
516 */
517
518 static uq_err_t
uq_quote_arg(char ** pdst,const char * src)519 uq_quote_arg(char **pdst, const char *src) {
520 char *dst; /* the real quoted string */
521 size_t l; /* length of src */
522 uq_err_t r; /* error code from called functions */
523
524 l = strlen(src);
525 if (dst = calloc(2*l + 1, 1), dst == NULL)
526 return (UQ_ERR_NOMEM);
527 if (r = uq_tohex(dst, src, l), r) {
528 free(dst);
529 return (r);
530 }
531
532 *pdst = dst;
533 return (UQ_ERR_NONE);
534 }
535
536 /*
537 * M A I N F U N C T I O N
538 */
539
540 int
uq_main(int argc,char * argv[])541 uq_main(int argc, char *argv[]) {
542 uq_err_t r; /* error code from called functions */
543
544 if (r = uq_init(argc, argv), r)
545 return (uq_prerror("init", r));
546
547 argc -= optind;
548 argv += optind;
549 if (r = uq_doit(argc, argv), r)
550 uq_prerror("doit", r);
551
552 return (r);
553 }
554