13 typedef struct Esc Esc;
14 typedef struct Req Req;
15 typedef struct Raw Raw;
17 /* escape sequence handler, like for \c */
25 /* raw request handler, like for .ie */
32 /* regular request handler, like for .ft */
37 void (*f)(int, Rune**);
59 addraw(Rune *name, void (*f)(Rune*))
63 if(nraw >= nelem(raw)){
64 fprint(2, "too many raw requets\n");
68 r->name = erunestrdup(name);
77 for(i=0; i<nraw; i++){
78 if(runestrcmp(raw[i].name, name) == 0){
89 renraw(Rune *from, Rune *to)
95 if(runestrcmp(raw[i].name, from) == 0){
97 raw[i].name = erunestrdup(to);
104 addreq(Rune *s, void (*f)(int, Rune**), int argc)
108 if(nreq >= nelem(req)){
109 fprint(2, "too many requests\n");
113 r->name = erunestrdup(s);
123 for(i=0; i<nreq; i++){
124 if(runestrcmp(req[i].name, name) == 0){
135 renreq(Rune *from, Rune *to)
140 for(i=0; i<nreq; i++)
141 if(runestrcmp(req[i].name, from) == 0){
143 req[i].name = erunestrdup(to);
149 addesc(Rune r, int (*f)(void), int mode)
153 if(nesc >= nelem(esc)){
154 fprint(2, "too many escapes\n");
164 * Get the next logical character in the input stream.
178 while((r = getrune()) >= 0 && r != Uunformatted){
185 if(r == Uunformatted)
191 for(i=0; i<nesc; i++){
192 if(r == esc[i].r && (inputmode&esc[i].mode)==inputmode){
193 if(esc[i].f == e_warn)
194 warn("ignoring %C%C", backslash, r);
201 if(inputmode&(ArgMode|CopyMode)){
213 * really we want to undo the getrunes that led us here,
214 * since the call after ungetnext might be getrune!
220 _readx(Rune *p, int n, int nmode, int line)
225 while((c = getrune()) == ' ' || c == '\t')
231 for(c=getnext(); p<e; c=getnext()){
234 if(!line && (c == ' ' || c == '\t'))
251 * Get the next argument from the current line.
256 static Rune buf[MaxLine];
260 if(_readx(buf, sizeof buf, ArgMode, 0) < 0)
262 r = runestrstr(buf, L("\\\""));
265 while((c = getrune()) >= 0 && c != '\n')
269 r = erunestrdup(buf);
274 * Read the current line in given mode. Newline not kept.
275 * Uses different buffer from copyarg!
280 static Rune buf[MaxLine];
283 if(_readx(buf, sizeof buf, m, 1) < 0)
285 r = erunestrdup(buf);
290 * Given the argument line (already read in copy+arg mode),
291 * parse into arguments. Note that \" has been left in place
292 * during copy+arg mode parsing, so comments still need to be stripped.
295 parseargs(Rune *p, Rune **argv)
300 for(argc=0; argc<MAXARG; argc++){
301 while(*p == ' ' || *p == '\t')
307 /* quoted argument */
313 /* parse quoted string */
316 if(*p == '"' && *(p+1) == '"')
327 /* unquoted argument - need to watch out for \" comment */
329 if(*p == ' ' || *p == '\t'){
333 if(*p == '\\' && *(p+1) == '"'){
346 * Process a dot line. The dot has been read.
352 Rune *a, *argv[1+MAXARG];
355 * Read request/macro name
358 if(a == nil || a[0] == 0){
365 * Check for .if, .ie, and others with special parsing.
367 for(i=0; i<nraw; i++){
368 if(runestrcmp(raw[i].name, a) == 0){
369 raw[i].f(raw[i].name);
376 * Read rest of line in copy mode, invoke regular request.
378 a = readline(ArgMode);
383 argc = 1+parseargs(a, argv+1);
384 for(i=0; i<nreq; i++){
385 if(runestrcmp(req[i].name, argv[0]) == 0){
386 if(req[i].argc != -1){
387 if(argc < 1+req[i].argc){
388 warn("not enough arguments for %C%S", dot, req[i].name);
393 if(argc > 1+req[i].argc)
394 warn("too many arguments for %C%S", dot, req[i].name);
396 req[i].f(argc, argv);
404 * Invoke user-defined macros.
406 runmacro(dot, argc, argv);
412 * newlines are magical in various ways.
423 if((n=getnr(L(".ce"))) > 0){
427 if(getnr(L(".fi")) == 0)
436 double ps, vs, lm, rm, ti;
443 if(getnr(L(".paragraph")) == 0)
454 vs = getnr(L(".v"))*getnr(L(".ls")) * 1.0/UPI;
455 vs /= (10.0/72.0); /* ps */
459 lm = (getnr(L(".o"))+getnr(L(".i"))) * 1.0/UPI;
460 ti = getnr(L(".ti")) * 1.0/UPI;
463 rm = 8.0 - getnr(L(".l"))*1.0/UPI - getnr(L(".o"))*1.0/UPI;
466 switch(getnr(L(".j"))){
483 if(!getnr(L(".margin")))
484 runesnprint(buf, nelem(buf), "<p style=\"line-height: %.1fem; text-indent: %.2fin; margin-top: 0; margin-bottom: 0; text-align: %s;\">\n",
487 runesnprint(buf, nelem(buf), "<p style=\"line-height: %.1fem; margin-left: %.2fin; text-indent: %.2fin; margin-right: %.2fin; margin-top: 0; margin-bottom: 0; text-align: %s;\">\n",
488 vs, lm, ti, rm, align);
501 if(getnr(L(".paragraph")))
506 r_margin(int argc, Rune **argv)
510 nr(L(".margin"), eval(argv[1]));
524 if((c == dot || c == tick) && bol){
565 /* t12init(); t12.c */
577 addreq(L("margin"), r_margin, 1);
579 nr(L(".paragraph"), 1);
609 runmacro1(L("font"));
612 runmacro1(L("font"));
619 Bprint(&bout, "&");
621 Bprint(&bout, "<");
623 Bprint(&bout, ">");
624 else if(r < Runeself || utf8)
625 Bprint(&bout, "%C", r);
627 Bprint(&bout, "%S", rune2html(r));
659 if(getnr(L(".fi")) == 0)
672 /* writing to bout */
695 Bprint(&bout, "’");
698 Bprint(&bout, "‘");
701 Bputrune(&bout, '&');
704 Bputrune(&bout, '<');
707 Bputrune(&bout, '>');
710 Bputrune(&bout, ' ');
714 * In Firefox, at least, the prime is not
715 * a superscript by default.
717 Bprint(&bout, "<sup>");
719 Bprint(&bout, "</sup>");
726 r_nop(int argc, Rune **argv)
733 r_warn(int argc, Rune **argv)
736 warn("ignoring %C%S", dot, argv[0]);
742 /* dispatch loop prints a warning for us */