shithub: neatroff

Download patch

ref: 4df77191c3b7d1c09255c9aaf0e159f7af20902d
parent: a7438c82192b2014f5848dce1253292a4dfb5ba1
author: Ali Gholami Rudi <ali@rudi.ir>
date: Thu Apr 11 14:05:39 EDT 2013

tr: implement conditional input

--- a/cp.c
+++ b/cp.c
@@ -4,8 +4,9 @@
 
 #define CPBUF		4
 
-static int cp_backed = 0;
-static int cp_buf[CPBUF];
+static int cp_buf[CPBUF];	/* pushed character stack */
+static int cp_backed;		/* number of pushed characters */
+static int cp_blk;		/* input block depth (text in \{ and \}) */
 
 static int regid(void)
 {
@@ -44,7 +45,7 @@
 		in_push(arg, NULL);
 }
 
-int cp_next(void)
+static int cp_raw(void)
 {
 	int c;
 	if (cp_backed)
@@ -52,21 +53,45 @@
 	c = in_next();
 	if (c == '\\') {
 		c = in_next();
-		if (c == 'n') {
+		if (c == '\n')
+			return in_next();
+		if (c == '.')
+			return '.';
+		if (c == '{') {
+			cp_blk++;
+			return cp_raw();
+		}
+		if (c == '}') {
+			cp_blk--;
+			return cp_raw();
+		}
+		cp_back(c);
+		return '\\';
+	}
+	return c;
+}
+
+int cp_next(void)
+{
+	int c;
+	if (cp_backed)
+		return cp_buf[--cp_backed];
+	c = cp_raw();
+	if (c == '\\') {
+		c = cp_raw();
+		if (c == '"') {
+			while (c >= 0 && c != '\n')
+				c = cp_raw();
+		} else if (c == 'n') {
 			cp_num();
-			c = in_next();
+			c = cp_raw();
 		} else if (c == '*') {
 			cp_str();
-			c = in_next();
+			c = cp_raw();
 		} else if (c == '$') {
 			cp_arg();
-			c = in_next();
-		} else if (c == '\n') {
-			c = cp_next();
-		} else if (c == '"') {
-			while (c >= 0 && c != '\n')
-				c = in_next();
-		} else if (c != '.') {
+			c = cp_raw();
+		} else {
 			cp_back(c);
 			c = '\\';
 		}
@@ -78,4 +103,17 @@
 {
 	if (cp_backed < CPBUF)
 		cp_buf[cp_backed++] = c;
+}
+
+void cp_skip(void)
+{
+	int c;
+	int old_blk = cp_blk;
+	do {
+		c = cp_raw();
+	} while (c == ' ' || c == '\t');
+	while (cp_blk > old_blk && c >= 0)
+		c = cp_raw();
+	while (c != '\n')
+		c = cp_raw();
 }
--- a/tr.c
+++ b/tr.c
@@ -119,6 +119,105 @@
 	sbuf_done(&sbuf);
 }
 
+/* read into sbuf until stop */
+static void read_until(struct sbuf *sbuf, int stop)
+{
+	int c;
+	while ((c = cp_next()) >= 0) {
+		if (c == stop)
+			return;
+		if (c == '\n') {
+			cp_back(c);
+			return;
+		}
+		sbuf_add(sbuf, c);
+	}
+}
+
+/* evaluate .if strcmp (i.e. 'str'str') */
+static int if_strcmp(void)
+{
+	struct sbuf s1, s2;
+	int ret;
+	sbuf_init(&s1);
+	sbuf_init(&s2);
+	read_until(&s1, '\'');
+	read_until(&s2, '\'');
+	ret = !strcmp(sbuf_buf(&s1), sbuf_buf(&s2));
+	sbuf_done(&s1);
+	sbuf_done(&s2);
+	return ret;
+}
+
+/* evaluate .if condition */
+static int if_cond(void)
+{
+	struct sbuf sbuf;
+	char *s;
+	int ret;
+	sbuf_init(&sbuf);
+	read_until(&sbuf, ' ');
+	s = sbuf_buf(&sbuf);
+	if (s[0] == 'o' && s[1] == '\0')
+		ret = n_pg % 2;
+	else if (s[0] == 'e' && s[1] == '\0')
+		ret = !(n_pg % 2);
+	else if (s[0] == 't' && s[1] == '\0')
+		ret = 1;
+	else if (s[0] == 'n' && s[1] == '\0')
+		ret = 0;
+	else
+		ret = eval(s, 0, '\0') > 0;
+	sbuf_done(&sbuf);
+	return ret;
+}
+
+/* execute or skip the line or block following .if */
+static void if_blk(int doexec)
+{
+	int c;
+	if (doexec) {
+		do {
+			c = cp_next();
+		} while (c == ' ');
+		cp_back(c);
+	} else {
+		cp_skip();
+	}
+}
+
+static int ie_cond[NIES];	/* .ie condition stack */
+static int ie_depth;
+
+static void tr_if(char **args)
+{
+	int neg = 0;
+	int ret;
+	int c;
+	do {
+		c = cp_next();
+	} while (c == ' ' || c == '\t');
+	if (c == '!') {
+		neg = 1;
+		c = cp_next();
+	}
+	if (c == '\'') {
+		ret = if_strcmp();
+	} else {
+		cp_back(c);
+		ret = if_cond();
+	}
+	if (args[0][1] == 'i' && args[0][2] == 'e')	/* .ie command */
+		if (ie_depth < NIES)
+			ie_cond[ie_depth++] = ret != neg;
+	if_blk(ret != neg);
+}
+
+static void tr_el(char **args)
+{
+	if_blk(ie_depth > 0 ? !ie_cond[--ie_depth] : 0);
+}
+
 static void tr_na(char **args)
 {
 	n_j = 0;
@@ -231,6 +330,12 @@
 	return mkargs(args + 1, s, e - s) + 1;
 }
 
+/* do not read arguments; for .if, .ie and .el */
+static int mkargs_null(char **args, char *buf, int len)
+{
+	return 0;
+}
+
 static struct cmd {
 	char *id;
 	void (*f)(char **args);
@@ -248,10 +353,13 @@
 	{"di", tr_di},
 	{"ds", tr_ds, mkargs_ds},
 	{"dt", tr_dt},
+	{"el", tr_el, mkargs_null},
 	{"ev", tr_ev},
 	{"fi", tr_fi},
 	{"fp", tr_fp},
 	{"ft", tr_ft},
+	{"ie", tr_if, mkargs_null},
+	{"if", tr_if, mkargs_null},
 	{"in", tr_in},
 	{"ll", tr_ll},
 	{"na", tr_na},
--- a/xroff.h
+++ b/xroff.h
@@ -15,6 +15,7 @@
 #define RLEN		4	/* register/macro name */
 #define NPREV		16	/* environment stack depth */
 #define NTRAPS		1024	/* number of traps per page */
+#define NIES		128	/* number of nested .ie commands */
 
 /* escape sequences */
 #define ESC_Q	"bCDhHlLNoSvwxX"	/* quoted escape sequences */
@@ -98,6 +99,7 @@
 char *in_arg(int i);
 void in_back(int c);
 void cp_back(int c);
+void cp_skip(void);	/* skip current input line or block */
 
 /* rendering */
 void render(void);	/* read from in.c and print the output */