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 */