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                int r;
  68
  69                if (flags & SCAN_MATCH_LEFT_HALF) {
  70                        char c = *loc;
  71                        *loc = '\0';
  72                        r = fnmatch(pattern, string, 0);
  73                        //bb_error_msg("fnmatch('%s','%s',0):%d", pattern, string, r);
  74                        *loc = c;
  75                } else {
  76                        r = fnmatch(pattern, loc, 0);
  77                        //bb_error_msg("fnmatch('%s','%s',0):%d", pattern, loc, r);
  78                }
  79                if (r == 0) /* match found */
  80                        return loc;
  81                if (early_exit) {
  82#ifdef STANDALONE
  83                        printf("(early exit) ");
  84#endif
  85                        break;
  86                }
  87
  88                if (flags & SCAN_MOVE_FROM_LEFT) {
  89                        loc++;
  90                } else {
  91                        loc--;
  92                }
  93        }
  94        return NULL;
  95}
  96
  97#ifdef STANDALONE
  98int main(int argc, char *argv[])
  99{
 100        char *string;
 101        char *op;
 102        char *pattern;
 103        char *loc;
 104
 105        setvbuf(stdout, NULL, _IONBF, 0);
 106
 107        if (!argv[1]) {
 108                puts(
 109                        "Usage: match <test> [test...]\n\n"
 110                        "Where a <test> is the form: <string><op><match>\n"
 111                        "This is to test the shell ${var#val} expression type.\n\n"
 112                        "e.g. `match 'abc#a*'` -> bc"
 113                );
 114                return 1;
 115        }
 116
 117        while (*++argv) {
 118                size_t off;
 119                unsigned scan_flags;
 120
 121                string = *argv;
 122                off = strcspn(string, "#%");
 123                if (!off) {
 124                        printf("invalid format\n");
 125                        continue;
 126                }
 127                op = string + off;
 128                scan_flags = pick_scan(op[0], op[1]);
 129
 130                printf("'%s': flags:%x, ", string, scan_flags);
 131                pattern = op + 1;
 132                if (op[0] == op[1])
 133                        pattern++;
 134                op[0] = '\0';
 135
 136                loc = scan_and_match(string, pattern, scan_flags);
 137
 138                if (scan_flags & SCAN_MATCH_LEFT_HALF) {
 139                        printf("'%s'\n", loc);
 140                } else {
 141                        if (loc)
 142                                *loc = '\0';
 143                        printf("'%s'\n", string);
 144                }
 145        }
 146
 147        return 0;
 148}
 149#endif
 150