Added regexp support, fixed Changelog.
This commit was genuinely broken at the time (not related to our build system): regexp.c bu commit'te eklendi ama karsilik gelen regexp.h unutuldu, 11 commit sonra (c8fdb5638) eklendi - o araliktaki her commit gercekten derlenemezdi
No binary (build failed)
—
(log yok)
| @@ -1,7 +1,81 @@ | |
| + | 0.31 |
| + | * I added a changelog for version 0.30. |
| + | * adjusted find internals to make it smaller, and removed |
| + | some redundancy. |
| + | * Fixed a segfault in ps when /etc/passwd or /etc/group |
| + | are absent. Now will warn you and carry on. |
| + | * Added in optional _real_ regular expression support (to be |
| + | the basis for a future sed utility). When compiled in, |
| + | adds 3.9k. |
| + | |
| + | |
| 0.30 | |
| Major changes -- lots of stuff rewritten. Many thanks to Lineo for | |
| paying me to make these updates. If you have any problems with busybox, | |
| - | or notice any bugs -- please let me know so I can fix it. |
| + | or notice any bugs -- please let me know so I can fix it. These |
| + | changes include: |
| + | |
| + | Core Changes: |
| + | * busybox can now invoke apps in two ways: via symlinks to the |
| + | busybox binary, and as 'busybox [function] [arguments]...' |
| + | * When invoked as busybox, the list of currently compiled in |
| + | functions is printed out (no this is not bloat -- the list |
| + | has to be there anyway to map invocation name to function). |
| + | * busybox no longer parses command lines for apps or displays their |
| + | usage info. Each app gets to handle (or not handle) this for |
| + | itself. |
| + | * Eliminated monadic, dyadic, descend, block_device, and |
| + | postprocess. It was cumbersome to have so many programs |
| + | cobbled together in this way. Without them, the app is much |
| + | more granular. |
| + | * All shared code now lives in utility.c, and is properly |
| + | ifdef'ed to be only included for those apps requiring it. |
| + | * Eliminated struct FileInfo (the basis of monadic, dyadic, etc) |
| + | so now each app has the function prototype of (da-dum): |
| + | extern int foo_main(int argc, char** argv); |
| + | which speeds integration of new apps. |
| + | * Adjusted the Makefile to make it easier to |
| + | {en|dis}able debugging. |
| + | * Changed default compiler optimization to -Os |
| + | (optimize for smaller binaries). |
| + | |
| + | App Changes: |
| + | * To cope with the new app function prototype and the removal of |
| + | monadic, dyadic, etc, the following apps were re-written: |
| + | * cat - Works same as always. |
| + | * chgrp, chmod, chown - rewrite. Combined into a single |
| + | source file. Absorbed patches from Enrique Zanardi <[email protected]> |
| + | that removes the dependency on libc6 libnss* libraries. |
| + | * cp - Can now do 'cp -a' can can copy devices, |
| + | pipes, symlinks, as well as recursive or non-recursive dir copies. |
| + | * fdflush - adjusted to remove dependancy on struct FileInfo. |
| + | * find - Now includes some basic regexp matching |
| + | which will be the basic of a future mini-sed. |
| + | * ln - Same functionality. |
| + | * mkdir - Added -p flag to feature set. |
| + | * mv - rewrite. |
| + | * rm - Added -f flag to feature set. |
| + | * rmdir - Same functionality. |
| + | * swapon, swapoff - Combined into a single binary. No longer |
| + | uses /etc/swaps. swap{on|off} -a uses /etc/fstab instead. |
| + | * touch - Same functionality. |
| + | * date - adjusted with a patch from Matthew Grant <[email protected]> |
| + | to accomodate glibc timezone support. I then ripped out GNU getopt. |
| + | * mkswap -- new version merged from util-linux. Can now make >128Meg swaps. |
| + | * Replaced the old and star, unstar, and tarcat with the tar |
| + | implementation from sash. Now tar behaves as god intended |
| + | it to (i.e. tar -xvf <file> and tar -cf <file> <dir> work). |
| + | * dd -- rewritten. Can with with files, stdin, stdout. |
| + | * Added the following new apps: |
| + | * loadfont -- added from debian boot floppies |
| + | * chroot -- added based on a patch from Paolo Molaro <[email protected]> |
| + | * grep -- I just wrote it. Only matches simple strings |
| + | * ps -- I just wrote it. Has _no_ options at all, but works. |
| + | * fsck_minix, mkfs_minix -- added from util-linux, but I ripped out |
| + | internationalization and such to make them smaller. |
| + | * sfdisk -- Added from util-linux (minus internationalization and such). |
| + | * Probably some other changes that I forgot to document... |
| + | |
| -Erik Andersen | |
| 0.28 | |
| @@ -40,6 +40,7 @@ | |
| //#define BB_PRINTF | |
| #define BB_PS | |
| #define BB_PWD | |
| + | #define BB_REGEXP |
| #define BB_REBOOT | |
| #define BB_RM | |
| #define BB_RMDIR | |
| @@ -52,6 +53,8 @@ | |
| //#define BB_TRUE_FALSE // Supplied by ash | |
| #define BB_UMOUNT | |
| #define BB_UPDATE | |
| - | #define BB_UTILITY |
| #define BB_ZCAT | |
| //#define BB_GZIP | |
| + | // Don't turn BB_UTILITY off. It contains support code |
| + | // that compiles to 0 if everything else if turned off. |
| + | #define BB_UTILITY |
| @@ -21,14 +21,15 @@ | |
| * | |
| */ | |
| + | #include "internal.h" |
| + | #include "regexp.h" |
| #include <stdio.h> | |
| #include <unistd.h> | |
| #include <dirent.h> | |
| - | #include "internal.h" |
| static char* pattern=NULL; | |
| - | static char* directory=NULL; |
| + | static char* directory="."; |
| static int dereferenceFlag=FALSE; | |
| static const char find_usage[] = "find [path...] [expression]\n" | |
| @@ -41,7 +42,7 @@ static int fileAction(const char *fileName, struct stat* statbuf) | |
| { | |
| if (pattern==NULL) | |
| fprintf(stdout, "%s\n", fileName); | |
| - | else if (match(fileName, pattern) == TRUE) |
| + | else if (find_match(fileName, pattern, TRUE) == TRUE) |
| fprintf(stdout, "%s\n", fileName); | |
| return( TRUE); | |
| } | |
| @@ -53,7 +54,7 @@ static int dirAction(const char *fileName, struct stat* statbuf) | |
| if (pattern==NULL) | |
| fprintf(stdout, "%s\n", fileName); | |
| - | else if (match(fileName, pattern) == TRUE) |
| + | else if (find_match(fileName, pattern, TRUE) == TRUE) |
| fprintf(stdout, "%s\n", fileName); | |
| dir = opendir( fileName); | |
| @@ -71,22 +72,18 @@ static int dirAction(const char *fileName, struct stat* statbuf) | |
| int find_main(int argc, char **argv) | |
| { | |
| - | if (argc <= 1) { |
| - | dirAction( ".", NULL); |
| - | } |
| - | |
| /* peel off the "find" */ | |
| argc--; | |
| argv++; | |
| - | if (**argv != '-') { |
| + | if ( argc > 0 && **argv != '-') { |
| directory=*argv; | |
| argc--; | |
| argv++; | |
| } | |
| /* Parse any options */ | |
| - | while (**argv == '-') { |
| + | while (argc > 0 && **argv == '-') { |
| int stopit=FALSE; | |
| while (*++(*argv) && stopit==FALSE) switch (**argv) { | |
| case 'f': | |
| @@ -120,6 +117,10 @@ int find_main(int argc, char **argv) | |
| break; | |
| } | |
| - | dirAction( directory, NULL); |
| + | if (recursiveAction(directory, TRUE, FALSE, FALSE, |
| + | fileAction, fileAction) == FALSE) { |
| + | exit( FALSE); |
| + | } |
| + | |
| exit(TRUE); | |
| } | |
| @@ -21,14 +21,15 @@ | |
| * | |
| */ | |
| + | #include "internal.h" |
| + | #include "regexp.h" |
| #include <stdio.h> | |
| #include <unistd.h> | |
| #include <dirent.h> | |
| - | #include "internal.h" |
| static char* pattern=NULL; | |
| - | static char* directory=NULL; |
| + | static char* directory="."; |
| static int dereferenceFlag=FALSE; | |
| static const char find_usage[] = "find [path...] [expression]\n" | |
| @@ -41,7 +42,7 @@ static int fileAction(const char *fileName, struct stat* statbuf) | |
| { | |
| if (pattern==NULL) | |
| fprintf(stdout, "%s\n", fileName); | |
| - | else if (match(fileName, pattern) == TRUE) |
| + | else if (find_match(fileName, pattern, TRUE) == TRUE) |
| fprintf(stdout, "%s\n", fileName); | |
| return( TRUE); | |
| } | |
| @@ -53,7 +54,7 @@ static int dirAction(const char *fileName, struct stat* statbuf) | |
| if (pattern==NULL) | |
| fprintf(stdout, "%s\n", fileName); | |
| - | else if (match(fileName, pattern) == TRUE) |
| + | else if (find_match(fileName, pattern, TRUE) == TRUE) |
| fprintf(stdout, "%s\n", fileName); | |
| dir = opendir( fileName); | |
| @@ -71,22 +72,18 @@ static int dirAction(const char *fileName, struct stat* statbuf) | |
| int find_main(int argc, char **argv) | |
| { | |
| - | if (argc <= 1) { |
| - | dirAction( ".", NULL); |
| - | } |
| - | |
| /* peel off the "find" */ | |
| argc--; | |
| argv++; | |
| - | if (**argv != '-') { |
| + | if ( argc > 0 && **argv != '-') { |
| directory=*argv; | |
| argc--; | |
| argv++; | |
| } | |
| /* Parse any options */ | |
| - | while (**argv == '-') { |
| + | while (argc > 0 && **argv == '-') { |
| int stopit=FALSE; | |
| while (*++(*argv) && stopit==FALSE) switch (**argv) { | |
| case 'f': | |
| @@ -120,6 +117,10 @@ int find_main(int argc, char **argv) | |
| break; | |
| } | |
| - | dirAction( directory, NULL); |
| + | if (recursiveAction(directory, TRUE, FALSE, FALSE, |
| + | fileAction, fileAction) == FALSE) { |
| + | exit( FALSE); |
| + | } |
| + | |
| exit(TRUE); | |
| } | |
| @@ -22,6 +22,7 @@ | |
| */ | |
| #include "internal.h" | |
| + | #include "regexp.h" |
| #include <stdio.h> | |
| #include <dirent.h> | |
| #include <errno.h> | |
| @@ -30,44 +31,17 @@ | |
| #include <time.h> | |
| #include <ctype.h> | |
| - | |
| static const char grep_usage[] = | |
| "grep [-ihn]... PATTERN [FILE]...\n" | |
| "Search for PATTERN in each FILE or standard input.\n\n" | |
| "\t-h\tsuppress the prefixing filename on output\n" | |
| "\t-i\tignore case distinctions\n" | |
| "\t-n\tprint line number with output lines\n\n" | |
| + | #if defined BB_REGEXP |
| + | "This version of grep matches full regexps.\n"; |
| + | #else |
| "This version of grep matches strings (not full regexps).\n"; | |
| - | |
| - | |
| - | /* |
| - | * See if the specified needle is found in the specified haystack. |
| - | */ |
| - | static int search (const char *haystack, const char *needle, int ignoreCase) |
| - | { |
| - | |
| - | if (ignoreCase == FALSE) { |
| - | haystack = strstr (haystack, needle); |
| - | if (haystack == NULL) |
| - | return FALSE; |
| - | return TRUE; |
| - | } else { |
| - | int i; |
| - | char needle1[BUF_SIZE]; |
| - | char haystack1[BUF_SIZE]; |
| - | |
| - | strncpy( haystack1, haystack, sizeof(haystack1)); |
| - | strncpy( needle1, needle, sizeof(needle1)); |
| - | for( i=0; i<sizeof(haystack1) && haystack1[i]; i++) |
| - | haystack1[i]=tolower( haystack1[i]); |
| - | for( i=0; i<sizeof(needle1) && needle1[i]; i++) |
| - | needle1[i]=tolower( needle1[i]); |
| - | haystack = strstr (haystack1, needle1); |
| - | if (haystack == NULL) |
| - | return FALSE; |
| - | return TRUE; |
| - | } |
| - | } |
| + | #endif |
| extern int grep_main (int argc, char **argv) | |
| @@ -80,7 +54,7 @@ extern int grep_main (int argc, char **argv) | |
| int ignoreCase=FALSE; | |
| int tellLine=FALSE; | |
| long line; | |
| - | char buf[BUF_SIZE]; |
| + | char haystack[BUF_SIZE]; |
| ignoreCase = FALSE; | |
| tellLine = FALSE; | |
| @@ -128,21 +102,21 @@ extern int grep_main (int argc, char **argv) | |
| line = 0; | |
| - | while (fgets (buf, sizeof (buf), fp)) { |
| + | while (fgets (haystack, sizeof (haystack), fp)) { |
| line++; | |
| - | cp = &buf[strlen (buf) - 1]; |
| + | cp = &haystack[strlen (haystack) - 1]; |
| if (*cp != '\n') | |
| fprintf (stderr, "%s: Line too long\n", name); | |
| - | if (search (buf, needle, ignoreCase)==TRUE) { |
| + | if (find_match(haystack, needle, ignoreCase) == TRUE) { |
| if (tellName==TRUE) | |
| printf ("%s: ", name); | |
| if (tellLine==TRUE) | |
| printf ("%ld: ", line); | |
| - | fputs (buf, stdout); |
| + | fputs (haystack, stdout); |
| } | |
| } | |
| @@ -22,6 +22,7 @@ | |
| */ | |
| #include "internal.h" | |
| + | #include "regexp.h" |
| #include <stdio.h> | |
| #include <dirent.h> | |
| #include <errno.h> | |
| @@ -30,44 +31,17 @@ | |
| #include <time.h> | |
| #include <ctype.h> | |
| - | |
| static const char grep_usage[] = | |
| "grep [-ihn]... PATTERN [FILE]...\n" | |
| "Search for PATTERN in each FILE or standard input.\n\n" | |
| "\t-h\tsuppress the prefixing filename on output\n" | |
| "\t-i\tignore case distinctions\n" | |
| "\t-n\tprint line number with output lines\n\n" | |
| + | #if defined BB_REGEXP |
| + | "This version of grep matches full regexps.\n"; |
| + | #else |
| "This version of grep matches strings (not full regexps).\n"; | |
| - | |
| - | |
| - | /* |
| - | * See if the specified needle is found in the specified haystack. |
| - | */ |
| - | static int search (const char *haystack, const char *needle, int ignoreCase) |
| - | { |
| - | |
| - | if (ignoreCase == FALSE) { |
| - | haystack = strstr (haystack, needle); |
| - | if (haystack == NULL) |
| - | return FALSE; |
| - | return TRUE; |
| - | } else { |
| - | int i; |
| - | char needle1[BUF_SIZE]; |
| - | char haystack1[BUF_SIZE]; |
| - | |
| - | strncpy( haystack1, haystack, sizeof(haystack1)); |
| - | strncpy( needle1, needle, sizeof(needle1)); |
| - | for( i=0; i<sizeof(haystack1) && haystack1[i]; i++) |
| - | haystack1[i]=tolower( haystack1[i]); |
| - | for( i=0; i<sizeof(needle1) && needle1[i]; i++) |
| - | needle1[i]=tolower( needle1[i]); |
| - | haystack = strstr (haystack1, needle1); |
| - | if (haystack == NULL) |
| - | return FALSE; |
| - | return TRUE; |
| - | } |
| - | } |
| + | #endif |
| extern int grep_main (int argc, char **argv) | |
| @@ -80,7 +54,7 @@ extern int grep_main (int argc, char **argv) | |
| int ignoreCase=FALSE; | |
| int tellLine=FALSE; | |
| long line; | |
| - | char buf[BUF_SIZE]; |
| + | char haystack[BUF_SIZE]; |
| ignoreCase = FALSE; | |
| tellLine = FALSE; | |
| @@ -128,21 +102,21 @@ extern int grep_main (int argc, char **argv) | |
| line = 0; | |
| - | while (fgets (buf, sizeof (buf), fp)) { |
| + | while (fgets (haystack, sizeof (haystack), fp)) { |
| line++; | |
| - | cp = &buf[strlen (buf) - 1]; |
| + | cp = &haystack[strlen (haystack) - 1]; |
| if (*cp != '\n') | |
| fprintf (stderr, "%s: Line too long\n", name); | |
| - | if (search (buf, needle, ignoreCase)==TRUE) { |
| + | if (find_match(haystack, needle, ignoreCase) == TRUE) { |
| if (tellName==TRUE) | |
| printf ("%s: ", name); | |
| if (tellLine==TRUE) | |
| printf ("%ld: ", line); | |
| - | fputs (buf, stdout); |
| + | fputs (haystack, stdout); |
| } | |
| } | |
| @@ -130,7 +130,6 @@ int fullRead(int fd, char *buf, int len); | |
| int recursiveAction(const char *fileName, int recurse, int followLinks, int delayDirAction, | |
| int (*fileAction) (const char *fileName, struct stat* statbuf), | |
| int (*dirAction) (const char *fileName, struct stat* statbuf)); | |
| - | int match(const char* text, const char * pattern); |
| const char* timeString(time_t timeVal); | |
| extern void createPath (const char *name, int mode); | |
| @@ -166,8 +165,9 @@ static inline int clrbit(char * addr,unsigned int nr) | |
| return __res != 0; | |
| } | |
| - | #endif |
| + | #endif /* inline bitops junk */ |
| - | #endif |
| + | |
| + | #endif /* _INTERNAL_H_ */ |
| @@ -0,0 +1,826 @@ | |
| + | /* regexp.c */ |
| + | |
| + | #include "internal.h" |
| + | #include "regexp.h" |
| + | #include <setjmp.h> |
| + | #include <stdio.h> |
| + | #include <ctype.h> |
| + | |
| + | |
| + | #if ( defined BB_GREP || defined BB_FIND ) |
| + | |
| + | /* This also tries to find a needle in a haystack, but uses |
| + | * real regular expressions.... The fake regular expression |
| + | * version of find_match lives in utility.c. Using this version |
| + | * will add 3.9k to busybox... |
| + | * -Erik Andersen |
| + | */ |
| + | extern int find_match(char *haystack, char *needle, int ignoreCase) |
| + | { |
| + | int status; |
| + | struct regexp* re; |
| + | re = regcomp( needle); |
| + | status = regexec(re, haystack, FALSE, ignoreCase); |
| + | free( re); |
| + | return( status); |
| + | } |
| + | |
| + | |
| + | /* code swiped from elvis-tiny 1.4 (a clone of vi) and adjusted to |
| + | * suit the needs of busybox by Erik Andersen. |
| + | * |
| + | * From the README: |
| + | * "Elvis is freely redistributable, in either source form or executable form. |
| + | * There are no restrictions on how you may use it". |
| + | * Elvis was written by Steve Kirkendall <[email protected]> |
| + | * |
| + | * |
| + | * This file contains the code that compiles regular expressions and executes |
| + | * them. It supports the same syntax and features as vi's regular expression |
| + | * code. Specifically, the meta characters are: |
| + | * ^ matches the beginning of a line |
| + | * $ matches the end of a line |
| + | * \< matches the beginning of a word |
| + | * \> matches the end of a word |
| + | * . matches any single character |
| + | * [] matches any character in a character class |
| + | * \( delimits the start of a subexpression |
| + | * \) delimits the end of a subexpression |
| + | * * repeats the preceding 0 or more times |
| + | * NOTE: You cannot follow a \) with a *. |
| + | * |
| + | * The physical structure of a compiled RE is as follows: |
| + | * - First, there is a one-byte value that says how many character classes |
| + | * are used in this regular expression |
| + | * - Next, each character class is stored as a bitmap that is 256 bits |
| + | * (32 bytes) long. |
| + | * - A mixture of literal characters and compiled meta characters follows. |
| + | * This begins with M_BEGIN(0) and ends with M_END(0). All meta chars |
| + | * are stored as a \n followed by a one-byte code, so they take up two |
| + | * bytes apiece. Literal characters take up one byte apiece. \n can't |
| + | * be used as a literal character. |
| + | * |
| + | */ |
| + | |
| + | |
| + | |
| + | static char *previous; /* the previous regexp, used when null regexp is given */ |
| + | static char *previous1; /* a copy of the text from the previous substitution for regsub()*/ |
| + | |
| + | |
| + | /* These are used to classify or recognize meta-characters */ |
| + | #define META '\0' |
| + | #define BASE_META(m) ((m) - 256) |
| + | #define INT_META(c) ((c) + 256) |
| + | #define IS_META(m) ((m) >= 256) |
| + | #define IS_CLASS(m) ((m) >= M_CLASS(0) && (m) <= M_CLASS(9)) |
| + | #define IS_START(m) ((m) >= M_START(0) && (m) <= M_START(9)) |
| + | #define IS_END(m) ((m) >= M_END(0) && (m) <= M_END(9)) |
| + | #define IS_CLOSURE(m) ((m) >= M_SPLAT && (m) <= M_QMARK) |
| + | #define ADD_META(s,m) (*(s)++ = META, *(s)++ = BASE_META(m)) |
| + | #define GET_META(s) (*(s) == META ? INT_META(*++(s)) : *s) |
| + | |
| + | /* These are the internal codes used for each type of meta-character */ |
| + | #define M_BEGLINE 256 /* internal code for ^ */ |
| + | #define M_ENDLINE 257 /* internal code for $ */ |
| + | #define M_BEGWORD 258 /* internal code for \< */ |
| + | #define M_ENDWORD 259 /* internal code for \> */ |
| + | #define M_ANY 260 /* internal code for . */ |
| + | #define M_SPLAT 261 /* internal code for * */ |
| + | #define M_PLUS 262 /* internal code for \+ */ |
| + | #define M_QMARK 263 /* internal code for \? */ |
| + | #define M_CLASS(n) (264+(n)) /* internal code for [] */ |
| + | #define M_START(n) (274+(n)) /* internal code for \( */ |
| + | #define M_END(n) (284+(n)) /* internal code for \) */ |
| + | |
| + | /* These are used during compilation */ |
| + | static int class_cnt; /* used to assign class IDs */ |
| + | static int start_cnt; /* used to assign start IDs */ |
| + | static int end_stk[NSUBEXP];/* used to assign end IDs */ |
| + | static int end_sp; |
| + | static char *retext; /* points to the text being compiled */ |
| + | |
| + | /* error-handling stuff */ |
| + | jmp_buf errorhandler; |
| + | #define FAIL(why) fprintf(stderr, why); longjmp(errorhandler, 1) |
| + | |
| + | |
| + | |
| + | |
| + | /* This function builds a bitmap for a particular class */ |
| + | /* text -- start of the class */ |
| + | /* bmap -- the bitmap */ |
| + | static char *makeclass(char* text, char* bmap) |
| + | { |
| + | int i; |
| + | int complement = 0; |
| + | |
| + | |
| + | /* zero the bitmap */ |
| + | for (i = 0; bmap && i < 32; i++) |
| + | { |
| + | bmap[i] = 0; |
| + | } |
| + | |
| + | /* see if we're going to complement this class */ |
| + | if (*text == '^') |
| + | { |
| + | text++; |
| + | complement = 1; |
| + | } |
| + | |
| + | /* add in the characters */ |
| + | while (*text && *text != ']') |
| + | { |
| + | /* is this a span of characters? */ |
| + | if (text[1] == '-' && text[2]) |
| + | { |
| + | /* spans can't be backwards */ |
| + | if (text[0] > text[2]) |
| + | { |
| + | FAIL("Backwards span in []"); |
| + | } |
| + | |
| + | /* add each character in the span to the bitmap */ |
| + | for (i = text[0]; bmap && i <= text[2]; i++) |
| + | { |
| + | bmap[i >> 3] |= (1 << (i & 7)); |
| + | } |
| + | |
| + | /* move past this span */ |
| + | text += 3; |
| + | } |
| + | else |
| + | { |
| + | /* add this single character to the span */ |
| + | i = *text++; |
| + | if (bmap) |
| + | { |
| + | bmap[i >> 3] |= (1 << (i & 7)); |
| + | } |
| + | } |
| + | } |
| + | |
| + | /* make sure the closing ] is missing */ |
| + | if (*text++ != ']') |
| + | { |
| + | FAIL("] missing"); |
| + | } |
| + | |
| + | /* if we're supposed to complement this class, then do so */ |
| + | if (complement && bmap) |
| + | { |
| + | for (i = 0; i < 32; i++) |
| + | { |
| + | bmap[i] = ~bmap[i]; |
| + | } |
| + | } |
| + | |
| + | return text; |
| + | } |
| + | |
| + | |
| + | |
| + | |
| + | /* This function gets the next character or meta character from a string. |
| + | * The pointer is incremented by 1, or by 2 for \-quoted characters. For [], |
| + | * a bitmap is generated via makeclass() (if re is given), and the |
| + | * character-class text is skipped. |
| + | */ |
| + | static int gettoken(sptr, re) |
| + | char **sptr; |
| + | regexp *re; |
| + | { |
| + | int c; |
| + | |
| + | c = **sptr; |
| + | ++*sptr; |
| + | if (c == '\\') |
| + | { |
| + | c = **sptr; |
| + | ++*sptr; |
| + | switch (c) |
| + | { |
| + | case '<': |
| + | return M_BEGWORD; |
| + | |
| + | case '>': |
| + | return M_ENDWORD; |
| + | |
| + | case '(': |
| + | if (start_cnt >= NSUBEXP) |
| + | { |
| + | FAIL("Too many \\(s"); |
| + | } |
| + | end_stk[end_sp++] = start_cnt; |
| + | return M_START(start_cnt++); |
| + | |
| + | case ')': |
| + | if (end_sp <= 0) |
| + | { |
| + | FAIL("Mismatched \\)"); |
| + | } |
| + | return M_END(end_stk[--end_sp]); |
| + | |
| + | case '*': |
| + | return M_SPLAT; |
| + | |
| + | case '.': |
| + | return M_ANY; |
| + | |
| + | case '+': |
| + | return M_PLUS; |
| + | |
| + | case '?': |
| + | return M_QMARK; |
| + | |
| + | default: |
| + | return c; |
| + | } |
| + | } |
| + | else { |
| + | switch (c) |
| + | { |
| + | case '^': |
| + | if (*sptr == retext + 1) |
| + | { |
| + | return M_BEGLINE; |
| + | } |
| + | return c; |
| + | |
| + | case '$': |
| + | if (!**sptr) |
| + | { |
| + | return M_ENDLINE; |
| + | } |
| + | return c; |
| + | |
| + | case '.': |
| + | return M_ANY; |
| + | |
| + | case '*': |
| + | return M_SPLAT; |
| + | |
| + | case '[': |
| + | /* make sure we don't have too many classes */ |
| + | if (class_cnt >= 10) |
| + | { |
| + | FAIL("Too many []s"); |
| + | } |
| + | |
| + | /* process the character list for this class */ |
| + | if (re) |
| + | { |
| + | /* generate the bitmap for this class */ |
| + | *sptr = makeclass(*sptr, re->program + 1 + 32 * class_cnt); |
| + | } |
| + | else |
| + | { |
| + | /* skip to end of the class */ |
| + | *sptr = makeclass(*sptr, (char *)0); |
| + | } |
| + | return M_CLASS(class_cnt++); |
| + | |
| + | default: |
| + | return c; |
| + | } |
| + | } |
| + | /*NOTREACHED*/ |
| + | } |
| + | |
| + | |
| + | |
| + | |
| + | /* This function calculates the number of bytes that will be needed for a |
| + | * compiled RE. Its argument is the uncompiled version. It is not clever |
| + | * about catching syntax errors; that is done in a later pass. |
| + | */ |
| + | static unsigned calcsize(text) |
| + | char *text; |
| + | { |
| + | unsigned size; |
| + | int token; |
| + | |
| + | retext = text; |
| + | class_cnt = 0; |
| + | start_cnt = 1; |
| + | end_sp = 0; |
| + | size = 5; |
| + | while ((token = gettoken(&text, (regexp *)0)) != 0) |
| + | { |
| + | if (IS_CLASS(token)) |
| + | { |
| + | size += 34; |
| + | } |
| + | else if (IS_META(token)) |
| + | { |
| + | size += 2; |
| + | } |
| + | else |
| + | { |
| + | size++; |
| + | } |
| + | } |
| + | |
| + | return size; |
| + | } |
| + | |
| + | |
| + | |
| + | /*---------------------------------------------------------------------------*/ |
| + | |
| + | |
| + | /* This function checks for a match between a character and a token which is |
| + | * known to represent a single character. It returns 0 if they match, or |
| + | * 1 if they don't. |
| + | */ |
| + | static int match1(regexp* re, char ch, int token, int ignoreCase) |
| + | { |
| + | if (!ch) |
| + | { |
| + | /* the end of a line can't match any RE of width 1 */ |
| + | return 1; |
| + | } |
| + | if (token == M_ANY) |
| + | { |
| + | return 0; |
| + | } |
| + | else if (IS_CLASS(token)) |
| + | { |
| + | if (re->program[1 + 32 * (token - M_CLASS(0)) + (ch >> 3)] & (1 << (ch & 7))) |
| + | return 0; |
| + | } |
| + | else if (ch == token |
| + | || (ignoreCase==TRUE && isupper(ch) && tolower(ch) == token)) |
| + | { |
| + | return 0; |
| + | } |
| + | return 1; |
| + | } |
| + | |
| + | |
| + | |
| + | /* This function checks characters up to and including the next closure, at |
| + | * which point it does a recursive call to check the rest of it. This function |
| + | * returns 0 if everything matches, or 1 if something doesn't match. |
| + | */ |
| + | /* re -- the regular expression */ |
| + | /* str -- the string */ |
| + | /* prog -- a portion of re->program, an compiled RE */ |
| + | /* here -- a portion of str, the string to compare it to */ |
| + | static int match(regexp* re, char* str, char* prog, char* here, int ignoreCase) |
| + | { |
| + | int token; |
| + | int nmatched; |
| + | int closure; |
| + | |
| + | for (token = GET_META(prog); !IS_CLOSURE(token); prog++, token = GET_META(prog)) |
| + | { |
| + | switch (token) |
| + | { |
| + | /*case M_BEGLINE: can't happen; re->bol is used instead */ |
| + | case M_ENDLINE: |
| + | if (*here) |
| + | return 1; |
| + | break; |
| + | |
| + | case M_BEGWORD: |
| + | if (here != str && |
| + | (here[-1] == '_' || |
| + | (isascii(here[-1]) && isalnum(here[-1])))) |
| + | return 1; |
| + | break; |
| + | |
| + | case M_ENDWORD: |
| + | if ((here[0] == '_' || isascii(here[0])) && isalnum(here[0])) |
| + | return 1; |
| + | break; |
| + | |
| + | case M_START(0): |
| + | case M_START(1): |
| + | case M_START(2): |
| + | case M_START(3): |
| + | case M_START(4): |
| + | case M_START(5): |
| + | case M_START(6): |
| + | case M_START(7): |
| + | case M_START(8): |
| + | case M_START(9): |
| + | re->startp[token - M_START(0)] = (char *)here; |
| + | break; |
| + | |
| + | case M_END(0): |
| + | case M_END(1): |
| + | case M_END(2): |
| + | case M_END(3): |
| + | case M_END(4): |
| + | case M_END(5): |
| + | case M_END(6): |
| + | case M_END(7): |
| + | case M_END(8): |
| + | case M_END(9): |
| + | re->endp[token - M_END(0)] = (char *)here; |
| + | if (token == M_END(0)) |
| + | { |
| + | return 0; |
| + | } |
| + | break; |
| + | |
| + | default: /* literal, M_CLASS(n), or M_ANY */ |
| + | if (match1(re, *here, token, ignoreCase) != 0) |
| + | return 1; |
| + | here++; |
| + | } |
| + | } |
| + | |
| + | /* C L O S U R E */ |
| + | |
| + | /* step 1: see what we have to match against, and move "prog" to point |
| + | * the the remainder of the compiled RE. |
| + | */ |
| + | closure = token; |
| + | prog++, token = GET_META(prog); |
| + | prog++; |
| + | |
| + | /* step 2: see how many times we can match that token against the string */ |
| + | for (nmatched = 0; |
| + | (closure != M_QMARK || nmatched < 1) && *here && match1(re, *here, token, ignoreCase) == 0; |
| + | nmatched++, here++) |
| + | { |
| + | } |
| + | |
| + | /* step 3: try to match the remainder, and back off if it doesn't */ |
| + | while (nmatched >= 0 && match(re, str, prog, here, ignoreCase) != 0) |
| + | { |
| + | nmatched--; |
| + | here--; |
| + | } |
| + | |
| + | /* so how did it work out? */ |
| + | if (nmatched >= ((closure == M_PLUS) ? 1 : 0)) |
| + | return 0; |
| + | return 1; |
| + | } |
| + | |
| + | |
| + | /* This function compiles a regexp. */ |
| + | extern regexp *regcomp(char* text) |
| + | { |
| + | int needfirst; |
| + | unsigned size; |
| + | int token; |
| + | int peek; |
| + | char *build; |
| + | regexp *re; |
| + | |
| + | |
| + | /* prepare for error handling */ |
| + | re = (regexp *)0; |
| + | if (setjmp(errorhandler)) |
| + | { |
| + | if (re) |
| + | { |
| + | free(re); |
| + | } |
| + | return (regexp *)0; |
| + | } |
| + | |
| + | /* if an empty regexp string was given, use the previous one */ |
| + | if (*text == 0) |
| + | { |
| + | if (!previous) |
| + | { |
| + | FAIL("No previous RE"); |
| + | } |
| + | text = previous; |
| + | } |
| + | else /* non-empty regexp given, so remember it */ |
| + | { |
| + | if (previous) |
| + | free(previous); |
| + | previous = (char *)malloc((unsigned)(strlen(text) + 1)); |
| + | if (previous) |
| + | strcpy(previous, text); |
| + | } |
| + | |
| + | /* allocate memory */ |
| + | class_cnt = 0; |
| + | start_cnt = 1; |
| + | end_sp = 0; |
| + | retext = text; |
| + | size = calcsize(text) + sizeof(regexp); |
| + | re = (regexp *)malloc((unsigned)size); |
| + | |
| + | if (!re) |
| + | { |
| + | FAIL("Not enough memory for this RE"); |
| + | } |
| + | |
| + | /* compile it */ |
| + | build = &re->program[1 + 32 * class_cnt]; |
| + | re->program[0] = class_cnt; |
| + | for (token = 0; token < NSUBEXP; token++) |
| + | { |
| + | re->startp[token] = re->endp[token] = (char *)0; |
| + | } |
| + | re->first = 0; |
| + | re->bol = 0; |
| + | re->minlen = 0; |
| + | needfirst = 1; |
| + | class_cnt = 0; |
| + | start_cnt = 1; |
| + | end_sp = 0; |
| + | retext = text; |
| + | for (token = M_START(0), peek = gettoken(&text, re); |
| + | token; |
| + | token = peek, peek = gettoken(&text, re)) |
| + | { |
| + | /* special processing for the closure operator */ |
| + | if (IS_CLOSURE(peek)) |
| + | { |
| + | /* detect misuse of closure operator */ |
| + | if (IS_START(token)) |
| + | { |
| + | FAIL("* or \\+ or \\? follows nothing"); |
| + | } |
| + | else if (IS_META(token) && token != M_ANY && !IS_CLASS(token)) |
| + | { |
| + | FAIL("* or \\+ or \\? can only follow a normal character or . or []"); |
| + | } |
| + | |
| + | /* it is okay -- make it prefix instead of postfix */ |
| + | ADD_META(build, peek); |
| + | |
| + | /* take care of "needfirst" - is this the first char? */ |
| + | if (needfirst && peek == M_PLUS && !IS_META(token)) |
| + | { |
| + | re->first = token; |
| + | } |
| + | needfirst = 0; |
| + | |
| + | /* we used "peek" -- need to refill it */ |
| + | peek = gettoken(&text, re); |
| + | if (IS_CLOSURE(peek)) |
| + | { |
| + | FAIL("* or \\+ or \\? doubled up"); |
| + | } |
| + | } |
| + | else if (!IS_META(token)) |
| + | { |
| + | /* normal char is NOT argument of closure */ |
| + | if (needfirst) |
| + | { |
| + | re->first = token; |
| + | needfirst = 0; |
| + | } |
| + | re->minlen++; |
| + | } |
| + | else if (token == M_ANY || IS_CLASS(token)) |
| + | { |
| + | /* . or [] is NOT argument of closure */ |
| + | needfirst = 0; |
| + | re->minlen++; |
| + | } |
| + | |
| + | /* the "token" character is not closure -- process it normally */ |
| + | if (token == M_BEGLINE) |
| + | { |
| + | /* set the BOL flag instead of storing M_BEGLINE */ |
| + | re->bol = 1; |
| + | } |
| + | else if (IS_META(token)) |
| + | { |
| + | ADD_META(build, token); |
| + | } |
| + | else |
| + | { |
| + | *build++ = token; |
| + | } |
| + | } |
| + | |
| + | /* end it with a \) which MUST MATCH the opening \( */ |
| + | ADD_META(build, M_END(0)); |
| + | if (end_sp > 0) |
| + | { |
| + | FAIL("Not enough \\)s"); |
| + | } |
| + | |
| + | return re; |
| + | } |
| + | |
| + | |
| + | |
| + | |
| + | /* This function searches through a string for text that matches an RE. */ |
| + | /* re -- the compiled regexp to search for */ |
| + | /* str -- the string to search through */ |
| + | /* bol -- does str start at the beginning of a line? (boolean) */ |
| + | /* ignoreCase -- ignoreCase or not */ |
| + | extern int regexec(struct regexp* re, char* str, int bol, int ignoreCase) |
| + | { |
| + | char *prog; /* the entry point of re->program */ |
| + | int len; /* length of the string */ |
| + | char *here; |
| + | |
| + | /* if must start at the beginning of a line, and this isn't, then fail */ |
| + | if (re->bol && bol==TRUE) |
| + | { |
| + | return FALSE; |
| + | } |
| + | |
| + | len = strlen(str); |
| + | prog = re->program + 1 + 32 * re->program[0]; |
| + | |
| + | /* search for the RE in the string */ |
| + | if (re->bol) |
| + | { |
| + | /* must occur at BOL */ |
| + | if ((re->first |
| + | && match1(re, *(char *)str, re->first, ignoreCase))/* wrong first letter? */ |
| + | || len < re->minlen /* not long enough? */ |
| + | || match(re, (char *)str, prog, str, ignoreCase)) /* doesn't match? */ |
| + | return FALSE; /* THEN FAIL! */ |
| + | } |
| + | else if (ignoreCase == FALSE) |
| + | { |
| + | /* can occur anywhere in the line, noignorecase */ |
| + | for (here = (char *)str; |
| + | (re->first && re->first != *here) |
| + | || match(re, (char *)str, prog, here, ignoreCase); |
| + | here++, len--) |
| + | { |
| + | if (len < re->minlen) |
| + | return FALSE; |
| + | } |
| + | } |
| + | else |
| + | { |
| + | /* can occur anywhere in the line, ignorecase */ |
| + | for (here = (char *)str; |
| + | (re->first && match1(re, *here, (int)re->first, ignoreCase)) |
| + | || match(re, (char *)str, prog, here, ignoreCase); |
| + | here++, len--) |
| + | { |
| + | if (len < re->minlen) |
| + | return FALSE; |
| + | } |
| + | } |
| + | |
| + | /* if we didn't fail, then we must have succeeded */ |
| + | return TRUE; |
| + | } |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | /* This performs substitutions after a regexp match has been found. */ |
| + | extern void regsub(regexp* re, char* src, char* dst) |
| + | { |
| + | char *cpy; |
| + | char *end; |
| + | char c; |
| + | char *start; |
| + | int mod; |
| + | |
| + | mod = 0; |
| + | |
| + | start = src; |
| + | while ((c = *src++) != '\0') |
| + | { |
| + | /* recognize any meta characters */ |
| + | if (c == '&') |
| + | { |
| + | cpy = re->startp[0]; |
| + | end = re->endp[0]; |
| + | } |
| + | else if (c == '~') |
| + | { |
| + | cpy = previous1; |
| + | if (cpy) |
| + | end = cpy + strlen(cpy); |
| + | } |
| + | else |
| + | if (c == '\\') |
| + | { |
| + | c = *src++; |
| + | switch (c) |
| + | { |
| + | case '0': |
| + | case '1': |
| + | case '2': |
| + | case '3': |
| + | case '4': |
| + | case '5': |
| + | case '6': |
| + | case '7': |
| + | case '8': |
| + | case '9': |
| + | /* \0 thru \9 mean "copy subexpression" */ |
| + | c -= '0'; |
| + | cpy = re->startp[(int)c]; |
| + | end = re->endp[(int)c]; |
| + | break; |
| + | case 'U': |
| + | case 'u': |
| + | case 'L': |
| + | case 'l': |
| + | /* \U and \L mean "convert to upper/lowercase" */ |
| + | mod = c; |
| + | continue; |
| + | |
| + | case 'E': |
| + | case 'e': |
| + | /* \E ends the \U or \L */ |
| + | mod = 0; |
| + | continue; |
| + | case '&': |
| + | /* "\&" means "original text" */ |
| + | *dst++ = c; |
| + | continue; |
| + | |
| + | case '~': |
| + | /* "\~" means "previous text, if any" */ |
| + | *dst++ = c; |
| + | continue; |
| + | default: |
| + | /* ordinary char preceded by backslash */ |
| + | *dst++ = c; |
| + | continue; |
| + | } |
| + | } |
| + | else |
| + | { |
| + | /* ordinary character, so just copy it */ |
| + | *dst++ = c; |
| + | continue; |
| + | } |
| + | |
| + | /* Note: to reach this point in the code, we must have evaded |
| + | * all "continue" statements. To do that, we must have hit |
| + | * a metacharacter that involves copying. |
| + | */ |
| + | |
| + | /* if there is nothing to copy, loop */ |
| + | if (!cpy) |
| + | continue; |
| + | |
| + | /* copy over a portion of the original */ |
| + | while (cpy < end) |
| + | { |
| + | switch (mod) |
| + | { |
| + | case 'U': |
| + | case 'u': |
| + | /* convert to uppercase */ |
| + | if (isascii(*cpy) && islower(*cpy)) |
| + | { |
| + | *dst++ = toupper(*cpy); |
| + | cpy++; |
| + | } |
| + | else |
| + | { |
| + | *dst++ = *cpy++; |
| + | } |
| + | break; |
| + | |
| + | case 'L': |
| + | case 'l': |
| + | /* convert to lowercase */ |
| + | if (isascii(*cpy) && isupper(*cpy)) |
| + | { |
| + | *dst++ = tolower(*cpy); |
| + | cpy++; |
| + | } |
| + | else |
| + | { |
| + | *dst++ = *cpy++; |
| + | } |
| + | break; |
| + | |
| + | default: |
| + | /* copy without any conversion */ |
| + | *dst++ = *cpy++; |
| + | } |
| + | |
| + | /* \u and \l end automatically after the first char */ |
| + | if (mod && (mod == 'u' || mod == 'l')) |
| + | { |
| + | mod = 0; |
| + | } |
| + | } |
| + | } |
| + | *dst = '\0'; |
| + | |
| + | /* remember what text we inserted this time */ |
| + | if (previous1) |
| + | free(previous1); |
| + | previous1 = (char *)malloc((unsigned)(strlen(start) + 1)); |
| + | if (previous1) |
| + | strcpy(previous1, start); |
| + | } |
| + | |
| + | |
| + | #endif /* BB_REGEXP */ |
| + | |
| + | |
| @@ -1,2 +0,0 @@ | |
| - | echo '.' >smtpout |
| echo 'QUIT' >smtpout | |
| - | kjfjkjd |
| @@ -53,12 +53,17 @@ volatile void usage(const char *usage) | |
| int | |
| get_kernel_revision() | |
| { | |
| - | FILE *f; |
| + | FILE *file; |
| int major=0, minor=0, patch=0; | |
| - | |
| - | f = fopen("/proc/sys/kernel/osrelease","r"); |
| - | fscanf(f,"%d.%d.%d",&major,&minor,&patch); |
| - | fclose(f); |
| + | char* filename="/proc/sys/kernel/osrelease"; |
| + | |
| + | file = fopen(filename,"r"); |
| + | if (file == NULL) { |
| + | perror(filename); |
| + | return( 0); |
| + | } |
| + | fscanf(file,"%d.%d.%d",&major,&minor,&patch); |
| + | fclose(file); |
| return major*65536 + minor*256 + patch; | |
| } | |
| @@ -311,94 +316,6 @@ const char *timeString(time_t timeVal) | |
| } | |
| - | /* |
| - | * Routine to see if a text string is matched by a wildcard pattern. |
| - | * Returns TRUE if the text is matched, or FALSE if it is not matched |
| - | * or if the pattern is invalid. |
| - | * * matches zero or more characters |
| - | * ? matches a single character |
| - | * [abc] matches 'a', 'b' or 'c' |
| - | * \c quotes character c |
| - | * Adapted from code written by Ingo Wilken. |
| - | */ |
| - | int match(const char *text, const char *pattern) |
| - | { |
| - | const char *retryPat; |
| - | const char *retryText; |
| - | int ch; |
| - | int found; |
| - | |
| - | retryPat = NULL; |
| - | retryText = NULL; |
| - | |
| - | while (*text || *pattern) { |
| - | ch = *pattern++; |
| - | |
| - | switch (ch) { |
| - | case '*': |
| - | retryPat = pattern; |
| - | retryText = text; |
| - | break; |
| - | |
| - | case '[': |
| - | found = FALSE; |
| - | |
| - | while ((ch = *pattern++) != ']') { |
| - | if (ch == '\\') |
| - | ch = *pattern++; |
| - | |
| - | if (ch == '\0') |
| - | return FALSE; |
| - | |
| - | if (*text == ch) |
| - | found = TRUE; |
| - | } |
| - | |
| - | if (!found) { |
| - | pattern = retryPat; |
| - | text = ++retryText; |
| - | } |
| - | |
| - | /* fall into next case */ |
| - | |
| - | case '?': |
| - | if (*text++ == '\0') |
| - | return FALSE; |
| - | |
| - | break; |
| - | |
| - | case '\\': |
| - | ch = *pattern++; |
| - | |
| - | if (ch == '\0') |
| - | return FALSE; |
| - | |
| - | /* fall into next case */ |
| - | |
| - | default: |
| - | if (*text == ch) { |
| - | if (*text) |
| - | text++; |
| - | break; |
| - | } |
| - | |
| - | if (*text) { |
| - | pattern = retryPat; |
| - | text = ++retryText; |
| - | break; |
| - | } |
| - | |
| - | return FALSE; |
| - | } |
| - | |
| - | if (pattern == NULL) |
| - | return FALSE; |
| - | } |
| - | |
| - | return TRUE; |
| - | } |
| - | |
| - | |
| /* | |
| * Write all of the supplied buffer out to a file. | |
| * This does multiple writes as necessary. | |
| @@ -695,13 +612,17 @@ parse_mode( const char* s, mode_t* theMode) | |
| uid_t | |
| my_getid(const char *filename, char *name, uid_t id) | |
| { | |
| - | FILE *stream; |
| + | FILE *file; |
| char *rname, *start, *end, buf[128]; | |
| uid_t rid; | |
| - | stream=fopen(filename,"r"); |
| + | file=fopen(filename,"r"); |
| + | if (file == NULL) { |
| + | perror(filename); |
| + | return (-1); |
| + | } |
| - | while (fgets (buf, 128, stream) != NULL) { |
| + | while (fgets (buf, 128, file) != NULL) { |
| if (buf[0] == '#') | |
| continue; | |
| @@ -731,7 +652,7 @@ my_getid(const char *filename, char *name, uid_t id) | |
| return( TRUE); | |
| } | |
| } | |
| - | fclose(stream); |
| + | fclose(file); |
| return (-1); | |
| } | |
| @@ -763,4 +684,40 @@ my_getgrgid(char* group, gid_t gid) | |
| #endif | |
| + | |
| + | #if !defined BB_REGEXP && (defined BB_GREP || defined BB_FIND ) |
| + | /* This tries to find a needle in a haystack, but does so by |
| + | * only trying to match literal strings (look 'ma, no regexps!) |
| + | * This is short, sweet, and carries _very_ little baggage, |
| + | * unlike its beefier cousin a few lines down... |
| + | * -Erik Andersen |
| + | */ |
| + | extern int find_match(char *haystack, char *needle, int ignoreCase) |
| + | { |
| + | |
| + | if (ignoreCase == FALSE) { |
| + | haystack = strstr (haystack, needle); |
| + | if (haystack == NULL) |
| + | return FALSE; |
| + | return TRUE; |
| + | } else { |
| + | int i; |
| + | char needle1[BUF_SIZE]; |
| + | char haystack1[BUF_SIZE]; |
| + | |
| + | strncpy( haystack1, haystack, sizeof(haystack1)); |
| + | strncpy( needle1, needle, sizeof(needle1)); |
| + | for( i=0; i<sizeof(haystack1) && haystack1[i]; i++) |
| + | haystack1[i]=tolower( haystack1[i]); |
| + | for( i=0; i<sizeof(needle1) && needle1[i]; i++) |
| + | needle1[i]=tolower( needle1[i]); |
| + | haystack = strstr (haystack1, needle1); |
| + | if (haystack == NULL) |
| + | return FALSE; |
| + | return TRUE; |
| + | } |
| + | } |
| + | #endif |
| + | |
| /* END CODE */ | |
| + | |