n4.c - 9base - revived minimalist port of Plan 9 userland to Unix | |
git clone git://git.suckless.org/9base | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
n4.c (12317B) | |
--- | |
1 /* | |
2 * troff4.c | |
3 * | |
4 * number registers, conversion, arithmetic | |
5 */ | |
6 | |
7 #include "tdef.h" | |
8 #include "fns.h" | |
9 #include "ext.h" | |
10 | |
11 | |
12 int regcnt = NNAMES; | |
13 int falsef = 0; /* on if inside false branch of if … | |
14 | |
15 #define NHASHSIZE 128 /* must be 2**n */ | |
16 #define NHASH(i) ((i>>6)^i) & (NHASHSIZE-1) | |
17 Numtab *nhash[NHASHSIZE]; | |
18 | |
19 Numtab *numtabp = NULL; | |
20 #define NDELTA 400 | |
21 int ncnt = 0; | |
22 | |
23 void setn(void) | |
24 { | |
25 int i, j, f; | |
26 Tchar ii; | |
27 Uchar *p; | |
28 char buf[NTM]; /* for \n(.S */ | |
29 | |
30 f = nform = 0; | |
31 if ((i = cbits(ii = getach())) == '+') | |
32 f = 1; | |
33 else if (i == '-') | |
34 f = -1; | |
35 else if (ii) /* don't put it back if it's already back (t… | |
36 ch = ii; | |
37 if (falsef) | |
38 f = 0; | |
39 if ((i = getsn()) == 0) | |
40 return; | |
41 p = unpair(i); | |
42 if (p[0] == '.') | |
43 switch (p[1]) { | |
44 case 's': | |
45 i = pts; | |
46 break; | |
47 case 'v': | |
48 i = lss; | |
49 break; | |
50 case 'f': | |
51 i = font; | |
52 break; | |
53 case 'p': | |
54 i = pl; | |
55 break; | |
56 case 't': | |
57 i = findt1(); | |
58 break; | |
59 case 'o': | |
60 i = po; | |
61 break; | |
62 case 'l': | |
63 i = ll; | |
64 break; | |
65 case 'i': | |
66 i = in; | |
67 break; | |
68 case '$': | |
69 i = frame->nargs; | |
70 break; | |
71 case 'A': | |
72 i = ascii; | |
73 break; | |
74 case 'c': | |
75 i = numtabp[CD].val; | |
76 break; | |
77 case 'n': | |
78 i = lastl; | |
79 break; | |
80 case 'a': | |
81 i = ralss; | |
82 break; | |
83 case 'h': | |
84 i = dip->hnl; | |
85 break; | |
86 case 'd': | |
87 if (dip != d) | |
88 i = dip->dnl; | |
89 else | |
90 i = numtabp[NL].val; | |
91 break; | |
92 case 'u': | |
93 i = fi; | |
94 break; | |
95 case 'j': | |
96 i = ad + 2 * admod; | |
97 break; | |
98 case 'w': | |
99 i = widthp; | |
100 break; | |
101 case 'x': | |
102 i = nel; | |
103 break; | |
104 case 'y': | |
105 i = un; | |
106 break; | |
107 case 'T': | |
108 i = dotT; | |
109 break; /* -Tterm used in nroff */ | |
110 case 'V': | |
111 i = VERT; | |
112 break; | |
113 case 'H': | |
114 i = HOR; | |
115 break; | |
116 case 'k': | |
117 i = ne; | |
118 break; | |
119 case 'P': | |
120 i = print; | |
121 break; | |
122 case 'L': | |
123 i = ls; | |
124 break; | |
125 case 'R': /* maximal # of regs that can be addres… | |
126 i = 255*256 - regcnt; | |
127 break; | |
128 case 'z': | |
129 p = unpair(dip->curd); | |
130 *pbp++ = p[1]; /* watch order */ | |
131 *pbp++ = p[0]; | |
132 return; | |
133 case 'b': | |
134 i = bdtab[font]; | |
135 break; | |
136 case 'F': | |
137 cpushback(cfname[ifi]); | |
138 return; | |
139 case 'S': | |
140 buf[0] = j = 0; | |
141 for( i = 0; tabtab[i] != 0 && i < NTAB; i++) { | |
142 if (i > 0) | |
143 buf[j++] = ' '; | |
144 sprintf(&buf[j], "%ld", tabtab[i] & TAB… | |
145 j = strlen(buf); | |
146 if ( tabtab[i] & RTAB) | |
147 sprintf(&buf[j], "uR"); | |
148 else if (tabtab[i] & CTAB) | |
149 sprintf(&buf[j], "uC"); | |
150 else | |
151 sprintf(&buf[j], "uL"); | |
152 j += 2; | |
153 } | |
154 cpushback(buf); | |
155 return; | |
156 default: | |
157 goto s0; | |
158 } | |
159 else { | |
160 s0: | |
161 if ((j = findr(i)) == -1) | |
162 i = 0; | |
163 else { | |
164 i = numtabp[j].val = numtabp[j].val + numtabp[j]… | |
165 nform = numtabp[j].fmt; | |
166 } | |
167 } | |
168 setn1(i, nform, (Tchar) 0); | |
169 } | |
170 | |
171 Tchar numbuf[25]; | |
172 Tchar *numbufp; | |
173 | |
174 int wrc(Tchar i) | |
175 { | |
176 if (numbufp >= &numbuf[24]) | |
177 return(0); | |
178 *numbufp++ = i; | |
179 return(1); | |
180 } | |
181 | |
182 | |
183 | |
184 /* insert into input number i, in format form, with size-font bits bits … | |
185 void setn1(int i, int form, Tchar bits) | |
186 { | |
187 numbufp = numbuf; | |
188 nrbits = bits; | |
189 nform = form; | |
190 fnumb(i, wrc); | |
191 *numbufp = 0; | |
192 pushback(numbuf); | |
193 } | |
194 | |
195 void prnumtab(Numtab *p) | |
196 { | |
197 int i; | |
198 for (i = 0; i < ncnt; i++) | |
199 if (p) | |
200 if (p[i].r != 0) | |
201 fprintf(stderr, "slot %d, %s, val %d\n",… | |
202 else | |
203 fprintf(stderr, "slot %d empty\n", i); | |
204 else | |
205 fprintf(stderr, "slot %d empty\n", i); | |
206 } | |
207 | |
208 void nnspace(void) | |
209 { | |
210 ncnt = sizeof(numtab)/sizeof(Numtab) + NDELTA; | |
211 numtabp = (Numtab *) grow((char *)numtabp, ncnt, sizeof(Numtab)); | |
212 if (numtabp == NULL) { | |
213 ERROR "not enough memory for registers (%d)", ncnt WARN; | |
214 exit(1); | |
215 } | |
216 numtabp = (Numtab *) memcpy((char *)numtabp, (char *)numtab, | |
217 sizeof(numtab)); | |
218 if (numtabp == NULL) { | |
219 ERROR "Cannot initialize registers" WARN; | |
220 exit(1); | |
221 } | |
222 } | |
223 | |
224 void grownumtab(void) | |
225 { | |
226 ncnt += NDELTA; | |
227 numtabp = (Numtab *) grow((char *) numtabp, ncnt, sizeof(Numtab)… | |
228 if (numtabp == NULL) { | |
229 ERROR "Too many number registers (%d)", ncnt WARN; | |
230 done2(04); | |
231 } else { | |
232 memset((char *)(numtabp) + (ncnt - NDELTA) * sizeof(Numt… | |
233 0, NDELTA * sizeof(Numta… | |
234 nrehash(); | |
235 } | |
236 } | |
237 | |
238 void nrehash(void) | |
239 { | |
240 Numtab *p; | |
241 int i; | |
242 | |
243 for (i=0; i<NHASHSIZE; i++) | |
244 nhash[i] = 0; | |
245 for (p=numtabp; p < &numtabp[ncnt]; p++) | |
246 p->link = 0; | |
247 for (p=numtabp; p < &numtabp[ncnt]; p++) { | |
248 if (p->r == 0) | |
249 continue; | |
250 i = NHASH(p->r); | |
251 p->link = nhash[i]; | |
252 nhash[i] = p; | |
253 } | |
254 } | |
255 | |
256 void nunhash(Numtab *rp) | |
257 { | |
258 Numtab *p; | |
259 Numtab **lp; | |
260 | |
261 if (rp->r == 0) | |
262 return; | |
263 lp = &nhash[NHASH(rp->r)]; | |
264 p = *lp; | |
265 while (p) { | |
266 if (p == rp) { | |
267 *lp = p->link; | |
268 p->link = 0; | |
269 return; | |
270 } | |
271 lp = &p->link; | |
272 p = p->link; | |
273 } | |
274 } | |
275 | |
276 int findr(int i) | |
277 { | |
278 Numtab *p; | |
279 int h = NHASH(i); | |
280 | |
281 if (i == 0) | |
282 return(-1); | |
283 a0: | |
284 for (p = nhash[h]; p; p = p->link) | |
285 if (i == p->r) | |
286 return(p - numtabp); | |
287 for (p = numtabp; p < &numtabp[ncnt]; p++) { | |
288 if (p->r == 0) { | |
289 p->r = i; | |
290 p->link = nhash[h]; | |
291 nhash[h] = p; | |
292 regcnt++; | |
293 return(p - numtabp); | |
294 } | |
295 } | |
296 grownumtab(); | |
297 goto a0; | |
298 } | |
299 | |
300 int usedr(int i) /* returns -1 if nr i has never been used */ | |
301 { | |
302 Numtab *p; | |
303 | |
304 if (i == 0) | |
305 return(-1); | |
306 for (p = nhash[NHASH(i)]; p; p = p->link) | |
307 if (i == p->r) | |
308 return(p - numtabp); | |
309 return -1; | |
310 } | |
311 | |
312 | |
313 int fnumb(int i, int (*f)(Tchar)) | |
314 { | |
315 int j; | |
316 | |
317 j = 0; | |
318 if (i < 0) { | |
319 j = (*f)('-' | nrbits); | |
320 i = -i; | |
321 } | |
322 switch (nform) { | |
323 default: | |
324 case '1': | |
325 case 0: | |
326 return decml(i, f) + j; | |
327 case 'i': | |
328 case 'I': | |
329 return roman(i, f) + j; | |
330 case 'a': | |
331 case 'A': | |
332 return abc(i, f) + j; | |
333 } | |
334 } | |
335 | |
336 | |
337 int decml(int i, int (*f)(Tchar)) | |
338 { | |
339 int j, k; | |
340 | |
341 k = 0; | |
342 nform--; | |
343 if ((j = i / 10) || (nform > 0)) | |
344 k = decml(j, f); | |
345 return(k + (*f)((i % 10 + '0') | nrbits)); | |
346 } | |
347 | |
348 | |
349 int roman(int i, int (*f)(Tchar)) | |
350 { | |
351 | |
352 if (!i) | |
353 return((*f)('0' | nrbits)); | |
354 if (nform == 'i') | |
355 return(roman0(i, f, "ixcmz", "vldw")); | |
356 else | |
357 return(roman0(i, f, "IXCMZ", "VLDW")); | |
358 } | |
359 | |
360 | |
361 int roman0(int i, int (*f)(Tchar), char *onesp, char *fivesp) | |
362 { | |
363 int q, rem, k; | |
364 | |
365 if (!i) | |
366 return(0); | |
367 k = roman0(i / 10, f, onesp + 1, fivesp + 1); | |
368 q = (i = i % 10) / 5; | |
369 rem = i % 5; | |
370 if (rem == 4) { | |
371 k += (*f)(*onesp | nrbits); | |
372 if (q) | |
373 i = *(onesp + 1); | |
374 else | |
375 i = *fivesp; | |
376 return(k += (*f)(i | nrbits)); | |
377 } | |
378 if (q) | |
379 k += (*f)(*fivesp | nrbits); | |
380 while (--rem >= 0) | |
381 k += (*f)(*onesp | nrbits); | |
382 return(k); | |
383 } | |
384 | |
385 | |
386 int abc(int i, int (*f)(Tchar)) | |
387 { | |
388 if (!i) | |
389 return((*f)('0' | nrbits)); | |
390 else | |
391 return(abc0(i - 1, f)); | |
392 } | |
393 | |
394 | |
395 int abc0(int i, int (*f)(Tchar)) | |
396 { | |
397 int j, k; | |
398 | |
399 k = 0; | |
400 if (j = i / 26) | |
401 k = abc0(j - 1, f); | |
402 return(k + (*f)((i % 26 + nform) | nrbits)); | |
403 } | |
404 | |
405 long atoi0(void) | |
406 { | |
407 int c, k, cnt; | |
408 Tchar ii; | |
409 long i, acc; | |
410 | |
411 acc = 0; | |
412 nonumb = 0; | |
413 cnt = -1; | |
414 a0: | |
415 cnt++; | |
416 ii = getch(); | |
417 c = cbits(ii); | |
418 switch (c) { | |
419 default: | |
420 ch = ii; | |
421 if (cnt) | |
422 break; | |
423 case '+': | |
424 i = ckph(); | |
425 if (nonumb) | |
426 break; | |
427 acc += i; | |
428 goto a0; | |
429 case '-': | |
430 i = ckph(); | |
431 if (nonumb) | |
432 break; | |
433 acc -= i; | |
434 goto a0; | |
435 case '*': | |
436 i = ckph(); | |
437 if (nonumb) | |
438 break; | |
439 acc *= i; | |
440 goto a0; | |
441 case '/': | |
442 i = ckph(); | |
443 if (nonumb) | |
444 break; | |
445 if (i == 0) { | |
446 flusho(); | |
447 ERROR "divide by zero." WARN; | |
448 acc = 0; | |
449 } else | |
450 acc /= i; | |
451 goto a0; | |
452 case '%': | |
453 i = ckph(); | |
454 if (nonumb) | |
455 break; | |
456 acc %= i; | |
457 goto a0; | |
458 case '&': /*and*/ | |
459 i = ckph(); | |
460 if (nonumb) | |
461 break; | |
462 if ((acc > 0) && (i > 0)) | |
463 acc = 1; | |
464 else | |
465 acc = 0; | |
466 goto a0; | |
467 case ':': /*or*/ | |
468 i = ckph(); | |
469 if (nonumb) | |
470 break; | |
471 if ((acc > 0) || (i > 0)) | |
472 acc = 1; | |
473 else | |
474 acc = 0; | |
475 goto a0; | |
476 case '=': | |
477 if (cbits(ii = getch()) != '=') | |
478 ch = ii; | |
479 i = ckph(); | |
480 if (nonumb) { | |
481 acc = 0; | |
482 break; | |
483 } | |
484 if (i == acc) | |
485 acc = 1; | |
486 else | |
487 acc = 0; | |
488 goto a0; | |
489 case '>': | |
490 k = 0; | |
491 if (cbits(ii = getch()) == '=') | |
492 k++; | |
493 else | |
494 ch = ii; | |
495 i = ckph(); | |
496 if (nonumb) { | |
497 acc = 0; | |
498 break; | |
499 } | |
500 if (acc > (i - k)) | |
501 acc = 1; | |
502 else | |
503 acc = 0; | |
504 goto a0; | |
505 case '<': | |
506 k = 0; | |
507 if (cbits(ii = getch()) == '=') | |
508 k++; | |
509 else | |
510 ch = ii; | |
511 i = ckph(); | |
512 if (nonumb) { | |
513 acc = 0; | |
514 break; | |
515 } | |
516 if (acc < (i + k)) | |
517 acc = 1; | |
518 else | |
519 acc = 0; | |
520 goto a0; | |
521 case ')': | |
522 break; | |
523 case '(': | |
524 acc = atoi0(); | |
525 goto a0; | |
526 } | |
527 return(acc); | |
528 } | |
529 | |
530 | |
531 long ckph(void) | |
532 { | |
533 Tchar i; | |
534 long j; | |
535 | |
536 if (cbits(i = getch()) == '(') | |
537 j = atoi0(); | |
538 else { | |
539 j = atoi1(i); | |
540 } | |
541 return(j); | |
542 } | |
543 | |
544 | |
545 /* | |
546 * print error about illegal numeric argument; | |
547 */ | |
548 void prnumerr(void) | |
549 { | |
550 char err_buf[40]; | |
551 static char warn[] = "Numeric argument expected"; | |
552 int savcd = numtabp[CD].val; | |
553 | |
554 if (numerr.type == RQERR) | |
555 sprintf(err_buf, "%c%s: %s", nb ? cbits(c2) : cbits(cc), | |
556 unpair(numerr.req), warn… | |
557 else | |
558 sprintf(err_buf, "\\%c'%s': %s", numerr.esc, &numerr.esc… | |
559 … | |
560 if (frame != stk) /* uncertainty correction */ | |
561 numtabp[CD].val--; | |
562 ERROR "%s", err_buf WARN; | |
563 numtabp[CD].val = savcd; | |
564 } | |
565 | |
566 | |
567 long atoi1(Tchar ii) | |
568 { | |
569 int i, j, digits; | |
570 double acc; /* this is the only double in troff! */ | |
571 int neg, abs, field, decpnt; | |
572 extern int ifnum; | |
573 | |
574 | |
575 neg = abs = field = decpnt = digits = 0; | |
576 acc = 0; | |
577 for (;;) { | |
578 i = cbits(ii); | |
579 switch (i) { | |
580 default: | |
581 break; | |
582 case '+': | |
583 ii = getch(); | |
584 continue; | |
585 case '-': | |
586 neg = 1; | |
587 ii = getch(); | |
588 continue; | |
589 case '|': | |
590 abs = 1 + neg; | |
591 neg = 0; | |
592 ii = getch(); | |
593 continue; | |
594 } | |
595 break; | |
596 } | |
597 a1: | |
598 while (i >= '0' && i <= '9') { | |
599 field++; | |
600 digits++; | |
601 acc = 10 * acc + i - '0'; | |
602 ii = getch(); | |
603 i = cbits(ii); | |
604 } | |
605 if (i == '.' && !decpnt++) { | |
606 field++; | |
607 digits = 0; | |
608 ii = getch(); | |
609 i = cbits(ii); | |
610 goto a1; | |
611 } | |
612 if (!field) { | |
613 ch = ii; | |
614 goto a2; | |
615 } | |
616 switch (i) { | |
617 case 'u': | |
618 i = j = 1; /* should this be related to HOR?? */ | |
619 break; | |
620 case 'v': /*VSs - vert spacing*/ | |
621 j = lss; | |
622 i = 1; | |
623 break; | |
624 case 'm': /*Ems*/ | |
625 j = EM; | |
626 i = 1; | |
627 break; | |
628 case 'n': /*Ens*/ | |
629 j = EM; | |
630 if (TROFF) | |
631 i = 2; | |
632 else | |
633 i = 1; /*Same as Ems in NROFF*/ | |
634 break; | |
635 case 'p': /*Points*/ | |
636 j = INCH; | |
637 i = 72; | |
638 break; | |
639 case 'i': /*Inches*/ | |
640 j = INCH; | |
641 i = 1; | |
642 break; | |
643 case 'c': /*Centimeters*/ | |
644 /* if INCH is too big, this will overflow */ | |
645 j = INCH * 50; | |
646 i = 127; | |
647 break; | |
648 case 'P': /*Picas*/ | |
649 j = INCH; | |
650 i = 6; | |
651 break; | |
652 default: | |
653 j = dfact; | |
654 ch = ii; | |
655 i = dfactd; | |
656 } | |
657 if (neg) | |
658 acc = -acc; | |
659 if (!noscale) { | |
660 acc = (acc * j) / i; | |
661 } | |
662 if (field != digits && digits > 0) | |
663 while (digits--) | |
664 acc /= 10; | |
665 if (abs) { | |
666 if (dip != d) | |
667 j = dip->dnl; | |
668 else | |
669 j = numtabp[NL].val; | |
670 if (!vflag) { | |
671 j = numtabp[HP].val; | |
672 } | |
673 if (abs == 2) | |
674 j = -j; | |
675 acc -= j; | |
676 } | |
677 a2: | |
678 nonumb = (!field || field == decpnt); | |
679 if (nonumb && (trace & TRNARGS) && !ismot(ii) && !nlflg && !ifnu… | |
680 if (cbits(ii) != RIGHT ) /* Too painful to do right */ | |
681 prnumerr(); | |
682 } | |
683 return(acc); | |
684 } | |
685 | |
686 | |
687 void caserr(void) | |
688 { | |
689 int i, j; | |
690 Numtab *p; | |
691 | |
692 lgf++; | |
693 while (!skip() && (i = getrq()) ) { | |
694 j = usedr(i); | |
695 if (j < 0) | |
696 continue; | |
697 p = &numtabp[j]; | |
698 nunhash(p); | |
699 p->r = p->val = p->inc = p->fmt = 0; | |
700 regcnt--; | |
701 } | |
702 } | |
703 | |
704 /* | |
705 * .nr request; if tracing, don't check optional | |
706 * 2nd argument because tbl generates .in 1.5n | |
707 */ | |
708 void casenr(void) | |
709 { | |
710 int i, j; | |
711 int savtr = trace; | |
712 | |
713 lgf++; | |
714 skip(); | |
715 if ((i = findr(getrq())) == -1) | |
716 goto rtn; | |
717 skip(); | |
718 j = inumb(&numtabp[i].val); | |
719 if (nonumb) | |
720 goto rtn; | |
721 numtabp[i].val = j; | |
722 skip(); | |
723 trace = 0; | |
724 j = atoi0(); /* BUG??? */ | |
725 trace = savtr; | |
726 if (nonumb) | |
727 goto rtn; | |
728 numtabp[i].inc = j; | |
729 rtn: | |
730 return; | |
731 } | |
732 | |
733 void caseaf(void) | |
734 { | |
735 int i, k; | |
736 Tchar j; | |
737 | |
738 lgf++; | |
739 if (skip() || !(i = getrq()) || skip()) | |
740 return; | |
741 k = 0; | |
742 j = getch(); | |
743 if (!isalpha(cbits(j))) { | |
744 ch = j; | |
745 while ((j = cbits(getch())) >= '0' && j <= '9') | |
746 k++; | |
747 } | |
748 if (!k) | |
749 k = j; | |
750 numtabp[findr(i)].fmt = k; /* was k & BYTEMASK */ | |
751 } | |
752 | |
753 void setaf(void) /* return format of number register */ | |
754 { | |
755 int i, j; | |
756 | |
757 i = usedr(getsn()); | |
758 if (i == -1) | |
759 return; | |
760 if (numtabp[i].fmt > 20) /* it was probably a, A, i or I … | |
761 *pbp++ = numtabp[i].fmt; | |
762 else | |
763 for (j = (numtabp[i].fmt ? numtabp[i].fmt : 1); j; j--) | |
764 *pbp++ = '0'; | |
765 } | |
766 | |
767 | |
768 int vnumb(int *i) | |
769 { | |
770 vflag++; | |
771 dfact = lss; | |
772 res = VERT; | |
773 return(inumb(i)); | |
774 } | |
775 | |
776 | |
777 int hnumb(int *i) | |
778 { | |
779 dfact = EM; | |
780 res = HOR; | |
781 return(inumb(i)); | |
782 } | |
783 | |
784 | |
785 int inumb(int *n) | |
786 { | |
787 int i, j, f; | |
788 Tchar ii; | |
789 | |
790 f = 0; | |
791 if (n) { | |
792 if ((j = cbits(ii = getch())) == '+') | |
793 f = 1; | |
794 else if (j == '-') | |
795 f = -1; | |
796 else | |
797 ch = ii; | |
798 } | |
799 i = atoi0(); | |
800 if (n && f) | |
801 i = *n + f * i; | |
802 i = quant(i, res); | |
803 vflag = 0; | |
804 res = dfactd = dfact = 1; | |
805 if (nonumb) | |
806 i = 0; | |
807 return(i); | |
808 } | |
809 | |
810 | |
811 int quant(int n, int m) | |
812 { | |
813 int i, neg; | |
814 | |
815 neg = 0; | |
816 if (n < 0) { | |
817 neg++; | |
818 n = -n; | |
819 } | |
820 /* better as i = ((n + m/2)/m)*m */ | |
821 i = n / m; | |
822 if (n - m * i > m / 2) | |
823 i += 1; | |
824 i *= m; | |
825 if (neg) | |
826 i = -i; | |
827 return(i); | |
828 } |