This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
add $Config('scriptdir'} on VMS
[perl5.git] / toke.c
diff --git a/toke.c b/toke.c
index ea0f650..52706a3 100644 (file)
--- a/toke.c
+++ b/toke.c
@@ -36,10 +36,17 @@ static I32 utf16rev_textfilter(pTHXo_ int idx, SV *sv, int maxlen);
 #define XFAKEBRACK 128
 #define XENUMMASK 127
 
-/*#define UTF (SvUTF8(PL_linestr) && !(PL_hints & HINT_BYTE))*/
-#define UTF ((PL_linestr && DO_UTF8(PL_linestr)) || PL_hints & HINT_UTF8)
+#ifdef USE_UTF8_SCRIPTS
+#   define UTF (!IN_BYTES)
+#else
+#   ifdef EBCDIC /* For now 'use utf8' does not affect tokenizer on EBCDIC */
+#       define UTF (PL_linestr && DO_UTF8(PL_linestr))
+#   else
+#       define UTF ((PL_linestr && DO_UTF8(PL_linestr)) || (PL_hints & HINT_UTF8))
+#   endif
+#endif
 
-/* In variables name $^X, these are the legal values for X.
+/* In variables named $^X, these are the legal values for X.
  * 1999-02-27 mjd-perl-patch@plover.com */
 #define isCONTROLVAR(x) (isUPPER(x) || strchr("[\\]^_?", (x)))
 
@@ -177,13 +184,15 @@ int yyactlevel = -1;
 /* grandfather return to old style */
 #define OLDLOP(f) return(yylval.ival=f,PL_expect = XTERM,PL_bufptr = s,(int)LSTOP)
 
+#ifdef DEBUGGING
+
 STATIC void
 S_tokereport(pTHX_ char *thing, char* s, I32 rv)
 {
-    SV *report;
     DEBUG_T({
-        report = newSVpv(thing, 0);
-        Perl_sv_catpvf(aTHX_ report, ":line %i:%i:", CopLINE(PL_curcop), rv);
+        SV* report = newSVpv(thing, 0);
+        Perl_sv_catpvf(aTHX_ report, ":line %d:%"IVdf":", CopLINE(PL_curcop),
+               (IV)rv);
 
         if (s - PL_bufptr > 0)
             sv_catpvn(report, PL_bufptr, s - PL_bufptr);
@@ -192,9 +201,11 @@ S_tokereport(pTHX_ char *thing, char* s, I32 rv)
                 sv_catpv(report, PL_tokenbuf);
         }
         PerlIO_printf(Perl_debug_log, "### %s\n", SvPV_nolen(report));
-    })
+    });
 }
 
+#endif
+
 /*
  * S_ao
  *
@@ -533,7 +544,7 @@ S_skipspace(pTHX_ register char *s)
     for (;;) {
        STRLEN prevlen;
        SSize_t oldprevlen, oldoldprevlen;
-       SSize_t oldloplen, oldunilen;
+       SSize_t oldloplen = 0, oldunilen = 0;
        while (s < PL_bufend && isSPACE(*s)) {
            if (*s++ == '\n' && PL_in_eval && !PL_rsfp)
                incline(s);
@@ -1038,6 +1049,7 @@ S_sublex_push(pTHX)
     SAVEI32(PL_lex_inwhat);
     SAVECOPLINE(PL_curcop);
     SAVEPPTR(PL_bufptr);
+    SAVEPPTR(PL_bufend);
     SAVEPPTR(PL_oldbufptr);
     SAVEPPTR(PL_oldoldbufptr);
     SAVEPPTR(PL_last_lop);
@@ -1218,22 +1230,22 @@ S_scan_const(pTHX_ char *start)
     register char *d = SvPVX(sv);              /* destination for copies */
     bool dorange = FALSE;                      /* are we in a translit range? */
     bool didrange = FALSE;                     /* did we just finish a range? */
-    bool has_utf8 = (PL_linestr && SvUTF8(PL_linestr));
-                                               /* the constant is UTF8 */
+    I32  has_utf8 = FALSE;                     /* Output constant is UTF8 */
+    I32  this_utf8 = UTF;                      /* The source string is assumed to be UTF8 */
     UV uv;
 
-    I32 utf = (PL_lex_inwhat == OP_TRANS && PL_sublex_info.sub_op)
-       ? (PL_sublex_info.sub_op->op_private & (OPpTRANS_FROM_UTF|OPpTRANS_TO_UTF))
-       : UTF;
-    I32 this_utf8 = (PL_lex_inwhat == OP_TRANS && PL_sublex_info.sub_op)
-       ? (PL_sublex_info.sub_op->op_private & (PL_lex_repl ?
-                                               OPpTRANS_FROM_UTF : OPpTRANS_TO_UTF))
-       : UTF;
     const char *leaveit =      /* set of acceptably-backslashed characters */
        PL_lex_inpat
            ? "\\.^$@AGZdDwWsSbBpPXC+*?|()-nrtfeaxcz0123456789[{]} \t\n\r\f\v#"
            : "";
 
