]> git.zerfleddert.de Git - micropolis/blame_incremental - src/tk/tkargv.c
handle spaces in filenames
[micropolis] / src / tk / tkargv.c
... / ...
CommitLineData
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
18static 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
29static 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
40static 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
68int
69Tk_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
341static void
342PrintUsage(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}
Impressum, Datenschutz