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