+    if (PL_lex_inwhat == OP_TRANS && PL_sublex_info.sub_op) {
+       /* If we are doing a trans and we know we want UTF8 set expectation */
+       has_utf8   = PL_sublex_info.sub_op->op_private & (OPpTRANS_FROM_UTF|OPpTRANS_TO_UTF);
+       this_utf8  = PL_sublex_info.sub_op->op_private & (PL_lex_repl ? OPpTRANS_FROM_UTF : OPpTRANS_TO_UTF);
+    }
+
+
     while (s < send || dorange) {
         /* get transliterations out of the way (they're most literal) */
        if (PL_lex_inwhat == OP_TRANS) {
@@ -1243,17 +1255,18 @@ S_scan_const(pTHX_ char *start)
                I32 min;                        /* first character in range */
                I32 max;                        /* last character in range */
 
-               if (utf) {
+               if (has_utf8) {
                    char *c = (char*)utf8_hop((U8*)d, -1);
                    char *e = d++;
                    while (e-- > c)
                        *(e + 1) = *e;
-                   *c = (char)0xff;
+                   *c = (char)UTF_TO_NATIVE(0xff);
                    /* mark the range as done, and continue */
                    dorange = FALSE;
                    didrange = TRUE;
                    continue;
                }
+
                i = d - SvPVX(sv);              /* remember current offset */
                SvGROW(sv, SvLEN(sv) + 256);    /* never more than 256 chars in a range */
                d = SvPVX(sv) + i;              /* refresh d after realloc */
@@ -1297,8 +1310,8 @@ S_scan_const(pTHX_ char *start)
                if (didrange) {
                    Perl_croak(aTHX_ "Ambiguous range in transliteration operator");
                }
-               if (utf) {
-                   *d++ = (char)0xff;  /* use illegal utf8 byte--see pmtrans */
+               if (has_utf8) {
+                   *d++ = (char)UTF_TO_NATIVE(0xff);   /* use illegal utf8 byte--see pmtrans */
                    s++;
                    continue;
                }
@@ -1364,10 +1377,12 @@ S_scan_const(pTHX_ char *start)
        else if (*s == '$') {
            if (!PL_lex_inpat)  /* not a regexp, so $ must be var */
                break;
-           if (s + 1 < send && !strchr("()| \n\t", s[1]))
+           if (s + 1 < send && !strchr("()| \r\n\t", s[1]))
                break;          /* in regexp, $ might be tail anchor */
        }
 
+       /* End of else if chain - OP_TRANS rejoin rest */
+
        /* backslashes */
        if (*s == '\\' && s+1 < send) {
            s++;
@@ -1430,14 +1445,14 @@ S_scan_const(pTHX_ char *start)
                ++s;
                if (*s == '{') {
                    char* e = strchr(s, '}');
+                   STRLEN len = 1;             /* allow underscores */
+
                    if (!e) {
                        yyerror("Missing right brace on \\x{}");
-                       e = s;
-                   }
-                   else {
-                       STRLEN len = 1;         /* allow underscores */
-                       uv = (UV)scan_hex(s + 1, e - s - 1, &len);
+                       ++s;
+                       continue;
                    }
+                   uv = (UV)scan_hex(s + 1, e - s - 1, &len);
                    s = e + 1;
                }
                else {
@@ -1502,7 +1517,6 @@ S_scan_const(pTHX_ char *start)
                            PL_sublex_info.sub_op->op_private |=
                                (PL_lex_repl ? OPpTRANS_FROM_UTF
                                             : OPpTRANS_TO_UTF);
-                           utf = TRUE;
                        }
                     }
                    else {
@@ -1603,40 +1617,40 @@ S_scan_const(pTHX_ char *start)
        } /* end if (backslash) */
 
     default_action:
-       if (!UTF8_IS_INVARIANT((U8)(*s)) && (this_utf8 || has_utf8)) {
-           STRLEN len = (STRLEN) -1;
-           UV uv;
-           if (this_utf8) {
-               uv = utf8n_to_uvchr((U8*)s, send - s, &len, 0);
-           }
-           if (len == (STRLEN)-1) {
-               /* Illegal UTF8 (a high-bit byte), make it valid. */
-               char *old_pvx = SvPVX(sv);
-               /* need space for one extra char (NOTE: SvCUR() not set here) */
-               d = SvGROW(sv, SvLEN(sv) + 1) + (d - old_pvx);
-               d = (char*)uvchr_to_utf8((U8*)d, (U8)*s++);
-           }
-           else {
-               while (len--)
-                   *d++ = *s++;
-           }
-           has_utf8 = TRUE;
-          if (PL_lex_inwhat == OP_TRANS && PL_sublex_info.sub_op) {
-              PL_sublex_info.sub_op->op_private |=
-                  (PL_lex_repl ? OPpTRANS_FROM_UTF : OPpTRANS_TO_UTF);
-              utf = TRUE;
-          }
-           continue;
-       }
-       *d++ = NATIVE_TO_NEED(has_utf8,*s++);
+       /* If we started with encoded form, or already know we want it
+          and then encode the next character */
+       if ((has_utf8 || this_utf8) && !NATIVE_IS_INVARIANT((U8)(*s))) {
+           STRLEN len  = 1;
+           UV uv       = (this_utf8) ? utf8n_to_uvchr((U8*)s, send - s, &len, 0) : (UV) ((U8) *s);
+           STRLEN need = UNISKIP(NATIVE_TO_UNI(uv));
+           s += len;
+           if (need > len) {
+               /* encoded value larger than old, need extra space (NOTE: SvCUR() not set here) */
+               STRLEN off = d - SvPVX(sv);
+               d = SvGROW(sv, SvLEN(sv) + (need-len)) + off;
+           }
+           d = (char*)uvchr_to_utf8((U8*)d, uv);
+           has_utf8 = TRUE;
+       }
+       else {
+           *d++ = NATIVE_TO_NEED(has_utf8,*s++);
+       }
     } /* while loop to process each character */
 
     /* terminate the string and set up the sv */
     *d = '\0';
     SvCUR_set(sv, d - SvPVX(sv));
+    if (SvCUR(sv) >= SvLEN(sv))
+      Perl_croak(aTHX_ "panic: constant overflowed allocated space");
+
     SvPOK_on(sv);
-    if (has_utf8)
+    if (has_utf8) {
        SvUTF8_on(sv);
+       if (PL_lex_inwhat == OP_TRANS && PL_sublex_info.sub_op) {
+               PL_sublex_info.sub_op->op_private |=
+                   (PL_lex_repl ? OPpTRANS_FROM_UTF : OPpTRANS_TO_UTF);
+       }
+    }
 
     /* shrink the sv if we allocated more than we used */
     if (SvCUR(sv) + 5 < SvLEN(sv)) {
@@ -2155,132 +2169,8 @@ Perl_yylex(pTHX)
     bool bof = FALSE;
 
     /* check if there's an identifier for us to look at */
-    if (PL_pending_ident) {
-        /* pit holds the identifier we read and pending_ident is reset */
-       char pit = PL_pending_ident;
-       PL_pending_ident = 0;
-
-       DEBUG_T({ PerlIO_printf(Perl_debug_log,
-              "### Tokener saw identifier '%s'\n", PL_tokenbuf); })
-
-       /* if we're in a my(), we can't allow dynamics here.
-          $foo'bar has already been turned into $foo::bar, so
-          just check for colons.
-
-          if it's a legal name, the OP is a PADANY.
-       */
-       if (PL_in_my) {
-           if (PL_in_my == KEY_our) {  /* "our" is merely analogous to "my" */
-               if (strchr(PL_tokenbuf,':'))
-                   yyerror(Perl_form(aTHX_ "No package name allowed for "
-                                     "variable %s in \"our\"",
-                                     PL_tokenbuf));
-               tmp = pad_allocmy(PL_tokenbuf);
-           }
-           else {
-               if (strchr(PL_tokenbuf,':'))
-                   yyerror(Perl_form(aTHX_ PL_no_myglob,PL_tokenbuf));
-
-               yylval.opval = newOP(OP_PADANY, 0);
-               yylval.opval->op_targ = pad_allocmy(PL_tokenbuf);
-               return PRIVATEREF;
-           }
-       }
-
-       /*
-          build the ops for accesses to a my() variable.
-
-          Deny my($a) or my($b) in a sort block, *if* $a or $b is
-          then used in a comparison.  This catches most, but not
-          all cases.  For instance, it catches
-              sort { my($a); $a <=> $b }
-          but not
-              sort { my($a); $a < $b ? -1 : $a == $b ? 0 : 1; }
-          (although why you'd do that is anyone's guess).
-       */
-
-       if (!strchr(PL_tokenbuf,':')) {
-#ifdef USE_THREADS
-           /* Check for single character per-thread SVs */
-           if (PL_tokenbuf[0] == '$' && PL_tokenbuf[2] == '\0'
-               && !isALPHA(PL_tokenbuf[1]) /* Rule out obvious non-threadsvs */
-               && (tmp = find_threadsv(&PL_tokenbuf[1])) != NOT_IN_PAD)
-           {
-               yylval.opval = newOP(OP_THREADSV, 0);
-               yylval.opval->op_targ = tmp;
-               return PRIVATEREF;
-           }
-#endif /* USE_THREADS */
-           if ((tmp = pad_findmy(PL_tokenbuf)) != NOT_IN_PAD) {
-               SV *namesv = AvARRAY(PL_comppad_name)[tmp];
-               /* might be an "our" variable" */
-               if (SvFLAGS(namesv) & SVpad_OUR) {
-                   /* build ops for a bareword */
-                   SV *sym = newSVpv(HvNAME(GvSTASH(namesv)),0);
-                   sv_catpvn(sym, "::", 2);
-                   sv_catpv(sym, PL_tokenbuf+1);
-                   yylval.opval = (OP*)newSVOP(OP_CONST, 0, sym);
-                   yylval.opval->op_private = OPpCONST_ENTERED;
-                   gv_fetchpv(SvPVX(sym),
-                       (PL_in_eval
-                           ? (GV_ADDMULTI | GV_ADDINEVAL)
-                           : TRUE
-                       ),
-                       ((PL_tokenbuf[0] == '$') ? SVt_PV
-                        : (PL_tokenbuf[0] == '@') ? SVt_PVAV
-                        : SVt_PVHV));
-                   return WORD;
-               }
-
-               /* if it's a sort block and they're naming $a or $b */
-               if (PL_last_lop_op == OP_SORT &&
-                   PL_tokenbuf[0] == '$' &&
-                   (PL_tokenbuf[1] == 'a' || PL_tokenbuf[1] == 'b')
-                   && !PL_tokenbuf[2])
-               {
-                   for (d = PL_in_eval ? PL_oldoldbufptr : PL_linestart;
-                        d < PL_bufend && *d != '\n';
-                        d++)
-                   {
-                       if (strnEQ(d,"<=>",3) || strnEQ(d,"cmp",3)) {
-                           Perl_croak(aTHX_ "Can't use \"my %s\" in sort comparison",
-                                 PL_tokenbuf);
-                       }
-                   }
-               }
-
-               yylval.opval = newOP(OP_PADANY, 0);
-               yylval.opval->op_targ = tmp;
-               return PRIVATEREF;
-           }
-       }
-
-       /*
-          Whine if they've said @foo in a doublequoted string,
-          and @foo isn't a variable we can find in the symbol
-          table.
-       */
-       if (pit == '@' && PL_lex_state != LEX_NORMAL && !PL_lex_brackets) {
-           GV *gv = gv_fetchpv(PL_tokenbuf+1, FALSE, SVt_PVAV);
-           if ((!gv || ((PL_tokenbuf[0] == '@') ? !GvAV(gv) : !GvHV(gv)))
-                && ckWARN(WARN_AMBIGUOUS))
-           {
-                /* Downgraded from fatal to warning 20000522 mjd */
-               Perl_warner(aTHX_ WARN_AMBIGUOUS,
-                           "Possible unintended interpolation of %s in string",
-                            PL_tokenbuf);
-           }
-       }
-
-       /* build ops for a bareword */
-       yylval.opval = (OP*)newSVOP(OP_CONST, 0, newSVpv(PL_tokenbuf+1, 0));
-       yylval.opval->op_private = OPpCONST_ENTERED;
-       gv_fetchpv(PL_tokenbuf+1, PL_in_eval ? (GV_ADDMULTI | GV_ADDINEVAL) : TRUE,
-                  ((PL_tokenbuf[0] == '$') ? SVt_PV
-                   : (PL_tokenbuf[0] == '@') ? SVt_PVAV
-                   : SVt_PVHV));
-       return WORD;
-    }
+    if (PL_pending_ident) 
+        return S_pending_ident(aTHX);
 
     /* no identifier pending identification */
 
@@ -2302,7 +2192,7 @@ Perl_yylex(pTHX)
        }
        DEBUG_T({ PerlIO_printf(Perl_debug_log,
               "### Next token after '%s' was known, type %"IVdf"\n", PL_bufptr,
-              (IV)PL_nexttype[PL_nexttoke]); })
+              (IV)PL_nexttype[PL_nexttoke]); });
 
        return(PL_nexttype[PL_nexttoke]);
 
