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
17 /* Command line argument list management */
18 #include "ctype_.h"
19 #include "stdio_.h"
20 #include "string_.h"
21 #include "gsexit.h"
22 #include "gsmemory.h"
23 #include "gsargs.h"
24 #include "gserrors.h"
25
26 /* Initialize an arg list. */
27 void
arg_init(arg_list * pal,const char ** argv,int argc,FILE * (* arg_fopen)(const char * fname,void * fopen_data),void * fopen_data)28 arg_init(arg_list * pal, const char **argv, int argc,
29 FILE * (*arg_fopen) (const char *fname, void *fopen_data),
30 void *fopen_data)
31 {
32 pal->expand_ats = true;
33 pal->arg_fopen = arg_fopen;
34 pal->fopen_data = fopen_data;
35 pal->argp = argv + 1;
36 pal->argn = argc - 1;
37 pal->depth = 0;
38 }
39
40 /* Push a string onto an arg list. */
41 int
arg_push_memory_string(arg_list * pal,char * str,bool parsed,gs_memory_t * mem)42 arg_push_memory_string(arg_list * pal, char *str, bool parsed, gs_memory_t * mem)
43 {
44 arg_source *pas;
45
46 if (pal->depth == arg_depth_max) {
47 lprintf("Too much nesting of @-files.\n");
48 return 1;
49 }
50 pas = &pal->sources[pal->depth];
51 pas->is_file = false;
52 pas->u.s.parsed = parsed;
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 if (pas->u.s.parsed)
99 /* this string is a "pushed-back" argument */
100 /* (retrieved by a precedeing arg_next(), but not processed) */
101 if (strlen(pas->u.s.str) >= arg_str_max) {
102 errprintf(pas->u.s.memory, "Command too long: %s\n", pas->u.s.str);
103 *code = gs_error_Fatal;
104 return NULL;
105 } else {
106 strcpy(pal->cstr, pas->u.s.str);
107 result = pal->cstr;
108 if (pas->u.s.memory)
109 gs_free_object(pas->u.s.memory, pas->u.s.chars, "arg_next");
110 pal->depth--;
111 pas--;
112 goto at;
113 }
114 else
115 astr = pas->u.s.str, f = NULL, endc = 0;
116 result = cstr = pal->cstr;
117 #define cfsgetc() (f == NULL ? (int)(unsigned char)(*astr ? *astr++ : 0) : fgetc(f))
118 #define is_eol(c) (c == '\r' || c == '\n')
119 i = 0;
120 in_quote = false;
121 eol = true;
122 c = cfsgetc();
123 for (i = 0;;) {
124 if (c == endc) {
125 if (in_quote) {
126 cstr[i] = 0;
127 errprintf(pas->u.s.memory,
128 "Unterminated quote in @-file: %s\n", cstr);
129 *code = gs_error_Fatal;
130 return NULL;
131 }
132 if (i == 0) {
133 /* EOF before any argument characters. */
134 if (f != NULL)
135 fclose(f);
136 else if (pas->u.s.memory)
137 gs_free_object(pas->u.s.memory, pas->u.s.chars,
138 "arg_next");
139 pal->depth--;
140 goto top;
141 }
142 break;
143 }
144 /* c != endc */
145 if (isspace(c)) {
146 if (i == 0) {
147 c = cfsgetc();
148 continue;
149 }
150 if (!in_quote)
151 break;
152 }
153 /* c isn't leading or terminating whitespace. */
154 if (c == '#' && eol) {
155 /* Skip a comment. */
156 do {
157 c = cfsgetc();
158 } while (!(c == endc || is_eol(c)));
159 if (c == '\r')
160 c = cfsgetc();
161 if (c == '\n')
162 c = cfsgetc();
163 continue;
164 }
165 if (c == '\\') {
166 /* Check for \ followed by newline. */
167 c = cfsgetc();
168 if (is_eol(c)) {
169 if (c == '\r')
170 c = cfsgetc();
171 if (c == '\n')
172 c = cfsgetc();
173 eol = true;
174 continue;
175 }
176 /* \ anywhere else is treated as a printing character. */
177 /* This is different from the Unix shells. */
178 if (i == arg_str_max - 1) {
179 cstr[i] = 0;
180 errprintf(pas->u.s.memory, "Command too long: %s\n", cstr);
181 *code = gs_error_Fatal;
182 return NULL;
183 }
184 cstr[i++] = '\\';
185 eol = false;
186 continue;
187 }
188 /* c will become part of the argument */
189 if (i == arg_str_max - 1) {
190 cstr[i] = 0;
191 errprintf(pas->u.s.memory, "Command too long: %s\n", cstr);
192 *code = gs_error_Fatal;
193 return NULL;
194 }
195 /* Allow quotes to protect whitespace. */
196 /* (special cases have already been handled and don't reach this point) */
197 if (c == '"')
198 in_quote = !in_quote;
199 else
200 cstr[i++] = c;
201 eol = is_eol(c);
202 c = cfsgetc();
203 }
204 cstr[i] = 0;
205 if (f == NULL)
206 pas->u.s.str = astr;
207 at:if (pal->expand_ats && result[0] == '@') {
208 if (pal->depth == arg_depth_max) {
209 errprintf(pas->u.s.memory, "Too much nesting of @-files.\n");
210 *code = gs_error_Fatal;
211 return NULL;
212 }
213 result++; /* skip @ */
214 f = (*pal->arg_fopen) (result, pal->fopen_data);
215 if (f == NULL) {
216 errprintf(pas->u.s.memory, "Unable to open command line file %s\n", result);
217 *code = gs_error_Fatal;
218 return NULL;
219 }
220 pal->depth++;
221 pas++;
222 pas->is_file = true;
223 pas->u.file = f;
224 goto top;
225 }
226 return result;
227 }
228
229 /* Copy an argument string to the heap. */
230 char *
arg_copy(const char * str,gs_memory_t * mem)231 arg_copy(const char *str, gs_memory_t * mem)
232 {
233 char *sstr = (char *)gs_alloc_bytes(mem, strlen(str) + 1, "arg_copy");
234
235 if (sstr == 0) {
236 lprintf("Out of memory!\n");
237 return NULL;
238 }
239 strcpy(sstr, str);
240 return sstr;
241 }
242
243 /* Free a previously arg_copy'd string */
244 void
arg_free(char * str,gs_memory_t * mem)245 arg_free(char *str, gs_memory_t * mem)
246 {
247 gs_free_object(mem, str, "arg_copy");
248 }
249