Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F112512910
D49201.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
D49201.diff
View Options
Index: bin/sh/options.h
===================================================================
--- bin/sh/options.h
+++ bin/sh/options.h
@@ -68,9 +68,10 @@
#define nologflag optval[19]
#define pipefailflag optval[20]
#define verifyflag optval[21]
+#define braceexpandflag optval[22]
#define NSHORTOPTS 19
-#define NOPTS 22
+#define NOPTS 23
extern char optval[NOPTS];
extern const char optletter[NSHORTOPTS];
@@ -100,6 +101,7 @@
"\005nolog"
"\010pipefail"
"\006verify"
+ "\013braceexpand"
;
#endif
Index: bin/sh/parser.c
===================================================================
--- bin/sh/parser.c
+++ bin/sh/parser.c
@@ -39,7 +39,9 @@
#endif /* not lint */
#include <sys/cdefs.h>
#include <sys/param.h>
+#include <ctype.h>
#include <pwd.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
@@ -132,6 +134,7 @@
static void setprompt(int);
static int pgetc_linecont(void);
static void getusername(char *, size_t);
+static bool expandbrace(FILE *, const char *);
static void *
@@ -820,6 +823,174 @@
return (t);
}
+static bool expandsequence(FILE *stream, const char prefix[static 1], const char *pattern) {
+ char format[] = "%0.*d%.*s";
+ int start, end, step, i;
+ const char *p, *close;
+ char *portal, *endptr;
+ const char *terminator = strchr(pattern, '\0');
+ int prefixlen = (int)(pattern - prefix - 1);
+ int size = prefixlen + 32;
+ int zeroes = 0;
+
+ /* Parse start value (letter or number) */
+ if (isalpha(pattern[0])) {
+ start = pattern[0];
+ p = pattern + 1;
+ } else if (isspace(pattern[0]))
+ /* strtol accepts space here, brace expansion doesn't */
+ return false;
+ else {
+ zeroes = strspn(pattern, "0");
+ start = strtol(pattern + zeroes, &endptr, 10);
+ if (pattern == endptr)
+ return false;
+ p = endptr;
+ }
+
+ /* Verify ".." separator */
+ if (*p++ != '.' || *p++ != '.')
+ return false;
+
+ /* Parse end value (must match type of start) */
+ if (isalpha(p[0])) {
+ if (!isalpha(pattern[0]))
+ return false;
+ end = *p++;
+ format[4] = 'c';
+ } else if (isspace(p[0]))
+ return false;
+ else {
+ int z = strspn(p, "0");
+
+ if (z > zeroes)
+ zeroes = z;
+ close = p + z;
+ end = strtol(close, &endptr, 10);
+ if (close == endptr)
+ return false;
+ p = endptr;
+ }
+
+ /* Parse optional step value */
+ if (*p == '}')
+ step = 1;
+ else if (*p++ == '.' && *p++ == '.') {
+ if (*p == '+' || *p == '-')
+ p++; /* Direction will be determined by testing start > end */
+ if (!isdigit(*p))
+ return false;
+ step = strtol(p, &endptr, 10);
+ if (*endptr != '}')
+ return false;
+ p = endptr;
+ } else
+ return false;
+ close = p;
+
+ /* Each of the generated terms will start from the same prefix */
+ if (!(portal = malloc(size)))
+ return false;
+ snprintf(portal, size, "%.*s", prefixlen, prefix);
+ size -= prefixlen;
+
+ /* Generate sequence */
+ i = start;
+ if (start > end)
+ step = -step;
+ goto first_iteration;
+ while (start > end ? i >= end : i <= end) {
+ if (isalpha(pattern[0]) && !isalpha(i))
+ break;
+ fputc(' ', stream);
+first_iteration:
+ /* Copy the generated term and the suffix */
+ snprintf(portal + prefixlen, size, format,
+ zeroes + 1, i, (int)(terminator - close), close + 1);
+
+ /* The string may be a brace expansion pattern too */
+ expandbrace(stream, portal);
+ i += step;
+ }
+ free(portal);
+
+ return true;
+}
+
+static bool expandbrace(FILE *stream, const char pattern[static 1]) {
+ const char *close, *separator, *last;
+ int prefixlen;
+ char *portal;
+ const char *open = pattern;
+ const char *end = strchr(pattern, '\0');
+ int size = end - pattern;
+
+ /* Unescaped braces and one of the separators are required */
+ if (open && open[0] != '{')
+ while ((open = strchr(open + 1, '{'))) {
+ if (open[-1] == '\\')
+ continue;
+ if (open[-1] != '$')
+ break;
+ /* '${' restarts the search for '{' from the closest '}' */
+ if (!(open = strchr(open + 1, '}')))
+ break;
+ }
+
+ /* When checking for sequence, don't go past the first closing brace */
+ if (open && (separator = strpbrk(open + 1, ".}")))
+ if (separator[0] == '.' && separator[1] == '.')
+ if (expandsequence(stream, pattern, open + 1))
+ return true;
+
+ /* If not a sequence, the comma is required */
+ if ((separator = open))
+ while ((separator = strchr(separator + 1, ',')))
+ if (separator[-1] != '\\')
+ break;
+
+ /* Only look for closing brace once the comma has been found */
+ if ((close = separator))
+ while ((close = strchr(close + 1, '}')))
+ if (close[-1] != '\\')
+ break;
+
+ /* Not a brace expansion pattern, treat literally */
+ if (!close || !(portal = malloc(size))) {
+ fputs(pattern, stream);
+ return false;
+ }
+
+ /* Each of the generated alternatives will start from the same prefix */
+ prefixlen = snprintf(portal, size, "%.*s",
+ (int)(open - pattern), pattern);
+ size -= prefixlen;
+
+ /* Generate alternatives */
+ last = open + 1;
+ goto first_iteration;
+ for (;;) {
+ fputc(' ', stream);
+first_iteration:
+ /* Copy the generated alternative and the suffix */
+ snprintf(portal + prefixlen, size, "%.*s%.*s",
+ (int)((separator ? separator : close) - last), last,
+ (int)(end - close), close + 1);
+
+ /* The string may be a brace expansion pattern too */
+ expandbrace(stream, portal);
+
+ /* Find the next alternative */
+ if (!separator)
+ break;
+ last = separator + 1;
+ separator = memchr(last, ',', close - separator - 1);
+ }
+ free(portal);
+ return true;
+}
+
+
static int
readtoken(void)
{
@@ -863,6 +1034,23 @@
pushstring(ap->val, strlen(ap->val), ap);
goto top;
}
+ if (braceexpandflag && wordtext != NULL) {
+ FILE *memstream;
+ char *buf;
+ size_t len;
+ bool expanded;
+
+ if ((memstream = open_memstream(&buf, &len)) == NULL)
+ goto out;
+
+ expanded = expandbrace(memstream, wordtext);
+ fclose(memstream);
+
+ if (expanded) {
+ pushstring(buf, len, NULL);
+ goto top;
+ }
+ }
}
out:
if (t != TNOT)
Index: bin/sh/sh.1
===================================================================
--- bin/sh/sh.1
+++ bin/sh/sh.1
@@ -364,6 +364,8 @@
when sourcing files or loading profiles.
.\" See also
.\" .Xr mac_veriexec 4 . TODO Does not exist; write it.
+.It Li braceexpand
+Enable brace expansion.
.El
.Pp
The
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Mar 20, 3:49 AM (21 h, 8 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17225525
Default Alt Text
D49201.diff (6 KB)
Attached To
Mode
D49201: [WIP] sh: implement non-nested brace expansion
Attached
Detach File
Event Timeline
Log In to Comment