@@ -2336,7 +2226,7 @@ Perl_yylex(pTHX)
        }
        else {
            DEBUG_T({ PerlIO_printf(Perl_debug_log,
-              "### Saw case modifier at '%s'\n", PL_bufptr); })
+              "### Saw case modifier at '%s'\n", PL_bufptr); });
            s = PL_bufptr + 1;
            if (strnEQ(s, "L\\u", 3) || strnEQ(s, "U\\l", 3))
                tmp = *s, *s = s[2], s[2] = tmp;        /* misordered... */
@@ -2388,7 +2278,7 @@ Perl_yylex(pTHX)
        if (PL_bufptr == PL_bufend)
            return sublex_done();
        DEBUG_T({ PerlIO_printf(Perl_debug_log,
-              "### Interpolated variable at '%s'\n", PL_bufptr); })
+              "### Interpolated variable at '%s'\n", PL_bufptr); });
        PL_expect = XTERM;
        PL_lex_dojoin = (*PL_bufptr == '@');
        PL_lex_state = LEX_INTERPNORMAL;
@@ -2488,7 +2378,7 @@ Perl_yylex(pTHX)
     DEBUG_T( {
        PerlIO_printf(Perl_debug_log, "### Tokener expecting %s at %s\n",
                      exp_name[PL_expect], s);
-    } )
+    } );
 
   retry:
     switch (*s) {
@@ -2507,7 +2397,7 @@ Perl_yylex(pTHX)
                yyerror("Missing right curly or square bracket");
             DEBUG_T( { PerlIO_printf(Perl_debug_log,
                         "### Tokener got EOF\n");
-            } )
+            } );
            TOKEN(0);
        }
        if (s++ < PL_bufend)
