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