ref: ad7078ab39dda36b08c4e673202bf5b34cd56acb
dir: /cp.c/
/* copy-mode character interpretation */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "roff.h" static int cp_blkdep; /* input block depth (text in \{ and \}) */ static int cp_cpmode; /* disable the interpretation of \w and \E */ static int cp_reqdep; /* the block depth of current request line */ /* just like cp_next(), but remove c_ni characters */ static int cp_noninext(void) { int c = cp_next(); while (c == c_ni) c = cp_next(); return c; } /* return 1 if \*[] includes a space */ static int cparg(char *d, int len) { int c = cp_noninext(); int i = 0; if (c == '(') { i += utf8next(d + i, cp_noninext); i += utf8next(d + i, cp_noninext); } else if (!n_cp && c == '[') { c = cp_noninext(); while (c >= 0 && c != ']' && c != ' ') { if (i + 1 < len) d[i++] = c; c = cp_noninext(); } d[i] = '\0'; return c == ' '; } else { cp_back(c); utf8next(d, cp_noninext); } return 0; } static int regid(void) { char regname[NMLEN]; cparg(regname, sizeof(regname)); return map(regname); } /* interpolate \n(xy */ static void cp_num(void) { int id; int c = cp_noninext(); if (c != '-' && c != '+') cp_back(c); id = regid(); if (c == '-' || c == '+') num_inc(id, c == '+'); if (num_str(id)) in_push(num_str(id), NULL); } /* interpolate \*(xy */ static void cp_str(void) { char reg[NMLEN]; char *args[NARGS + 2] = {reg}; char *buf = NULL; if (cparg(reg, sizeof(reg))) { buf = tr_args(args + 1, ']', cp_noninext, cp_back); cp_noninext(); } if (str_get(map(reg))) in_push(str_get(map(reg)), buf ? args : NULL); else if (!n_cp) tr_req(map(reg), args); free(buf); } /* interpolate \g(xy */ static void cp_numfmt(void) { in_push(num_getfmt(regid()), NULL); } /* interpolate \$*, \$@, and \$^ */ static void cp_args(int quote, int escape) { struct sbuf sb; char *s; int i; sbuf_init(&sb); for (i = 1; i < in_nargs(); i++) { sbuf_append(&sb, i > 1 ? " " : ""); sbuf_append(&sb, quote ? "\"" : ""); s = in_arg(i); while (*s) { sbuf_append(&sb, escape && *s == '"' ? "\"" : ""); sbuf_add(&sb, (unsigned char) *s++); } sbuf_append(&sb, quote ? "\"" : ""); } in_push(sbuf_buf(&sb), NULL); sbuf_done(&sb); } /* interpolate \$1 */ static void cp_arg(void) { char argname[NMLEN]; char *arg = NULL; int argnum; cparg(argname, sizeof(argname)); if (!strcmp("@", argname)) { cp_args(1, 0); return; } if (!strcmp("*", argname)) { cp_args(0, 0); return; } if (!strcmp("^", argname)) { cp_args(1, 1); return; } argnum = atoi(argname); if (argnum >= 0 && argnum < NARGS) arg = in_arg(argnum); if (arg) in_push(arg, NULL); } /* interpolate \w'xyz' */ static void cp_width(void) { char wid[16]; sprintf(wid, "%d", ren_wid(cp_next, cp_back)); in_push(wid, NULL); } /* define a register as \R'xyz expr' */ static void cp_numdef(void) { char *arg = quotednext(cp_noninext, cp_back); char *s = arg; while (*s && *s != ' ') s++; if (!*s) { free(arg); return; } *s++ = '\0'; num_set(map(arg), eval_re(s, num_get(map(arg)), 'u')); free(arg); } /* conditional interpolation as \?'cond@expr1@expr2@' */ static void cp_cond(void) { char delim[GNLEN], cs[GNLEN]; char *r, *s; char *s1, *s2; int n; char *arg = quotednext(cp_noninext, cp_back); s = arg; n = eval_up(&s, '\0'); if (charread(&s, delim) < 0) { free(arg); return; } if (!strcmp(delim, "\\&") && charread(&s, delim) < 0) { free(arg); return; } s1 = s; r = s; while (charread_delim(&s, cs, delim) >= 0) r = s; *r = '\0'; s2 = s; r = s; while (charread_delim(&s, cs, delim) >= 0) r = s; *r = '\0'; in_push(n > 0 ? s1 : s2, NULL); free(arg); } static int cp_raw(void) { int c; if (in_top() >= 0) return in_next(); do { c = in_next(); } while (c == c_ni); if (c == c_ec) { do { c = in_next(); } while (c == c_ni); if (c == '\n') return cp_raw(); if (c == '.') return '.'; if (c == '\\') { in_back('\\'); return c_ni; } if (c == 't') { in_back('\t'); return c_ni; } if (c == 'a') { in_back(''); return c_ni; } /* replace \{ and \} with a space if not in copy mode */ if (c == '}' && !cp_cpmode) { cp_blkdep--; return ' '; } if (c == '{' && !cp_cpmode) { cp_blkdep++; return ' '; } in_back(c); return c_ec; } return c; } int cp_next(void) { int c; if (in_top() >= 0) return in_next(); c = cp_raw(); if (c == c_ec) { c = cp_raw(); if (c == 'E' && !cp_cpmode) c = cp_next(); if (c == '"') { while (c >= 0 && c != '\n') c = cp_raw(); } else if (c == 'w' && !cp_cpmode) { cp_width(); c = cp_next(); } else if (c == 'n') { cp_num(); c = cp_next(); } else if (c == '*') { cp_str(); c = cp_next(); } else if (c == 'g') { cp_numfmt(); c = cp_next(); } else if (c == '$') { cp_arg(); c = cp_next(); } else if (c == '?') { cp_cond(); c = cp_next(); } else if (c == 'R' && !cp_cpmode) { cp_numdef(); c = cp_next(); } else { cp_back(c); c = c_ec; } } return c; } void cp_blk(int skip) { if (skip) { int c = cp_raw(); while (c >= 0 && (c != '\n' || cp_blkdep > cp_reqdep)) c = cp_raw(); } else { int c = cp_next(); while (c == ' ') c = cp_next(); /* push back if the space is not inserted due to \{ and \} */ if (c != ' ') cp_back(c); } } void cp_copymode(int mode) { cp_cpmode = mode; } /* beginning of a request; save current cp_blkdep */ void cp_reqbeg(void) { cp_reqdep = cp_blkdep; }