@@ -2534,9 +2424,6 @@ Perl_yylex(pTHX)
                if (PL_minus_l)
                    sv_catpv(PL_linestr,"chomp;");
                if (PL_minus_a) {
-                   GV* gv = gv_fetchpv("::F", TRUE, SVt_PVAV);
-                   if (gv)
-                       GvIMPORTED_AV_on(gv);
                    if (PL_minus_F) {
                        if (strchr("/'\"", *PL_splitstr)
                              && strchr(PL_splitstr + 1, *PL_splitstr))
@@ -2546,7 +2433,7 @@ Perl_yylex(pTHX)
                            s = "'~#\200\1'"; /* surely one char is unused...*/
                            while (s[1] && strchr(PL_splitstr, *s))  s++;
                            delim = *s;
-                           Perl_sv_catpvf(aTHX_ PL_linestr, "@F=split(%s%c",
+                           Perl_sv_catpvf(aTHX_ PL_linestr, "our @F=split(%s%c",
                                      "q" + (delim == '\''), delim);
                            for (s = PL_splitstr; *s; s++) {
                                if (*s == '\\')
@@ -2557,7 +2444,7 @@ Perl_yylex(pTHX)
                        }
                    }
                    else
-                       sv_catpv(PL_linestr,"@F=split(' ');");
+                       sv_catpv(PL_linestr,"our @F=split(' ');");
                }
            }
            sv_catpv(PL_linestr, "\n");
@@ -2840,6 +2727,8 @@ Perl_yylex(pTHX)
                s++;
            if (s < d)
                s++;
+           else if (s > d) /* Found by Ilya: feed random input to Perl. */
+             Perl_croak(aTHX_ "panic: input overflow");
            incline(s);
            if (PL_lex_formbrack && PL_lex_brackets <= PL_lex_formbrack) {
                PL_bufptr = s;
@@ -2867,7 +2756,7 @@ Perl_yylex(pTHX)
                s = force_word(PL_bufptr,WORD,FALSE,FALSE,FALSE);
                 DEBUG_T( { PerlIO_printf(Perl_debug_log,
                             "### Saw unary minus before =>, forcing word '%s'\n", s);
-                } )
+                } );
                OPERATOR('-');          /* unary minus */
            }
            PL_last_uni = PL_oldbufptr;
@@ -2912,7 +2801,7 @@ Perl_yylex(pTHX)
                PL_last_lop_op = ftst;
                DEBUG_T( { PerlIO_printf(Perl_debug_log,
                         "### Saw file test %c\n", (int)ftst);
-               } )
+               } );
                FTST(ftst);
            }
            else {
@@ -2921,7 +2810,7 @@ Perl_yylex(pTHX)
                DEBUG_T( { PerlIO_printf(Perl_debug_log,
                        "### %c looked like a file test but was not\n",
                        (int)ftst);
-               } )
+               } );
                s -= 2;
            }
        }
@@ -3078,8 +2967,8 @@ Perl_yylex(pTHX)
                    else if (!PL_in_my && len == 6 && strnEQ(s, "method", len))
                        CvMETHOD_on(PL_compcv);
 #ifdef USE_ITHREADS
-                   else if (PL_in_my == KEY_our && len == 6 && strnEQ(s, "shared", len))
-                       GvSHARED_on(cGVOPx_gv(yylval.opval));
+      else if (PL_in_my == KEY_our && len == 6 && strnEQ(s, "unique", len))
+                       GvUNIQUE_on(cGVOPx_gv(yylval.opval));
 #endif
                    /* After we've set the flags, it could be argued that
                       we don't need to do the attributes.pm-based setting
@@ -3218,8 +3107,16 @@ Perl_yylex(pTHX)
                else
                    PL_lex_brackstack[PL_lex_brackets++] = XOPERATOR;
                s = skipspace(s);
-               if (*s == '}')
+               if (*s == '}') {
+                   if (PL_expect == XREF && PL_lex_state == LEX_INTERPNORMAL) {
+                       PL_expect = XTERM;
+                       /* This hack is to get the ${} in the message. */
+                       PL_bufptr = s+1;
+                       yyerror("syntax error");
+                       break;
+                   }
                    OPERATOR(HASHBRACK);
+               }
                /* This hack serves to disambiguate a pair of curlies
                 * as being a block or an anon hash.  Normally, expectation
                 * determines that, but in cases where we're not in a
@@ -3681,7 +3578,7 @@ Perl_yylex(pTHX)
        s = scan_num(s, &yylval);
         DEBUG_T( { PerlIO_printf(Perl_debug_log,
                     "### Saw number in '%s'\n", s);
-        } )
+        } );
        if (PL_expect == XOPERATOR)
            no_op("Number",s);
        TERM(THING);
@@ -3690,7 +3587,7 @@ Perl_yylex(pTHX)
        s = scan_str(s,FALSE,FALSE);
         DEBUG_T( { PerlIO_printf(Perl_debug_log,
                     "### Saw string before '%s'\n", s);
-        } )
+        } );
        if (PL_expect == XOPERATOR) {
            if (PL_lex_formbrack && PL_lex_brackets == PL_lex_formbrack) {
                PL_expect = XTERM;
@@ -3709,7 +3606,7 @@ Perl_yylex(pTHX)
        s = scan_str(s,FALSE,FALSE);
         DEBUG_T( { PerlIO_printf(Perl_debug_log,
                     "### Saw string before '%s'\n", s);
-        } )
+        } );
        if (PL_expect == XOPERATOR) {
            if (PL_lex_formbrack && PL_lex_brackets == PL_lex_formbrack) {
                PL_expect = XTERM;
@@ -3734,7 +3631,7 @@ Perl_yylex(pTHX)
        s = scan_str(s,FALSE,FALSE);
         DEBUG_T( { PerlIO_printf(Perl_debug_log,
                     "### Saw backtick string before '%s'\n", s);
-        } )
+        } );
        if (PL_expect == XOPERATOR)
            no_op("Backticks",s);
        if (!s)
@@ -3764,7 +3661,7 @@ Perl_yylex(pTHX)
                TERM(THING);
            }
            /* avoid v123abc() or $h{v1}, allow C<print v10;> */
