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