xref: /plan9/sys/src/cmd/gs/src/gsargs.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1997, 1998, 1999 Aladdin Enterprises.  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: gsargs.c,v 1.9 2004/08/04 23:33:29 stefan Exp $ */
18 /* Command line argument list management */
19 #include "ctype_.h"
20 #include "stdio_.h"
21 #include "string_.h"
22 #include "gsexit.h"
23 #include "gsmemory.h"
24 #include "gsargs.h"
25 #include "gserrors.h"
26 
27 /* Initialize an arg list. */
28 void
arg_init(arg_list * pal,const char ** argv,int argc,FILE * (* arg_fopen)(const char * fname,void * fopen_data),void * fopen_data)29 arg_init(arg_list * pal, const char **argv, int argc,
30 	 FILE * (*arg_fopen) (const char *fname, void *fopen_data),
31 	 void *fopen_data)
32 {
33     pal->expand_ats = true;
34     pal->arg_fopen = arg_fopen;
35     pal->fopen_data = fopen_data;
36     pal->argp = argv + 1;
37     pal->argn = argc - 1;
38     pal->depth = 0;
39 }
40 
41 /* Push a string onto an arg list. */
42 int
arg_push_memory_string(arg_list * pal,char * str,gs_memory_t * mem)43 arg_push_memory_string(arg_list * pal, char *str, gs_memory_t * mem)
44 {
45     arg_source *pas;
46 
47     if (pal->depth == arg_depth_max) {
48 	lprintf("Too much nesting of @-files.\n");
49 	return 1;
50     }
51     pas = &pal->sources[pal->depth];
52     pas->is_file = false;
53     pas->u.s.chars = str;
54     pas->u.s.memory = mem;
55     pas->u.s.str = str;
56     pal->depth++;
57     return 0;
58 }
59 
60 /* Clean up an arg list. */
61 void
arg_finit(arg_list * pal)62 arg_finit(arg_list * pal)
63 {
64     while (pal->depth) {
65 	arg_source *pas = &pal->sources[--(pal->depth)];
66 
67 	if (pas->is_file)
68 	    fclose(pas->u.file);
69 	else if (pas->u.s.memory)
70 	    gs_free_object(pas->u.s.memory, pas->u.s.chars, "arg_finit");
71     }
72 }
73 
74 /* Get the next arg from a list. */
75 /* Note that these are not copied to the heap. */
76 const char *
arg_next(arg_list * pal,int * code)77 arg_next(arg_list * pal, int *code)
78 {
79     arg_source *pas;
80     FILE *f;
81     const char *astr = 0;	/* initialized only to pacify gcc */
82     char *cstr;
83     const char *result;
84     int endc;
85     int c, i;
86     bool in_quote, eol;
87 
88   top:pas = &pal->sources[pal->depth - 1];
89     if (pal->depth == 0) {
90 	if (pal->argn <= 0)	/* all done */
91 	    return 0;
92 	pal->argn--;
93 	result = *(pal->argp++);
94 	goto at;
95     }
96     if (pas->is_file)
97 	f = pas->u.file, endc = EOF;
98     else
99 	astr = pas->u.s.str, f = NULL, endc = 0;
100     result = cstr = pal->cstr;
101 #define cfsgetc() (f == NULL ? (*astr ? *astr++ : 0) : fgetc(f))
102 #define is_eol(c) (c == '\r' || c == '\n')
103     i = 0;
104     in_quote = false;
105     eol = true;
106     c = cfsgetc();
107     for (i = 0;;) {
108 	if (c == endc) {
109 	    if (in_quote) {
110 		cstr[i] = 0;
111 		errprintf("Unterminated quote in @-file: %s\n", cstr);
112 		*code = gs_error_Fatal;
113 		return NULL;
114 	    }
115 	    if (i == 0) {
116 		/* EOF before any argument characters. */
117 		if (f != NULL)
118 		    fclose(f);
119 		else if (pas->u.s.memory)
120 		    gs_free_object(pas->u.s.memory, pas->u.s.chars,
121 				   "arg_next");
122 		pal->depth--;
123 		goto top;
124 	    }
125 	    break;
126 	}
127 	/* c != endc */
128 	if (isspace(c)) {
129 	    if (i == 0) {
130 		c = cfsgetc();
131 		continue;
132 	    }
133 	    if (!in_quote)
134 		break;
135 	}
136 	/* c isn't leading or terminating whitespace. */
137 	if (c == '#' && eol) {
138 	    /* Skip a comment. */
139 	    do {
140 		c = cfsgetc();
141 	    } while (!(c == endc || is_eol(c)));
142 	    if (c == '\r')
143 		c = cfsgetc();
144 	    if (c == '\n')
145 		c = cfsgetc();
146 	    continue;
147 	}
148 	if (c == '\\') {
149 	    /* Check for \ followed by newline. */
150 	    c = cfsgetc();
151 	    if (is_eol(c)) {
152 		if (c == '\r')
153 		    c = cfsgetc();
154 		if (c == '\n')
155 		    c = cfsgetc();
156 		eol = true;
157 		continue;
158 	    }
159 	    /* \ anywhere else is treated as a printing character. */
160 	    /* This is different from the Unix shells. */
161 	    if (i == arg_str_max - 1) {
162 		cstr[i] = 0;
163 		errprintf("Command too long: %s\n", cstr);
164 		*code = gs_error_Fatal;
165 		return NULL;
166 	    }
167 	    cstr[i++] = '\\';
168 	    eol = false;
169 	    continue;
170 	}
171 	/* c will become part of the argument */
172 	if (i == arg_str_max - 1) {
173 	    cstr[i] = 0;
174 	    errprintf("Command too long: %s\n", cstr);
175 	    *code = gs_error_Fatal;
176 	    return NULL;
177 	}
178 	/* If input is coming from an @-file, allow quotes */
179 	/* to protect whitespace. */
180 	if (c == '"' && f != NULL)
181 	    in_quote = !in_quote;
182 	else
183 	    cstr[i++] = c;
184 	eol = is_eol(c);
185 	c = cfsgetc();
186     }
187     cstr[i] = 0;
188     if (f == NULL)
189 	pas->u.s.str = astr;
190   at:if (pal->expand_ats && result[0] == '@') {
191 	if (pal->depth == arg_depth_max) {
192 	    lprintf("Too much nesting of @-files.\n");
193 	    *code = gs_error_Fatal;
194 	    return NULL;
195 	}
196 	result++;		/* skip @ */
197 	f = (*pal->arg_fopen) (result, pal->fopen_data);
198 	if (f == NULL) {
199 	    errprintf("Unable to open command line file %s\n", result);
200 	    *code = gs_error_Fatal;
201 	    return NULL;
202 	}
203 	pal->depth++;
204 	pas++;
205 	pas->is_file = true;
206 	pas->u.file = f;
207 	goto top;
208     }
209     return result;
210 }
211 
212 /* Copy an argument string to the heap. */
213 char *
arg_copy(const char * str,gs_memory_t * mem)214 arg_copy(const char *str, gs_memory_t * mem)
215 {
216     char *sstr = (char *)gs_alloc_bytes(mem, strlen(str) + 1, "arg_copy");
217 
218     if (sstr == 0) {
219 	lprintf("Out of memory!\n");
220 	return NULL;
221     }
222     strcpy(sstr, str);
223     return sstr;
224 }
225