2
* regcomp and regexec -- regsub and regerror are elsewhere @(#)regexp.c 1.3
5
* Copyright (c) 1986 by University of Toronto. Written by Henry Spencer. Not
6
* derived from licensed software.
8
* Permission is granted to anyone to use this software for any purpose on any
9
* computer system, and to redistribute it freely, subject to the following
12
* 1. The author is not responsible for the consequences of use of this
13
* software, no matter how awful, even if they arise from defects in it.
15
* 2. The origin of this software must not be misrepresented, either by explicit
16
* claim or by omission.
18
* 3. Altered versions must be plainly marked as such, and must not be
19
* misrepresented as being the original software.
21
* Beware that some of this code is subtly aware of the way operator precedence
22
* is structured in regular expressions. Serious changes in
23
* regular-expression syntax might require a total rethink.
26
/* Lots of changes for Jade. See the file README.regexp for more details */
30
#define rep_NEED_REGEXP_INTERNALS
41
* Utility definitions.
44
#define UCHARAT(p) ((int)*(unsigned char *)(p))
46
#define UCHARAT(p) ((int)*(p)&CHARBITS)
49
#define FAIL(m) { rep_regerror(m); return(NULL); }
50
#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?')
51
#define META "^$.[()|?+*\\"
54
* Flags to be passed up and down.
56
#define HASWIDTH 01 /* Known never to match null string. */
57
#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */
58
#define SPSTART 04 /* Starts with * or +. */
59
#define WORST 0 /* Worst case. */
62
* Global work variables for regcomp().
64
static char *regparse; /* Input-scan pointer. */
65
static int regnpar; /* () count. */
67
static char *regcode; /* Code-emit pointer; ®dummy = don't. */
68
static long regsize; /* Code size. */
71
* Forward declarations for regcomp()'s friends.
73
static char *reg(int, int *);
74
static char *regbranch(int *);
75
static char *regpiece(int *);
76
static char *regatom(int *);
77
static char *regnode(char);
78
static char *regnext(char *);
79
static void regc(char);
80
static void reginsert(char, char *);
81
static void regtail(char *, char *);
82
static void regoptail(char *, char *);
83
extern void rep_regerror(char *);
85
void regdump(rep_regexp *);
86
int regenable_debug = 0;
90
int strcspn(char *, char *);
94
* - regcomp - compile a regular expression into internal code
96
* We can't allocate space until we know how big the compiled form will be, but
97
* we can't compile it (and thus know how big it is) until we've got a place
98
* to put the code. So we cheat: we compile it twice, once with code
99
* generation turned off and size counting turned on, and once "for real".
100
* This also means that we don't allocate space until we are sure that the
101
* thing really will compile successfully, and we never have to move the code
102
* and thus invalidate pointers into it. (Note that it has to be in one
103
* piece because free() must be able to free it all.)
105
* Beware that the optimization-preparation code in here knows about some of the
106
* structure of the compiled regexp.
109
rep_regcomp(char *exp)
111
register rep_regexp *r;
113
register char *longest;
118
FAIL("NULL argument");
120
/* First pass: determine size, legality. */
126
if (reg(0, &flags) == NULL)
129
/* Small enough for pointer-storage convention? */
130
if (regsize >= 32767L) /* Probably could be 65535L. */
131
FAIL("regexp too big");
133
/* Allocate space. */
134
r = (rep_regexp *) malloc(sizeof(rep_regexp) + (unsigned) regsize);
136
FAIL("out of space");
138
/* Second pass: emit code. */
141
regcode = r->program;
143
if (reg(0, &flags) == NULL)
146
/* Dig out information for optimizations. */
147
r->regstart = '\0'; /* Worst-case defaults. */
151
r->regsize = sizeof(rep_regexp) + (unsigned)regsize;
152
scan = r->program + 1; /* First BRANCH. */
153
if (OP(regnext(scan)) == END) { /* Only one top-level choice. */
154
scan = OPERAND(scan);
156
/* Starting-point info. */
157
if (OP(scan) == EXACTLY)
158
r->regstart = UCHARAT(OPERAND(scan));
159
else if (OP(scan) == BOL)
163
* If there's something expensive in the r.e., find the longest
164
* literal string that must appear and make it the regmust. Resolve
165
* ties in favor of later strings, since the regstart check works
166
* with the beginning of the r.e. and avoiding duplication
167
* strengthens checking. Not a strong reason, but sufficient in the
170
if (flags & SPSTART) {
173
for (; scan != NULL; scan = regnext(scan))
174
if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
175
longest = OPERAND(scan);
176
len = strlen(OPERAND(scan));
178
r->regmust = longest;
183
if (regenable_debug) {
184
printf ("compiled `%s' to:\n", exp);
192
* - reg - regular expression, i.e. main body or parenthesized thing
194
* Caller must absorb opening parenthesis.
196
* Combining parenthesis handling with the base level of regular expression is a
197
* trifle forced, but the need to tie the tails of the branches to what
198
* follows makes it hard to avoid.
201
reg(int paren, int *flagp)
205
register char *ender;
206
register int parno = 0;
209
*flagp = HASWIDTH; /* Tentatively. */
211
/* Make an OPEN node, if parenthesized. */
213
if (regnpar >= rep_NSUBEXP)
217
ret = regnode(OPEN + parno);
221
/* Pick up the branches, linking them together. */
222
br = regbranch(&flags);
226
regtail(ret, br); /* OPEN -> first. */
229
if (!(flags & HASWIDTH))
231
*flagp |= flags & SPSTART;
232
while (*regparse == '|') {
234
br = regbranch(&flags);
237
regtail(ret, br); /* BRANCH -> BRANCH. */
238
if (!(flags & HASWIDTH))
240
*flagp |= flags & SPSTART;
243
/* Make a closing node, and hook it on the end. */
244
ender = regnode((paren) ? CLOSE + parno : END);
247
/* Hook the tails of the branches to the closing node. */
248
for (br = ret; br != NULL; br = regnext(br))
249
regoptail(br, ender);
251
/* Check for proper termination. */
252
if (paren && *regparse++ != ')') {
253
FAIL("unmatched ()");
254
} else if (!paren && *regparse != '\0') {
255
if (*regparse == ')') {
256
FAIL("unmatched ()");
258
FAIL("junk on end");/* "Can't happen". */
265
* - regbranch - one alternative of an | operator
267
* Implements the concatenation operator.
270
regbranch(int *flagp)
273
register char *chain;
274
register char *latest;
277
*flagp = WORST; /* Tentatively. */
279
ret = regnode(BRANCH);
281
while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
282
latest = regpiece(&flags);
285
*flagp |= flags & HASWIDTH;
286
if (chain == NULL) /* First piece. */
287
*flagp |= flags & SPSTART;
289
regtail(chain, latest);
292
if (chain == NULL) /* Loop ran zero times. */
293
(void) regnode(NOTHING);
299
* - regpiece - something followed by possible [*+?]
301
* Note that the branching code sequences used for ? and the general cases of *
302
* and + are somewhat optimized: they use the same NOTHING node as both the
303
* endmarker for their branch list and the body of the last branch. It might
304
* seem that this node could be dispensed with entirely, but the endmarker
305
* role is not redundant.
316
ret = regatom(&flags);
325
if (!(flags & HASWIDTH) && op != '?')
326
FAIL("*+ operand could be empty");
327
*flagp = (op != '+') ? (WORST | SPSTART) : (WORST | HASWIDTH);
328
greedy = (regparse[1] != '?');
330
if (op == '*' && (flags & SIMPLE))
331
reginsert(greedy ? STAR : NGSTAR, ret);
332
else if (op == '*') {
334
/* Emit x* as (x&|), where & means "self". */
335
reginsert(BRANCH, ret); /* Either x */
336
regoptail(ret, regnode(BACK)); /* and loop */
337
regoptail(ret, ret); /* back */
338
regtail(ret, regnode(BRANCH)); /* or */
339
regtail(ret, regnode(NOTHING)); /* null. */
341
/* Emit x*? as (|x&), where & means "self". */
342
reginsert(BRANCH, ret); /* Either */
343
reginsert(NOTHING, ret); /* null. */
344
reginsert(BRANCH, ret); /* or x */
345
regtail(ret+9, regnode(BACK)); /* and loop */
346
regtail(ret+9, ret); /* back */
348
regtail(ret+3, regcode);
350
} else if (op == '+' && (flags & SIMPLE))
351
reginsert(greedy ? PLUS : NGPLUS, ret);
352
else if (op == '+') {
354
/* Emit x+ as x(&|), where & means "self". */
355
next = regnode(BRANCH); /* Either */
357
regtail(regnode(BACK), ret); /* loop back */
358
regtail(next, regnode(BRANCH)); /* or */
359
regtail(ret, regnode(NOTHING)); /* null. */
362
/* Emit x+? as x(|&), where & means "self". */
363
next = regnode(BRANCH); /* Either */
365
null = regnode(NOTHING); /* null */
366
b2 = regnode(BRANCH);
367
regtail(regnode(BACK), ret); /* or loop back */
369
regtail(null, regcode);
371
} else if (op == '?') {
373
/* Emit x? as (x|) */
374
reginsert(BRANCH, ret); /* Either x */
375
regtail(ret, regnode(BRANCH)); /* or */
376
next = regnode(NOTHING); /* null. */
378
regoptail(ret, next);
380
/* Emit x?? as (|x) */
381
reginsert(BRANCH, ret);
382
reginsert(NOTHING, ret); /* Either null */
383
reginsert(BRANCH, ret); /* or x. */
384
regoptail(ret, regcode);
385
regtail(ret, ret + 6);
386
regtail(ret, regcode);
393
if (ISMULT(*regparse))
400
* - regatom - the lowest level
402
* Optimization: gobbles an entire sequence of ordinary characters so that it
403
* can turn them into a single node, which is smaller to store and faster to
404
* run. Backslashed characters are exceptions, each becoming a separate
405
* node; the code is simpler that way and it's not worth fixing.
413
*flagp = WORST; /* Tentatively. */
415
switch (*regparse++) {
424
*flagp |= HASWIDTH | SIMPLE;
428
register int classend;
430
if (*regparse == '^') { /* Complement of range. */
431
ret = regnode(ANYBUT);
434
ret = regnode(ANYOF);
435
if (*regparse == ']' || *regparse == '-')
437
while (*regparse != '\0' && *regparse != ']') {
438
if (*regparse == '-') {
440
if (*regparse == ']' || *regparse == '\0')
443
class = UCHARAT(regparse - 2) + 1;
444
classend = UCHARAT(regparse);
445
if (class > classend + 1)
446
FAIL("invalid [] range");
447
for (; class <= classend; class++)
455
if (*regparse != ']')
456
FAIL("unmatched []");
458
*flagp |= HASWIDTH | SIMPLE;
462
ret = reg(1, &flags);
465
*flagp |= flags & (HASWIDTH | SPSTART);
470
FAIL("internal urp"); /* Supposed to be caught earlier. */
475
FAIL("?+* follows nothing");
484
ret = regnode (WORD);
485
*flagp |= HASWIDTH | SIMPLE;
488
ret = regnode (NWORD);
489
*flagp |= HASWIDTH | SIMPLE;
492
ret = regnode (WSPC);
493
*flagp |= HASWIDTH | SIMPLE;
496
ret = regnode (NWSPC);
497
*flagp |= HASWIDTH | SIMPLE;
500
ret = regnode (DIGI);
501
*flagp |= HASWIDTH | SIMPLE;
504
ret = regnode (NDIGI);
505
*flagp |= HASWIDTH | SIMPLE;
508
ret = regnode (WEDGE);
511
ret = regnode (NWEDGE);
514
ret = regnode(EXACTLY);
517
*flagp |= HASWIDTH | SIMPLE;
525
len = strcspn(regparse, META);
527
FAIL("internal disaster");
528
ender = *(regparse + len);
529
if (len > 1 && ISMULT(ender))
530
len--; /* Back off clear of ?+* operand. */
534
ret = regnode(EXACTLY);
548
* - regnode - emit a node
550
static char * /* Location. */
557
if (ret == ®dummy) {
563
*ptr++ = '\0'; /* Null "next" pointer. */
571
* - regc - emit (if appropriate) a byte of code
576
if (regcode != ®dummy)
583
* - reginsert - insert an operator in front of already-emitted operand
585
* Means relocating the operand.
588
reginsert(char op, char *opnd)
592
register char *place;
594
if (regcode == ®dummy) {
604
place = opnd; /* Op node, where operand used to be. */
611
* - regtail - set the next-pointer at the end of a node chain
614
regtail(char *p, char *val)
620
if (regcode == ®dummy)
623
/* Find last node. */
626
temp = regnext(scan);
632
if (OP(scan) == BACK)
636
*(scan + 1) = (offset >> 8) & 0377;
637
*(scan + 2) = offset & 0377;
641
* - regoptail - regtail on operand of first argument; nop if operandless
644
regoptail(char *p, char *val)
646
/* "Operandless" and "op != BRANCH" are synonymous in practice. */
647
if (p == NULL || regcode == ®dummy || OP(p) != BRANCH)
649
regtail(OPERAND(p), val);
653
* regexec and friends
657
* Global work variables for regexec().
659
static char *reginput; /* String-input pointer. */
660
static char *regbol; /* Beginning of input, for ^ check. */
661
static char **regstartp; /* Pointer to startp array. */
662
static char **regendp; /* Ditto for endp. */
663
static char regnocase; /* Ignore case when string-matching. */
664
static int regnest; /* depth of recursion */
666
int rep_regexp_max_depth = 2048;
671
static int regtry(rep_regexp *, char *);
672
static int regmatch(char *);
673
static int regrepeat(char *);
677
char *regprop(char *);
682
* - regexec - match a regexp against a string
684
* jsh -- changed regexec to regexec2 with an extra argument for flag bits,
685
* flags are REG_NOTBOL and REG_NOCASE.
688
rep_regexec2(rep_regexp *prog, char *string, int eflags)
691
/* For REG_NOCASE and strpbrk() */
692
static char mat[3] = "xX";
695
if (prog == NULL || string == NULL) {
696
rep_regerror("NULL parameter");
699
/* Check validity of program. */
700
if (UCHARAT(prog->program) != MAGIC) {
701
rep_regerror("corrupted program");
705
/* jsh -- Check for REG_NOCASE, means ignore case in string matches. */
706
regnocase = ((eflags & rep_REG_NOCASE) != 0);
708
/* If there is a "must appear" string, look for it. */
709
if (prog->regmust != NULL)
714
mat[0] = tolower(UCHARAT(prog->regmust));
715
mat[1] = toupper(UCHARAT(prog->regmust));
716
while ((s = strpbrk(s, mat)) != NULL)
718
if(strncasecmp(s, prog->regmust, prog->regmlen) == 0)
719
break; /* Found it. */
725
while ((s = strchr(s, prog->regmust[0])) != NULL)
727
if(strncmp(s, prog->regmust, prog->regmlen) == 0)
728
break; /* Found it. */
732
if (s == NULL) /* Not present. */
735
/* Mark beginning of line for ^ . */
736
/* jsh -- if REG_NOTBOL is set then set regbol to something absurd
737
to guarantee ^ doesn't match */
738
regbol = (eflags & rep_REG_NOTBOL) ? "" : string;
740
/* Simplest case: anchored match need be tried only once. */
742
return (regtry(prog, string));
744
/* Messy cases: unanchored match. */
746
if (prog->regstart != '\0')
748
/* We know what char it must start with. */
751
mat[0] = tolower(prog->regstart);
752
mat[1] = toupper(prog->regstart);
753
while((s = strpbrk(s, mat)) != NULL)
762
while((s = strchr(s, prog->regstart)) != NULL)
771
/* We don't -- general case. */
775
} while (*s++ != '\0');
782
* - regmatch_string - match a regexp against the string STRING.
786
rep_regmatch_string(rep_regexp *prog, char *string, int eflags)
788
/* Check for REG_NOCASE, means ignore case in string matches. */
789
regnocase = ((eflags & rep_REG_NOCASE) != 0);
791
/* Mark beginning of line for ^ . */
792
/* jsh -- if REG_NOTBOL is set then set regbol to something absurd
793
to guarantee ^ doesn't match */
794
regbol = (eflags & rep_REG_NOTBOL) ? "" : string;
796
return regtry(prog, string);
800
* - regtry - try match at specific point
802
static int /* 0 failure, 1 success */
803
regtry(rep_regexp *prog, char *string)
810
regstartp = prog->matches.string.startp;
811
regendp = prog->matches.string.endp;
814
sp = prog->matches.string.startp;
815
ep = prog->matches.string.endp;
816
for (i = rep_NSUBEXP; i > 0; i--) {
820
if (regmatch(prog->program + 1)) {
821
regstartp[0] = string;
822
regendp[0] = reginput;
823
prog->lasttype = rep_reg_string;
829
/* get around the insane number of return statements in regmatch () */
831
nested_regmatch (char *prog)
835
ret = regmatch (prog);
841
* - regmatch - main matching routine
843
* Conceptually the strategy is simple: check to see whether the current node
844
* matches, call self recursively to see whether the rest matches, and then
845
* act accordingly. In practice we make some effort to avoid recursion, in
846
* particular by going through "ordinary" nodes (that don't need to know
847
* whether the rest of the match failed) by a loop instead of by recursion.
849
static int /* 0 failure, 1 success */
852
register char *scan; /* Current node. */
853
char *next; /* Next node. */
855
if (regnest >= rep_regexp_max_depth)
857
/* recursion overload, bail out */
858
rep_regerror ("stack overflow");
864
if (scan != NULL && regnarrate)
865
fprintf(stderr, "%s(\n", regprop(scan));
867
while (scan != NULL) {
870
fprintf(stderr, "%s...\n", regprop(scan));
872
next = regnext(scan);
876
if (reginput != regbol)
880
if (*reginput != '\0')
884
if (*reginput == '\0')
891
opnd = OPERAND(scan);
894
/* Inline the first character, for speed. */
895
if(toupper(UCHARAT(opnd)) != toupper(UCHARAT(reginput)))
898
if(len > 1 && strncasecmp(opnd, reginput, len) != 0)
903
/* Inline the first character, for speed. */
904
if(*opnd != *reginput)
907
if(len > 1 && strncmp(opnd, reginput, len) != 0)
914
if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
919
if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
939
no = OP(scan) - OPEN;
942
if (nested_regmatch(next)) {
944
* Don't set startp if some later invocation of the same
945
* parentheses already has.
947
if (regstartp[no] == NULL)
948
regstartp[no] = save;
966
no = OP(scan) - CLOSE;
969
if (nested_regmatch(next)) {
971
* Don't set endp if some later invocation of the same
972
* parentheses already has.
974
if (regendp[no] == NULL)
984
if (OP(next) != BRANCH) /* No choice. */
985
next = OPERAND(scan); /* Avoid recursion. */
989
if (nested_regmatch(OPERAND(scan)))
992
scan = regnext(scan);
993
} while (scan != NULL && OP(scan) == BRANCH);
1001
register u_char nextch;
1003
register char *save;
1007
* Lookahead to avoid useless match attempts when we know
1008
* what character comes next.
1011
if (OP(next) == EXACTLY)
1012
nextch = UCHARAT(OPERAND(next));
1014
nextch = toupper(nextch);
1015
min = (OP(scan) == STAR) ? 0 : 1;
1017
no = regrepeat(OPERAND(scan));
1019
/* If it could work, try it. */
1021
|| (regnocase ? toupper(UCHARAT(reginput))
1022
: *reginput) == nextch)
1023
if (nested_regmatch(next))
1025
/* Couldn't or didn't -- back up. */
1027
reginput = save + no;
1034
register u_char nextch;
1036
register char *save;
1040
* Lookahead to avoid useless match attempts when we know
1041
* what character comes next.
1044
if (OP(next) == EXACTLY)
1045
nextch = UCHARAT(OPERAND(next));
1047
nextch = toupper(nextch);
1048
no = (OP(scan) == NGSTAR) ? 0 : 1;
1050
max = regrepeat(OPERAND(scan));
1052
reginput = save + no;
1053
/* If it could work, try it. */
1055
|| (regnocase ? toupper(UCHARAT(reginput))
1056
: *reginput) == nextch)
1057
if (nested_regmatch(next))
1059
/* Couldn't or didn't -- move up. */
1066
if (*reginput != '_' && !isalnum (UCHARAT(reginput)))
1071
if (*reginput == '_' || isalnum (UCHARAT(reginput)))
1076
if (!isspace (UCHARAT(reginput)))
1081
if (isspace (UCHARAT(reginput)))
1086
if (!isdigit (UCHARAT(reginput)))
1091
if (isdigit (UCHARAT(reginput)))
1096
if (reginput == regbol || *reginput == '\0'
1097
|| ((reginput[-1] == '_' || isalnum (UCHARAT(reginput - 1)))
1098
&& (*reginput != '_' && !isalnum (UCHARAT(reginput))))
1099
|| ((reginput[-1] != '_' && !isalnum (UCHARAT(reginput - 1)))
1100
&& (*reginput == '_' || isalnum (UCHARAT(reginput)))))
1104
if (!(reginput == regbol || *reginput == '\0'
1105
|| ((reginput[-1] == '_' || isalnum (UCHARAT(reginput - 1)))
1106
&& (*reginput != '_' && !isalnum (UCHARAT(reginput))))
1107
|| ((reginput[-1] != '_' && !isalnum (UCHARAT(reginput - 1)))
1108
&& (*reginput == '_' || isalnum (UCHARAT(reginput))))))
1112
return (1); /* Success! */
1115
rep_regerror("memory corruption");
1124
* We get here only if there's trouble -- normally "case END" is the
1125
* terminating point.
1127
rep_regerror("corrupted pointers");
1132
* - regrepeat - repeatedly match something simple, report how many
1138
register char *scan;
1139
register char *opnd;
1145
scan += strlen(scan);
1150
while(toupper(UCHARAT(opnd)) == toupper(UCHARAT(scan))) {
1156
while(*opnd == *scan) {
1162
while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
1167
while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
1172
while (*scan != '\0' && (*scan == '_' || isalnum (UCHARAT(scan)))) {
1177
while (*scan != '\0' && (*scan != '_' && !isalnum (UCHARAT(scan)))) {
1182
while (*scan != '\0' && isspace (UCHARAT(scan))) {
1187
while (*scan != '\0' && !isspace (UCHARAT(scan))) {
1192
while (*scan != '\0' && isdigit (UCHARAT(scan))) {
1197
while (*scan != '\0' && !isdigit (UCHARAT(scan))) {
1201
default: /* Oh dear. Called inappropriately. */
1202
rep_regerror("internal foulup");
1203
return 0; /* Best compromise. */
1207
count = scan - reginput;
1214
* - regnext - dig the "next" pointer out of a node
1219
register int offset;
1229
return (p - offset);
1231
return (p + offset);
1239
* - regdump - dump a regexp onto stdout in vaguely comprehensible form
1242
regdump(rep_regexp *r)
1245
register char op = EXACTLY; /* Arbitrary non-END op. */
1246
register char *next;
1250
while (op != END) { /* While that wasn't END last time... */
1252
printf("\t%4d%s", s - r->program, regprop(s)); /* Where, what. */
1254
if (next == NULL) /* Next ptr. */
1257
printf("(%d)", (s - r->program) + (next - s));
1259
if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
1260
/* Literal string, where present. */
1261
while (*s != '\0') {
1270
/* Header fields of interest. */
1271
if (r->regstart != '\0')
1272
printf("start `%c' ", r->regstart);
1274
printf("anchored ");
1275
if (r->regmust != NULL)
1276
printf("must have \"%s\"", r->regmust);
1281
* - regprop - printable representation of opcode
1287
static char buf[50];
1289
(void) strcpy(buf, ":");
1331
sprintf(buf + strlen(buf), "OPEN%d", OP(op) - OPEN);
1343
sprintf(buf + strlen(buf), "CLOSE%d", OP(op) - CLOSE);
1383
rep_regerror("corrupted opcode");
1388
(void) strcat(buf, p);
1394
* The following is provided for those people who do not have strcspn() in
1395
* their C libraries. They should get off their butts and do something about
1396
* it; at least one public-domain implementation of those (highly useful)
1397
* string routines has been published on Usenet.
1399
#ifndef HAVE_STRCSPN
1401
* strcspn - find length of initial segment of s1 consisting entirely of
1402
* characters not from s2
1405
strcspn(char *s1, char *s2)
1407
register char *scan1;
1408
register char *scan2;
1412
for (scan1 = s1; *scan1 != '\0'; scan1++) {
1413
for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */
1414
if (*scan1 == *scan2++)