- g = nglob;
-
- if((len == 1 && nglob[0] == separator) ||
- (len == 2 && nglob[0] == '^' && nglob[1] == separator) ||
- (len == 2 && nglob[0] == separator && nglob[1] == '$') ||
- (len == 3 && nglob[0] == '^' && nglob[1] == separator &&
- nglob[2] == '$')) {
- *r++ = '^';
- *r++ = '\\';
- *r++ = separator;
- *r++ = '\\';
- *r++ = separator;
- *r++ = '$';
- }
- else {
- /*
- * Do the conversion:
- *
- * ? -> [^\separator]
- * * -> [^\separator]*
- * [!...] -> [^...]
- * ** -> .*
- *
- * The following are given a leading backslash to protect them
- * unless they already have a backslash:
- *
- * ( ) { } + . ^ $ |
- *
- * If the last
- * non-escaped character is \ leave it to cause a syntax
- * error when the regex is compiled.
- */
-
- if(*g == '^') {
- *r++ = '^';
- *r++ = '\\'; /* escape the separator */
- *r++ = separator;
- g++;
- if(*g == separator) g++;
- }
- else if(*g != separator) {
- *r++ = '\\'; /* add a leading \separator */
- *r++ = separator;
- }
- last_ch = '\0';
- for (ch = *g++; ch != '\0'; last_ch = ch, ch = *g++) {
- next_ch = *g;
- if (last_ch == '\\') {
- *r++ = (char)ch;
- ch = '\0'; /* so last_ch != '\\' next time */
- } else if (last_ch == '[' && ch == '!') {
- *r++ = '^';
- } else if (ch == '\\') {
- *r++ = (char)ch;
- } else if (ch == '*' || ch == '?') {
- if(ch == '*' && next_ch == '*') {
- *r++ = '.';
- g++;
- }
- else {
- *r++ = '[';
- *r++ = '^';
- *r++ = '\\';
- *r++ = separator;
- *r++ = ']';
- }
- if (ch == '*') {
- *r++ = '*';
- }
- } else if (ch == '$' && next_ch == '\0') {
- if(last_ch != separator) {
- *r++ = '\\';
- *r++ = separator;
- }
- *r++ = (char)ch;
- } else if ( ch == '('
- || ch == ')'
- || ch == '{'
- || ch == '}'
- || ch == '+'
- || ch == '.'
- || ch == '^'
- || ch == '$'
- || ch == '|') {
- *r++ = '\\';
- *r++ = (char)ch;
- } else {
- *r++ = (char)ch;
- }
- }
- if(last_ch != '\\') {
- if(last_ch != separator && last_ch != '$') {
- *r++ = '\\';
- *r++ = separator; /* add a trailing \separator */
- }
- }
+
+ if(glob_is_separator_only(nglob, separator)) {
+ regex = alloc(7); /* Length of what is written below plus '\0' */
+ dst = regex;
+ *dst++ = '^';
+ *dst++ = '\\';
+ *dst++ = separator;
+ *dst++ = '\\';
+ *dst++ = separator;
+ *dst++ = '$';
+ *dst = '\0';
+ } else {
+ /*
+ * Unlike what happens for tar and disk expressions, here the
+ * substitution table needs to be dynamically allocated. When we enter
+ * here, we know what the expansions will be for the question mark, the
+ * star and the double star, and also the worst case expansion. We
+ * calculate the begin and end expansions below.
+ */
+
+#define MATCHWORD_STAR_EXPANSION(c) (const char []) { \
+ '[', '^', (c), ']', '*', 0 \
+}
+#define MATCHWORD_QUESTIONMARK_EXPANSION(c) (const char []) { \
+ '[', '^', (c), ']', 0 \
+}
+#define MATCHWORD_DOUBLESTAR_EXPANSION ".*"
+
+ struct subst_table table;
+ size_t worst_case = 5;
+ const char *begin, *end;
+ char *p, *g = nglob;
+
+ /*
+ * Calculate the beginning of the regex:
+ * - by default, it is an unanchored separator;
+ * - if the glob begins with a caret, make that an anchored separator,
+ * and increment g appropriately;
+ * - if it begins with a separator, make it the empty string.
+ */
+
+ p = nglob;
+
+#define REGEX_BEGIN_FULL(c) (const char[]) { '^', '\\', (c), 0 }
+#define REGEX_BEGIN_NOANCHOR(c) (const char[]) { '\\', (c), 0 }
+#define REGEX_BEGIN_ANCHORONLY "^" /* Unused, but defined for consistency */
+#define REGEX_BEGIN_EMPTY ""
+
+ begin = REGEX_BEGIN_NOANCHOR(separator);
+
+ if (*p == '^') {
+ begin = REGEX_BEGIN_FULL(separator);
+ p++, g++;
+ if (*p == separator)
+ g++;
+ } else if (*p == separator)
+ begin = REGEX_BEGIN_EMPTY;
+
+ /*
+ * Calculate the end of the regex:
+ * - an unanchored separator by default;
+ * - if the last character is a backslash or the separator itself, it
+ * should be the empty string;
+ * - if it is a dollar sign, overwrite it with 0 and look at the
+ * character before it: if it is the separator, only anchor at the
+ * end, otherwise, add a separator before the anchor.
+ */
+
+ p = &(nglob[strlen(nglob) - 1]);
+
+#define REGEX_END_FULL(c) (const char[]) { '\\', (c), '$', 0 }
+#define REGEX_END_NOANCHOR(c) REGEX_BEGIN_NOANCHOR(c)
+#define REGEX_END_ANCHORONLY "$"
+#define REGEX_END_EMPTY REGEX_BEGIN_EMPTY
+
+ end = REGEX_END_NOANCHOR(separator);
+
+ if (*p == '\\' || *p == separator)
+ end = REGEX_END_EMPTY;
+ else if (*p == '$') {
+ char prev = *(p - 1);
+ *p = '\0';
+ if (prev == separator)
+ end = REGEX_END_ANCHORONLY;
+ else
+ end = REGEX_END_FULL(separator);
+ }
+
+ /*
+ * Fill in our substitution table and generate the regex
+ */
+
+ table.begin = begin;
+ table.end = end;
+ table.question_mark = MATCHWORD_QUESTIONMARK_EXPANSION(separator);
+ table.star = MATCHWORD_STAR_EXPANSION(separator);
+ table.double_star = MATCHWORD_DOUBLESTAR_EXPANSION;
+
+ regex = amglob_to_regex(g, &table, worst_case);