1 /*
2 Copyright (C) 2005-2017,2018 John E. Davis
3
4 This file is part of the S-Lang Library.
5
6 The S-Lang Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
11 The S-Lang Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 USA.
20 */
21
22 #include "config.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #ifdef __WIN32__
26 # include <windows.h>
27 # include <io.h>
28 #endif
29
30 #include <sys/stat.h>
31
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #include <string.h>
36 #include <signal.h>
37 #include <slang.h>
38
39 #ifdef SLSYSWRAP
40 # include <slsyswrap.h>
41 #endif
42
43 static SLFUTURE_CONST char *Slsh_Version = "0.9.4-0";
44 #define SLSHRC_FILE "slsh.rc"
45 #include "slsh.h"
46
47 #ifndef SLSH_CONF_DIR_ENV
48 # define SLSH_CONF_DIR_ENV "SLSH_CONF_DIR"
49 #endif
50
51 #ifndef SLSH_PATH_ENV
52 # define SLSH_PATH_ENV "SLSH_PATH"
53 #endif
54
55 #ifndef SLSH_LIB_DIR_ENV
56 # define SLSH_LIB_DIR_ENV "SLSH_LIB_DIR"
57 #endif
58
59 #if defined(REAL_UNIX_SYSTEM) || defined(__APPLE__)
60 /* # define DEFAULT_LIBRARY_PATH "/usr/local/share/slsh:/usr/local/lib/slsh:/usr/share/slsh:/usr/lib/slsh"; */
61 /* # define DEFAULT_CONF_PATH "/usr/local/etc:/usr/local/etc/slsh:/etc:/etc/slsh"; */
62 # define DEFAULT_CONF_PATH NULL
63 # define USER_SLSHRC ".slshrc"
64 #else
65 # define DEFAULT_CONF_PATH NULL
66 # define USER_SLSHRC "slsh.rc"
67 #endif
68
69 #ifdef __os2__
70 # ifdef __IBMC__
71 /* IBM VA3 doesn't declare S_IFMT */
72 # define S_IFMT (S_IFDIR | S_IFCHR | S_IFREG)
73 # endif
74 #endif
75
76 #ifndef S_ISLNK
77 # ifdef S_IFLNK
78 # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
79 # else
80 # define S_ISLNK(m) 0
81 # endif
82 #endif
83
84 #ifndef S_ISREG
85 # ifdef S_IFREG
86 # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
87 # else
88 # define S_ISREG(m) 0
89 # endif
90 #endif
91
92 #ifndef S_ISDIR
93 # ifdef S_IFDIR
94 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
95 # else
96 # define S_ISDIR(m) 0
97 # endif
98 #endif
99
100 #ifndef S_ISCHR
101 # ifdef S_IFCHR
102 # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
103 # else
104 # define S_ISCHR(m) 0
105 # endif
106 #endif
107
108 #ifndef S_ISBLK
109 # ifdef S_IFBLK
110 # define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
111 # else
112 # define S_ISBLK(m) 0
113 # endif
114 #endif
115
116 #ifndef S_ISFIFO
117 # ifdef S_IFIFO
118 # define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
119 # else
120 # define S_ISFIFO(m) 0
121 # endif
122 #endif
123
124 #ifndef S_ISSOCK
125 # ifdef S_IFSOCK
126 # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
127 # else
128 # define S_ISSOCK(m) 0
129 # endif
130 #endif
131
132 #ifndef S_IRUSR
133 # define S_IRUSR 0400
134 #endif
135 #ifndef S_IWUSR
136 # define S_IWUSR 0200
137 #endif
138 #ifndef S_IXUSR
139 # define S_IXUSR 0100
140 #endif
141 #ifndef S_IRGRP
142 # define S_IRGRP 0040
143 #endif
144 #ifndef S_IWGRP
145 # define S_IWGRP 0020
146 #endif
147 #ifndef S_IXGRP
148 # define S_IXGRP 0010
149 #endif
150 #ifndef S_IROTH
151 # define S_IROTH 0004
152 #endif
153 #ifndef S_IWOTH
154 # define S_IWOTH 0002
155 #endif
156 #ifndef S_IXOTH
157 # define S_IXOTH 0001
158 #endif
159 #ifndef S_ISUID
160 # define S_ISUID 04000
161 #endif
162 #ifndef S_ISGID
163 # define S_ISGID 02000
164 #endif
165 #ifndef S_ISVTX
166 # define S_ISVTX 01000
167 #endif
168
169 typedef struct _AtExit_Type
170 {
171 SLang_Name_Type *nt;
172 struct _AtExit_Type *next;
173 }
174 AtExit_Type;
175
176 static AtExit_Type *AtExit_Hooks;
177
at_exit(SLang_Ref_Type * ref)178 static void at_exit (SLang_Ref_Type *ref)
179 {
180 SLang_Name_Type *nt;
181 AtExit_Type *a;
182
183 if (NULL == (nt = SLang_get_fun_from_ref (ref)))
184 return;
185
186 a = (AtExit_Type *) SLmalloc (sizeof (AtExit_Type));
187 if (a == NULL)
188 return;
189
190 a->nt = nt;
191 a->next = AtExit_Hooks;
192 AtExit_Hooks = a;
193 }
194
c_exit(int status)195 static void c_exit (int status)
196 {
197 /* Clear the error to allow exit hooks to run */
198 if (SLang_get_error ())
199 SLang_restart (1);
200
201 while (AtExit_Hooks != NULL)
202 {
203 AtExit_Type *next = AtExit_Hooks->next;
204 if (SLang_get_error () == 0)
205 (void) SLexecute_function (AtExit_Hooks->nt);
206
207 SLfree ((char *) AtExit_Hooks);
208 AtExit_Hooks = next;
209 }
210
211 SLang_restart (1);
212 exit (status);
213 }
214
exit_intrin(void)215 static void exit_intrin (void)
216 {
217 int status;
218
219 if (SLang_Num_Function_Args == 0)
220 status = 0;
221 else if (-1 == SLang_pop_int (&status))
222 return;
223
224 c_exit (status);
225 }
226
stat_mode_to_string(void)227 static void stat_mode_to_string (void)
228 {
229 int mode, opts;
230 char mode_string[12];
231
232 opts = 0;
233 if (SLang_Num_Function_Args == 2)
234 {
235 if (-1 == SLang_pop_integer (&opts))
236 return;
237 }
238
239 if (-1 == SLang_pop_integer (&mode))
240 return;
241
242 if (S_ISREG(mode)) mode_string[0] = '-';
243 else if (S_ISDIR(mode)) mode_string[0] = 'd';
244 else if (S_ISLNK(mode)) mode_string[0] = 'l';
245 else if (S_ISCHR(mode)) mode_string[0] = 'c';
246 else if (S_ISFIFO(mode)) mode_string[0] = 'p';
247 else if (S_ISSOCK(mode)) mode_string[0] = 's';
248 else if (S_ISBLK(mode)) mode_string[0] = 'b';
249
250 if (mode & S_IRUSR) mode_string[1] = 'r'; else mode_string[1] = '-';
251 if (mode & S_IWUSR) mode_string[2] = 'w'; else mode_string[2] = '-';
252 if (mode & S_IXUSR) mode_string[3] = 'x'; else mode_string[3] = '-';
253 if (mode & S_ISUID) mode_string[3] = 's';
254
255 #ifdef __WIN32__
256 mode_string[4] = '-';
257 mode_string[5] = '-';
258 mode_string[6] = '-';
259
260 if (opts & FILE_ATTRIBUTE_ARCHIVE) mode_string[7] = 'A'; else mode_string[7] = '-';
261 if (opts & FILE_ATTRIBUTE_SYSTEM) mode_string[8] = 'S'; else mode_string[8] = '-';
262 if (opts & FILE_ATTRIBUTE_HIDDEN) mode_string[9] = 'H'; else mode_string[9] = '-';
263 #else
264 if (mode & S_IRGRP) mode_string[4] = 'r'; else mode_string[4] = '-';
265 if (mode & S_IWGRP) mode_string[5] = 'w'; else mode_string[5] = '-';
266 if (mode & S_IXGRP) mode_string[6] = 'x'; else mode_string[6] = '-';
267 if (mode & S_ISGID) mode_string[6] = 'g';
268
269 if (mode & S_IROTH) mode_string[7] = 'r'; else mode_string[7] = '-';
270 if (mode & S_IWOTH) mode_string[8] = 'w'; else mode_string[8] = '-';
271 if (mode & S_IXOTH) mode_string[9] = 'x'; else mode_string[9] = '-';
272 if (mode & S_ISVTX) mode_string[9] = 't';
273 #endif
274
275 mode_string[10] = 0;
276 (void) SLang_push_string (mode_string);
277 }
278
279 /* Root/
280 * bin/
281 * slsh.exe
282 * slsh/
283 * slsh.rc
284 * autoload.sl
285 * ( ... )
286 */
prepend_exec_root(const char * pgm,const char * suffix,char * pathbuf,size_t size)287 static char *prepend_exec_root (const char *pgm, const char *suffix,
288 char *pathbuf, size_t size)
289 {
290 char *p;
291 unsigned int len;
292 char pathsep;
293
294 len = strlen (suffix);
295 if (len >= size)
296 return (char *)suffix;
297
298 #ifdef __WIN32__
299 if (0 == GetModuleFileName(NULL, pathbuf, size-len))
300 return suffix;
301 pathsep = '\\';
302 #else
303 if (pgm == NULL)
304 return (char *)suffix;
305
306 strncpy (pathbuf, pgm, size);
307 pathbuf[size-1] = 0;
308 pathsep = '/';
309 #endif
310
311 /* drop file name */
312 p = strrchr(pathbuf, pathsep);
313 if (p == NULL)
314 p = pathbuf;
315 *p = 0;
316
317 /* drop also 'bin' */
318 p = strrchr(pathbuf, pathsep);
319 if (p == NULL)
320 p = pathbuf;
321
322 strcpy(p, suffix);
323
324 return pathbuf;
325 }
326
327 static int Verbose_Loading;
328
try_to_load_file(SLFUTURE_CONST char * path,char * file,char * ns)329 static int try_to_load_file (SLFUTURE_CONST char *path, char *file, char *ns)
330 {
331 int status;
332
333 if (file != NULL)
334 {
335 int free_path = 0;
336 if (path == NULL)
337 {
338 free_path = 1;
339 path = SLpath_getcwd ();
340 if (path == NULL)
341 {
342 path = ".";
343 free_path = 0;
344 }
345 }
346
347 file = SLpath_find_file_in_path (path, file);
348 if (free_path) SLfree (path);
349
350 if (file == NULL)
351 return 0;
352 }
353 /* otherwise use stdin */
354
355 status = SLns_load_file (file, ns);
356 SLfree (file);
357 if (status == 0)
358 return 1;
359 return -1;
360 }
361
load_startup_file(const char * pgm,int is_interactive)362 static int load_startup_file (const char *pgm, int is_interactive)
363 {
364 char pathbuf[2048];
365 SLFUTURE_CONST char *dir;
366 int status;
367
368 dir = getenv (SLSH_CONF_DIR_ENV);
369 if (dir == NULL)
370 dir = getenv (SLSH_LIB_DIR_ENV);
371
372 if (NULL == dir)
373 {
374 #ifdef SLSH_CONF_DIR
375 dir = SLSH_CONF_DIR;
376 if (dir != NULL)
377 {
378 status = try_to_load_file (dir, (char *)SLSHRC_FILE, NULL);
379 if (status == -1)
380 return -1;
381 if (status == 1)
382 return 0;
383 }
384 #endif
385
386 #ifdef __WIN32__
387 dir = prepend_exec_root (pgm, "\\etc", pathbuf, sizeof(pathbuf));
388 #else
389 dir = prepend_exec_root (pgm, "/etc", pathbuf, sizeof(pathbuf));
390 #endif
391 }
392
393 if (-1 == (status = try_to_load_file (dir, (char *)SLSHRC_FILE, NULL)))
394 return -1;
395
396 if ((status == 0)
397 && (Verbose_Loading || is_interactive))
398 {
399 SLang_vmessage ("*** Installation Problem? Unable to find the %s config file.",
400 SLSHRC_FILE);
401 }
402
403 return 0;
404 }
405
setup_paths(const char * pgm)406 static int setup_paths (const char *pgm)
407 {
408 SLFUTURE_CONST char *libpath = NULL;
409 char pathbuf[2048];
410
411 #ifdef SLSH_PATH
412 libpath = SLSH_PATH;
413 #endif
414
415 if (libpath != NULL)
416 {
417 if (2 != SLpath_file_exists (libpath))
418 libpath = NULL;
419 }
420
421 if (libpath == NULL)
422 {
423 #ifdef __WIN32__
424 libpath = prepend_exec_root (pgm, "\\share\\slsh", pathbuf, sizeof(pathbuf));
425 #else
426 libpath = prepend_exec_root (pgm, "/share/slsh", pathbuf, sizeof(pathbuf));
427 #endif
428 }
429
430 if (-1 == SLpath_set_load_path (libpath))
431 return -1;
432
433 #ifdef __WIN32__
434 /* hack a directory for modules */
435 {
436 char *dir;
437 dir = prepend_exec_root (pgm, "\\lib\\slang\\v2\\modules", pathbuf, sizeof(pathbuf));
438 if (-1 == SLang_set_module_load_path (dir))
439 return -1;
440 }
441 #endif
442
443 return 0;
444 }
445
set_verbose_loading(int * val)446 static int set_verbose_loading (int *val)
447 {
448 return SLang_load_file_verbose (*val);
449 }
450
451 /* Create the Table that S-Lang requires */
452 static SLang_Intrin_Fun_Type Intrinsics [] =
453 {
454 MAKE_INTRINSIC_0("exit", exit_intrin, VOID_TYPE),
455 MAKE_INTRINSIC_1("atexit", at_exit, VOID_TYPE, SLANG_REF_TYPE),
456 MAKE_INTRINSIC_0("stat_mode_to_string", stat_mode_to_string, VOID_TYPE),
457 MAKE_INTRINSIC_1("set_verbose_loading", set_verbose_loading, SLANG_INT_TYPE, SLANG_INT_TYPE),
458 SLANG_END_INTRIN_FUN_TABLE
459 };
460
461 static char *Slsh_Path_Env = SLSH_PATH_ENV;
462 static char *Slsh_Conf_Dir_Env = SLSH_CONF_DIR_ENV;
463 static SLang_Intrin_Var_Type Intrinsic_Variables [] =
464 {
465 MAKE_VARIABLE("SLSH_PATH_ENV", &Slsh_Path_Env, SLANG_STRING_TYPE, 1),
466 MAKE_VARIABLE("SLSH_CONF_DIR_ENV", &Slsh_Conf_Dir_Env, SLANG_STRING_TYPE, 1),
467 SLANG_END_INTRIN_VAR_TABLE
468 };
469
usage(void)470 static void usage (void)
471 {
472 char *libpath;
473 fprintf (stderr, "\
474 Usage: slsh [OPTIONS] [-|file [args...]]\n\
475 --help Print this help\n\
476 --version Show slsh version information\n\
477 -e string Execute 'string' as S-Lang code\n\
478 -g Compile with debugging code, tracebacks, etc\n\
479 -n Don't load personal init file\n\
480 --init file Use this file instead of ~/%s\n",
481 USER_SLSHRC);
482 fprintf (stderr, "\
483 --no-readline Do not use readline\n\
484 -i Force interactive input\n\
485 -q, --quiet Do not print startup messages\n\
486 -t Test mode. If slsh_main exists, do not call it\n\
487 -v Show verbose loading messages\n\
488 -Dname Define \"name\" as a preprocessor symbol\n\
489 \n\
490 Note: - and -i are mutually exclusive\n\
491 \n\
492 "
493 );
494 libpath = SLpath_get_load_path ();
495 fprintf (stderr, "Default search path: %s\n", (libpath == NULL) ? "" : libpath);
496 SLang_free_slstring (libpath);
497
498 exit (1);
499 }
500
output_version(void)501 static void output_version (void)
502 {
503 fprintf (stdout, "slsh version %s; ", Slsh_Version);
504 fprintf (stdout, "S-Lang version: %s\n", SLang_Version_String);
505 if (SLANG_VERSION != SLang_Version)
506 {
507 fprintf (stdout, "\t** Note: This program was compiled against version %s.\n",
508 SLANG_VERSION_STRING);
509 }
510 }
511
output_copyright(void)512 static int output_copyright (void)
513 {
514 output_version ();
515 fprintf (stdout, "Copyright (C) 2005-2017,2018 John E. Davis <jed@jedsoft.org>\r\n");
516 fprintf (stdout, "This is free software with ABSOLUTELY NO WARRANTY.\r\n");
517 fprintf (stdout, "\n");
518
519 return 0;
520 }
521
version(void)522 static void version (void)
523 {
524 output_version ();
525 exit (0);
526 }
527
main(int argc,char ** argv)528 int main (int argc, char **argv)
529 {
530 char *file = NULL, *pgm;
531 SLFUTURE_CONST char *init_file = USER_SLSHRC;
532 char *init_file_dir;
533 int exit_val;
534 int is_interactive = 0;
535 int use_readline = 1;
536 int test_mode = 0;
537 char *exec_string = NULL;
538 int quiet = 0;
539
540 #ifdef SLSYSWRAP
541 (void) SLsyswrap_set_syscall_failure (0);
542 #endif
543
544 (void) SLutf8_enable (-1);
545
546 if ((-1 == SLang_init_all ())
547 || (-1 == SLang_init_array_extra ())
548 #ifndef SLSH_STATIC
549 || (-1 == SLang_init_import ()) /* dynamic linking */
550 #endif
551 || (-1 == SLadd_intrin_fun_table (Intrinsics, NULL))
552 || (-1 == SLadd_intrin_var_table (Intrinsic_Variables, NULL))
553 || (-1 == slsh_init_readline_intrinsics ()))
554 {
555 fprintf(stderr, "Unable to initialize S-Lang.\n");
556 return 1;
557 }
558
559 #ifdef SIGPIPE
560 (void) SLsignal (SIGPIPE, SIG_IGN);
561 #endif
562
563 #ifdef SLSYSWRAP
564 (void) SLsyswrap_set_syscall_failure (1);
565 #endif
566
567 /* FIXME for other systems */
568 #ifdef __WIN32__
569 init_file_dir = getenv ("USERPROFILE");
570 #else
571 init_file_dir = getenv ("HOME");
572 #endif
573
574 pgm = argv[0];
575 if (-1 == setup_paths (pgm))
576 return -1;
577
578 while (argc > 1)
579 {
580 char *arg = argv[1];
581
582 if (0 == strcmp (arg, "--version"))
583 version ();
584
585 if (0 == strcmp (arg, "--help"))
586 usage ();
587
588 if (0 == strcmp (arg, "-i"))
589 {
590 argc--;
591 argv++;
592 is_interactive = 1;
593 continue;
594 }
595
596 if ((0 == strcmp (arg, "-e"))
597 && (argc > 2))
598 {
599 argc -= 2;
600 argv += 2;
601 exec_string = *argv;
602 continue;
603 }
604
605 if (0 == strcmp (arg, "-g"))
606 {
607 SLang_generate_debug_info (1);
608 SLang_Traceback = SL_TB_FULL;
609 argc--;
610 argv++;
611 continue;
612 }
613
614 if (0 == strcmp (arg, "-n"))
615 {
616 init_file = NULL;
617 argc--;
618 argv++;
619 continue;
620 }
621
622 if ((0 == strcmp (arg, "-q")) || (0 == strcmp (arg, "--quiet")))
623 {
624 quiet = 1;
625 argc--;
626 argv++;
627 continue;
628 }
629
630 if (0 == strcmp (arg, "-t"))
631 {
632 test_mode = 1;
633 argc--;
634 argv++;
635 continue;
636 }
637
638 if (0 == strcmp (arg, "-v"))
639 {
640 (void) SLang_load_file_verbose (3);
641 Verbose_Loading = 1;
642 argc--;
643 argv++;
644 continue;
645 }
646
647 if (0 == strcmp (arg, "--no-readline"))
648 {
649 use_readline = 0;
650 argc--;
651 argv++;
652 continue;
653 }
654
655 if ((0 == strcmp (arg, "--init"))
656 && (argc > 2))
657 {
658 init_file = argv[2];
659 init_file_dir = NULL;
660 argc -= 2;
661 argv += 2;
662 continue;
663 }
664
665 if (0 == strncmp (arg, "-D", 2))
666 {
667 char *prep = arg + 2;
668 if (*prep != 0)
669 (void) SLdefine_for_ifdef (prep);
670 argc--;
671 argv++;
672 continue;
673 }
674
675 break;
676 }
677
678 if (argc == 1)
679 {
680 if ((exec_string == NULL) && (is_interactive == 0))
681 is_interactive = (isatty (fileno(stdin)) && isatty (fileno(stdout)));
682 file = NULL;
683 }
684 else
685 {
686 file = argv[1];
687 if (0 == strcmp (file, "-"))
688 {
689 if (is_interactive)
690 usage ();
691 file = NULL;
692 }
693
694 argc--;
695 argv++;
696 }
697
698 if ((is_interactive == 0)
699 && (SLang_Version < SLANG_VERSION))
700 {
701 fprintf (stderr, "***Warning: Executable compiled against S-Lang %s but linked to %s\n",
702 SLANG_VERSION_STRING, SLang_Version_String);
703 fflush (stderr);
704 }
705
706 /* fprintf (stdout, "slsh: argv[0]=%s\n", argv[0]); */
707 if (-1 == SLang_set_argc_argv (argc, argv))
708 return 1;
709
710 if (is_interactive)
711 (void) SLdefine_for_ifdef ("__INTERACTIVE__");
712
713 if (-1 == load_startup_file (pgm, is_interactive))
714 return SLang_get_error ();
715
716 if (-1 == SLang_run_hooks ("__slsh_startup_hook", 0))
717 return SLang_get_error ();
718
719 /* Initializing the readline interface causes the .slrlinerc file
720 * to be loaded. It is put here after the startup files have been loaded.
721 */
722 if (-1 == slsh_use_readline (SLpath_basename (argv[0]), use_readline, is_interactive))
723 return 1;
724
725 if ((init_file != NULL)
726 && (-1 == try_to_load_file (init_file_dir, (char *)init_file, NULL)))
727 return SLang_get_error ();
728
729 if ((file != NULL)
730 || ((is_interactive == 0) && (exec_string == NULL)))
731 {
732 if (0 == try_to_load_file (NULL, file, NULL))
733 {
734 fprintf (stderr, "%s: file not found\n", file);
735 exit (1);
736 }
737 if (test_mode == 0)
738 (void) SLang_run_hooks ("slsh_main", 0);
739 }
740
741 if (exec_string != NULL)
742 {
743 (void) SLang_load_string (exec_string);
744 }
745
746 if (is_interactive)
747 {
748 if (quiet == 0)
749 output_copyright ();
750 if (SLang_Traceback != SL_TB_FULL)
751 SLang_Traceback = SL_TB_NONE;
752
753 (void) slsh_interactive ();
754 }
755
756 exit_val = SLang_get_error ();
757 c_exit (exit_val);
758 return SLang_get_error ();
759 }
760