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