-           else if (!isALPHA(*start) && (PL_expect == XTERM || PL_expect == XREF)) {
+           else if (!isALPHA(*start) && (PL_expect == XTERM || PL_expect == XREF || PL_expect == XSTATE)) {
                char c = *start;
                GV *gv;
                *start = '\0';
@@ -3850,7 +3747,7 @@ Perl_yylex(pTHX)
            CLINE;
            yylval.opval = (OP*)newSVOP(OP_CONST, 0, newSVpv(PL_tokenbuf,0));
            yylval.opval->op_private = OPpCONST_BARE;
-           if (UTF && !IN_BYTE && is_utf8_string((U8*)PL_tokenbuf, len))
+           if (UTF && !IN_BYTES && is_utf8_string((U8*)PL_tokenbuf, len))
              SvUTF8_on(((SVOP*)yylval.opval)->op_sv);
            TERM(WORD);
        }
@@ -3904,6 +3801,7 @@ Perl_yylex(pTHX)
        default:                        /* not a keyword */
          just_a_word: {
                SV *sv;
+               int pkgname = 0;
                char lastchar = (PL_bufptr == PL_oldoldbufptr ? 0 : PL_bufptr[-1]);
 
                /* Get the rest if it looks like a package qualifier */
@@ -3916,6 +3814,7 @@ Perl_yylex(pTHX)
                        Perl_croak(aTHX_ "Bad name after %s%s", PL_tokenbuf,
                                *s == '\'' ? "'" : "::");
                    len += morelen;
+                   pkgname = 1;
                }
 
                if (PL_expect == XOPERATOR) {
@@ -4003,15 +3902,14 @@ Perl_yylex(pTHX)
                    }
                }
 
-
                PL_expect = XOPERATOR;
                s = skipspace(s);
 
                /* Is this a word before a => operator? */
-               if (*s == '=' && s[1] == '>') {
+               if (*s == '=' && s[1] == '>' && !pkgname) {
                    CLINE;
                    sv_setpv(((SVOP*)yylval.opval)->op_sv, PL_tokenbuf);
-                   if (UTF && !IN_BYTE && is_utf8_string((U8*)PL_tokenbuf, len))
+                   if (UTF && !IN_BYTES && is_utf8_string((U8*)PL_tokenbuf, len))
                      SvUTF8_on(((SVOP*)yylval.opval)->op_sv);
                    TERM(WORD);
                }
@@ -4174,14 +4072,24 @@ Perl_yylex(pTHX)
                        loc = PerlIO_tell(PL_rsfp);
                        (void)PerlIO_seek(PL_rsfp, 0L, 0);
                    }
+#ifdef NETWARE
+                       if (PerlLIO_setmode(PL_rsfp, O_TEXT) != -1) {
+#else
                    if (PerlLIO_setmode(PerlIO_fileno(PL_rsfp), O_TEXT) != -1) {
+#endif /* NETWARE */
+#ifdef PERLIO_IS_STDIO /* really? */
+#  if defined(__BORLANDC__)
+                       /* XXX see note in do_binmode() */
+                       ((FILE*)PL_rsfp)->flags &= ~_F_BIN;
+#  endif
+#endif
                        if (loc > 0)
                            PerlIO_seek(PL_rsfp, loc, 0);
                    }
                }
 #endif
 #ifdef PERLIO_LAYERS
-               if (UTF && !IN_BYTE)
+               if (UTF && !IN_BYTES)
                    PerlIO_apply_layers(aTHX_ PL_rsfp, NULL, ":utf8");
 #endif
                PL_rsfp = Nullfp;
@@ -4270,12 +4178,6 @@ Perl_yylex(pTHX)
            LOP(OP_CRYPT,XTERM);
 
        case KEY_chmod:
-           if (ckWARN(WARN_CHMOD)) {
-               for (d = s; d < PL_bufend && (isSPACE(*d) || *d == '('); d++) ;
-               if (*d != '0' && isDIGIT(*d))
-                   Perl_warner(aTHX_ WARN_CHMOD,
-                               "chmod() mode argument is missing initial 0");
-           }
            LOP(OP_CHMOD,XTERM);
 
        case KEY_chown:
