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