perl 3.0 patch #36 patch #29, continued
[p5sagit/p5-mst-13.2.git] / perly.c
1 char rcsid[] = "$Header: perly.c,v 3.0.1.8 90/10/16 10:14:20 lwall Locked $\nPatch level: ###\n";
2 /*
3  *    Copyright (c) 1989, Larry Wall
4  *
5  *    You may distribute under the terms of the GNU General Public License
6  *    as specified in the README file that comes with the perl 3.0 kit.
7  *
8  * $Log:        perly.c,v $
9  * Revision 3.0.1.8  90/10/16  10:14:20  lwall
10  * patch29: *foo now prints as *package'foo
11  * patch29: added waitpid
12  * patch29: the debugger now understands packages and evals
13  * patch29: added -M, -A and -C
14  * patch29: -w sometimes printed spurious warnings about ARGV and ENV
15  * patch29: require "./foo" didn't work right
16  * patch29: require error messages referred to wrong file
17  * 
18  * Revision 3.0.1.7  90/08/13  22:22:22  lwall
19  * patch28: defined(@array) and defined(%array) didn't work right
20  * 
21  * Revision 3.0.1.6  90/08/09  04:55:50  lwall
22  * patch19: added -x switch to extract script from input trash
23  * patch19: Added -c switch to do compilation only
24  * patch19: added numeric interpretation of $]
25  * patch19: added require operator
26  * patch19: $0, %ENV, @ARGV were wrong in dumped script
27  * patch19: . is now explicitly in @INC (and last)
28  * 
29  * Revision 3.0.1.5  90/03/27  16:20:57  lwall
30  * patch16: MSDOS support
31  * patch16: do FILE inside eval blows up
32  * 
33  * Revision 3.0.1.4  90/02/28  18:06:41  lwall
34  * patch9: perl can now start up other interpreters scripts
35  * patch9: nested evals clobbered their longjmp environment
36  * patch9: eval could mistakenly return undef in array context
37  * 
38  * Revision 3.0.1.3  89/12/21  20:15:41  lwall
39  * patch7: ANSI strerror() is now supported
40  * patch7: errno may now be a macro with an lvalue
41  * patch7: allowed setuid scripts to have a space after #!
42  * 
43  * Revision 3.0.1.2  89/11/17  15:34:42  lwall
44  * patch5: fixed possible confusion about current effective gid
45  * 
46  * Revision 3.0.1.1  89/11/11  04:50:04  lwall
47  * patch2: moved yydebug to where its type didn't matter
48  * 
49  * Revision 3.0  89/10/18  15:22:21  lwall
50  * 3.0 baseline
51  * 
52  */
53
54 #include "EXTERN.h"
55 #include "perl.h"
56 #include "perly.h"
57 #ifdef MSDOS
58 #include "patchlev.h"
59 #else
60 #include "patchlevel.h"
61 #endif
62
63 #ifdef IAMSUID
64 #ifndef DOSUID
65 #define DOSUID
66 #endif
67 #endif
68
69 #ifdef SETUID_SCRIPTS_ARE_SECURE_NOW
70 #ifdef DOSUID
71 #undef DOSUID
72 #endif
73 #endif
74
75 static char* moreswitches();
76 static char* cddir;
77 extern char **environ;
78 static bool minus_c;
79
80 main(argc,argv,env)
81 register int argc;
82 register char **argv;
83 register char **env;
84 {
85     register STR *str;
86     register char *s;
87     char *index(), *strcpy(), *getenv();
88     bool dosearch = FALSE;
89 #ifdef DOSUID
90     char *validarg = "";
91 #endif
92
93 #ifdef SETUID_SCRIPTS_ARE_SECURE_NOW
94 #ifdef IAMSUID
95 #undef IAMSUID
96     fatal("suidperl is no longer needed since the kernel can now execute\n\
97 setuid perl scripts securely.\n");
98 #endif
99 #endif
100
101     origargv = argv;
102     origargc = argc;
103     uid = (int)getuid();
104     euid = (int)geteuid();
105     gid = (int)getgid();
106     egid = (int)getegid();
107 #ifdef MSDOS
108     /*
109      * There is no way we can refer to them from Perl so close them to save
110      * space.  The other alternative would be to provide STDAUX and STDPRN
111      * filehandles.
112      */
113     (void)fclose(stdaux);
114     (void)fclose(stdprn);
115 #endif
116     if (do_undump) {
117         origfilename = savestr(argv[0]);
118         do_undump = 0;
119         loop_ptr = -1;          /* start label stack again */
120         goto just_doit;
121     }
122     (void)sprintf(index(rcsid,'#'), "%d\n", PATCHLEVEL);
123     linestr = Str_new(65,80);
124     str_nset(linestr,"",0);
125     str = str_make("",0);               /* first used for -I flags */
126     curstash = defstash = hnew(0);
127     curstname = str_make("main",4);
128     stab_xhash(stabent("_main",TRUE)) = defstash;
129     defstash->tbl_name = "main";
130     incstab = hadd(aadd(stabent("INC",TRUE)));
131     incstab->str_pok |= SP_MULTI;
132     for (argc--,argv++; argc > 0; argc--,argv++) {
133         if (argv[0][0] != '-' || !argv[0][1])
134             break;
135 #ifdef DOSUID
136     if (*validarg)
137         validarg = " PHOOEY ";
138     else
139         validarg = argv[0];
140 #endif
141         s = argv[0]+1;
142       reswitch:
143         switch (*s) {
144         case 'a':
145         case 'c':
146         case 'd':
147         case 'D':
148         case 'i':
149         case 'n':
150         case 'p':
151         case 'u':
152         case 'U':
153         case 'v':
154         case 'w':
155             if (s = moreswitches(s))
156                 goto reswitch;
157             break;
158
159         case 'e':
160 #ifdef TAINT
161             if (euid != uid || egid != gid)
162                 fatal("No -e allowed in setuid scripts");
163 #endif
164             if (!e_fp) {
165                 e_tmpname = savestr(TMPPATH);
166                 (void)mktemp(e_tmpname);
167                 e_fp = fopen(e_tmpname,"w");
168                 if (!e_fp)
169                     fatal("Cannot open temporary file");
170             }
171             if (argv[1]) {
172                 fputs(argv[1],e_fp);
173                 argc--,argv++;
174             }
175             (void)putc('\n', e_fp);
176             break;
177         case 'I':
178 #ifdef TAINT
179             if (euid != uid || egid != gid)
180                 fatal("No -I allowed in setuid scripts");
181 #endif
182             str_cat(str,"-");
183             str_cat(str,s);
184             str_cat(str," ");
185             if (*++s) {
186                 (void)apush(stab_array(incstab),str_make(s,0));
187             }
188             else if (argv[1]) {
189                 (void)apush(stab_array(incstab),str_make(argv[1],0));
190                 str_cat(str,argv[1]);
191                 argc--,argv++;
192                 str_cat(str," ");
193             }
194             break;
195         case 'P':
196 #ifdef TAINT
197             if (euid != uid || egid != gid)
198                 fatal("No -P allowed in setuid scripts");
199 #endif
200             preprocess = TRUE;
201             s++;
202             goto reswitch;
203         case 's':
204 #ifdef TAINT
205             if (euid != uid || egid != gid)
206                 fatal("No -s allowed in setuid scripts");
207 #endif
208             doswitches = TRUE;
209             s++;
210             goto reswitch;
211         case 'S':
212             dosearch = TRUE;
213             s++;
214             goto reswitch;
215         case 'x':
216             doextract = TRUE;
217             s++;
218             if (*s)
219                 cddir = savestr(s);
220             break;
221         case '-':
222             argc--,argv++;
223             goto switch_end;
224         case 0:
225             break;
226         default:
227             fatal("Unrecognized switch: -%s",s);
228         }
229     }
230   switch_end:
231     if (e_fp) {
232         (void)fclose(e_fp);
233         argc++,argv--;
234         argv[0] = e_tmpname;
235     }
236 #ifndef PRIVLIB
237 #define PRIVLIB "/usr/local/lib/perl"
238 #endif
239     (void)apush(stab_array(incstab),str_make(PRIVLIB,0));
240     (void)apush(stab_array(incstab),str_make(".",1));
241
242     str_set(&str_no,No);
243     str_set(&str_yes,Yes);
244
245     /* open script */
246
247     if (argv[0] == Nullch)
248         argv[0] = "-";
249     if (dosearch && !index(argv[0], '/') && (s = getenv("PATH"))) {
250         char *xfound = Nullch, *xfailed = Nullch;
251         int len;
252
253         bufend = s + strlen(s);
254         while (*s) {
255 #ifndef MSDOS
256             s = cpytill(tokenbuf,s,bufend,':',&len);
257 #else
258             for (len = 0; *s && *s != ';'; tokenbuf[len++] = *s++);
259             tokenbuf[len] = '\0';
260 #endif
261             if (*s)
262                 s++;
263 #ifndef MSDOS
264             if (len && tokenbuf[len-1] != '/')
265 #else
266             if (len && tokenbuf[len-1] != '\\')
267 #endif
268                 (void)strcat(tokenbuf+len,"/");
269             (void)strcat(tokenbuf+len,argv[0]);
270 #ifdef DEBUGGING
271             if (debug & 1)
272                 fprintf(stderr,"Looking for %s\n",tokenbuf);
273 #endif
274             if (stat(tokenbuf,&statbuf) < 0)            /* not there? */
275                 continue;
276             if ((statbuf.st_mode & S_IFMT) == S_IFREG
277              && cando(S_IREAD,TRUE,&statbuf) && cando(S_IEXEC,TRUE,&statbuf)) {
278                 xfound = tokenbuf;              /* bingo! */
279                 break;
280             }
281             if (!xfailed)
282                 xfailed = savestr(tokenbuf);
283         }
284         if (!xfound)
285             fatal("Can't execute %s", xfailed ? xfailed : argv[0] );
286         if (xfailed)
287             Safefree(xfailed);
288         argv[0] = savestr(xfound);
289     }
290
291     fdpid = anew(Nullstab);     /* for remembering popen pids by fd */
292     pidstatus = hnew(Nullstab); /* for remembering status of dead pids */
293
294     origfilename = savestr(argv[0]);
295     curcmd->c_filestab = fstab(origfilename);
296     if (strEQ(origfilename,"-"))
297         argv[0] = "";
298     if (preprocess) {
299         str_cat(str,"-I");
300         str_cat(str,PRIVLIB);
301         (void)sprintf(buf, "\
302 %ssed %s -e '/^[^#]/b' \
303  -e '/^#[       ]*include[      ]/b' \
304  -e '/^#[       ]*define[       ]/b' \
305  -e '/^#[       ]*if[   ]/b' \
306  -e '/^#[       ]*ifdef[        ]/b' \
307  -e '/^#[       ]*ifndef[       ]/b' \
308  -e '/^#[       ]*else/b' \
309  -e '/^#[       ]*endif/b' \
310  -e 's/^#.*//' \
311  %s | %s -C %s %s",
312 #ifdef MSDOS
313           "",
314 #else
315           "/bin/",
316 #endif
317           (doextract ? "-e '1,/^#/d\n'" : ""),
318           argv[0], CPPSTDIN, str_get(str), CPPMINUS);
319           doextract = FALSE;
320 #ifdef IAMSUID                          /* actually, this is caught earlier */
321         if (euid != uid && !euid)       /* if running suidperl */
322 #ifdef SETEUID
323             (void)seteuid(uid);         /* musn't stay setuid root */
324 #else
325 #ifdef SETREUID
326             (void)setreuid(-1, uid);
327 #else
328             setuid(uid);
329 #endif
330 #endif
331 #endif /* IAMSUID */
332         rsfp = mypopen(buf,"r");
333     }
334     else if (!*argv[0])
335         rsfp = stdin;
336     else
337         rsfp = fopen(argv[0],"r");
338     if (rsfp == Nullfp) {
339 #ifdef DOSUID
340 #ifndef IAMSUID         /* in case script is not readable before setuid */
341         if (euid && stat(stab_val(curcmd->c_filestab)->str_ptr,&statbuf) >= 0 &&
342           statbuf.st_mode & (S_ISUID|S_ISGID)) {
343             (void)sprintf(buf, "%s/%s", BIN, "suidperl");
344             execv(buf, origargv);       /* try again */
345             fatal("Can't do setuid\n");
346         }
347 #endif
348 #endif
349         fatal("Can't open perl script \"%s\": %s\n",
350           stab_val(curcmd->c_filestab)->str_ptr, strerror(errno));
351     }
352     str_free(str);              /* free -I directories */
353
354     /* do we need to emulate setuid on scripts? */
355
356     /* This code is for those BSD systems that have setuid #! scripts disabled
357      * in the kernel because of a security problem.  Merely defining DOSUID
358      * in perl will not fix that problem, but if you have disabled setuid
359      * scripts in the kernel, this will attempt to emulate setuid and setgid
360      * on scripts that have those now-otherwise-useless bits set.  The setuid
361      * root version must be called suidperl.  If regular perl discovers that
362      * it has opened a setuid script, it calls suidperl with the same argv
363      * that it had.  If suidperl finds that the script it has just opened
364      * is NOT setuid root, it sets the effective uid back to the uid.  We
365      * don't just make perl setuid root because that loses the effective
366      * uid we had before invoking perl, if it was different from the uid.
367      *
368      * DOSUID must be defined in both perl and suidperl, and IAMSUID must
369      * be defined in suidperl only.  suidperl must be setuid root.  The
370      * Configure script will set this up for you if you want it.
371      *
372      * There is also the possibility of have a script which is running
373      * set-id due to a C wrapper.  We want to do the TAINT checks
374      * on these set-id scripts, but don't want to have the overhead of
375      * them in normal perl, and can't use suidperl because it will lose
376      * the effective uid info, so we have an additional non-setuid root
377      * version called taintperl that just does the TAINT checks.
378      */
379
380 #ifdef DOSUID
381     if (fstat(fileno(rsfp),&statbuf) < 0)       /* normal stat is insecure */
382         fatal("Can't stat script \"%s\"",origfilename);
383     if (statbuf.st_mode & (S_ISUID|S_ISGID)) {
384         int len;
385
386 #ifdef IAMSUID
387 #ifndef SETREUID
388         /* On this access check to make sure the directories are readable,
389          * there is actually a small window that the user could use to make
390          * filename point to an accessible directory.  So there is a faint
391          * chance that someone could execute a setuid script down in a
392          * non-accessible directory.  I don't know what to do about that.
393          * But I don't think it's too important.  The manual lies when
394          * it says access() is useful in setuid programs.
395          */
396         if (access(stab_val(curcmd->c_filestab)->str_ptr,1))    /*double check*/
397             fatal("Permission denied");
398 #else
399         /* If we can swap euid and uid, then we can determine access rights
400          * with a simple stat of the file, and then compare device and
401          * inode to make sure we did stat() on the same file we opened.
402          * Then we just have to make sure he or she can execute it.
403          */
404         {
405             struct stat tmpstatbuf;
406
407             if (setreuid(euid,uid) < 0 || getuid() != euid || geteuid() != uid)
408                 fatal("Can't swap uid and euid");       /* really paranoid */
409             if (stat(stab_val(curcmd->c_filestab)->str_ptr,&tmpstatbuf) < 0)
410                 fatal("Permission denied");     /* testing full pathname here */
411             if (tmpstatbuf.st_dev != statbuf.st_dev ||
412                 tmpstatbuf.st_ino != statbuf.st_ino) {
413                 (void)fclose(rsfp);
414                 if (rsfp = mypopen("/bin/mail root","w")) {     /* heh, heh */
415                     fprintf(rsfp,
416 "User %d tried to run dev %d ino %d in place of dev %d ino %d!\n\
417 (Filename of set-id script was %s, uid %d gid %d.)\n\nSincerely,\nperl\n",
418                         uid,tmpstatbuf.st_dev, tmpstatbuf.st_ino,
419                         statbuf.st_dev, statbuf.st_ino,
420                         stab_val(curcmd->c_filestab)->str_ptr,
421                         statbuf.st_uid, statbuf.st_gid);
422                     (void)mypclose(rsfp);
423                 }
424                 fatal("Permission denied\n");
425             }
426             if (setreuid(uid,euid) < 0 || getuid() != uid || geteuid() != euid)
427                 fatal("Can't reswap uid and euid");
428             if (!cando(S_IEXEC,FALSE,&statbuf))         /* can real uid exec? */
429                 fatal("Permission denied\n");
430         }
431 #endif /* SETREUID */
432 #endif /* IAMSUID */
433
434         if ((statbuf.st_mode & S_IFMT) != S_IFREG)
435             fatal("Permission denied");
436         if ((statbuf.st_mode >> 6) & S_IWRITE)
437             fatal("Setuid/gid script is writable by world");
438         doswitches = FALSE;             /* -s is insecure in suid */
439         curcmd->c_line++;
440         if (fgets(tokenbuf,sizeof tokenbuf, rsfp) == Nullch ||
441           strnNE(tokenbuf,"#!",2) )     /* required even on Sys V */
442             fatal("No #! line");
443         s = tokenbuf+2;
444         if (*s == ' ') s++;
445         while (!isspace(*s)) s++;
446         if (strnNE(s-4,"perl",4))       /* sanity check */
447             fatal("Not a perl script");
448         while (*s == ' ' || *s == '\t') s++;
449         /*
450          * #! arg must be what we saw above.  They can invoke it by
451          * mentioning suidperl explicitly, but they may not add any strange
452          * arguments beyond what #! says if they do invoke suidperl that way.
453          */
454         len = strlen(validarg);
455         if (strEQ(validarg," PHOOEY ") ||
456             strnNE(s,validarg,len) || !isspace(s[len]))
457             fatal("Args must match #! line");
458
459 #ifndef IAMSUID
460         if (euid != uid && (statbuf.st_mode & S_ISUID) &&
461             euid == statbuf.st_uid)
462             if (!do_undump)
463                 fatal("YOU HAVEN'T DISABLED SET-ID SCRIPTS IN THE KERNEL YET!\n\
464 FIX YOUR KERNEL, PUT A C WRAPPER AROUND THIS SCRIPT, OR USE -u AND UNDUMP!\n");
465 #endif /* IAMSUID */
466
467         if (euid) {     /* oops, we're not the setuid root perl */
468             (void)fclose(rsfp);
469 #ifndef IAMSUID
470             (void)sprintf(buf, "%s/%s", BIN, "suidperl");
471             execv(buf, origargv);       /* try again */
472 #endif
473             fatal("Can't do setuid\n");
474         }
475
476         if (statbuf.st_mode & S_ISGID && statbuf.st_gid != egid)
477 #ifdef SETEGID
478             (void)setegid(statbuf.st_gid);
479 #else
480 #ifdef SETREGID
481             (void)setregid((GIDTYPE)-1,statbuf.st_gid);
482 #else
483             setgid(statbuf.st_gid);
484 #endif
485 #endif
486         if (statbuf.st_mode & S_ISUID) {
487             if (statbuf.st_uid != euid)
488 #ifdef SETEUID
489                 (void)seteuid(statbuf.st_uid);  /* all that for this */
490 #else
491 #ifdef SETREUID
492                 (void)setreuid((UIDTYPE)-1,statbuf.st_uid);
493 #else
494                 setuid(statbuf.st_uid);
495 #endif
496 #endif
497         }
498         else if (uid)                   /* oops, mustn't run as root */
499 #ifdef SETEUID
500             (void)seteuid((UIDTYPE)uid);
501 #else
502 #ifdef SETREUID
503             (void)setreuid((UIDTYPE)-1,(UIDTYPE)uid);
504 #else
505             setuid((UIDTYPE)uid);
506 #endif
507 #endif
508         uid = (int)getuid();
509         euid = (int)geteuid();
510         gid = (int)getgid();
511         egid = (int)getegid();
512         if (!cando(S_IEXEC,TRUE,&statbuf))
513             fatal("Permission denied\n");       /* they can't do this */
514     }
515 #ifdef IAMSUID
516     else if (preprocess)
517         fatal("-P not allowed for setuid/setgid script\n");
518     else
519         fatal("Script is not setuid/setgid in suidperl\n");
520 #else
521 #ifndef TAINT           /* we aren't taintperl or suidperl */
522     /* script has a wrapper--can't run suidperl or we lose euid */
523     else if (euid != uid || egid != gid) {
524         (void)fclose(rsfp);
525         (void)sprintf(buf, "%s/%s", BIN, "taintperl");
526         execv(buf, origargv);   /* try again */
527         fatal("Can't run setuid script with taint checks");
528     }
529 #endif /* TAINT */
530 #endif /* IAMSUID */
531 #else /* !DOSUID */
532 #ifndef TAINT           /* we aren't taintperl or suidperl */
533     if (euid != uid || egid != gid) {   /* (suidperl doesn't exist, in fact) */
534 #ifndef SETUID_SCRIPTS_ARE_SECURE_NOW
535         fstat(fileno(rsfp),&statbuf);   /* may be either wrapped or real suid */
536         if ((euid != uid && euid == statbuf.st_uid && statbuf.st_mode & S_ISUID)
537             ||
538             (egid != gid && egid == statbuf.st_gid && statbuf.st_mode & S_ISGID)
539            )
540             if (!do_undump)
541                 fatal("YOU HAVEN'T DISABLED SET-ID SCRIPTS IN THE KERNEL YET!\n\
542 FIX YOUR KERNEL, PUT A C WRAPPER AROUND THIS SCRIPT, OR USE -u AND UNDUMP!\n");
543 #endif /* SETUID_SCRIPTS_ARE_SECURE_NOW */
544         /* not set-id, must be wrapped */
545         (void)fclose(rsfp);
546         (void)sprintf(buf, "%s/%s", BIN, "taintperl");
547         execv(buf, origargv);   /* try again */
548         fatal("Can't run setuid script with taint checks");
549     }
550 #endif /* TAINT */
551 #endif /* DOSUID */
552
553 #if !defined(IAMSUID) && !defined(TAINT)
554
555     /* skip forward in input to the real script? */
556
557     while (doextract) {
558         if ((s = str_gets(linestr, rsfp, 0)) == Nullch)
559             fatal("No Perl script found in input\n");
560         if (*s == '#' && s[1] == '!' && instr(s,"perl")) {
561             ungetc('\n',rsfp);          /* to keep line count right */
562             doextract = FALSE;
563             if (s = instr(s,"perl -")) {
564                 s += 6;
565                 while (s = moreswitches(s)) ;
566             }
567             if (cddir && chdir(cddir) < 0)
568                 fatal("Can't chdir to %s",cddir);
569         }
570     }
571 #endif /* !defined(IAMSUID) && !defined(TAINT) */
572
573     defstab = stabent("_",TRUE);
574
575     if (perldb) {
576         debstash = hnew(0);
577         stab_xhash(stabent("_DB",TRUE)) = debstash;
578         curstash = debstash;
579         dbargs = stab_xarray(aadd((tmpstab = stabent("args",TRUE))));
580         tmpstab->str_pok |= SP_MULTI;
581         dbargs->ary_flags = 0;
582         subname = str_make("main",4);
583         DBstab = stabent("DB",TRUE);
584         DBstab->str_pok |= SP_MULTI;
585         DBline = stabent("dbline",TRUE);
586         DBline->str_pok |= SP_MULTI;
587         DBsub = hadd(tmpstab = stabent("sub",TRUE));
588         tmpstab->str_pok |= SP_MULTI;
589         DBsingle = stab_val((tmpstab = stabent("single",TRUE)));
590         tmpstab->str_pok |= SP_MULTI;
591         DBtrace = stab_val((tmpstab = stabent("trace",TRUE)));
592         tmpstab->str_pok |= SP_MULTI;
593         DBsignal = stab_val((tmpstab = stabent("signal",TRUE)));
594         tmpstab->str_pok |= SP_MULTI;
595         curstash = defstash;
596     }
597
598     /* init tokener */
599
600     bufend = bufptr = str_get(linestr);
601
602     savestack = anew(Nullstab);         /* for saving non-local values */
603     stack = anew(Nullstab);             /* for saving non-local values */
604     stack->ary_flags = 0;               /* not a real array */
605     afill(stack,63); afill(stack,-1);   /* preextend stack */
606     afill(savestack,63); afill(savestack,-1);
607
608     /* now parse the script */
609
610     error_count = 0;
611     if (yyparse() || error_count) {
612         if (minus_c)
613             fatal("%s had compilation errors.\n", origfilename);
614         else {
615             fatal("Execution of %s aborted due to compilation errors.\n",
616                 origfilename);
617         }
618     }
619
620     New(50,loop_stack,128,struct loop);
621 #ifdef DEBUGGING
622     if (debug) {
623         New(51,debname,128,char);
624         New(52,debdelim,128,char);
625     }
626 #endif
627     curstash = defstash;
628
629     preprocess = FALSE;
630     if (e_fp) {
631         e_fp = Nullfp;
632         (void)UNLINK(e_tmpname);
633     }
634
635     /* initialize everything that won't change if we undump */
636
637     if (sigstab = stabent("SIG",allstabs)) {
638         sigstab->str_pok |= SP_MULTI;
639         (void)hadd(sigstab);
640     }
641
642     magicalize("!#?^~=-%0123456789.+&*()<>,\\/[|`':\024");
643     userinit();         /* in case linked C routines want magical variables */
644
645     amperstab = stabent("&",allstabs);
646     leftstab = stabent("`",allstabs);
647     rightstab = stabent("'",allstabs);
648     sawampersand = (amperstab || leftstab || rightstab);
649     if (tmpstab = stabent(":",allstabs))
650         str_set(STAB_STR(tmpstab),chopset);
651     if (tmpstab = stabent("\024",allstabs))
652         time(&basetime);
653
654     /* these aren't necessarily magical */
655     if (tmpstab = stabent(";",allstabs))
656         str_set(STAB_STR(tmpstab),"\034");
657     if (tmpstab = stabent("]",allstabs)) {
658         str = STAB_STR(tmpstab);
659         str_set(str,rcsid);
660         strncpy(tokenbuf,rcsid+19,3);
661         sprintf(tokenbuf+3,"%2.2d",PATCHLEVEL);
662         str->str_u.str_nval = atof(tokenbuf);
663         str->str_nok = 1;
664     }
665     str_nset(stab_val(stabent("\"", TRUE)), " ", 1);
666
667     stdinstab = stabent("STDIN",TRUE);
668     stdinstab->str_pok |= SP_MULTI;
669     stab_io(stdinstab) = stio_new();
670     stab_io(stdinstab)->ifp = stdin;
671     tmpstab = stabent("stdin",TRUE);
672     stab_io(tmpstab) = stab_io(stdinstab);
673     tmpstab->str_pok |= SP_MULTI;
674
675     tmpstab = stabent("STDOUT",TRUE);
676     tmpstab->str_pok |= SP_MULTI;
677     stab_io(tmpstab) = stio_new();
678     stab_io(tmpstab)->ofp = stab_io(tmpstab)->ifp = stdout;
679     defoutstab = tmpstab;
680     tmpstab = stabent("stdout",TRUE);
681     stab_io(tmpstab) = stab_io(defoutstab);
682     tmpstab->str_pok |= SP_MULTI;
683
684     curoutstab = stabent("STDERR",TRUE);
685     curoutstab->str_pok |= SP_MULTI;
686     stab_io(curoutstab) = stio_new();
687     stab_io(curoutstab)->ofp = stab_io(curoutstab)->ifp = stderr;
688     tmpstab = stabent("stderr",TRUE);
689     stab_io(tmpstab) = stab_io(curoutstab);
690     tmpstab->str_pok |= SP_MULTI;
691     curoutstab = defoutstab;            /* switch back to STDOUT */
692
693     statname = Str_new(66,0);           /* last filename we did stat on */
694
695     if (do_undump)
696         abort();
697
698   just_doit:            /* come here if running an undumped a.out */
699     argc--,argv++;      /* skip name of script */
700     if (doswitches) {
701         for (; argc > 0 && **argv == '-'; argc--,argv++) {
702             if (argv[0][1] == '-') {
703                 argc--,argv++;
704                 break;
705             }
706             str_numset(stab_val(stabent(argv[0]+1,TRUE)),(double)1.0);
707         }
708     }
709 #ifdef TAINT
710     tainted = 1;
711 #endif
712     if (tmpstab = stabent("0",allstabs))
713         str_set(STAB_STR(tmpstab),origfilename);
714     if (argvstab = stabent("ARGV",allstabs)) {
715         argvstab->str_pok |= SP_MULTI;
716         (void)aadd(argvstab);
717         aclear(stab_array(argvstab));
718         for (; argc > 0; argc--,argv++) {
719             (void)apush(stab_array(argvstab),str_make(argv[0],0));
720         }
721     }
722 #ifdef TAINT
723     (void) stabent("ENV",TRUE);         /* must test PATH and IFS */
724 #endif
725     if (envstab = stabent("ENV",allstabs)) {
726         envstab->str_pok |= SP_MULTI;
727         (void)hadd(envstab);
728         hclear(stab_hash(envstab), FALSE);
729         if (env != environ)
730             environ[0] = Nullch;
731         for (; *env; env++) {
732             if (!(s = index(*env,'=')))
733                 continue;
734             *s++ = '\0';
735             str = str_make(s--,0);
736             str_magic(str, envstab, 'E', *env, s - *env);
737             (void)hstore(stab_hash(envstab), *env, s - *env, str, 0);
738             *s = '=';
739         }
740     }
741 #ifdef TAINT
742     tainted = 0;
743 #endif
744     if (tmpstab = stabent("$",allstabs))
745         str_numset(STAB_STR(tmpstab),(double)getpid());
746
747     if (dowarn) {
748         stab_check('A','Z');
749         stab_check('a','z');
750     }
751
752     if (setjmp(top_env))        /* sets goto_targ on longjump */
753         loop_ptr = -1;          /* start label stack again */
754
755 #ifdef DEBUGGING
756     if (debug & 1024)
757         dump_all();
758     if (debug)
759         fprintf(stderr,"\nEXECUTING...\n\n");
760 #endif
761
762     if (minus_c) {
763         fprintf(stderr,"%s syntax OK\n", origfilename);
764         exit(0);
765     }
766
767     /* do it */
768
769     (void) cmd_exec(main_root,G_SCALAR,-1);
770
771     if (goto_targ)
772         fatal("Can't find label \"%s\"--aborting",goto_targ);
773     exit(0);
774     /* NOTREACHED */
775 }
776
777 magicalize(list)
778 register char *list;
779 {
780     char sym[2];
781
782     sym[1] = '\0';
783     while (*sym = *list++)
784         magicname(sym, Nullch, 0);
785 }
786
787 int
788 magicname(sym,name,namlen)
789 char *sym;
790 char *name;
791 int namlen;
792 {
793     register STAB *stab;
794
795     if (stab = stabent(sym,allstabs)) {
796         stab_flags(stab) = SF_VMAGIC;
797         str_magic(stab_val(stab), stab, 0, name, namlen);
798     }
799 }
800
801 /* this routine is in perly.c by virtue of being sort of an alternate main() */
802
803 int
804 do_eval(str,optype,stash,gimme,arglast)
805 STR *str;
806 int optype;
807 HASH *stash;
808 int gimme;
809 int *arglast;
810 {
811     STR **st = stack->ary_array;
812     int retval;
813     CMD *myroot;
814     ARRAY *ar;
815     int i;
816     CMD * VOLATILE oldcurcmd = curcmd;
817     VOLATILE int oldtmps_base = tmps_base;
818     VOLATILE int oldsave = savestack->ary_fill;
819     VOLATILE int oldperldb = perldb;
820     SPAT * VOLATILE oldspat = curspat;
821     static char *last_eval = Nullch;
822     static CMD *last_root = Nullcmd;
823     VOLATILE int sp = arglast[0];
824     char *specfilename;
825     char *tmpfilename;
826
827     tmps_base = tmps_max;
828     if (curstash != stash) {
829         (void)savehptr(&curstash);
830         curstash = stash;
831     }
832     str_set(stab_val(stabent("@",TRUE)),"");
833     if (curcmd->c_line == 0)            /* don't debug debugger... */
834         perldb = FALSE;
835     curcmd = &compiling;
836     if (optype == O_EVAL) {             /* normal eval */
837         curcmd->c_filestab = fstab("(eval)");
838         curcmd->c_line = 1;
839         str_sset(linestr,str);
840         str_cat(linestr,";");           /* be kind to them */
841     }
842     else {
843         if (last_root && !in_eval) {
844             Safefree(last_eval);
845             cmd_free(last_root);
846             last_root = Nullcmd;
847         }
848         specfilename = str_get(str);
849         str_set(linestr,"");
850         if (optype == O_REQUIRE && &str_undef !=
851           hfetch(stab_hash(incstab), specfilename, strlen(specfilename), 0)) {
852             curcmd = oldcurcmd;
853             tmps_base = oldtmps_base;
854             st[++sp] = &str_yes;
855             perldb = oldperldb;
856             return sp;
857         }
858         tmpfilename = savestr(specfilename);
859         if (index("/.", *tmpfilename))
860             rsfp = fopen(tmpfilename,"r");
861         else {
862             ar = stab_array(incstab);
863             for (i = 0; i <= ar->ary_fill; i++) {
864                 (void)sprintf(buf, "%s/%s",
865                   str_get(afetch(ar,i,TRUE)), specfilename);
866                 rsfp = fopen(buf,"r");
867                 if (rsfp) {
868                     char *s = buf;
869
870                     if (*s == '.' && s[1] == '/')
871                         s += 2;
872                     Safefree(tmpfilename);
873                     tmpfilename = savestr(s);
874                     break;
875                 }
876             }
877         }
878         curcmd->c_filestab = fstab(tmpfilename);
879         Safefree(tmpfilename);
880         if (!rsfp) {
881             curcmd = oldcurcmd;
882             tmps_base = oldtmps_base;
883             if (optype == O_REQUIRE) {
884                 sprintf(tokenbuf,"Can't locate %s in @INC", specfilename);
885                 if (instr(tokenbuf,".h "))
886                     strcat(tokenbuf," (change .h to .ph maybe?)");
887                 if (instr(tokenbuf,".ph "))
888                     strcat(tokenbuf," (did you run h2ph?)");
889                 fatal("%s",tokenbuf);
890             }
891             if (gimme != G_ARRAY)
892                 st[++sp] = &str_undef;
893             perldb = oldperldb;
894             return sp;
895         }
896         curcmd->c_line = 0;
897     }
898     in_eval++;
899     oldoldbufptr = oldbufptr = bufptr = str_get(linestr);
900     bufend = bufptr + linestr->str_cur;
901     if (++loop_ptr >= loop_max) {
902         loop_max += 128;
903         Renew(loop_stack, loop_max, struct loop);
904     }
905     loop_stack[loop_ptr].loop_label = "_EVAL_";
906     loop_stack[loop_ptr].loop_sp = sp;
907 #ifdef DEBUGGING
908     if (debug & 4) {
909         deb("(Pushing label #%d _EVAL_)\n", loop_ptr);
910     }
911 #endif
912     if (setjmp(loop_stack[loop_ptr].loop_env)) {
913         retval = 1;
914         last_root = Nullcmd;
915     }
916     else {
917         error_count = 0;
918         if (rsfp) {
919             retval = yyparse();
920             retval |= error_count;
921         }
922         else if (last_root && *bufptr == *last_eval && strEQ(bufptr,last_eval)){
923             retval = 0;
924             eval_root = last_root;      /* no point in reparsing */
925         }
926         else if (in_eval == 1) {
927             if (last_root) {
928                 Safefree(last_eval);
929                 cmd_free(last_root);
930             }
931             last_eval = savestr(bufptr);
932             last_root = Nullcmd;
933             retval = yyparse();
934             retval |= error_count;
935             if (!retval)
936                 last_root = eval_root;
937         }
938         else
939             retval = yyparse();
940     }
941     myroot = eval_root;         /* in case cmd_exec does another eval! */
942
943     if (retval) {
944         st = stack->ary_array;
945         sp = arglast[0];
946         if (gimme != G_ARRAY)
947             st[++sp] = &str_undef;
948         last_root = Nullcmd;    /* can't free on error, for some reason */
949         if (rsfp) {
950             fclose(rsfp);
951             rsfp = 0;
952         }
953     }
954     else {
955         sp = cmd_exec(eval_root,gimme,sp);
956         st = stack->ary_array;
957         for (i = arglast[0] + 1; i <= sp; i++)
958             st[i] = str_static(st[i]);
959                                 /* if we don't save result, free zaps it */
960         if (in_eval != 1 && myroot != last_root)
961             cmd_free(myroot);
962     }
963
964     perldb = oldperldb;
965     in_eval--;
966 #ifdef DEBUGGING
967     if (debug & 4) {
968         char *tmps = loop_stack[loop_ptr].loop_label;
969         deb("(Popping label #%d %s)\n",loop_ptr,
970             tmps ? tmps : "" );
971     }
972 #endif
973     loop_ptr--;
974     tmps_base = oldtmps_base;
975     curspat = oldspat;
976     if (savestack->ary_fill > oldsave)  /* let them use local() */
977         restorelist(oldsave);
978
979     if (optype != O_EVAL) {
980         if (retval) {
981             if (optype == O_REQUIRE)
982                 fatal("%s", str_get(stab_val(stabent("@",TRUE))));
983         }
984         else {
985             curcmd = oldcurcmd;
986             if (gimme == G_SCALAR ? str_true(st[sp]) : sp > arglast[0]) {
987                 (void)hstore(stab_hash(incstab), specfilename,
988                   strlen(specfilename), str_smake(stab_val(curcmd->c_filestab)),
989                       0 );
990             }
991             else if (optype == O_REQUIRE)
992                 fatal("%s did not return a true value", specfilename);
993         }
994     }
995     curcmd = oldcurcmd;
996     return sp;
997 }
998
999 /* This routine handles any switches that can be given during run */
1000
1001 static char *
1002 moreswitches(s)
1003 char *s;
1004 {
1005   reswitch:
1006     switch (*s) {
1007     case 'a':
1008         minus_a = TRUE;
1009         s++;
1010         return s;
1011     case 'c':
1012         minus_c = TRUE;
1013         s++;
1014         return s;
1015     case 'd':
1016 #ifdef TAINT
1017         if (euid != uid || egid != gid)
1018             fatal("No -d allowed in setuid scripts");
1019 #endif
1020         perldb = TRUE;
1021         s++;
1022         return s;
1023     case 'D':
1024 #ifdef DEBUGGING
1025 #ifdef TAINT
1026         if (euid != uid || egid != gid)
1027             fatal("No -D allowed in setuid scripts");
1028 #endif
1029         debug = atoi(s+1);
1030 #else
1031         warn("Recompile perl with -DDEBUGGING to use -D switch\n");
1032 #endif
1033         break;
1034     case 'i':
1035         inplace = savestr(s+1);
1036         for (s = inplace; *s && !isspace(*s); s++) ;
1037         *s = '\0';
1038         argvoutstab = stabent("ARGVOUT",TRUE);
1039         break;
1040     case 'I':
1041 #ifdef TAINT
1042         if (euid != uid || egid != gid)
1043             fatal("No -I allowed in setuid scripts");
1044 #endif
1045         if (*++s) {
1046             (void)apush(stab_array(incstab),str_make(s,0));
1047         }
1048         else
1049             fatal("No space allowed after -I");
1050         break;
1051     case 'n':
1052         minus_n = TRUE;
1053         s++;
1054         return s;
1055     case 'p':
1056         minus_p = TRUE;
1057         s++;
1058         return s;
1059     case 'u':
1060         do_undump = TRUE;
1061         s++;
1062         return s;
1063     case 'U':
1064         unsafe = TRUE;
1065         s++;
1066         return s;
1067     case 'v':
1068         fputs("\nThis is perl, version 3.0\n\n",stdout);
1069         fputs(rcsid,stdout);
1070         fputs("\nCopyright (c) 1989, 1990, Larry Wall\n",stdout);
1071 #ifdef MSDOS
1072         fputs("MS-DOS port Copyright (c) 1989, 1990, Diomidis Spinellis\n",
1073         stdout);
1074 #ifdef OS2
1075         fputs("OS/2 port Copyright (c) 1990, Raymond Chen, Kai Uwe Rommel\n",
1076         stdout);
1077 #endif
1078 #endif
1079         fputs("\n\
1080 Perl may be copied only under the terms of the GNU General Public License,\n\
1081 a copy of which can be found with the Perl 3.0 distribution kit.\n",stdout);
1082 #ifdef MSDOS
1083         usage(origargv[0]);
1084 #endif
1085         exit(0);
1086     case 'w':
1087         dowarn = TRUE;
1088         s++;
1089         return s;
1090     case ' ':
1091     case '\n':
1092     case '\t':
1093         break;
1094     default:
1095         fatal("Switch meaningless after -x: -%s",s);
1096     }
1097     return Nullch;
1098 }