Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

open-axiom repository from github

24005 views
1
/*
2
Copyright (C) 2008-2011, Gabriel Dos Reis.
3
All rights reserved.
4
5
Redistribution and use in source and binary forms, with or without
6
modification, are permitted provided that the following conditions are
7
met:
8
9
- Redistributions of source code must retain the above copyright
10
notice, this list of conditions and the following disclaimer.
11
12
- Redistributions in binary form must reproduce the above copyright
13
notice, this list of conditions and the following disclaimer in
14
the documentation and/or other materials provided with the
15
distribution.
16
17
- Neither the name of The Numerical Algorithms Group Ltd. nor the
18
names of its contributors may be used to endorse or promote products
19
derived from this software without specific prior written permission.
20
21
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
25
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
*/
33
34
#include <stdlib.h>
35
#include <string.h>
36
#include <errno.h>
37
#include <stdio.h>
38
#include "open-axiom.h"
39
40
namespace OpenAxiom {
41
42
/* The basename of the file holding the OpenAxiom core executable. */
43
#define OPENAXIOM_CORE_EXECUTABLE \
44
"AXIOMsys" OPENAXIOM_EXEEXT
45
46
/* The basename of the file holding the session manager executable. */
47
#define OPENAXIOM_SMAN_EXECUTABLE \
48
"sman" OPENAXIOM_EXEEXT
49
50
/* Path to the OpenAxiom executable core, relative to
51
OPENAXIOM_ROOT_DIRECTORY, or to the system root directory as specified
52
on command line. */
53
#define OPENAXIOM_CORE_PATH \
54
"/bin/" OPENAXIOM_CORE_EXECUTABLE
55
56
#define OPENAXIOM_GUI_SUBPATH \
57
"/bin/" OPENAXIOM_GUI_EXECUTABLE
58
59
#define OPENAXIOM_GUI_EXECUTABLE \
60
"open-axiom" OPENAXIOM_EXEEXT
61
62
/* Path to the session manager, relative to OPENAXIOM_ROOT_DIRECTORY,
63
or to the system root directory as specified on command line. */
64
#define OPENAXIOM_SMAN_PATH \
65
"/bin/" OPENAXIOM_SMAN_EXECUTABLE
66
67
/* Name of the entry point for Lisp-based OpenAxiom. */
68
#define OPENAXIOM_LISP_CORE_ENTRY_POINT \
69
"|AxiomCore|::|topLevel|"
70
71
// -- Arguments --
72
Arguments::Arguments(int n) : std::vector<char*>(n + 1)
73
{ }
74
75
int Arguments::size() const {
76
return std::vector<char*>::size() - 1;
77
}
78
79
void Arguments::allocate(int n) {
80
resize(n + 1);
81
}
82
83
char* const* Arguments::data() const {
84
return &*begin();
85
};
86
87
// -- Command --
88
Command::Command()
89
: core(),
90
rt_args(),
91
root_dir(),
92
exec_path()
93
{ }
94
95
static const char*
96
get_suffix(const char* first, const char* last, const char* seq) {
97
for (; first < last; ++first, ++seq)
98
if (*first != *seq)
99
return 0;
100
return seq;
101
}
102
103
// -- Return non-null if `lhs' is a prefix of `rhs'. When non-null
104
// -- the pointer points to the '=' character that starts of the
105
// -- value supplied to the argument.
106
template<int N>
107
const char* is_prefix(const char (&lhs)[N], const char* rhs) {
108
for (int i = 0; i < N - 1; ++i)
109
if (lhs[i] != rhs[i])
110
return 0;
111
return rhs + N - 1;
112
}
113
114
const char*
115
option_value(const Command* command, const char* opt) {
116
const size_t n = strlen(opt);
117
for (int i = 1; i < command->core.argc; ++i) {
118
const char* arg = command->core.argv[i];
119
if (strlen(arg) < n)
120
continue;
121
if(const char* val = get_suffix(opt, opt + n, arg)) {
122
if (*val++ == '=')
123
return val;
124
break;
125
}
126
}
127
return 0;
128
}
129
130
/* Return a path to the running system, either as specified on command
131
line through --system=, or as specified at configuration time. */
132
const char*
133
get_systemdir(int argc, char* argv[])
134
{
135
int i;
136
137
/* Root directory specified on command line takes precedence
138
over location specified at configuration time. */
139
for (i = 1; i < argc; ++i)
140
if (strcmp("--", argv[i]) == 0)
141
break;
142
else if (strncmp("--system=", argv[i], sizeof("--system=") - 1) == 0) {
143
return argv[i] + sizeof ("--system=") - 1;
144
}
145
146
/* Command line did not change the system location to use.
147
Return what was computed at configuration time. */
148
return OPENAXIOM_ROOT_DIRECTORY;
149
}
150
151
/* Return the path to `driver'. */
152
static const char*
153
get_driver_subpath(Driver driver)
154
{
155
switch (driver) {
156
case Driver::sman:
157
return OPENAXIOM_SMAN_PATH;
158
159
case Driver::gui:
160
return OPENAXIOM_GUI_SUBPATH;
161
162
case Driver::script:
163
case Driver::compiler:
164
case Driver::core:
165
case Driver::translator:
166
case Driver::linker:
167
return OPENAXIOM_CORE_PATH;
168
169
default:
170
abort();
171
}
172
}
173
174
175
/* Return a path for PROG specified as a relative path to PREFIX. */
176
const char*
177
make_path_for(const char* prefix, Driver driver)
178
{
179
const int prefix_length = strlen(prefix);
180
const char* prog = get_driver_subpath(driver);
181
char* execpath = (char*) malloc(prefix_length + strlen(prog) + 1);
182
strcpy(execpath, prefix);
183
strcpy(execpath + prefix_length, prog);
184
return execpath;
185
}
186
187
/* Build arguments, if any, to be supplied to the runtime system
188
of `driver'. */
189
void
190
build_rts_options(Command* command, Driver driver)
191
{
192
switch (driver) {
193
case Driver::config:
194
case Driver::sman:
195
case Driver::gui:
196
case Driver::execute:
197
case Driver::unknown:
198
case Driver::core:
199
break;
200
201
case Driver::compiler:
202
case Driver::script:
203
case Driver::translator:
204
case Driver::linker:
205
switch (OPENAXIOM_BASE_RTS) {
206
case Runtime::gcl:
207
command->rt_args.allocate(3);
208
command->rt_args[0] = (char*) "-batch";
209
command->rt_args[1] = (char*) "-eval";
210
command->rt_args[2] =
211
(char*) ("(" OPENAXIOM_LISP_CORE_ENTRY_POINT ")");
212
break;
213
214
case Runtime::sbcl:
215
command->rt_args.allocate(6);
216
command->rt_args[0] = (char*) "--noinform";
217
command->rt_args[1] = (char*) "--end-runtime-options";
218
command->rt_args[2] = (char*) "--noprint";
219
command->rt_args[3] = (char*) "--no-sysinit";
220
command->rt_args[4] = (char*) "--no-userinit";
221
command->rt_args[5] = (char*) "--end-toplevel-options";
222
break;
223
224
case Runtime::clozure:
225
command->rt_args.allocate(2);
226
command->rt_args[0] = (char*) "--no-init";
227
command->rt_args[1] = (char*) "--batch";
228
break;
229
230
case Runtime::clisp:
231
command->rt_args.allocate(2);
232
command->rt_args[0] = (char*) "--quiet";
233
command->rt_args[1] = (char*) "-norc";
234
break;
235
236
case Runtime::ecl:
237
command->rt_args.allocate(2);
238
command->rt_args[0] = (char*) "-q";
239
command->rt_args[1] = (char*) "-norc";
240
break;
241
242
default:
243
abort();
244
}
245
break;
246
247
default:
248
abort();
249
}
250
}
251
252
// Return a description of the driver to invokve by default.
253
static Driver default_driver(bool explicit_no_gui) {
254
if (OPENAXIOM_USE_SMAN)
255
return Driver::sman;
256
else if (OPENAXIOM_USE_GUI and not explicit_no_gui)
257
return Driver::gui;
258
else
259
return Driver::core;
260
}
261
262
263
static void print_line(const char* line) {
264
fputs(line, stdout);
265
fputc('\n', stdout);
266
}
267
268
/* Print OpenAxiom version information. */
269
static void print_version(void) {
270
print_line(PACKAGE_STRING);
271
}
272
273
/* Print OpenAxiom invokation syntax (e.g. options) on standard
274
output stream. */
275
276
static void print_usage(void) {
277
print_line("Usage: open-axiom [options] [file]");
278
print_line("General options:");
279
print_line(" --help Print this information and exit.");
280
print_line(" --version Print OpenAxiom version and exit.");
281
print_line(" --verbose Make the system more talkative.");
282
print_line(" The interpreter echos commands and prints results as if in toplevel read-eval-print loop.");
283
print_line(" --script Execute the file argument as a Spad script.");
284
print_line(" If specified, this option should be last before file argument.");
285
print_line(" --compile Invoke the compiler on the file argument.");
286
print_line(" If specified, this option should be last before file argument.");
287
print_line(" --server Start the Superman as master process.");
288
print_line(" --no-server Do not start Superman as master process.");
289
print_line("");
290
print_line("Superman options:");
291
print_line(" --no-gui Do not start the Graphics or HyperDoc components.");
292
print_line(" --graph Start the Graphics component. This option is meaningful");
293
print_line(" only if OpenAxiom was built with graphics support.");
294
print_line(" --no-graph Do not start the Graphics component.");
295
print_line(" --hyperdoc Start the HyperDoc component. This option is meaningful");
296
print_line(" only if OpenAxiom was built with graphics support.");
297
print_line(" --no-hyperdoc Do not start the HyperDoc component.");
298
print_line(" --execute cmd args execute `cmd' with arguments `args'");
299
300
print_line("");
301
print_line("Compiler options:");
302
print_line(" --optimize=<n> Set compiler optimization level to <n>, a natural number.");
303
print_line("");
304
print_line("If invoked without options and without an input file "
305
"OpenAxiom will start as an interative program with Superman"
306
" as the master process, the majority of uses. If invoked "
307
"with a file as single argument, OpenAxiom assumes the file is a Spad "
308
"script and will attempt to execute it as such.");
309
print_line("");
310
print_line("Submit bug report to " PACKAGE_BUGREPORT);
311
}
312
313
// Map a option to the driver that implement that action.
314
struct DriverMap {
315
const char* action;
316
const Driver driver;
317
};
318
319
static const DriverMap driver_table[] = {
320
{ "--script", Driver::script },
321
{ "--compile", Driver::compiler },
322
{ "--translate", Driver::compiler },
323
{ "--build-databases", Driver::compiler },
324
{ "--build-initdb", Driver::core },
325
{ "--make", Driver::linker },
326
};
327
328
// Obtain the driver that implement a specific action requested
329
// on command line.
330
static Driver
331
option_driver(const char* opt) {
332
for (int i = 0; i < length(driver_table); ++i)
333
if (strcmp(opt, driver_table[i].action) == 0)
334
return driver_table[i].driver;
335
return Driver::unknown;
336
}
337
338
/* Determine driver to be used for executing `command'. */
339
Driver
340
preprocess_arguments(Command* command, int argc, char** argv)
341
{
342
int i;
343
int other = 1;
344
int files = 0;
345
Driver driver = Driver::unknown;
346
bool explicit_no_gui = false; // True if --no-gui explicitly specified.
347
348
command->root_dir = get_systemdir(argc, argv);
349
for (i = 1; i < argc; ++i)
350
if(strcmp(argv[i], "--no-server") == 0)
351
driver = Driver::core;
352
else if (strcmp(argv[i], "--server") == 0)
353
driver = Driver::sman;
354
else if (strcmp(argv[i], "--gui") == 0)
355
driver = Driver::gui;
356
else if (strcmp(argv[i], "--config") == 0)
357
driver = Driver::config;
358
else if (strcmp(argv[i], "--execute") == 0) {
359
driver = Driver::execute;
360
break;
361
}
362
else if (strcmp(argv[i], "--help") == 0) {
363
print_usage();
364
driver = Driver::null;
365
break;
366
}
367
else if (strcmp(argv[i], "--version") == 0) {
368
print_version();
369
driver = Driver::null;
370
break;
371
}
372
else if (const char* val = is_prefix("--execpath=", argv[i])) {
373
command->exec_path = val;
374
}
375
else {
376
/* Apparently we will invoke the Core system; we need to
377
pass on this option. */
378
const Driver d = option_driver(argv[i]);
379
if (d != Driver::unknown)
380
driver = d;
381
else {
382
/* Maybe option for the driver. */
383
if (argv[i][0] == '-') {
384
// this is awkward to handle here since it supposed
385
// to go to Superman. FIXME when sman is gone.
386
if (strcmp(argv[i], "--no-gui") == 0)
387
explicit_no_gui = true;
388
}
389
else if (strlen(argv[i]) > 0)
390
/* Assume a file. */
391
++files;
392
else
393
/* Silly. */
394
continue;
395
}
396
/* Save it for the core executable. */
397
argv[other++] = argv[i];
398
}
399
400
/* Determine argument vector. */
401
if (driver == Driver::execute) {
402
command->core.argc = argc - i - 1;
403
command->core.argv = argv + i + 1;
404
}
405
else {
406
command->core.argc = other;
407
command->core.argv = argv;
408
}
409
410
if (driver != Driver::null) {
411
/* If we have a file but not instructed to compile, assume
412
we are asked to interpret a script. */
413
if (files > 0)
414
switch (driver) {
415
case Driver::unknown:
416
case Driver::sman:
417
case Driver::gui:
418
command->core.argc += 1;
419
command->core.argv =
420
(char**) malloc((other + 2) * sizeof(char*));
421
command->core.argv[0] = argv[0];
422
command->core.argv[1] = (char*) "--script";
423
for (i = 0; i < other; ++i)
424
command->core.argv[2 + i] = argv[1 + i];
425
driver = Driver::script;
426
break;
427
default:
428
/* Driver specified by user. */
429
break;
430
}
431
else if (driver == Driver::unknown)
432
driver = default_driver(explicit_no_gui);
433
command->core.argv[command->core.argc] = NULL;
434
435
build_rts_options(command, driver);
436
}
437
return driver;
438
}
439
440
// Return a pointer to the path to the program to execute, as
441
// specified by `command' and `driver'.
442
static const char*
443
executable_path(const Command* command, Driver driver) {
444
return command->exec_path != 0
445
? command->exec_path
446
: make_path_for(command->root_dir, driver);
447
}
448
449
// Return the total number of command-line arguments.
450
static int
451
args_count(const Command* cmd) {
452
return cmd->core.argc > 1
453
? cmd->rt_args.size() + cmd->core.argc + 2
454
: cmd->rt_args.size() + 1;
455
}
456
457
/* Execute the Core Executable as described by `command'. On
458
POSIX systems, this is a non-return function on success.
459
See execv(). */
460
int
461
execute_core(const Command* command, Driver driver)
462
{
463
char* execpath = (char*) executable_path(command, driver);
464
#ifdef __WIN32__
465
char* command_line;
466
int cur = strlen(command->core.argv[0]);
467
int command_line_length = 0;
468
int i;
469
PROCESS_INFORMATION procInfo;
470
STARTUPINFO startupInfo = { 0 };
471
DWORD status; /* Exit code for this program masqueraded as
472
the child created below. */
473
474
/* How long is the final command line for the MS system? */
475
command_line_length += cur;
476
for (i = 0; i < command->rt_args.size(); ++i)
477
command_line_length += 1 /* blank char as separator */
478
+ 2 /* quotes around every argument. */
479
+ strlen(command->rt_args[i]); /* room for each argument */
480
/* Don't forget room for the doubledash string. */
481
command_line_length += sizeof("--") - 1;
482
/* And arguments to the actual command. */
483
for (i = 1; i < command->core.argc; ++i)
484
command_line_length += 1 + 2 + strlen(command->core.argv[i]);
485
486
/* Now, build the actual command line. This is done by
487
concatenating the arguments into a single string. */
488
command_line = (char*) malloc(command_line_length + 1);
489
strcpy(command_line, command->core.argv[0]);
490
for (i = 0; i < command->rt_args.size(); ++i) {
491
const int arg_length = strlen(command->rt_args[i]);
492
command_line[cur++] = ' ';
493
command_line[cur++] = '"';
494
strcpy(command_line + cur, command->rt_args[i]);
495
cur += arg_length;
496
command_line[cur++] = '"';
497
}
498
command_line[cur++] = ' ';
499
command_line[cur++] = '-'; /* start arguments to the core executable. */
500
command_line[cur++] = '-';
501
for (i = 1; i < command->core.argc; ++i) {
502
const int arg_length = strlen(command->core.argv[i]);
503
command_line[cur++] = ' ';
504
command_line[cur++] = '"';
505
strcpy(command_line + cur, command->core.argv[i]);
506
cur += arg_length;
507
command_line[cur++] = '"';
508
}
509
command_line[cur] = '\0'; /* The command line is done. */
510
511
if(CreateProcess(/* lpApplicationName */ execpath,
512
/* lpCommandLine */ command_line,
513
/* lpProcessAttributes */ NULL,
514
/* lpThreadAttributes */ NULL,
515
/* bInheritHandles */ TRUE,
516
/* dwCreationFlags */ 0,
517
/* lpEnvironment */ NULL,
518
/* lpCurrentDirectory */ NULL,
519
/* lpstartupInfo */ &startupInfo,
520
/* lpProcessInformation */ &procInfo) == 0) {
521
fprintf(stderr, "error %lu\n", GetLastError());
522
abort();
523
}
524
WaitForSingleObject(procInfo.hProcess, INFINITE);
525
GetExitCodeProcess(procInfo.hProcess, &status);
526
CloseHandle(procInfo.hThread);
527
CloseHandle(procInfo.hProcess);
528
return status;
529
530
#else /* __WIN32__ */
531
int i;
532
Arguments args(args_count(command));
533
/* GCL has this oddity that it wants to believe that argv[0] has
534
something to tell about what GCL's own runtime is. Silly. */
535
if (OPENAXIOM_BASE_RTS == Runtime::gcl)
536
args[0] = (char*) "";
537
/* And CLISP wants to believe that argv[0] is where it hides stuff
538
from the saved image. */
539
else
540
args[0] = execpath;
541
/* Now, make sure we copy whatever arguments are required by the
542
runtime system. */
543
for (i = 0; i < command->rt_args.size(); ++i)
544
args[i + 1] = command->rt_args[i];
545
546
if (command->core.argc > 1) {
547
/* We do have arguments from the command line. We want to
548
differentiate this from the base runtime system arguments.
549
We do this by inserting a doubledash to indicate beginning
550
of arguments. */
551
args[command->rt_args.size() + 1] = (char*) "--";
552
/* Then, copy over the arguments received from the command line. */
553
for (i = 1; i < command->core.argc; ++i)
554
args[command->rt_args.size() + i + 1] = command->core.argv[i];
555
}
556
557
execv(execpath, args.data());
558
perror(execpath);
559
return -1;
560
#endif /* __WIN32__ */
561
}
562
563
}
564
565