Blob
1 #include "a.h"3 enum4 {5 MAXREQ = 100,6 MAXRAW = 40,7 MAXESC = 60,8 MAXLINE = 1024,9 MAXIF = 20,10 MAXARG = 1011 };13 typedef struct Esc Esc;14 typedef struct Req Req;15 typedef struct Raw Raw;17 /* escape sequence handler, like for \c */18 struct Esc19 {20 Rune r;21 int (*f)(void);22 int mode;23 };25 /* raw request handler, like for .ie */26 struct Raw27 {28 Rune *name;29 void (*f)(Rune*);30 };32 /* regular request handler, like for .ft */33 struct Req34 {35 int argc;36 Rune *name;37 void (*f)(int, Rune**);38 };40 int dot = '.';41 int tick = '\'';42 int backslash = '\\';44 int inputmode;45 Req req[MAXREQ];46 int nreq;47 Raw raw[MAXRAW];48 int nraw;49 Esc esc[MAXESC];50 int nesc;51 int iftrue[MAXIF];52 int niftrue;54 int isoutput;55 int linepos;58 void59 addraw(Rune *name, void (*f)(Rune*))60 {61 Raw *r;63 if(nraw >= nelem(raw)){64 fprint(2, "too many raw requets\n");65 return;66 }67 r = &raw[nraw++];68 r->name = erunestrdup(name);69 r->f = f;70 }72 void73 delraw(Rune *name)74 {75 int i;77 for(i=0; i<nraw; i++){78 if(runestrcmp(raw[i].name, name) == 0){79 if(i != --nraw){80 free(raw[i].name);81 raw[i] = raw[nraw];82 }83 return;84 }85 }86 }88 void89 renraw(Rune *from, Rune *to)90 {91 int i;93 delraw(to);94 for(i=0; i<nraw; i++)95 if(runestrcmp(raw[i].name, from) == 0){96 free(raw[i].name);97 raw[i].name = erunestrdup(to);98 return;99 }100 }103 void104 addreq(Rune *s, void (*f)(int, Rune**), int argc)105 {106 Req *r;108 if(nreq >= nelem(req)){109 fprint(2, "too many requests\n");110 return;111 }112 r = &req[nreq++];113 r->name = erunestrdup(s);114 r->f = f;115 r->argc = argc;116 }118 void119 delreq(Rune *name)120 {121 int i;123 for(i=0; i<nreq; i++){124 if(runestrcmp(req[i].name, name) == 0){125 if(i != --nreq){126 free(req[i].name);127 req[i] = req[nreq];128 }129 return;130 }131 }132 }134 void135 renreq(Rune *from, Rune *to)136 {137 int i;139 delreq(to);140 for(i=0; i<nreq; i++)141 if(runestrcmp(req[i].name, from) == 0){142 free(req[i].name);143 req[i].name = erunestrdup(to);144 return;145 }146 }148 void149 addesc(Rune r, int (*f)(void), int mode)150 {151 Esc *e;153 if(nesc >= nelem(esc)){154 fprint(2, "too many escapes\n");155 return;156 }157 e = &esc[nesc++];158 e->r = r;159 e->f = f;160 e->mode = mode;161 }163 /*164 * Get the next logical character in the input stream.165 */166 int167 getnext(void)168 {169 int i, r;171 next:172 r = getrune();173 if(r < 0)174 return -1;175 if(r == Uformatted){176 br();177 assert(!isoutput);178 while((r = getrune()) >= 0 && r != Uunformatted){179 if(r == Uformatted)180 continue;181 outrune(r);182 }183 goto next;184 }185 if(r == Uunformatted)186 goto next;187 if(r == backslash){188 r = getrune();189 if(r < 0)190 return -1;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);195 r = esc[i].f();196 if(r <= 0)197 goto next;198 return r;199 }200 }201 if(inputmode&(ArgMode|CopyMode)){202 ungetrune(r);203 r = backslash;204 }205 }206 return r;207 }209 void210 ungetnext(Rune r)211 {212 /*213 * really we want to undo the getrunes that led us here,214 * since the call after ungetnext might be getrune!215 */216 ungetrune(r);217 }219 int220 _readx(Rune *p, int n, int nmode, int line)221 {222 int c, omode;223 Rune *e;225 while((c = getrune()) == ' ' || c == '\t')226 ;227 ungetrune(c);228 omode = inputmode;229 inputmode = nmode;230 e = p+n-1;231 for(c=getnext(); p<e; c=getnext()){232 if(c < 0)233 break;234 if(!line && (c == ' ' || c == '\t'))235 break;236 if(c == '\n'){237 if(!line)238 ungetnext(c);239 break;240 }241 *p++ = c;242 }243 inputmode = omode;244 *p = 0;245 if(c < 0)246 return -1;247 return 0;248 }250 /*251 * Get the next argument from the current line.252 */253 Rune*254 copyarg(void)255 {256 static Rune buf[MaxLine];257 int c;258 Rune *r;260 if(_readx(buf, sizeof buf, ArgMode, 0) < 0)261 return nil;262 r = runestrstr(buf, L("\\\""));263 if(r){264 *r = 0;265 while((c = getrune()) >= 0 && c != '\n')266 ;267 ungetrune('\n');268 }269 r = erunestrdup(buf);270 return r;271 }273 /*274 * Read the current line in given mode. Newline not kept.275 * Uses different buffer from copyarg!276 */277 Rune*278 readline(int m)279 {280 static Rune buf[MaxLine];281 Rune *r;283 if(_readx(buf, sizeof buf, m, 1) < 0)284 return nil;285 r = erunestrdup(buf);286 return r;287 }289 /*290 * Given the argument line (already read in copy+arg mode),291 * parse into arguments. Note that \" has been left in place292 * during copy+arg mode parsing, so comments still need to be stripped.293 */294 int295 parseargs(Rune *p, Rune **argv)296 {297 int argc;298 Rune *w;300 for(argc=0; argc<MAXARG; argc++){301 while(*p == ' ' || *p == '\t')302 p++;303 if(*p == 0)304 break;305 argv[argc] = p;306 if(*p == '"'){307 /* quoted argument */308 if(*(p+1) == '"'){309 /* empty argument */310 *p = 0;311 p += 2;312 }else{313 /* parse quoted string */314 w = p++;315 for(; *p; p++){316 if(*p == '"' && *(p+1) == '"')317 *w++ = '"';318 else if(*p == '"'){319 p++;320 break;321 }else322 *w++ = *p;323 }324 *w = 0;325 }326 }else{327 /* unquoted argument - need to watch out for \" comment */328 for(; *p; p++){329 if(*p == ' ' || *p == '\t'){330 *p++ = 0;331 break;332 }333 if(*p == '\\' && *(p+1) == '"'){334 *p = 0;335 if(p != argv[argc])336 argc++;337 return argc;338 }339 }340 }341 }342 return argc;343 }345 /*346 * Process a dot line. The dot has been read.347 */348 void349 dotline(int dot)350 {351 int argc, i;352 Rune *a, *argv[1+MAXARG];354 /*355 * Read request/macro name356 */357 a = copyarg();358 if(a == nil || a[0] == 0){359 free(a);360 getrune(); /* \n */361 return;362 }363 argv[0] = a;364 /*365 * Check for .if, .ie, and others with special parsing.366 */367 for(i=0; i<nraw; i++){368 if(runestrcmp(raw[i].name, a) == 0){369 raw[i].f(raw[i].name);370 free(a);371 return;372 }373 }375 /*376 * Read rest of line in copy mode, invoke regular request.377 */378 a = readline(ArgMode);379 if(a == nil){380 free(argv[0]);381 return;382 }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);389 free(argv[0]);390 free(a);391 return;392 }393 if(argc > 1+req[i].argc)394 warn("too many arguments for %C%S", dot, req[i].name);395 }396 req[i].f(argc, argv);397 free(argv[0]);398 free(a);399 return;400 }401 }403 /*404 * Invoke user-defined macros.405 */406 runmacro(dot, argc, argv);407 free(argv[0]);408 free(a);409 }411 /*412 * newlines are magical in various ways.413 */414 int bol;415 void416 newline(void)417 {418 int n;420 if(bol)421 sp(eval(L("1v")));422 bol = 1;423 if((n=getnr(L(".ce"))) > 0){424 nr(L(".ce"), n-1);425 br();426 }427 if(getnr(L(".fi")) == 0)428 br();429 outrune('\n');430 }432 void433 startoutput(void)434 {435 char *align;436 double ps, vs, lm, rm, ti;437 Rune buf[200];439 if(isoutput)440 return;441 isoutput = 1;443 if(getnr(L(".paragraph")) == 0)444 return;446 nr(L(".ns"), 0);447 isoutput = 1;448 ps = getnr(L(".s"));449 if(ps <= 1)450 ps = 10;451 ps /= 72.0;452 USED(ps);454 vs = getnr(L(".v"))*getnr(L(".ls")) * 1.0/UPI;455 vs /= (10.0/72.0); /* ps */456 if(vs == 0)457 vs = 1.2;459 lm = (getnr(L(".o"))+getnr(L(".i"))) * 1.0/UPI;460 ti = getnr(L(".ti")) * 1.0/UPI;461 nr(L(".ti"), 0);463 rm = 8.0 - getnr(L(".l"))*1.0/UPI - getnr(L(".o"))*1.0/UPI;464 if(rm < 0)465 rm = 0;466 switch(getnr(L(".j"))){467 default:468 case 0:469 align = "left";470 break;471 case 1:472 align = "justify";473 break;474 case 3:475 align = "center";476 break;477 case 5:478 align = "right";479 break;480 }481 if(getnr(L(".ce")))482 align = "center";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",485 vs, ti, align);486 else487 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);489 outhtml(buf);490 }491 void492 br(void)493 {494 if(!isoutput)495 return;496 isoutput = 0;498 nr(L(".dv"), 0);499 dv(0);500 hideihtml();501 if(getnr(L(".paragraph")))502 outhtml(L("</p>"));503 }505 void506 r_margin(int argc, Rune **argv)507 {508 USED(argc);510 nr(L(".margin"), eval(argv[1]));511 }513 int inrequest;514 void515 runinput(void)516 {517 int c;519 bol = 1;520 for(;;){521 c = getnext();522 if(c < 0)523 break;524 if((c == dot || c == tick) && bol){525 inrequest = 1;526 dotline(c);527 bol = 1;528 inrequest = 0;529 }else if(c == '\n'){530 newline();531 itrap();532 linepos = 0;533 }else{534 outtrap();535 startoutput();536 showihtml();537 if(c == '\t'){538 /* XXX do better */539 outrune(' ');540 while(++linepos%4)541 outrune(' ');542 }else{543 outrune(c);544 linepos++;545 }546 bol = 0;547 }548 }549 }551 void552 run(void)553 {554 t1init();555 t2init();556 t3init();557 t4init();558 t5init();559 t6init();560 t7init();561 t8init();562 /* t9init(); t9.c */563 t10init();564 t11init();565 /* t12init(); t12.c */566 t13init();567 t14init();568 t15init();569 t16init();570 t17init();571 t18init();572 t19init();573 t20init();574 htmlinit();575 hideihtml();577 addreq(L("margin"), r_margin, 1);578 nr(L(".margin"), 1);579 nr(L(".paragraph"), 1);581 runinput();582 while(popinput())583 ;584 dot = '.';585 if(verbose)586 fprint(2, "eof\n");587 runmacro1(L("eof"));588 closehtml();589 }591 void592 out(Rune *s)593 {594 if(s == nil)595 return;596 for(; *s; s++)597 outrune(*s);598 }600 void (*outcb)(Rune);602 void603 inroman(Rune r)604 {605 int f;607 f = getnr(L(".f"));608 nr(L(".f"), 1);609 runmacro1(L("font"));610 outrune(r);611 nr(L(".f"), f);612 runmacro1(L("font"));613 }615 void616 Brune(Rune r)617 {618 if(r == '&')619 Bprint(&bout, "&");620 else if(r == '<')621 Bprint(&bout, "<");622 else if(r == '>')623 Bprint(&bout, ">");624 else if(r < Runeself || utf8)625 Bprint(&bout, "%C", r);626 else627 Bprint(&bout, "%S", rune2html(r));628 }630 void631 outhtml(Rune *s)632 {633 Rune r;635 for(; *s; s++){636 switch(r = *s){637 case '<':638 r = Ult;639 break;640 case '>':641 r = Ugt;642 break;643 case '&':644 r = Uamp;645 break;646 case ' ':647 r = Uspace;648 break;649 }650 outrune(r);651 }652 }654 void655 outrune(Rune r)656 {657 switch(r){658 case ' ':659 if(getnr(L(".fi")) == 0)660 r = Unbsp;661 break;662 case Uformatted:663 case Uunformatted:664 abort();665 }666 if(outcb){667 if(r == ' ')668 r = Uspace;669 outcb(r);670 return;671 }672 /* writing to bout */673 switch(r){674 case Uempty:675 return;676 case Upl:677 inroman('+');678 return;679 case Ueq:680 inroman('=');681 return;682 case Umi:683 inroman(0x2212);684 return;685 case Utick:686 r = '\'';687 break;688 case Ubtick:689 r = '`';690 break;691 case Uminus:692 r = '-';693 break;694 case '\'':695 Bprint(&bout, "’");696 return;697 case '`':698 Bprint(&bout, "‘");699 return;700 case Uamp:701 Bputrune(&bout, '&');702 return;703 case Ult:704 Bputrune(&bout, '<');705 return;706 case Ugt:707 Bputrune(&bout, '>');708 return;709 case Uspace:710 Bputrune(&bout, ' ');711 return;712 case 0x2032:713 /*714 * In Firefox, at least, the prime is not715 * a superscript by default.716 */717 Bprint(&bout, "<sup>");718 Brune(r);719 Bprint(&bout, "</sup>");720 return;721 }722 Brune(r);723 }725 void726 r_nop(int argc, Rune **argv)727 {728 USED(argc);729 USED(argv);730 }732 void733 r_warn(int argc, Rune **argv)734 {735 USED(argc);736 warn("ignoring %C%S", dot, argv[0]);737 }739 int740 e_warn(void)741 {742 /* dispatch loop prints a warning for us */743 return 0;744 }746 int747 e_nop(void)748 {749 return 0;750 }