]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * tkArgv.c -- | |
3 | * | |
4 | * This file contains a procedure that handles table-based | |
5 | * argv-argc parsing. | |
6 | * | |
7 | * Copyright 1990 Regents of the University of California | |
8 | * Permission to use, copy, modify, and distribute this | |
9 | * software and its documentation for any purpose and without | |
10 | * fee is hereby granted, provided that the above copyright | |
11 | * notice appear in all copies. The University of California | |
12 | * makes no representations about the suitability of this | |
13 | * software for any purpose. It is provided "as is" without | |
14 | * express or implied warranty. | |
15 | */ | |
16 | ||
17 | #ifndef lint | |
18 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkArgv.c,v 1.12 92/08/07 08:39:48 ouster Exp $ SPRITE (Berkeley)"; | |
19 | #endif | |
20 | ||
21 | #include "tkconfig.h" | |
22 | #include "tk.h" | |
23 | ||
24 | /* | |
25 | * Default table of argument descriptors. These are normally available | |
26 | * in every application. | |
27 | */ | |
28 | ||
29 | static Tk_ArgvInfo defaultTable[] = { | |
30 | {"-help", TK_ARGV_HELP, (char *) NULL, (char *) NULL, | |
31 | "Print summary of command-line options and abort"}, | |
32 | {NULL, TK_ARGV_END, (char *) NULL, (char *) NULL, | |
33 | (char *) NULL} | |
34 | }; | |
35 | ||
36 | /* | |
37 | * Forward declarations for procedures defined in this file: | |
38 | */ | |
39 | ||
40 | static void PrintUsage _ANSI_ARGS_((Tcl_Interp *interp, | |
41 | Tk_ArgvInfo *argTable, int flags)); | |
42 | \f | |
43 | /* | |
44 | *---------------------------------------------------------------------- | |
45 | * | |
46 | * Tk_ParseArgv -- | |
47 | * | |
48 | * Process an argv array according to a table of expected | |
49 | * command-line options. See the manual page for more details. | |
50 | * | |
51 | * Results: | |
52 | * The return value is a standard Tcl return value. If an | |
53 | * error occurs then an error message is left in interp->result. | |
54 | * Under normal conditions, both *argcPtr and *argv are modified | |
55 | * to return the arguments that couldn't be processed here (they | |
56 | * didn't match the option table, or followed an TK_ARGV_REST | |
57 | * argument). | |
58 | * | |
59 | * Side effects: | |
60 | * Variables may be modified, resources may be entered for tkwin, | |
61 | * or procedures may be called. It all depends on the arguments | |
62 | * and their entries in argTable. See the user documentation | |
63 | * for details. | |
64 | * | |
65 | *---------------------------------------------------------------------- | |
66 | */ | |
67 | ||
68 | int | |
69 | Tk_ParseArgv(interp, tkwin, argcPtr, argv, argTable, flags) | |
70 | Tcl_Interp *interp; /* Place to store error message. */ | |
71 | Tk_Window tkwin; /* Window to use for setting Tk options. | |
72 | * NULL means ignore Tk option specs. */ | |
73 | int *argcPtr; /* Number of arguments in argv. Modified | |
74 | * to hold # args left in argv at end. */ | |
75 | char **argv; /* Array of arguments. Modified to hold | |
76 | * those that couldn't be processed here. */ | |
77 | Tk_ArgvInfo *argTable; /* Array of option descriptions */ | |
78 | int flags; /* Or'ed combination of various flag bits, | |
79 | * such as TK_ARGV_NO_DEFAULTS. */ | |
80 | { | |
81 | register Tk_ArgvInfo *infoPtr; | |
82 | /* Pointer to the current entry in the | |
83 | * table of argument descriptions. */ | |
84 | Tk_ArgvInfo *matchPtr; /* Descriptor that matches current argument. */ | |
85 | char *curArg; /* Current argument */ | |
86 | register char c; /* Second character of current arg (used for | |
87 | * quick check for matching; use 2nd char. | |
88 | * because first char. will almost always | |
89 | * be '-'). */ | |
90 | int srcIndex; /* Location from which to read next argument | |
91 | * from argv. */ | |
92 | int dstIndex; /* Index into argv to which next unused | |
93 | * argument should be copied (never greater | |
94 | * than srcIndex). */ | |
95 | int argc; /* # arguments in argv still to process. */ | |
96 | int length; /* Number of characters in current argument. */ | |
97 | int i; | |
98 | ||
99 | if (flags & TK_ARGV_DONT_SKIP_FIRST_ARG) { | |
100 | srcIndex = dstIndex = 0; | |
101 | argc = *argcPtr; | |
102 | } else { | |
103 | srcIndex = dstIndex = 1; | |
104 | argc = *argcPtr-1; | |
105 | } | |
106 | ||
107 | while (argc > 0) { | |
108 | curArg = argv[srcIndex]; | |
109 | srcIndex++; | |
110 | argc--; | |
111 | c = curArg[1]; | |
112 | length = strlen(curArg); | |
113 | ||
114 | /* | |
115 | * Loop throught the argument descriptors searching for one with | |
116 | * the matching key string. If found, leave a pointer to it in | |
117 | * matchPtr. | |
118 | */ | |
119 | ||
120 | matchPtr = NULL; | |
121 | for (i = 0; i < 2; i++) { | |
122 | if (i == 0) { | |
123 | infoPtr = argTable; | |
124 | } else { | |
125 | infoPtr = defaultTable; | |
126 | } | |
127 | for (; infoPtr->type != TK_ARGV_END; infoPtr++) { | |
128 | if (infoPtr->key == NULL) { | |
129 | continue; | |
130 | } | |
131 | if ((infoPtr->key[1] != c) | |
132 | || (strncmp(infoPtr->key, curArg, length) != 0)) { | |
133 | continue; | |
134 | } | |
135 | if ((tkwin == NULL) | |
136 | && ((infoPtr->type == TK_ARGV_CONST_OPTION) | |
137 | || (infoPtr->type == TK_ARGV_OPTION_VALUE) | |
138 | || (infoPtr->type == TK_ARGV_OPTION_NAME_VALUE))) { | |
139 | continue; | |
140 | } | |
141 | if (infoPtr->key[length] == 0) { | |
142 | matchPtr = infoPtr; | |
143 | goto gotMatch; | |
144 | } | |
145 | if (flags & TK_ARGV_NO_ABBREV) { | |
146 | continue; | |
147 | } | |
148 | if (matchPtr != NULL) { | |
149 | Tcl_AppendResult(interp, "ambiguous option \"", curArg, | |
150 | "\"", (char *) NULL); | |
151 | return TCL_ERROR; | |
152 | } | |
153 | matchPtr = infoPtr; | |
154 | } | |
155 | } | |
156 | if (matchPtr == NULL) { | |
157 | ||
158 | /* | |
159 | * Unrecognized argument. Just copy it down, unless the caller | |
160 | * prefers an error to be registered. | |
161 | */ | |
162 | ||
163 | if (flags & TK_ARGV_NO_LEFTOVERS) { | |
164 | Tcl_AppendResult(interp, "unrecognized argument \"", | |
165 | curArg, "\"", (char *) NULL); | |
166 | return TCL_ERROR; | |
167 | } | |
168 | argv[dstIndex] = curArg; | |
169 | dstIndex++; | |
170 | continue; | |
171 | } | |
172 | ||
173 | /* | |
174 | * Take the appropriate action based on the option type | |
175 | */ | |
176 | ||
177 | gotMatch: | |
178 | infoPtr = matchPtr; | |
179 | switch (infoPtr->type) { | |
180 | case TK_ARGV_CONSTANT: | |
181 | *((int *) infoPtr->dst) = (int) infoPtr->src; | |
182 | break; | |
183 | case TK_ARGV_INT: | |
184 | if (argc == 0) { | |
185 | goto missingArg; | |
186 | } else { | |
187 | char *endPtr; | |
188 | ||
189 | *((int *) infoPtr->dst) = | |
190 | strtol(argv[srcIndex], &endPtr, 0); | |
191 | if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) { | |
192 | Tcl_AppendResult(interp, "expected integer argument ", | |
193 | "for \"", infoPtr->key, "\" but got \"", | |
194 | argv[srcIndex], "\"", (char *) NULL); | |
195 | return TCL_ERROR; | |
196 | } | |
197 | srcIndex++; | |
198 | argc--; | |
199 | } | |
200 | break; | |
201 | case TK_ARGV_STRING: | |
202 | if (argc == 0) { | |
203 | goto missingArg; | |
204 | } else { | |
205 | *((char **)infoPtr->dst) = argv[srcIndex]; | |
206 | srcIndex++; | |
207 | argc--; | |
208 | } | |
209 | break; | |
210 | case TK_ARGV_UID: | |
211 | if (argc == 0) { | |
212 | goto missingArg; | |
213 | } else { | |
214 | *((Tk_Uid *)infoPtr->dst) = Tk_GetUid(argv[srcIndex]); | |
215 | srcIndex++; | |
216 | argc--; | |
217 | } | |
218 | break; | |
219 | case TK_ARGV_REST: | |
220 | *((int *) infoPtr->dst) = dstIndex; | |
221 | goto argsDone; | |
222 | case TK_ARGV_FLOAT: | |
223 | if (argc == 0) { | |
224 | goto missingArg; | |
225 | } else { | |
226 | char *endPtr; | |
227 | ||
228 | *((double *) infoPtr->dst) = | |
229 | strtod(argv[srcIndex], &endPtr); | |
230 | if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) { | |
231 | Tcl_AppendResult(interp, "expected floating-point ", | |
232 | "argument for \"", infoPtr->key, | |
233 | "\" but got \"", argv[srcIndex], "\"", | |
234 | (char *) NULL); | |
235 | return TCL_ERROR; | |
236 | } | |
237 | srcIndex++; | |
238 | argc--; | |
239 | } | |
240 | break; | |
241 | case TK_ARGV_FUNC: { | |
242 | int (*handlerProc)(); | |
243 | ||
244 | handlerProc = (int (*)())infoPtr->src; | |
245 | ||
246 | if ((*handlerProc)(infoPtr->dst, infoPtr->key, | |
247 | argv[srcIndex])) { | |
248 | srcIndex += 1; | |
249 | argc -= 1; | |
250 | } | |
251 | break; | |
252 | } | |
253 | case TK_ARGV_GENFUNC: { | |
254 | int (*handlerProc)(); | |
255 | ||
256 | handlerProc = (int (*)())infoPtr->src; | |
257 | ||
258 | argc = (*handlerProc)(infoPtr->dst, interp, infoPtr->key, | |
259 | argc, argv+srcIndex); | |
260 | if (argc < 0) { | |
261 | return TCL_ERROR; | |
262 | } | |
263 | break; | |
264 | } | |
265 | case TK_ARGV_HELP: | |
266 | PrintUsage (interp, argTable, flags); | |
267 | return TCL_ERROR; | |
268 | case TK_ARGV_CONST_OPTION: | |
269 | Tk_AddOption(tkwin, infoPtr->dst, infoPtr->src, | |
270 | TK_INTERACTIVE_PRIO); | |
271 | break; | |
272 | case TK_ARGV_OPTION_VALUE: | |
273 | if (argc < 1) { | |
274 | goto missingArg; | |
275 | } | |
276 | Tk_AddOption(tkwin, infoPtr->dst, argv[srcIndex], | |
277 | TK_INTERACTIVE_PRIO); | |
278 | srcIndex++; | |
279 | argc--; | |
280 | break; | |
281 | case TK_ARGV_OPTION_NAME_VALUE: | |
282 | if (argc < 2) { | |
283 | Tcl_AppendResult(interp, "\"", curArg, | |
284 | "\" option requires two following arguments", | |
285 | (char *) NULL); | |
286 | return TCL_ERROR; | |
287 | } | |
288 | Tk_AddOption(tkwin, argv[srcIndex], argv[srcIndex+1], | |
289 | TK_INTERACTIVE_PRIO); | |
290 | srcIndex += 2; | |
291 | argc -= 2; | |
292 | break; | |
293 | default: | |
294 | sprintf(interp->result, "bad argument type %d in Tk_ArgvInfo", | |
295 | infoPtr->type); | |
296 | return TCL_ERROR; | |
297 | } | |
298 | } | |
299 | ||
300 | /* | |
301 | * If we broke out of the loop because of an OPT_REST argument, | |
302 | * copy the remaining arguments down. | |
303 | */ | |
304 | ||
305 | argsDone: | |
306 | while (argc) { | |
307 | argv[dstIndex] = argv[srcIndex]; | |
308 | srcIndex++; | |
309 | dstIndex++; | |
310 | argc--; | |
311 | } | |
312 | argv[dstIndex] = (char *) NULL; | |
313 | *argcPtr = dstIndex; | |
314 | return TCL_OK; | |
315 | ||
316 | missingArg: | |
317 | Tcl_AppendResult(interp, "\"", curArg, | |
318 | "\" option requires an additional argument", (char *) NULL); | |
319 | return TCL_ERROR; | |
320 | } | |
321 | \f | |
322 | /* | |
323 | *---------------------------------------------------------------------- | |
324 | * | |
325 | * PrintUsage -- | |
326 | * | |
327 | * Generate a help string describing command-line options. | |
328 | * | |
329 | * Results: | |
330 | * Interp->result will be modified to hold a help string | |
331 | * describing all the options in argTable, plus all those | |
332 | * in the default table unless TK_ARGV_NO_DEFAULTS is | |
333 | * specified in flags. | |
334 | * | |
335 | * Side effects: | |
336 | * None. | |
337 | * | |
338 | *---------------------------------------------------------------------- | |
339 | */ | |
340 | ||
341 | static void | |
342 | PrintUsage(interp, argTable, flags) | |
343 | Tcl_Interp *interp; /* Place information in this interp's | |
344 | * result area. */ | |
345 | Tk_ArgvInfo *argTable; /* Array of command-specific argument | |
346 | * descriptions. */ | |
347 | int flags; /* If the TK_ARGV_NO_DEFAULTS bit is set | |
348 | * in this word, then don't generate | |
349 | * information for default options. */ | |
350 | { | |
351 | register Tk_ArgvInfo *infoPtr; | |
352 | int width, i, numSpaces; | |
353 | #define NUM_SPACES 20 | |
354 | static char spaces[] = " "; | |
355 | char tmp[30]; | |
356 | ||
357 | /* | |
358 | * First, compute the width of the widest option key, so that we | |
359 | * can make everything line up. | |
360 | */ | |
361 | ||
362 | width = 4; | |
363 | for (i = 0; i < 2; i++) { | |
364 | for (infoPtr = i ? defaultTable : argTable; | |
365 | infoPtr->type != TK_ARGV_END; infoPtr++) { | |
366 | int length; | |
367 | if (infoPtr->key == NULL) { | |
368 | continue; | |
369 | } | |
370 | length = strlen(infoPtr->key); | |
371 | if (length > width) { | |
372 | width = length; | |
373 | } | |
374 | } | |
375 | } | |
376 | ||
377 | Tcl_AppendResult(interp, "Command-specific options:", (char *) NULL); | |
378 | for (i = 0; ; i++) { | |
379 | for (infoPtr = i ? defaultTable : argTable; | |
380 | infoPtr->type != TK_ARGV_END; infoPtr++) { | |
381 | if ((infoPtr->type == TK_ARGV_HELP) && (infoPtr->key == NULL)) { | |
382 | Tcl_AppendResult(interp, "\n", infoPtr->help, (char *) NULL); | |
383 | continue; | |
384 | } | |
385 | Tcl_AppendResult(interp, "\n ", infoPtr->key, ":", (char *) NULL); | |
386 | numSpaces = width + 1 - strlen(infoPtr->key); | |
387 | while (numSpaces > 0) { | |
388 | if (numSpaces >= NUM_SPACES) { | |
389 | Tcl_AppendResult(interp, spaces, (char *) NULL); | |
390 | } else { | |
391 | Tcl_AppendResult(interp, spaces+NUM_SPACES-numSpaces, | |
392 | (char *) NULL); | |
393 | } | |
394 | numSpaces -= NUM_SPACES; | |
395 | } | |
396 | Tcl_AppendResult(interp, infoPtr->help, (char *) NULL); | |
397 | switch (infoPtr->type) { | |
398 | case TK_ARGV_INT: { | |
399 | sprintf(tmp, "%d", *((int *) infoPtr->dst)); | |
400 | Tcl_AppendResult(interp, "\n\t\tDefault value: ", | |
401 | tmp, (char *) NULL); | |
402 | break; | |
403 | } | |
404 | case TK_ARGV_FLOAT: { | |
405 | sprintf(tmp, "%lg", *((double *) infoPtr->dst)); | |
406 | Tcl_AppendResult(interp, "\n\t\tDefault value: ", | |
407 | tmp, (char *) NULL); | |
408 | break; | |
409 | } | |
410 | case TK_ARGV_STRING: { | |
411 | char *string; | |
412 | ||
413 | string = *((char **) infoPtr->dst); | |
414 | if (string != NULL) { | |
415 | Tcl_AppendResult(interp, "\n\t\tDefault value: \"", | |
416 | string, "\"", (char *) NULL); | |
417 | } | |
418 | break; | |
419 | } | |
420 | default: { | |
421 | break; | |
422 | } | |
423 | } | |
424 | } | |
425 | ||
426 | if ((flags & TK_ARGV_NO_DEFAULTS) || (i > 0)) { | |
427 | break; | |
428 | } | |
429 | Tcl_AppendResult(interp, "\nGeneric options for all commands:", | |
430 | (char *) NULL); | |
431 | } | |
432 | } |