@@ -4970,7 +4872,7 @@ Perl_yylex(pTHX)
          really_sub:
            {
                char tmpbuf[sizeof PL_tokenbuf];
-               SSize_t tboffset;
+               SSize_t tboffset = 0;
                expectation attrful;
                bool have_name, have_proto;
                int key = tmp;
@@ -5133,12 +5035,6 @@ Perl_yylex(pTHX)
            LOP(OP_UTIME,XTERM);
 
        case KEY_umask:
-           if (ckWARN(WARN_UMASK)) {
-               for (d = s; d < PL_bufend && (isSPACE(*d) || *d == '('); d++) ;
-               if (*d != '0' && isDIGIT(*d))
-                   Perl_warner(aTHX_ WARN_UMASK,
-                               "umask: argument is missing initial 0");
-           }
            UNI(OP_UMASK);
 
        case KEY_unshift:
@@ -5219,6 +5115,137 @@ Perl_yylex(pTHX)
 #pragma segment Main
 #endif
 
+static int
+S_pending_ident(pTHX)
+{
+    register char *d;
+    register I32 tmp;
+    /* pit holds the identifier we read and pending_ident is reset */
+    char pit = PL_pending_ident;
+    PL_pending_ident = 0;
+
+    DEBUG_T({ PerlIO_printf(Perl_debug_log,
+          "### Tokener saw identifier '%s'\n", PL_tokenbuf); });
+
+    /* if we're in a my(), we can't allow dynamics here.
+       $foo'bar has already been turned into $foo::bar, so
+       just check for colons.
+
+       if it's a legal name, the OP is a PADANY.
+    */
+    if (PL_in_my) {
+        if (PL_in_my == KEY_our) {     /* "our" is merely analogous to "my" */
+            if (strchr(PL_tokenbuf,':'))
+                yyerror(Perl_form(aTHX_ "No package name allowed for "
+                                  "variable %s in \"our\"",
+                                  PL_tokenbuf));
+            tmp = pad_allocmy(PL_tokenbuf);
+        }
+        else {
+            if (strchr(PL_tokenbuf,':'))
+                yyerror(Perl_form(aTHX_ PL_no_myglob,PL_tokenbuf));
+
+            yylval.opval = newOP(OP_PADANY, 0);
+            yylval.opval->op_targ = pad_allocmy(PL_tokenbuf);
+            return PRIVATEREF;
+        }
+    }
+
+    /*
+       build the ops for accesses to a my() variable.
+
+       Deny my($a) or my($b) in a sort block, *if* $a or $b is
+       then used in a comparison.  This catches most, but not
+       all cases.  For instance, it catches
+           sort { my($a); $a <=> $b }
+       but not
+           sort { my($a); $a < $b ? -1 : $a == $b ? 0 : 1; }
+       (although why you'd do that is anyone's guess).
+    */
+
+    if (!strchr(PL_tokenbuf,':')) {
+#ifdef USE_THREADS
+        /* Check for single character per-thread SVs */
+        if (PL_tokenbuf[0] == '$' && PL_tokenbuf[2] == '\0'
+            && !isALPHA(PL_tokenbuf[1]) /* Rule out obvious non-threadsvs */
+            && (tmp = find_threadsv(&PL_tokenbuf[1])) != NOT_IN_PAD)
+        {
+            yylval.opval = newOP(OP_THREADSV, 0);
+            yylval.opval->op_targ = tmp;
+            return PRIVATEREF;
+        }
+#endif /* USE_THREADS */
+        if ((tmp = pad_findmy(PL_tokenbuf)) != NOT_IN_PAD) {
+            SV *namesv = AvARRAY(PL_comppad_name)[tmp];
+            /* might be an "our" variable" */
+            if (SvFLAGS(namesv) & SVpad_OUR) {
+                /* build ops for a bareword */
+                SV *sym = newSVpv(HvNAME(GvSTASH(namesv)),0);
+                sv_catpvn(sym, "::", 2);
+                sv_catpv(sym, PL_tokenbuf+1);
+                yylval.opval = (OP*)newSVOP(OP_CONST, 0, sym);
+                yylval.opval->op_private = OPpCONST_ENTERED;
+                gv_fetchpv(SvPVX(sym),
+                    (PL_in_eval
+                        ? (GV_ADDMULTI | GV_ADDINEVAL)
+                        : TRUE
+                    ),
+                    ((PL_tokenbuf[0] == '$') ? SVt_PV
+                     : (PL_tokenbuf[0] == '@') ? SVt_PVAV
+                     : SVt_PVHV));
+                return WORD;
+            }
+
+            /* if it's a sort block and they're naming $a or $b */
+            if (PL_last_lop_op == OP_SORT &&
+                PL_tokenbuf[0] == '$' &&
+                (PL_tokenbuf[1] == 'a' || PL_tokenbuf[1] == 'b')
+                && !PL_tokenbuf[2])
+            {
+                for (d = PL_in_eval ? PL_oldoldbufptr : PL_linestart;
+                     d < PL_bufend && *d != '\n';
+                     d++)
+                {
+                    if (strnEQ(d,"<=>",3) || strnEQ(d,"cmp",3)) {
+                        Perl_croak(aTHX_ "Can't use \"my %s\" in sort comparison",
+                              PL_tokenbuf);
+                    }
+                }
+            }
+
+            yylval.opval = newOP(OP_PADANY, 0);
+            yylval.opval->op_targ = tmp;
+            return PRIVATEREF;
+        }
+    }
+
+    /*
+       Whine if they've said @foo in a doublequoted string,
+       and @foo isn't a variable we can find in the symbol
+       table.
+    */
+    if (pit == '@' && PL_lex_state != LEX_NORMAL && !PL_lex_brackets) {
+        GV *gv = gv_fetchpv(PL_tokenbuf+1, FALSE, SVt_PVAV);
+        if ((!gv || ((PL_tokenbuf[0] == '@') ? !GvAV(gv) : !GvHV(gv)))
+             && ckWARN(WARN_AMBIGUOUS))
+        {
+            /* Downgraded from fatal to warning 20000522 mjd */
+            Perl_warner(aTHX_ WARN_AMBIGUOUS,
+                        "Possible unintended interpolation of %s in string",
+                         PL_tokenbuf);
+        }
+    }
+
+    /* build ops for a bareword */
+    yylval.opval = (OP*)newSVOP(OP_CONST, 0, newSVpv(PL_tokenbuf+1, 0));
+    yylval.opval->op_private = OPpCONST_ENTERED;
+    gv_fetchpv(PL_tokenbuf+1, PL_in_eval ? (GV_ADDMULTI | GV_ADDINEVAL) : TRUE,
+               ((PL_tokenbuf[0] == '$') ? SVt_PV
+                : (PL_tokenbuf[0] == '@') ? SVt_PVAV
+                : SVt_PVHV));
+    return WORD;
+}
+
 I32
 Perl_keyword(pTHX_ register char *d, I32 len)
 {
@@ -5614,7 +5641,7 @@ Perl_keyword(pTHX_ register char *d, I32 len)
            if (strEQ(d,"rindex"))              return -KEY_rindex;
            break;
        case 7:
-           if (strEQ(d,"require"))             return -KEY_require;
+           if (strEQ(d,"require"))             return KEY_require;
            if (strEQ(d,"reverse"))             return -KEY_reverse;
            if (strEQ(d,"readdir"))             return -KEY_readdir;
            break;
@@ -6509,7 +6536,7 @@ retval:
        Renew(SvPVX(tmpstr), SvLEN(tmpstr), char);
     }
     SvREFCNT_dec(herewas);
-    if (UTF && !IN_BYTE && is_utf8_string((U8*)SvPVX(tmpstr), SvCUR(tmpstr)))
+    if (UTF && !IN_BYTES && is_utf8_string((U8*)SvPVX(tmpstr), SvCUR(tmpstr)))
        SvUTF8_on(tmpstr);
     PL_lex_stuff = tmpstr;
     yylval.ival = op_type;
@@ -6863,11 +6890,11 @@ S_scan_str(pTHX_ char *start, int keep_quoted, int keep_delims)
 
   Read a number in any of the formats that Perl accepts:
 
-  0(x[0-7A-F]+)|([0-7]+)|(b[01])
-  [\d_]+(\.[\d_]*)?[Ee](\d+)
-
-  Underbars (_) are allowed in decimal numbers.  If -w is on,
-  underbars before a decimal point must be at three digit intervals.
+  \d(_?\d)*(\.(\d(_?\d)*)?)?[Ee][\+\-]?(\d(_?\d)*)     12 12.34 12.
+  \.\d(_?\d)*[Ee][\+\-]?(\d(_?\d)*)                    .34
+  0b[01](_?[01])*
+  0[0-7](_?[0-7])*
+  0x[0-9A-Fa-f](_?[0-9A-Fa-f])*
 
   Like most scan_ routines, it uses the PL_tokenbuf buffer to hold the
   thing it reads.
@@ -6937,8 +6964,17 @@ Perl_scan_num(pTHX_ char *start, YYSTYPE* lvalp)
            else if (s[1] == '.' || s[1] == 'e' || s[1] == 'E')
                goto decimal;
            /* so it must be octal */
-           else
+           else {
                shift = 3;
+               s++;
+           }
+
+           if (*s == '_') {
+              if (ckWARN(WARN_SYNTAX))
+                  Perl_warner(aTHX_ WARN_SYNTAX,
+                              "Misplaced _ in number");
+              lastub = s++;
+           }
 
            base = bases[shift];
            Base = Bases[shift];
@@ -6956,9 +6992,12 @@ Perl_scan_num(pTHX_ char *start, YYSTYPE* lvalp)
                default:
                    goto out;
 
-               /* _ are ignored */
+               /* _ are ignored -- but warned about if consecutive */
                case '_':
-                   s++;
+                   if (ckWARN(WARN_SYNTAX) && lastub && s == lastub + 1)
+                       Perl_warner(aTHX_ WARN_SYNTAX,
+                                   "Misplaced _ in number");
+                   lastub = s++;
                    break;
 
                /* 8 and 9 are not octal */
@@ -7025,6 +7064,13 @@ Perl_scan_num(pTHX_ char *start, YYSTYPE* lvalp)
             the number.
          */
          out:
+
+           /* final misplaced underbar check */
+           if (s[-1] == '_') {
+               if (ckWARN(WARN_SYNTAX))
+                   Perl_warner(aTHX_ WARN_SYNTAX, "Misplaced _ in number");
+           }
+
            sv = NEWSV(92,0);
            if (overflowed) {
                if (ckWARN(WARN_PORTABLE) && n > 4294967295.0)
@@ -7064,9 +7110,10 @@ Perl_scan_num(pTHX_ char *start, YYSTYPE* lvalp)
               if -w is on
            */
            if (*s == '_') {
-               if (ckWARN(WARN_SYNTAX) && lastub && s - lastub != 3)
-                   Perl_warner(aTHX_ WARN_SYNTAX, "Misplaced _ in number");
-               lastub = ++s;
+               if (ckWARN(WARN_SYNTAX) && lastub && s == lastub + 1)
+                   Perl_warner(aTHX_ WARN_SYNTAX,
+                               "Misplaced _ in number");
+               lastub = s++;
            }
            else {
                /* check for end of fixed-length buffer */
@@ -7078,7 +7125,7 @@ Perl_scan_num(pTHX_ char *start, YYSTYPE* lvalp)
        }
 
        /* final misplaced underbar check */
-       if (lastub && s - lastub != 3) {
+       if (lastub && s == lastub + 1) {
            if (ckWARN(WARN_SYNTAX))
                Perl_warner(aTHX_ WARN_SYNTAX, "Misplaced _ in number");
        }
@@ -7091,16 +7138,34 @@ Perl_scan_num(pTHX_ char *start, YYSTYPE* lvalp)
            floatit = TRUE;
            *d++ = *s++;
 
-           /* copy, ignoring underbars, until we run out of
-              digits.  Note: no misplaced underbar checks!
+           if (*s == '_') {
+               if (ckWARN(WARN_SYNTAX))
+                   Perl_warner(aTHX_ WARN_SYNTAX,
+                               "Misplaced _ in number");
+               lastub = s;
+           }
+
+           /* copy, ignoring underbars, until we run out of digits.
            */
            for (; isDIGIT(*s) || *s == '_'; s++) {
                /* fixed length buffer check */
                if (d >= e)
                    Perl_croak(aTHX_ number_too_long);
-               if (*s != '_')
+               if (*s == '_') {
+                  if (ckWARN(WARN_SYNTAX) && lastub && s == lastub + 1)
+                      Perl_warner(aTHX_ WARN_SYNTAX,
+                                  "Misplaced _ in number");
+                  lastub = s;
+               }
+               else
                    *d++ = *s;
            }
+           /* fractional part ending in underbar? */
+           if (s[-1] == '_') {
+               if (ckWARN(WARN_SYNTAX))
+                   Perl_warner(aTHX_ WARN_SYNTAX,
+                               "Misplaced _ in number");
+           }
            if (*s == '.' && isDIGIT(s[1])) {
                /* oops, it's really a v-string, but without the "v" */
                s = start - 1;
@@ -7109,110 +7174,84 @@ Perl_scan_num(pTHX_ char *start, YYSTYPE* lvalp)
        }
 
        /* read exponent part, if present */
-       if (*s && strchr("eE",*s) && strchr("+-0123456789",s[1])) {
+       if (*s && strchr("eE",*s) && strchr("+-0123456789_", s[1])) {
            floatit = TRUE;
            s++;
 
            /* regardless of whether user said 3E5 or 3e5, use lower 'e' */
            *d++ = 'e';         /* At least some Mach atof()s don't grok 'E' */
 
+           /* stray preinitial _ */
+           if (*s == '_') {
+               if (ckWARN(WARN_SYNTAX))
+                   Perl_warner(aTHX_ WARN_SYNTAX,
+                               "Misplaced _ in number");
+               lastub = s++;
+           }
+
            /* allow positive or negative exponent */
            if (*s == '+' || *s == '-')
                *d++ = *s++;
 
-           /* read digits of exponent (no underbars :-) */
-           while (isDIGIT(*s)) {
-               if (d >= e)
-                   Perl_croak(aTHX_ number_too_long);
-               *d++ = *s++;
+           /* stray initial _ */
+           if (*s == '_') {
+               if (ckWARN(WARN_SYNTAX))
+                   Perl_warner(aTHX_ WARN_SYNTAX,
+                               "Misplaced _ in number");
+               lastub = s++;
+           }
+
+           /* read digits of exponent */
+           while (isDIGIT(*s) || *s == '_') {
+               if (isDIGIT(*s)) {
+                   if (d >= e)
+                       Perl_croak(aTHX_ number_too_long);
+                   *d++ = *s++;
+               }
+               else {
+                  if (ckWARN(WARN_SYNTAX) &&
+                      ((lastub && s == lastub + 1) ||
+                       (!isDIGIT(s[1]) && s[1] != '_')))
+                      Perl_warner(aTHX_ WARN_SYNTAX,
+                                  "Misplaced _ in number");
+                  lastub = s++;
+               }
            }
        }
 
-       /* terminate the string */
-       *d = '\0';
 
        /* make an sv from the string */
        sv = NEWSV(92,0);
 
-#if defined(Strtol) && defined(Strtoul)
-
        /*
-          strtol/strtoll sets errno to ERANGE if the number is too big
-          for an integer. We try to do an integer conversion first
-          if no characters indicating "float" have been found.
+           We try to do an integer conversion first if no characters
+           indicating "float" have been found.
         */
 
        if (!floatit) {
-           IV iv;
            UV uv;
-           errno = 0;
-           if (*PL_tokenbuf == '-')
-               iv = Strtol(PL_tokenbuf, (char**)NULL, 10);
-           else
-               uv = Strtoul(PL_tokenbuf, (char**)NULL, 10);
-           if (errno)
-               floatit = TRUE; /* Probably just too large. */
-           else if (*PL_tokenbuf == '-')
-               sv_setiv(sv, iv);
-           else if (uv <= IV_MAX)
+            int flags = grok_number (PL_tokenbuf, d - PL_tokenbuf, &uv);
+
+            if (flags == IS_NUMBER_IN_UV) {
+              if (uv <= IV_MAX)
                sv_setiv(sv, uv); /* Prefer IVs over UVs. */
-           else
+              else
                sv_setuv(sv, uv);
-       }
+            } else if (flags == (IS_NUMBER_IN_UV | IS_NUMBER_NEG)) {
+              if (uv <= (UV) IV_MIN)
+                sv_setiv(sv, -(IV)uv);
+              else
+               floatit = TRUE;
+            } else
+              floatit = TRUE;
+        }
        if (floatit) {
+           /* terminate the string */
+           *d = '\0';
            nv = Atof(PL_tokenbuf);
            sv_setnv(sv, nv);
        }
-#else
-       /*
-          No working strtou?ll?.
-
-          Unfortunately atol() doesn't do range checks (returning
-          LONG_MIN/LONG_MAX, and setting errno to ERANGE on overflows)
-          everywhere [1], so we cannot use use atol() (or atoll()).
-          If we could, they would be used, as Atol(), very much like
-          Strtol() and Strtoul() are used above.
-
-          [1] XXX Configure test needed to check for atol()
-                  (and atoll()) overflow behaviour XXX
-
-          --jhi
-
-          We need to do this the hard way.  */
 
-       nv = Atof(PL_tokenbuf);
-
-       /* See if we can make do with an integer value without loss of
-          precision.  We use U_V to cast to a UV, because some
-          compilers have issues.  Then we try casting it back and see
-          if it was the same [1].  We only do this if we know we
-          specifically read an integer.  If floatit is true, then we
-          don't need to do the conversion at all.
-
-          [1] Note that this is lossy if our NVs cannot preserve our
-          UVs.  There are metaconfig defines NV_PRESERVES_UV (a boolean)
-          and NV_PRESERVES_UV_BITS (a number), but in general we really
-          do hope all such potentially lossy platforms have strtou?ll?
-          to do a lossless IV/UV conversion.
-
-          Maybe could do some tricks with DBL_DIG, LDBL_DIG and
-          DBL_MANT_DIG and LDBL_MANT_DIG (these are already available
-          as NV_DIG and NV_MANT_DIG)?
-       
-          --jhi
-          */
-       {
-           UV uv = U_V(nv);
-           if (!floatit && (NV)uv == nv) {
-               if (uv <= IV_MAX)
-                   sv_setiv(sv, uv); /* Prefer IVs over UVs. */
-               else
-                   sv_setuv(sv, uv);
-           }
-           else
-               sv_setnv(sv, nv);
-       }
-#endif
        if ( floatit ? (PL_hints & HINT_NEW_FLOAT) :
                       (PL_hints & HINT_NEW_INTEGER) )
            sv = new_constant(PL_tokenbuf, d - PL_tokenbuf,
@@ -7323,15 +7362,19 @@ S_scan_formline(pTHX_ register char *s)
                if (*t == '@' || *t == '^')
                    needargs = TRUE;
            }
-           sv_catpvn(stuff, s, eol-s);
+           if (eol > s) {
+               sv_catpvn(stuff, s, eol-s);
 #ifndef PERL_STRICT_CR
-           if (eol-s > 1 && eol[-2] == '\r' && eol[-1] == '\n') {
-               char *end = SvPVX(stuff) + SvCUR(stuff);
-               end[-2] = '\n';
-               end[-1] = '\0';
-               SvCUR(stuff)--;
-           }
+               if (eol-s > 1 && eol[-2] == '\r' && eol[-1] == '\n') {
+                   char *end = SvPVX(stuff) + SvCUR(stuff);
+                   end[-2] = '\n';
+                   end[-1] = '\0';
+                   SvCUR(stuff)--;
+               }
 #endif
+           }
+           else
+             break;
        }
        s = eol;
        if (PL_rsfp) {