summary refs log tree commit diff
path: root/msed.c
diff options
context:
space:
mode:
Diffstat (limited to 'msed.c')
-rw-r--r--msed.c293
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;
+}