#ifdef CRIPPLED_CC
static int uni _((I32 f, char *s));
#endif
+static char * filter_gets _((SV *sv, FILE *fp));
/* The following are arranged oddly so that the guard on the switch statement
* can get by with a single comparison (if the compiler is smart enough).
}
if (s < bufend || !rsfp || lex_state != LEX_NORMAL)
return s;
- if ((s = sv_gets(linestr, rsfp, 0)) == Nullch) {
+ if ((s = filter_gets(linestr, rsfp)) == Nullch) {
if (minus_n || minus_p) {
sv_setpv(linestr,minus_p ? ";}continue{print" : "");
sv_catpv(linestr,";}");
}
-/* Encrypted script support: cryptswitch_add() may be called to */
-/* define a function which may manipulate the input stream */
-/* (via popen() etc) to decode the input if required. */
-/* At the moment we only allow one cryptswitch function. */
+/* Encoded script support. filter_add() effectively inserts a
+ * 'pre-processing' function into the current source input stream.
+ * Note that the filter function only applies to the current source file
+ * (e.g., it will not affect files 'require'd or 'use'd by this one).
+ *
+ * The datasv parameter (which may be NULL) can be used to pass
+ * private data to this instance of the filter. The filter function
+ * can recover the SV using the FILTER_DATA macro and use it to
+ * store private buffers and state information.
+ *
+ * The supplied datasv parameter is upgraded to a PVIO type
+ * and the IoDIRP field is used to store the function pointer.
+ * Note that IoTOP_NAME, IoFMT_NAME, IoBOTTOM_NAME, if set for
+ * private use must be set using malloc'd pointers.
+ */
+static int filter_debug = 0;
+
+SV *
+filter_add(funcp, datasv)
+ filter_t funcp;
+ SV *datasv;
+{
+ if (!funcp){ /* temporary handy debugging hack to be deleted */
+ filter_debug = atoi((char*)datasv);
+ return NULL;
+ }
+ if (!rsfp_filters)
+ rsfp_filters = newAV();
+ if (!datasv)
+ datasv = newSV(0);
+ if (!SvUPGRADE(datasv, SVt_PVIO))
+ die("Can't upgrade filter_add data to SVt_PVIO");
+ IoDIRP(datasv) = (DIR*)funcp; /* stash funcp into spare field */
+ if (filter_debug)
+ warn("filter_add func %lx (%s)", funcp, SvPV(datasv,na));
+ av_unshift(rsfp_filters, 1);
+ av_store(rsfp_filters, 0, datasv) ;
+ return(datasv);
+}
+
+
+/* Delete most recently added instance of this filter function. */
void
-cryptswitch_add(funcp)
- cryptswitch_t funcp;
+filter_del(funcp)
+ filter_t funcp;
{
- cryptswitch_fp = funcp;
+ if (filter_debug)
+ warn("filter_del func %lx", funcp);
+ if (!rsfp_filters || AvFILL(rsfp_filters)<0)
+ return;
+ /* if filter is on top of stack (usual case) just pop it off */
+ if (IoDIRP(FILTER_DATA(0)) == (void*)funcp){
+ /* sv_free(av_pop(rsfp_filters)); */
+ sv_free(av_shift(rsfp_filters));
+
+ return;
+ }
+ /* we need to search for the correct entry and clear it */
+ die("filter_del can only delete in reverse order (currently)");
+}
+
+
+/* Invoke the n'th filter function for the current rsfp. */
+I32
+filter_read(idx, buf_sv, maxlen)
+ int idx;
+ SV *buf_sv;
+ int maxlen; /* 0 = read one text line */
+{
+ filter_t funcp;
+ SV *datasv = NULL;
+
+ if (!rsfp_filters)
+ return -1;
+ if (idx > AvFILL(rsfp_filters)){ /* Any more filters? */
+ /* Provide a default input filter to make life easy. */
+ /* Note that we append to the line. This is handy. */
+ if (filter_debug)
+ warn("filter_read %d: from rsfp\n", idx);
+ if (maxlen) {
+ /* Want a block */
+ int len ;
+ int old_len = SvCUR(buf_sv) ;
+
+ /* ensure buf_sv is large enough */
+ SvGROW(buf_sv, old_len + maxlen) ;
+ if ((len = fread(SvPVX(buf_sv) + old_len, 1, maxlen, rsfp)) <= 0){
+ if (ferror(rsfp))
+ return -1; /* error */
+ else
+ return 0 ; /* end of file */
+ }
+ SvCUR_set(buf_sv, old_len + len) ;
+ } else {
+ /* Want a line */
+ if (sv_gets(buf_sv, rsfp, SvCUR(buf_sv)) == NULL) {
+ if (ferror(rsfp))
+ return -1; /* error */
+ else
+ return 0 ; /* end of file */
+ }
+ }
+ return SvCUR(buf_sv);
+ }
+ /* Skip this filter slot if filter has been deleted */
+ if ( (datasv = FILTER_DATA(idx)) == &sv_undef){
+ if (filter_debug)
+ warn("filter_read %d: skipped (filter deleted)\n", idx);
+ return FILTER_READ(idx+1, buf_sv, maxlen); /* recurse */
+ }
+ /* Get function pointer hidden within datasv */
+ funcp = (filter_t)IoDIRP(datasv);
+ if (filter_debug)
+ warn("filter_read %d: via function %lx (%s)\n",
+ idx, funcp, SvPV(datasv,na));
+ /* Call function. The function is expected to */
+ /* call "FILTER_READ(idx+1, buf_sv)" first. */
+ /* Return: <0:error, =0:eof, >0:not eof */
+ return (*funcp)(idx, buf_sv, maxlen);
+}
+
+static char *
+filter_gets(sv,fp)
+register SV *sv;
+register FILE *fp;
+{
+ if (rsfp_filters) {
+
+ SvCUR_set(sv, 0); /* start with empty line */
+ if (FILTER_READ(0, sv, 0) > 0)
+ return ( SvPVX(sv) ) ;
+ else
+ return Nullch ;
+ }
+ else
+ return (sv_gets(sv, fp, 0)) ;
+
}
}
goto retry;
}
- /* Give cryptswitch a chance. Note that cryptswitch_fp may */
- /* be either be called once if it redirects rsfp and unregisters */
- /* itself, or it may be called on every line if it loads linestr. */
- if (cryptswitch_fp && (*cryptswitch_fp)()) {
- oldoldbufptr = oldbufptr = s = SvPVX(linestr);
- bufend = SvPVX(linestr) + SvCUR(linestr);
- goto retry;
- }
do {
- if ((s = sv_gets(linestr, rsfp, 0)) == Nullch) {
+ if ((s = filter_gets(linestr, rsfp)) == Nullch) {
fake_eof:
if (rsfp) {
if (preprocess && !in_eval)
OPERATOR(tmp);
case ')':
tmp = *s++;
+ s = skipspace(s);
+ if (*s == '{')
+ PREBLOCK(tmp);
TERM(tmp);
case ']':
s++;
lex_state = LEX_INTERPEND;
}
}
- TOKEN(']');
+ TERM(']');
case '{':
leftbracket:
s++;
AOPERATOR(ANDAND);
s--;
if (expect == XOPERATOR) {
- if (isALPHA(*s) && bufptr == SvPVX(linestr)) {
+ if (dowarn && isALPHA(*s) && bufptr == SvPVX(linestr)) {
curcop->cop_line--;
warn(warn_nosemi);
curcop->cop_line++;
}
else if (!strchr(tokenbuf,':')) {
if (oldexpect != XREF || oldoldbufptr == last_lop) {
- if (*s == '[')
- tokenbuf[0] = '@';
- else if (*s == '{')
- tokenbuf[0] = '%';
+ if (intuit_more(s)) {
+ if (*s == '[')
+ tokenbuf[0] = '@';
+ else if (*s == '{')
+ tokenbuf[0] = '%';
+ }
}
if (tmp = pad_findmy(tokenbuf)) {
nextval[nexttoke].opval = newOP(OP_PADANY, 0);
nextval[nexttoke].opval->op_targ = tmp;
force_next(PRIVATEREF);
}
- else {
- if ((tainting || !euid) &&
- !isLOWER(tokenbuf[1]) &&
- (isDIGIT(tokenbuf[1]) ||
- strchr("&`'+", tokenbuf[1]) ||
- instr(tokenbuf,"MATCH") ))
- hints |= HINT_BLOCK_SCOPE; /* Can't optimize block out*/
+ else
force_ident(tokenbuf+1, *tokenbuf);
- }
}
else
force_ident(tokenbuf+1, *tokenbuf);
TERM('@');
}
else if (!strchr(tokenbuf,':')) {
- if (*s == '{')
- tokenbuf[0] = '%';
+ if (intuit_more(s)) {
+ if (*s == '{')
+ tokenbuf[0] = '%';
+ }
if (tmp = pad_findmy(tokenbuf)) {
nextval[nexttoke].opval = newOP(OP_PADANY, 0);
nextval[nexttoke].opval->op_targ = tmp;
}
/* Force them to make up their mind on "@foo". */
- if (lex_state != LEX_NORMAL &&
+ if (lex_state != LEX_NORMAL && !lex_brackets &&
( !(gv = gv_fetchpv(tokenbuf+1, FALSE, SVt_PVAV)) ||
(*tokenbuf == '@'
? !GvAV(gv)
}
if (!s)
missingterm((char*)0);
- yylval.ival = OP_STRINGIFY;
+ yylval.ival = OP_CONST;
+ for (d = SvPV(lex_stuff, len); len; len--, d++) {
+ if (*d == '$' || *d == '@' || *d == '\\') {
+ yylval.ival = OP_STRINGIFY;
+ break;
+ }
+ }
TERM(sublex_start());
case '`':
bufptr = s;
s = scan_word(s, tokenbuf, FALSE, &len);
+ if (*s == ':' && s[1] == ':' && strNE(tokenbuf, "CORE"))
+ goto just_a_word;
+
tmp = keyword(tokenbuf, len);
/* Is this a word before a => operator? */
/* If not a declared subroutine, it's an indirect object. */
/* (But it's an indir obj regardless for sort.) */
- if (last_lop_op == OP_SORT ||
- (!immediate_paren && (!gv || !GvCV(gv))) ) {
+ if ((last_lop_op == OP_SORT ||
+ (!immediate_paren && (!gv || !GvCV(gv))) ) &&
+ (last_lop_op != OP_MAPSTART && last_lop_op != OP_GREPSTART)){
expect = (last_lop == oldoldbufptr) ? XTERM : XOPERATOR;
goto bareword;
}
/* Not a method, so call it a subroutine (if defined) */
if (gv && GvCV(gv)) {
+ CV* cv = GvCV(gv);
nextval[nexttoke].opval = yylval.opval;
if (*s == '(') {
expect = XTERM;
TOKEN('&');
}
if (lastchar == '-')
- warn("Ambiguious use of -%s resolved as -&%s()",
+ warn("Ambiguous use of -%s resolved as -&%s()",
tokenbuf, tokenbuf);
last_lop = oldbufptr;
last_lop_op = OP_ENTERSUB;
+ /* Is there a prototype? */
+ if (SvPOK(cv)) {
+ STRLEN len;
+ char *proto = SvPV((SV*)cv, len);
+ if (!len)
+ TERM(FUNC0SUB);
+ if (strEQ(proto, "$"))
+ OPERATOR(UNIOPSUB);
+ if (*proto == '&' && *s == '{') {
+ sv_setpv(subname,"__ANON__");
+ PREBLOCK(LSTOPSUB);
+ }
+ }
expect = XTERM;
force_next(WORD);
TOKEN(NOAMP);
if (lastchar && strchr("*%&", lastchar)) {
warn("Operator or semicolon missing before %c%s",
lastchar, tokenbuf);
- warn("Ambiguious use of %c resolved as operator %c",
+ warn("Ambiguous use of %c resolved as operator %c",
lastchar, lastchar);
}
TOKEN(WORD);
TERM(THING);
}
+ case KEY___DATA__:
case KEY___END__: {
GV *gv;
/*SUPPRESS 560*/
- if (!in_eval) {
- gv = gv_fetchpv("main::DATA",TRUE, SVt_PVIO);
+ if (!in_eval || tokenbuf[2] == 'D') {
+ char dname[256];
+ char *pname = "main";
+ if (tokenbuf[2] == 'D')
+ pname = HvNAME(curstash ? curstash : defstash);
+ sprintf(dname,"%s::DATA", pname);
+ gv = gv_fetchpv(dname,TRUE, SVt_PVIO);
SvMULTI_on(gv);
if (!GvIO(gv))
GvIOp(gv) = newIO();
case KEY_sub:
really_sub:
s = skipspace(s);
- if (*s == '{' && tmp == KEY_sub) {
- sv_setpv(subname,"__ANON__");
- PRETERMBLOCK(ANONSUB);
- }
- expect = XBLOCK;
+
if (isIDFIRST(*s) || *s == '\'' || *s == ':') {
char tmpbuf[128];
+ expect = XBLOCK;
d = scan_word(s, tmpbuf, TRUE, &len);
if (strchr(tmpbuf, ':'))
sv_setpv(subname, tmpbuf);
sv_catpvn(subname,tmpbuf,len);
}
s = force_word(s,WORD,FALSE,TRUE,TRUE);
+ s = skipspace(s);
}
- else
+ else {
+ expect = XTERMBLOCK;
sv_setpv(subname,"?");
+ }
+
+ if (tmp == KEY_format) {
+ s = skipspace(s);
+ if (*s == '=')
+ lex_formbrack = lex_brackets + 1;
+ OPERATOR(FORMAT);
+ }
- if (tmp != KEY_format)
- PREBLOCK(SUB);
+ /* Look for a prototype */
+ if (*s == '(') {
+ s = scan_str(s);
+ if (!s) {
+ if (lex_stuff)
+ SvREFCNT_dec(lex_stuff);
+ lex_stuff = Nullsv;
+ croak("Prototype not terminated");
+ }
+ nexttoke++;
+ nextval[1] = nextval[0];
+ nexttype[1] = nexttype[0];
+ nextval[0].opval = (OP*)newSVOP(OP_CONST, 0, lex_stuff);
+ nexttype[0] = THING;
+ if (nexttoke == 1) {
+ lex_defer = lex_state;
+ lex_expect = expect;
+ lex_state = LEX_KNOWNEXT;
+ }
+ lex_stuff = Nullsv;
+ }
- s = skipspace(s);
- if (*s == '=')
- lex_formbrack = lex_brackets + 1;
- OPERATOR(FORMAT);
+ if (*SvPV(subname,na) == '?') {
+ sv_setpv(subname,"__ANON__");
+ TOKEN(ANONSUB);
+ }
+ PREBLOCK(SUB);
case KEY_system:
set_csh();
if (d[1] == '_') {
if (strEQ(d,"__LINE__")) return -KEY___LINE__;
if (strEQ(d,"__FILE__")) return -KEY___FILE__;
+ if (strEQ(d,"__DATA__")) return KEY___DATA__;
if (strEQ(d,"__END__")) return KEY___END__;
}
break;
break;
case 6:
if (strEQ(d,"exists")) return KEY_exists;
+ if (strEQ(d,"elseif")) warn("elseif should be elsif");
break;
case 8:
if (strEQ(d,"endgrent")) return -KEY_endgrent;
if (*s == ',') {
int kw;
*s = '\0';
- kw = keyword(w, s - w);
+ kw = keyword(w, s - w) || perl_get_cv(w, FALSE) != 0;
*s = ',';
if (kw)
return;
while (*s && strchr("iogmsx", *s))
pmflag(&pm->op_pmflags,*s++);
+ pm->op_pmpermflags = pm->op_pmflags;
lex_op = (OP*)pm;
yylval.ival = OP_MATCH;
return s;
lex_repl = repl;
}
+ pm->op_pmpermflags = pm->op_pmflags;
lex_op = (OP*)pm;
yylval.ival = OP_SUBST;
return s;
SV *tmpstr;
char term;
register char *d;
+ char *peek;
s += 2;
d = tokenbuf;
if (!rsfp)
*d++ = '\n';
- if (*s && strchr("`'\"",*s)) {
+ for (peek = s; *peek == ' ' || *peek == '\t'; peek++) ;
+ if (*peek && strchr("`'\"",*peek)) {
+ s = peek;
term = *s++;
s = cpytill(d,s,bufend,term,&len);
if (s < bufend)
s++, term = '\'';
else
term = '"';
+ if (!isALNUM(*s))
+ deprecate("bare << to mean <<\"\"");
while (isALNUM(*s))
*d++ = *s++;
} /* assuming tokenbuf won't clobber */
sv_setpvn(tmpstr,"",0); /* avoid "uninitialized" warning */
while (s >= bufend) { /* multiple line string? */
if (!rsfp ||
- !(oldoldbufptr = oldbufptr = s = sv_gets(linestr, rsfp, 0))) {
+ !(oldoldbufptr = oldbufptr = s = filter_gets(linestr, rsfp))) {
curcop->cop_line = multi_start;
missingterm(tokenbuf);
}
else
croak("Unterminated <> operator");
- if (*d == '$') d++;
+ if (*d == '$' && d[1]) d++;
while (*d && (isALNUM(*d) || *d == '\'' || *d == ':'))
d++;
if (d - tokenbuf != len) {
if (s < bufend) break; /* string ends on this line? */
if (!rsfp ||
- !(oldoldbufptr = oldbufptr = s = sv_gets(linestr, rsfp, 0))) {
+ !(oldoldbufptr = oldbufptr = s = filter_gets(linestr, rsfp))) {
curcop->cop_line = multi_start;
return Nullch;
}
}
s = eol;
if (rsfp) {
- s = sv_gets(linestr, rsfp, 0);
+ s = filter_gets(linestr, rsfp);
oldoldbufptr = oldbufptr = bufptr = SvPVX(linestr);
bufend = bufptr + SvCUR(linestr);
if (!s) {
CV* outsidecv = compcv;
AV* comppadlist;
+ if (compcv) {
+ assert(SvTYPE(compcv) == SVt_PVCV);
+ }
save_I32(&subline);
save_item(subname);
SAVEINT(padix);
sv_upgrade((SV *)compcv, SVt_PVCV);
comppad = newAV();
- SAVEFREESV((SV*)comppad);
comppad_name = newAV();
- SAVEFREESV((SV*)comppad_name);
comppad_name_fill = 0;
min_intro_pending = 0;
av_push(comppad, Nullsv);
comppadlist = newAV();
AvREAL_off(comppadlist);
- av_store(comppadlist, 0, SvREFCNT_inc((SV*)comppad_name));
- av_store(comppadlist, 1, SvREFCNT_inc((SV*)comppad));
+ av_store(comppadlist, 0, (SV*)comppad_name);
+ av_store(comppadlist, 1, (SV*)comppad);
CvPADLIST(compcv) = comppadlist;
- CvOUTSIDE(compcv) = outsidecv;
+ CvOUTSIDE(compcv) = (CV*)SvREFCNT_inc((SV*)outsidecv);
return oldsavestack_ix;
}
if (lex_state == LEX_NORMAL ||
(lex_state == LEX_KNOWNEXT && lex_defer == LEX_NORMAL))
(void)strcpy(tname,"at end of line");
+ else if (lex_inpat)
+ (void)strcpy(tname,"within pattern");
else
(void)strcpy(tname,"within string");
}
if (in_eval & 2)
warn("%s",buf);
else if (in_eval)
- sv_catpv(GvSV(gv_fetchpv("@",TRUE, SVt_PV)),buf);
+ sv_catpv(GvSV(errgv),buf);
else
fputs(buf,stderr);
if (++error_count >= 10)
croak("%s has too many errors.\n",
SvPVX(GvSV(curcop->cop_filegv)));
+ in_my = 0;
return 0;
}