busybox/shell/match.c
<<
>>
Prefs
   1/*
   2 * ##/%% variable matching code ripped out of ash shell for code sharing
   3 *
   4 * This code is derived from software contributed to Berkeley by
   5 * Kenneth Almquist.
   6 *
   7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   8 *
   9 * Copyright (c) 1989, 1991, 1993, 1994
  10 *      The Regents of the University of California.  All rights reserved.
  11 *
  12 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
  13 * was re-ported from NetBSD and debianized.
  14 */
  15#ifdef STANDALONE
  16# include <stdbool.h>
  17# include <stdio.h>
  18# include <stdlib.h>
  19# include <string.h>
  20# include <unistd.h>
  21# define FAST_FUNC /* nothing */
  22# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* nothing */
  23# define POP_SAVED_FUNCTION_VISIBILITY /* nothing */
  24#else
  25# include "libbb.h"
  26#endif
  27#include <fnmatch.h>
  28#include "match.h"
  29
  30char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags)
  31{
  32        char *loc;
  33        char *end;
  34        unsigned len = strlen(string);
  35        int early_exit;
  36
  37        /* We can stop the scan early only if the string part
  38         * we are matching against is shrinking, and the pattern has
  39         * an unquoted "star" at the corresponding end. There are two cases.
  40         * Case 1:
  41         * "qwerty" does not match against pattern "*zy",
  42         * no point in trying to match "werty", "erty" etc:
  43         */
  44        early_exit = (flags == (SCAN_MOVE_FROM_LEFT + SCAN_MATCH_RIGHT_HALF) && pattern[0] == '*');
  45
  46        if (flags & SCAN_MOVE_FROM_LEFT) {
  47                loc = string;
  48                end = string + len + 1;
  49        } else {
  50                loc = string + len;
  51                end = string - 1;
  52                if (flags == (SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF)) {
  53                        /* Case 2:
  54                         * "qwerty" does not match against pattern "qz*",
  55                         * no point in trying to match "qwert", "qwer" etc:
  56                         */
  57                        const char *p = pattern + strlen(pattern);
  58                        if (--p >= pattern && *p == '*') {
  59                                early_exit = 1;
  60                                while (--p >= pattern && *p == '\\')
  61                                        early_exit ^= 1;
  62                        }
  63                }
  64        }
  65
  66        while (loc != end) {
  67                char c;
  68                int r;
  69
  70                c = *loc;
  71                if (flags & SCAN_MATCH_LEFT_HALF) {
  72                        *loc = '\0';
  73                        r = fnmatch(pattern, string, 0);
  74                        *loc = c;
  75                } else {
  76                        r = fnmatch(pattern, loc, 0);
  77                }
  78                if (r == 0) /* match found */
  79                        return loc;
  80                if (early_exit) {
  81#ifdef STANDALONE
  82                        printf("(early exit) ");
  83#endif
  84                        break;
  85                }
  86
  87                if (flags & SCAN_MOVE_FROM_LEFT) {
  88                        loc++;
  89                } else {
  90                        loc--;
  91                }
  92        }
  93        return NULL;
  94}
  95
  96#ifdef STANDALONE
  97int main(int argc, char *argv[])
  98{
  99        char *string;
 100        char *op;
 101        char *pattern;
 102        char *loc;
 103
 104        setvbuf(stdout, NULL, _IONBF, 0);
 105
 106        if (!argv[1]) {
 107                puts(
 108                        "Usage: match <test> [test...]\n\n"
 109                        "Where a <test> is the form: <string><op><match>\n"
 110                        "This is to test the shell ${var#val} expression type.\n\n"
 111                        "e.g. `match 'abc#a*'` -> bc"
 112                );
 113                return 1;
 114        }
 115
 116        while (*++argv) {
 117                size_t off;
 118                unsigned scan_flags;
 119
 120                string = *argv;
 121                off = strcspn(string, "#%");
 122                if (!off) {
 123                        printf("invalid format\n");
 124                        continue;
 125                }
 126                op = string + off;
 127                scan_flags = pick_scan(op[0], op[1]);
 128
 129                printf("'%s': flags:%x, ", string, scan_flags);
 130                pattern = op + 1;
 131                if (op[0] == op[1])
 132                        pattern++;
 133                op[0] = '\0';
 134
 135                loc = scan_and_match(string, pattern, scan_flags);
 136
 137                if (scan_flags & SCAN_MATCH_LEFT_HALF) {
 138                        printf("'%s'\n", loc);
 139                } else {
 140                        if (loc)
 141                                *loc = '\0';
 142                        printf("'%s'\n", string);
 143                }
 144        }
 145
 146        return 0;
 147}
 148#endif
 149