diff options
Diffstat (limited to 'msed.c')
-rw-r--r-- | msed.c | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/msed.c b/msed.c new file mode 100644 index 0000000..43e0c35 --- /dev/null +++ b/msed.c @@ -0,0 +1,293 @@ +#include <sys/stat.h> +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <regex.h> +#include <unistd.h> + +#include "blaze822.h" + +static char *expr; + +char * +subst(char *str, char *srch, char *repl, char *flags) +{ + static char buf[4096]; + char *bufe = buf + sizeof buf; + + int iflag = !!strchr(flags, 'i'); + int gflag = !!strchr(flags, 'g'); + +#define APP(o,l) do {if(bufe-b<(ssize_t)l) return str; memcpy(b,str+i+o,l); b+=l;} while(0) +#define APPC(c) do {if(b>=bufe) return str; *b++=c;} while(0) + + regex_t srchrx; + regmatch_t pmatch[10]; + if (regcomp(&srchrx, srch, iflag ? REG_ICASE : 0) != 0) + return str; + + char *b = buf; + + regoff_t i = 0; + while (1) { + if (regexec(&srchrx, str+i, 9, pmatch, 0) != 0) + break; + + APP(0, pmatch[0].rm_so); + + char *t = repl; + while (*t) { + // & == \0 + if (*t == '&' || (*t == '\\' && isdigit(*(t+1)))) { + int n; + if (*t == '&') { + t++; + n = 0; + } else { + t++; + n = *t++ - '0'; + } + + APP(pmatch[n].rm_so, + pmatch[n].rm_eo - pmatch[n].rm_so); + } else if (*t == '\\' && *(t+1)) { + t++; + APPC(*t++); + } else { + APPC(*t++); + } + } + + i += pmatch[0].rm_eo; // advance to end of match + if (!gflag) + break; + } + + if (i > 0) { // any match? + APP(0, strlen(str + i)); + *b = 0; + return buf; + } + + return str; +} + +void +printhdr(char *hdr, int rest) +{ + int uc = 1; + + while (*hdr && *hdr != ':') { + putc(uc ? toupper(*hdr) : *hdr, stdout); + uc = (*hdr == '-'); + hdr++; + } + + if (rest) { + printf("%s\n", hdr); + } +} + +void +sed(char *file) +{ + struct message *msg = blaze822_file(file); + if (!msg) + return; + + char *h = 0; + while ((h = blaze822_next_header(msg, h))) { + regex_t headerrx; + char headersel[1024]; + + char *v = strchr(h, ':'); + if (!v) + continue; + v++; + while (*v && (*v == ' ' || *v == '\t')) + v++; + + v = strdup(v); + + char *e = expr; + while (*e) { + while (*e && + (*e == ' ' || *e == '\t' || *e == '\n' || *e == ';')) + e++; + + *headersel = 0; + if (*e == '/') { + e++; + char *s = e; + // parse_headers, sets headersel + while (*e && *e != '/') + e++; + snprintf(headersel, sizeof headersel, + "^(%.*s)*:", e-s, s); + for (s = headersel; *s && *(s+1); s++) + if (*s == ':') + *s = '|'; + regcomp(&headerrx, headersel, REG_EXTENDED); + if (*e) + e++; + } + + char sep; + char *s; + if (!*headersel || regexec(&headerrx, h, 0, 0, 0) == 0) { + switch (*e) { + case 'd': + free(v); + v = 0; + break; + case 'a': + // skipped here; + sep = *++e; + if (!sep) { + fprintf(stderr, "unterminated a command\n"); + exit(1); + } + while (*e && *e != sep) + e++; + break; + if (!(*e == ' ' || *e == ';' || *e == '\n' || !*e)) { + fprintf(stderr, "unterminated a command\n"); + exit(1); + } + + case 'c': + sep = *++e; + s = ++e; + while (*e && *e != sep) + e++; + free(v); + v = strndup(s, e-s); + break; + case 's': + sep = *++e; + s = ++e; + while (*e && *e != sep) + e++; + char *t = ++e; + while (*e && *e != sep) + e++; + char *u = ++e; + while (*e == 'i' || *e == 'g') + e++; + + if (!(*e == ' ' || *e == ';' || *e == '\n' || !*e)) { + fprintf(stderr, "unterminated s command\n"); + exit(1); + } + + // XXX stack allocate + char *from = strndup(s, t-s-1); + char *to = strndup(t, u-t-1); + char *flags = strndup(u, e-u); + + char *ov = v; + v = strdup(subst(ov, from, to, flags)); + free(ov); + + free(from); + free(to); + free(flags); + + break; + default: + fprintf(stderr, "unknown command: '%c'\n", *e); + exit(1); + } + } + while (*e && *e != ';') + e++; + } + if (v) { + printhdr(h, 0); + printf(": %s\n", v); + free(v); + } + } + + // loop, do all a// + + char *hs, *he; + char *e = expr; + while (*e) { + while (*e && + (*e == ' ' || *e == '\t' || *e == '\n' || *e == ';')) + e++; + + hs = he = 0; + if (*e == '/') { + e++; + hs = e; + // parse_headers, sets headersel + while (*e && *e != '/') + e++; + he = e; + if (*e) + e++; + } + + char sep; + char *s; + switch (*e) { + case 'a': + sep = *++e; + if (!sep) { + fprintf(stderr, "unterminated a command\n"); + exit(1); + } + + s = ++e; + while (*e && *e != sep) + e++; + if (he != hs) { + char *h = strndup(hs, he-hs); + char *v = strndup(s, e-s); + printhdr(h, 0); + printf(": %s\n", v); + } + break; + + case 'c': + case 'd': + case 's': + // ignore here; + break; + } + while (*e && *e != ';') + e++; + } + + printf("\n"); + fwrite(blaze822_body(msg), 1, blaze822_bodylen(msg), stdout); +} + +int +main(int argc, char *argv[]) +{ + int c; + while ((c = getopt(argc, argv, "")) != -1) + switch(c) { + default: + fprintf(stderr, "Usage: msed [expr] [msgs...]\n"); + exit(1); + } + + expr = argv[optind]; + optind++; + + if (argc == optind && isatty(0)) + blaze822_loop1(".", sed); + else + blaze822_loop(argc-optind, argv+optind, sed); + + return 0; +} |