Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 505 lines (440 sloc) 11.855 kb
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
1 /***********************************************************************
2 *
3 * Option handling and dispatching to installed .st scripts
4 *
5 *
6 ***********************************************************************/
7
8 /***********************************************************************
9 *
ea6fa64 @bonzini update copyright years
bonzini authored
10 * Copyright 2007, 2008 Free Software Foundation, Inc.
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
11 * Written by Paolo Bonzini.
12 *
13 * This file is part of GNU Smalltalk.
14 *
15 * GNU Smalltalk is free software; you can redistribute it and/or modify it
16 * under the terms of the GNU General Public License as published by the Free
17 * Software Foundation; either version 2, or (at your option) any later
18 * version.
19 *
20 * Linking GNU Smalltalk statically or dynamically with other modules is
21 * making a combined work based on GNU Smalltalk. Thus, the terms and
22 * conditions of the GNU General Public License cover the whole
23 * combination.
24 *
25 * In addition, as a special exception, the Free Software Foundation
26 * give you permission to combine GNU Smalltalk with free software
27 * programs or libraries that are released under the GNU LGPL and with
28 * independent programs running under the GNU Smalltalk virtual machine.
29 *
30 * You may copy and distribute such a system following the terms of the
31 * GNU GPL for GNU Smalltalk and the licenses of the other code
32 * concerned, provided that you include the source code of that other
33 * code when and as the GNU GPL requires distribution of source code.
34 *
35 * Note that people who make modified versions of GNU Smalltalk are not
36 * obligated to grant this special exception for their modified
37 * versions; it is their choice whether to do so. The GNU General
38 * Public License gives permission to release a modified version without
39 * this exception; this exception also makes it possible to release a
40 * modified version which carries forward this exception.
41 *
42 * GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
43 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
44 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
45 * more details.
46 *
47 * You should have received a copy of the GNU General Public License along with
48 * GNU Smalltalk; see the file COPYING. If not, write to the Free Software
49 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
50 *
51 ***********************************************************************/
52
53 #ifdef HAVE_CONFIG_H
54 #include "config.h"
55 #endif
56
57 #include "gstpub.h"
58
59 #include <ctype.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <stdarg.h>
63 #include <stdio.h>
408dfd6 @bonzini print error messages on file not found
bonzini authored
64 #include <errno.h>
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
65
c7dd7fb @bonzini add gst-remote tool
bonzini authored
66 #ifdef HAVE_UNISTD_H
67 #include <unistd.h>
68 #endif
69
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
70 const char *program_name;
2ff0265 @bonzini redo library API, remove --image-directory, move cmdline to main.c
bonzini authored
71 const char *kernel_dir;
72 const char *image_file;
73 int flags = GST_NO_TTY;
c7dd7fb @bonzini add gst-remote tool
bonzini authored
74 int run_as_daemon;
75 int usage;
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
76
77 struct tool {
78 const char *name;
79 const char *script;
80 const char *options;
81 const char *force_opt;
82 };
83
84 struct tool tools[] = {
85 {
d43c6c1 @bonzini handle command-line options in scripts/Convert.st, install gst-convert
bonzini authored
86 "gst-convert", "scripts/Convert.st",
38b53d6 @bonzini add rewriting to gst-convert
bonzini authored
87 "-h|--help --version -q|--quiet -v|-V|--verbose -C|--class: -r|--rule: \
d43c6c1 @bonzini handle command-line options in scripts/Convert.st, install gst-convert
bonzini authored
88 -c|--category: -f|--format: -o|--output: -I|--image-file: \
89 --kernel-directory:",
90 NULL
91 },
92 {
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
93 "gst-load", "scripts/Load.st",
4c87a76 @bonzini add gst-* man pages
bonzini authored
94 "-h|--help --version -q|--quiet -v|-V|--verbose -n|--dry-run -f|--force \
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
95 -t|--test -I|--image-file: --kernel-directory:",
96 NULL
97 },
98 {
99 "gst-reload", "scripts/Load.st",
4c87a76 @bonzini add gst-* man pages
bonzini authored
100 "-h|--help --version -q|--quiet -v|-V|--verbose -n|--dry-run -f|--force \
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
101 -t|--test -I|--image-file: --kernel-directory:",
102 "--force"
103 },
104 {
105 "gst-package", "scripts/Package.st",
106 "-h|--help --version --load --no-load --no-install --uninstall --dist \
6ba19e9 @bonzini gst-package --prepare
bonzini authored
107 --prepare --test -t|--target-directory: --list-files: --list-packages \
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
108 --srcdir: --distdir|--destdir: --copy --all-files --vpath \
109 -n|--dry-run -I|--image-file: --kernel-directory:",
110 NULL
111 },
112 {
113 "gst-sunit", "scripts/Test.st",
4c87a76 @bonzini add gst-* man pages
bonzini authored
114 "-h|--help --version -q|--quiet -v|-V|--verbose -f|--file: -p|--package: \
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
115 -I|--image-file: --kernel-directory:",
116 NULL
117 },
118 {
119 "gst-blox", "scripts/Browser.st",
120 "-I|--image-file: --kernel-directory:",
121 NULL
122 },
f5e5fa6 @bonzini install doc generator as gst-doc
bonzini authored
123 {
124 "gst-doc", "scripts/GenDoc.st",
125 "-h|--help --version -p|--package: -f|--file: -I|--image-file: \
a508ca6 @bonzini gst-doc: add --namespace, use #fullyDefinedLoadedClasses
bonzini authored
126 -n|--namespace: -o|--output: --kernel-directory:",
f5e5fa6 @bonzini install doc generator as gst-doc
bonzini authored
127 NULL
128 },
c7dd7fb @bonzini add gst-remote tool
bonzini authored
129 {
130 "gst-remote", "scripts/Remote.st",
131 "-h|--help --version --daemon --server -p|--port -f|--file: -e|--eval: \
132 --pid --kill --snapshot:: -I|--image-file: --kernel-directory:",
133 NULL
134 },
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
135 { NULL, NULL, NULL, NULL }
136 };
137
138 /* An option parser compatible with the one in the Getopt class.
139 Does not support canonical option names, otherwise it is pretty
140 complete. */
141
ff12740 @bonzini gst-tool.c: rename "struct option"
bonzini authored
142 struct long_option {
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
143 char arg;
144 const char *name;
ff12740 @bonzini gst-tool.c: rename "struct option"
bonzini authored
145 struct long_option *next;
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
146 };
147
148 #define OPT_NONE 0
149 #define OPT_MANDATORY 1
150 #define OPT_OPTIONAL 2
151
152 char short_opts[1 << (sizeof (char) * 8)];
ff12740 @bonzini gst-tool.c: rename "struct option"
bonzini authored
153 struct long_option *long_opts;
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
154
155 void
156 option_error (const char *s, ...)
157 {
158 va_list ap;
159 va_start (ap, s);
2ff0265 @bonzini redo library API, remove --image-directory, move cmdline to main.c
bonzini authored
160 fprintf (stderr, "%s: ", program_name);
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
161 vfprintf (stderr, s, ap);
162 fprintf (stderr, "\n");
163 va_end (ap);
164 exit (1);
165 }
166
167 void
168 setup_option (char *p, char *end)
169 {
170 int arg = 0;
171 if (*p != '-')
172 abort ();
173
174 while (*end == ':')
175 {
176 *end-- = '\0';
177 arg++;
178 }
179
180 if (arg > 2)
181 abort ();
182
183 while (p)
184 {
185 unsigned char short_opt = 0;
186 const char *long_opt = NULL;
187
188 if (*p++ != '-')
189 abort ();
190 if (*p == '-')
191 {
192 ++p;
193 long_opt = strsep (&p, "|");
194 }
195 else
196 {
197 short_opt = *p++;
198 if (!*p)
199 p = NULL;
200 else if (*p++ != '|')
201 abort ();
202 }
203
204 if (long_opt)
205 {
ff12740 @bonzini gst-tool.c: rename "struct option"
bonzini authored
206 struct long_option *opt = malloc (sizeof (struct long_option));
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
207 opt->name = strdup (long_opt);
208 opt->arg = arg;
209 opt->next = long_opts;
210 long_opts = opt;
211 }
212 else
213 short_opts[(unsigned char) short_opt] = arg;
214 }
215 }
216
217 void
218 setup_options (const char *str)
219 {
220 char *copy = strdup (str);
221 char *p = copy;
222
223 memset (short_opts, -1, sizeof (short_opts));
224 do
225 {
226 while (isspace (*p))
227 p++;
228 if (*p)
229 {
230 char *begin, *end;
231 begin = strsep (&p, " \t\n");
232 end = begin + strlen (begin) - 1;
233 setup_option (begin, end);
234 }
235 }
236 while (p && *p);
237
238 free (copy);
239 }
240
241 void
242 parse_option (int short_opt, const char *long_opt, const char *arg)
243 {
244 if (short_opt == 'I'
245 || (long_opt && !strcmp (long_opt, "image-file")))
246 {
2ff0265 @bonzini redo library API, remove --image-directory, move cmdline to main.c
bonzini authored
247 if (image_file)
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
248 option_error ("duplicate --image-file option");
2ff0265 @bonzini redo library API, remove --image-directory, move cmdline to main.c
bonzini authored
249 image_file = arg;
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
250 }
251
252 if (long_opt && !strcmp (long_opt, "kernel-directory"))
253 {
2ff0265 @bonzini redo library API, remove --image-directory, move cmdline to main.c
bonzini authored
254 if (kernel_dir)
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
255 option_error ("duplicate --kernel-directory option");
2ff0265 @bonzini redo library API, remove --image-directory, move cmdline to main.c
bonzini authored
256 kernel_dir = arg;
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
257 }
c7dd7fb @bonzini add gst-remote tool
bonzini authored
258
259 if (long_opt && !strcmp (long_opt, "daemon"))
260 {
261 #ifdef HAVE_FORK
262 run_as_daemon = 1;
263 #else
264 fprintf (stderr, "Daemon operation not supported.");
265 exit (77);
266 #endif
267 }
268
269 if (long_opt && !strcmp (long_opt, "version"))
270 usage = 1;
271
272 if (short_opt == 'h'
273 || (long_opt && !strcmp (long_opt, "help")))
274 usage = 1;
275 }
276
277 #ifdef HAVE_FORK
278 static void
279 fork_daemon (void)
280 {
281 int child_pid;
282
283 #ifdef SIGHUP
284 signal (SIGHUP, SIG_IGN);
285 #endif
286
287 child_pid = fork();
288 if (child_pid < 0)
289 {
290 perror("Failed to fork daemon");
291 exit(1);
292 }
293
294 /* Stop parent. */
295 if (child_pid != 0)
296 exit (0);
297
298 /* Detach and spawn server.
299 Create a new SID for the child process */
300 #ifdef HAVE_SETSID
301 if (setsid() < 0)
302 {
303 perror("setsid failed");
304 exit(1);
305 }
306 #endif
307
308 #ifdef SIGHUP
309 signal (SIGHUP, SIG_DFL);
310 #endif
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
311 }
c7dd7fb @bonzini add gst-remote tool
bonzini authored
312 #endif
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
313
314 int
315 parse_short_options (const char *name, const char *arg)
316 {
317 while (*name)
318 {
319 unsigned char short_opt = (unsigned char) *name++;
320 int have_arg = short_opts[short_opt];
321 if (have_arg == -1)
322 option_error ("invalid option -%c", short_opt);
323
324 if (have_arg == OPT_NONE || (have_arg == OPT_OPTIONAL && !*name))
325 parse_option (short_opt, NULL, NULL);
326
327 else if (*name || arg)
328 {
329 parse_option (short_opt, NULL, *name ? name : arg);
330 return *name ? 1 : 2;
331 }
332
333 else /* if (have_arg == OPT_MANDATORY) */
334 option_error ("expected argument for option -%s", name[-1]);
335 }
336
337 return 1;
338 }
339
340 int
341 parse_long_option (const char *name, const char *arg)
342 {
ff12740 @bonzini gst-tool.c: rename "struct option"
bonzini authored
343 struct long_option *all_opts, *opt = NULL;
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
344 int num_matches = 0;
345 int len;
346 const char *p = strchr (name, '=');
347
348 if (!p)
349 len = strlen (name);
350 else
351 len = p++ - name;
352
353 for (all_opts = long_opts; all_opts; all_opts = all_opts->next)
354 if (!memcmp (name, all_opts->name, len))
355 {
356 opt = all_opts;
357 if (opt->name[len] == '\0')
358 {
359 /* Exact match! */
360 num_matches = 1;
361 break;
362 }
363 else
364 num_matches++;
365 }
366
367 if (!opt)
368 option_error ("invalid option --%.*s", len, name);
369
370 if (num_matches > 1)
371 option_error ("ambiguous option --%.*s", len, name);
372
373 if (opt->arg == OPT_NONE && p)
374 option_error ("unexpected argument for option --%s", opt->name);
375
376 else if (p || opt->arg != OPT_MANDATORY)
377 {
378 parse_option (0, opt->name, p);
379 return 1;
380 }
381
382 else if (!arg)
383 option_error ("expected argument for option --%s", opt->name);
384
385 else
386 {
387 parse_option (0, opt->name, arg);
388 return 2;
389 }
390
391 return 1;
392 }
393
394 void
395 parse_options (const char **argv)
396 {
397 int at_end = 0;
398 while (*argv)
399 {
d43c6c1 @bonzini handle command-line options in scripts/Convert.st, install gst-convert
bonzini authored
400 if (at_end || argv[0][0] != '-' || argv[0][1] == '\0')
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
401 {
402 parse_option (0, NULL, argv[0]);
403 argv++;
404 }
405
406 else if (argv[0][1] != '-')
407 argv += parse_short_options (&argv[0][1], argv[1]);
408
409 else if (argv[0][2] == '\0')
410 {
411 at_end = true;
412 argv++;
413 }
414
415 else
416 argv += parse_long_option (&argv[0][2], argv[1]);
417 }
418 }
419
420 int
421 main(int argc, const char **argv)
422 {
2ff0265 @bonzini redo library API, remove --image-directory, move cmdline to main.c
bonzini authored
423 int smalltalk_argc;
424 const char **smalltalk_argv;
408dfd6 @bonzini print error messages on file not found
bonzini authored
425 const char *executable_name;
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
426 int i;
427 int result;
428
51c3127 @bonzini update Win32 port
bonzini authored
429 #ifdef _WIN32
430 executable_name = strrchr (argv[0], '\\');
431 if (!executable_name || strchr (executable_name, '/'))
432 executable_name = strrchr (argv[0], '/');
433 #else
408dfd6 @bonzini print error messages on file not found
bonzini authored
434 executable_name = strrchr (argv[0], '/');
51c3127 @bonzini update Win32 port
bonzini authored
435 #endif /* _WIN32 */
436
408dfd6 @bonzini print error messages on file not found
bonzini authored
437 if (executable_name)
438 executable_name++;
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
439 else
408dfd6 @bonzini print error messages on file not found
bonzini authored
440 executable_name = argv[0];
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
441
2ff0265 @bonzini redo library API, remove --image-directory, move cmdline to main.c
bonzini authored
442 /* Check if used in the build tree. */
51c3127 @bonzini update Win32 port
bonzini authored
443 if (!strcmp (executable_name, "gst-tool" EXEEXT)
a9a5104 @bonzini more Cygwin fixes
bonzini authored
444 || !strcmp (executable_name, "gst-tool" ARGV_EXEEXT)
445 || !strcmp (executable_name, "lt-gst-tool" EXEEXT)
446 || !strcmp (executable_name, "lt-gst-tool" ARGV_EXEEXT))
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
447 {
448 argv++, argc--;
449 program_name = argv[0];
2ff0265 @bonzini redo library API, remove --image-directory, move cmdline to main.c
bonzini authored
450 flags |= GST_IGNORE_USER_FILES;
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
451 }
408dfd6 @bonzini print error messages on file not found
bonzini authored
452 else
453 program_name = executable_name;
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
454
455 for (i = 0; ; i++)
456 if (!tools[i].name)
457 exit (127);
458 else if (!strcmp (tools[i].name, program_name))
459 break;
460
461 setup_options (tools[i].options);
462 parse_options (&argv[1]);
463
c7dd7fb @bonzini add gst-remote tool
bonzini authored
464 #ifdef HAVE_FORK
465 if (run_as_daemon && !usage)
466 fork_daemon ();
467 #endif
468
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
469 if (tools[i].force_opt)
2ff0265 @bonzini redo library API, remove --image-directory, move cmdline to main.c
bonzini authored
470 {
471 smalltalk_argv = alloca (sizeof (const char *) * (argc + 1));
472 smalltalk_argc = argc;
473 smalltalk_argv[0] = tools[i].force_opt;
474 memcpy (&smalltalk_argv[1], &argv[1], argc * sizeof (char *));
475 }
476 else
477 {
478 smalltalk_argv = argv + 1;
479 smalltalk_argc = argc - 1;
480 }
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
481
482 #ifdef CMD_LN_S
483 setenv ("LN_S", CMD_LN_S, 0);
484 #endif
485 #ifdef CMD_INSTALL
486 setenv ("INSTALL", CMD_INSTALL, 0);
487 #endif
488 #ifdef CMD_ZIP
489 setenv ("XZIP", CMD_ZIP, 0);
490 #endif
491
d43c6c1 @bonzini handle command-line options in scripts/Convert.st, install gst-convert
bonzini authored
492 gst_set_var (GST_VERBOSITY, 0);
2ff0265 @bonzini redo library API, remove --image-directory, move cmdline to main.c
bonzini authored
493 gst_smalltalk_args (smalltalk_argc, smalltalk_argv);
494 result = gst_initialize (kernel_dir, image_file, flags);
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
495 if (result != 0)
496 exit (result < 0 ? 1 : result);
497
408dfd6 @bonzini print error messages on file not found
bonzini authored
498 if (!gst_process_file (tools[i].script, GST_DIR_KERNEL_SYSTEM))
499 fprintf (stderr, "%s: Couldn't open kernel file `%s': %s\n",
500 executable_name, tools[i].script, strerror (errno));
501
1bc9af6 @bonzini invoke aboutToQuit hook in gst-tool
bonzini authored
502 gst_invoke_hook (GST_ABOUT_TO_QUIT);
2ff0265 @bonzini redo library API, remove --image-directory, move cmdline to main.c
bonzini authored
503 exit (0);
19f4bef @bonzini replace shell script wrappers with a C wrapper
bonzini authored
504 }
Something went wrong with that request. Please try again.