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