├── Makefile ├── README ├── box.c ├── def.c ├── eqn.c ├── eqn.h ├── reg.c ├── sbuf.c ├── src.c └── tok.c /Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | CFLAGS = -Wall -O2 3 | LDFLAGS = 4 | OBJS = eqn.o tok.o src.o def.o box.o reg.o sbuf.o 5 | 6 | all: eqn 7 | %.o: %.c eqn.h 8 | $(CC) -c $(CFLAGS) $< 9 | eqn: $(OBJS) 10 | $(CC) -o $@ $(OBJS) $(LDFLAGS) 11 | clean: 12 | rm -f *.o eqn 13 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | NEATEQN 2 | ======= 3 | 4 | Neateqn is an eqn Neatroff preprocessor for typesetting mathematics. 5 | It implements most of the extensions introduced by Groff's eqn 6 | preprocessor. More information about Neateqn and Neatroff is 7 | available at http://litcave.rudi.ir/. 8 | 9 | The following lines instruct Neateqn to use Computer Modern font for 10 | brackets and radicals: 11 | 12 | .fp 11 CMSY CMSY10 13 | .fp 12 CMEX CMEX10 14 | .fspecial R CMSY CMEX 15 | 16 | You can also redefine other symbols to use Computer Modern glyphs, 17 | like those for summation and product: 18 | 19 | .EQ 20 | define sum @{vcenter roman "\N'summationdisplay'"}@ 21 | define tsum @{vcenter roman "\N'summationtext'"}@ 22 | define prod @{vcenter roman "\N'productdisplay'"}@ 23 | define tprod @{vcenter roman "\N'producttext'"}@ 24 | .EN 25 | 26 | Neateqn uses TeX's text styles for inline equations, which make inline 27 | equations more compact. However, it does not provide smallover, which 28 | is available in Groff's eqn. Instead, in neateqn one can define a 29 | more general macro like the following: 30 | 31 | .EQ 32 | define small @size -4@ 33 | .EN 34 | 35 | Which can be used as $tsum sub i=0 sup n small {left ( pile {n above i} right )}$. 36 | -------------------------------------------------------------------------------- /box.c: -------------------------------------------------------------------------------- 1 | /* equation boxes */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "eqn.h" 7 | 8 | struct box *box_alloc(int szreg, int pre, int style) 9 | { 10 | struct box *box = malloc(sizeof(*box)); 11 | memset(box, 0, sizeof(*box)); 12 | sbuf_init(&box->raw); 13 | box->szreg = szreg; 14 | box->atoms = 0; 15 | box->style = style; 16 | if (pre) 17 | box->tcur = pre; 18 | return box; 19 | } 20 | 21 | void box_free(struct box *box) 22 | { 23 | if (box->reg) 24 | sregrm(box->reg); 25 | if (box->szown) 26 | nregrm(box->szreg); 27 | sbuf_done(&box->raw); 28 | free(box); 29 | } 30 | 31 | static void box_put(struct box *box, char *s) 32 | { 33 | sbuf_append(&box->raw, s); 34 | if (box->reg) 35 | printf(".as %s \"%s\n", sregname(box->reg), s); 36 | } 37 | 38 | void box_putf(struct box *box, char *s, ...) 39 | { 40 | char buf[LNLEN]; 41 | va_list ap; 42 | va_start(ap, s); 43 | vsnprintf(buf, sizeof(buf), s, ap); 44 | va_end(ap); 45 | box_put(box, buf); 46 | } 47 | 48 | char *box_buf(struct box *box) 49 | { 50 | return sbuf_buf(&box->raw); 51 | } 52 | 53 | /* change box's point size; return the number register storing it */ 54 | int box_size(struct box *box, char *val) 55 | { 56 | int szreg = box->szreg; 57 | if (!val || !*val) 58 | return szreg; 59 | if (!box->szown) { 60 | box->szown = 1; 61 | box->szreg = nregmk(); 62 | } 63 | if (val[0] == '-' || val[0] == '+') 64 | printf(".nr %s %s%s\n", nregname(box->szreg), nreg(szreg), val); 65 | else 66 | printf(".nr %s %s\n", nregname(box->szreg), val); 67 | return box->szreg; 68 | } 69 | 70 | void box_move(struct box *box, int dy, int dx) 71 | { 72 | if (dy) 73 | box_putf(box, "\\v'%du*%sp/100u'", dy, nreg(box->szreg)); 74 | if (dx) 75 | box_putf(box, "\\h'%du*%sp/100u'", dx, nreg(box->szreg)); 76 | } 77 | 78 | /* T_ORD, T_BIGOP, T_BINOP, T_RELOP, T_LEFT, T_RIGHT, T_PUNC, T_INNER */ 79 | static int spacing[][8] = { 80 | {0, 1, 2, 3, 0, 0, 0, 1}, 81 | {1, 1, 0, 3, 0, 0, 0, 1}, 82 | {2, 2, 0, 0, 2, 0, 0, 2}, 83 | {3, 3, 0, 0, 3, 0, 0, 3}, 84 | {0, 0, 0, 0, 0, 0, 0, 0}, 85 | {0, 1, 2, 3, 0, 0, 0, 1}, 86 | {1, 1, 0, 1, 1, 1, 1, 1}, 87 | {1, 1, 2, 3, 1, 0, 1, 1}, 88 | }; 89 | 90 | /* return the amount of automatic spacing before adding the given token */ 91 | static int eqn_gaps(struct box *box, int cur) 92 | { 93 | int s = 0; 94 | int a1 = T_ATOM(box->tcur); /* previous atom */ 95 | int a2 = T_ATOM(cur); /* current atom */ 96 | if (!TS_SZ(box->style) && a1 && a2) 97 | s = spacing[T_ATOMIDX(a1)][T_ATOMIDX(a2)]; 98 | if (s == 3) 99 | return S_S3; 100 | if (s == 2) 101 | return S_S2; 102 | return s ? S_S1 : 0; 103 | } 104 | 105 | /* call just before inserting a non-italic character */ 106 | static void box_italiccorrection(struct box *box) 107 | { 108 | if (box->atoms && (box->tcur & T_ITALIC)) 109 | box_put(box, "\\/"); 110 | box->tcur &= ~T_ITALIC; 111 | } 112 | 113 | static int box_fixatom(int cur, int pre) 114 | { 115 | if (cur == T_BINOP && (!pre || pre == T_RELOP || pre == T_BIGOP || 116 | pre == T_LEFT || pre == T_PUNC)) 117 | return T_ORD; 118 | if (cur == T_RELOP && (!pre || pre == T_LEFT)) 119 | return T_ORD; 120 | return cur; 121 | } 122 | 123 | /* call before inserting a token with box_put() and box_putf() */ 124 | static void box_beforeput(struct box *box, int type, int breakable) 125 | { 126 | int autogaps; /* automatically inserted space before this token */ 127 | if (box->atoms) { 128 | autogaps = eqn_gaps(box, T_ATOM(type)); 129 | if (!(type & T_ITALIC)) 130 | box_italiccorrection(box); 131 | if (autogaps && type != T_GAP && box->tcur != T_GAP) { 132 | box_italiccorrection(box); 133 | if (breakable) { /* enlarge a space to match autogaps */ 134 | box_putf(box, "\\s[\\En(.s*%du*%sp/100u/\\w' 'u]\\j'%d' \\s0", 135 | autogaps, nreg(box->szreg), 136 | def_brcost(T_ATOM(box->tcur))); 137 | } else { 138 | box_putf(box, "\\h'%du*%sp/100u'", 139 | autogaps, nreg(box->szreg)); 140 | } 141 | } 142 | } 143 | if (box->tomark) { 144 | printf(".nr %s 0\\w'%s'\n", box->tomark, box_toreg(box)); 145 | box->tomark = NULL; 146 | } 147 | } 148 | 149 | /* call after inserting a token with box_put() and box_putf() */ 150 | static void box_afterput(struct box *box, int type) 151 | { 152 | box->atoms++; 153 | box->tcur = T_FONT(type) | box_fixatom(T_TOK(type), T_TOK(box->tcur)); 154 | if (!box->tbeg) 155 | box->tbeg = box->tcur; 156 | } 157 | 158 | /* insert s with the given type */ 159 | void box_puttext(struct box *box, int type, char *s, ...) 160 | { 161 | char buf[LNLEN]; 162 | va_list ap; 163 | va_start(ap, s); 164 | vsnprintf(buf, sizeof(buf), s, ap); 165 | va_end(ap); 166 | box_beforeput(box, type, 0); 167 | if (!(box->tcur & T_ITALIC) && (type & T_ITALIC)) 168 | box_put(box, "\\,"); 169 | box_put(box, buf); 170 | box_afterput(box, type); 171 | } 172 | 173 | /* append sub to box */ 174 | void box_merge(struct box *box, struct box *sub, int breakable) 175 | { 176 | if (box_empty(sub)) 177 | return; 178 | box_beforeput(box, sub->tbeg, breakable); 179 | box_toreg(box); 180 | box_put(box, box_toreg(sub)); 181 | if (!box->tbeg) 182 | box->tbeg = sub->tbeg; 183 | /* fix atom type only if merging a single atom */ 184 | if (sub->atoms == 1) { 185 | box_afterput(box, sub->tcur); 186 | } else { 187 | box->tcur = sub->tcur; 188 | box->atoms += sub->atoms; 189 | } 190 | } 191 | 192 | /* put the maximum of number registers a and b into register dst */ 193 | static void roff_max(int dst, int a, int b) 194 | { 195 | printf(".ie %s>=%s .nr %s 0+%s\n", 196 | nreg(a), nreg(b), nregname(dst), nreg(a)); 197 | printf(".el .nr %s 0+%s\n", nregname(dst), nreg(b)); 198 | } 199 | 200 | /* return the width, height and depth of a string */ 201 | static void tok_dim(char *s, int wd, int ht, int dp) 202 | { 203 | printf(".nr %s 0\\w'%s'\n", nregname(wd), s); 204 | if (ht) 205 | printf(".nr %s 0-\\n[bbury]\n", nregname(ht)); 206 | if (dp) 207 | printf(".nr %s 0\\n[bblly]\n", nregname(dp)); 208 | } 209 | 210 | static int box_suprise(struct box *box) 211 | { 212 | if (TS_0(box->style)) 213 | return e_sup3; 214 | return box->style == TS_D ? e_sup1 : e_sup2; 215 | } 216 | 217 | void box_sub(struct box *box, struct box *sub, struct box *sup) 218 | { 219 | int box_wd = nregmk(); 220 | int box_wdnoic = nregmk(); 221 | int box_dp = nregmk(); 222 | int box_ht = nregmk(); 223 | int sub_wd = nregmk(); 224 | int sup_wd = nregmk(); 225 | int all_wd = nregmk(); 226 | int sup_dp = nregmk(); 227 | int sub_ht = nregmk(); 228 | int sup_rise = nregmk(); 229 | int sub_fall = nregmk(); 230 | int tmp_18e = nregmk(); 231 | int sub_cor = nregmk(); 232 | if (sub) 233 | box_italiccorrection(sub); 234 | if (sup) 235 | box_italiccorrection(sup); 236 | if (sub) 237 | tok_dim(box_toreg(box), box_wdnoic, 0, 0); 238 | box_italiccorrection(box); 239 | printf(".ps %s\n", nreg(box->szreg)); 240 | tok_dim(box_toreg(box), box_wd, box_ht, box_dp); 241 | box_putf(box, "\\h'5m/100u'"); 242 | if (sup) { 243 | tok_dim(box_toreg(sup), sup_wd, 0, sup_dp); 244 | /* 18a */ 245 | printf(".nr %s 0%su-(%dm/100u)\n", 246 | nregname(sup_rise), nreg(box_ht), e_supdrop); 247 | /* 18c */ 248 | printf(".if %s<(%dm/100u) .nr %s (%dm/100u)\n", 249 | nreg(sup_rise), box_suprise(box), 250 | nregname(sup_rise), box_suprise(box)); 251 | printf(".if %s<(%s+(%dm/100u/4)) .nr %s 0%s+(%dm/100u/4)\n", 252 | nreg(sup_rise), nreg(sup_dp), e_xheight, 253 | nregname(sup_rise), nreg(sup_dp), e_xheight); 254 | } 255 | if (sub) { 256 | tok_dim(box_toreg(sub), sub_wd, sub_ht, 0); 257 | /* 18a */ 258 | printf(".nr %s 0%su+(%dm/100u)\n", 259 | nregname(sub_fall), nreg(box_dp), e_subdrop); 260 | } 261 | if (sub && !sup) { 262 | /* 18b */ 263 | printf(".if %s<(%dm/100u) .nr %s (%dm/100u)\n", 264 | nreg(sub_fall), e_sub1, 265 | nregname(sub_fall), e_sub1); 266 | printf(".if %s<(%s-(%dm/100u*4/5)) .nr %s 0%s-(%dm/100u*4/5)\n", 267 | nreg(sub_fall), nreg(sub_ht), e_xheight, 268 | nregname(sub_fall), nreg(sub_ht), e_xheight); 269 | } 270 | if (sub && sup) { 271 | /* 18d */ 272 | printf(".if %s<(%dm/100u) .nr %s (%dm/100u)\n", 273 | nreg(sub_fall), e_sub2, 274 | nregname(sub_fall), e_sub2); 275 | /* 18e */ 276 | printf(".if (%s-%s)-(%s-%s)<(%dm/100u*4) \\{\\\n", 277 | nreg(sup_rise), nreg(sup_dp), 278 | nreg(sub_ht), nreg(sub_fall), e_rulethickness); 279 | printf(".nr %s (%dm/100u*4)+%s-(%s-%s)\n", 280 | nregname(sub_fall), e_rulethickness, 281 | nreg(sub_ht), nreg(sup_rise), nreg(sup_dp)); 282 | printf(".nr %s (%dm/100u*4/5)-(%s-%s)\n", 283 | nregname(tmp_18e), e_xheight, 284 | nreg(sup_rise), nreg(sup_dp)); 285 | printf(".if %s>0 .nr %s +%s\n", 286 | nreg(tmp_18e), nregname(sup_rise), nreg(tmp_18e)); 287 | printf(".if %s>0 .nr %s -%s \\}\n", 288 | nreg(tmp_18e), nregname(sub_fall), nreg(tmp_18e)); 289 | } 290 | /* writing the superscript */ 291 | if (sup) { 292 | box_putf(box, "\\v'-%su'%s\\v'%su'", 293 | nreg(sup_rise), box_toreg(sup), nreg(sup_rise)); 294 | if (sub) 295 | box_putf(box, "\\h'-%su'", nreg(sup_wd)); 296 | } 297 | /* writing the subscript */ 298 | if (sub) { 299 | /* subscript correction */ 300 | printf(".nr %s (%s-%s)\n", nregname(sub_cor), 301 | nreg(box_wd), nreg(box_wdnoic)); 302 | printf(".if %s>0 .nr %s (%s+%s)*(%s-%s)/%s\n", 303 | nreg(box_ht), nregname(sub_cor), 304 | nreg(box_ht), nreg(sub_fall), 305 | nreg(box_wd), nreg(box_wdnoic), nreg(box_ht)); 306 | printf(".nr %s -%s\n", nregname(sub_wd), nreg(sub_cor)); 307 | box_putf(box, "\\h'-%su'", nreg(sub_cor)); 308 | box_putf(box, "\\v'%su'%s\\v'-%su'", 309 | nreg(sub_fall), box_toreg(sub), nreg(sub_fall)); 310 | if (sup) { 311 | box_putf(box, "\\h'-%su'", nreg(sub_wd)); 312 | roff_max(all_wd, sub_wd, sup_wd); 313 | box_putf(box, "\\h'+%su'", nreg(all_wd)); 314 | } 315 | } 316 | box_putf(box, "\\h'%dm/100u'", e_scriptspace); 317 | nregrm(box_wd); 318 | nregrm(box_wdnoic); 319 | nregrm(box_dp); 320 | nregrm(box_ht); 321 | nregrm(sub_wd); 322 | nregrm(sup_wd); 323 | nregrm(all_wd); 324 | nregrm(sup_dp); 325 | nregrm(sub_ht); 326 | nregrm(sup_rise); 327 | nregrm(sub_fall); 328 | nregrm(tmp_18e); 329 | nregrm(sub_cor); 330 | } 331 | 332 | void box_from(struct box *box, struct box *lim, struct box *llim, struct box *ulim) 333 | { 334 | int lim_wd = nregmk(); /* box's width */ 335 | int lim_ht = nregmk(); /* box's height */ 336 | int lim_dp = nregmk(); /* box's depth */ 337 | int llim_wd = nregmk(); /* llim's width */ 338 | int ulim_wd = nregmk(); /* ulim's width */ 339 | int ulim_dp = nregmk(); /* ulim's depth */ 340 | int llim_ht = nregmk(); /* llim's height */ 341 | int ulim_rise = nregmk(); /* the position of ulim */ 342 | int llim_fall = nregmk(); /* the position of llim */ 343 | int all_wd = nregmk(); /* the width of all */ 344 | box_italiccorrection(lim); 345 | box_beforeput(box, T_BIGOP, 0); 346 | tok_dim(box_toreg(lim), lim_wd, lim_ht, lim_dp); 347 | printf(".ps %s\n", nreg(box->szreg)); 348 | if (ulim) 349 | tok_dim(box_toreg(ulim), ulim_wd, 0, ulim_dp); 350 | if (llim) 351 | tok_dim(box_toreg(llim), llim_wd, llim_ht, 0); 352 | if (ulim && llim) 353 | roff_max(all_wd, llim_wd, ulim_wd); 354 | else 355 | printf(".nr %s %s\n", nregname(all_wd), 356 | ulim ? nreg(ulim_wd) : nreg(llim_wd)); 357 | printf(".if %s>%s .nr %s 0%s\n", 358 | nreg(lim_wd), nreg(all_wd), 359 | nregname(all_wd), nreg(lim_wd)); 360 | box_putf(box, "\\h'%su-%su/2u'", nreg(all_wd), nreg(lim_wd)); 361 | box_merge(box, lim, 0); 362 | box_putf(box, "\\h'-%su/2u'", nreg(lim_wd)); 363 | if (ulim) { 364 | /* 13a */ 365 | printf(".nr %s (%dm/100u)-%s\n", 366 | nregname(ulim_rise), e_bigopspacing3, nreg(ulim_dp)); 367 | printf(".if %s<(%dm/100u) .nr %s (%dm/100u)\n", 368 | nreg(ulim_rise), e_bigopspacing1, 369 | nregname(ulim_rise), e_bigopspacing1); 370 | printf(".nr %s +%s+%s\n", 371 | nregname(ulim_rise), nreg(lim_ht), nreg(ulim_dp)); 372 | box_putf(box, "\\h'-%su/2u'\\v'-%su'%s\\v'%su'\\h'-%su/2u'", 373 | nreg(ulim_wd), nreg(ulim_rise), box_toreg(ulim), 374 | nreg(ulim_rise), nreg(ulim_wd)); 375 | } 376 | if (llim) { 377 | /* 13a */ 378 | printf(".nr %s (%dm/100u)-%s\n", 379 | nregname(llim_fall), e_bigopspacing4, nreg(llim_ht)); 380 | printf(".if %s<(%dm/100u) .nr %s (%dm/100u)\n", 381 | nreg(llim_fall), e_bigopspacing2, 382 | nregname(llim_fall), e_bigopspacing2); 383 | printf(".nr %s +%s+%s\n", 384 | nregname(llim_fall), nreg(lim_dp), nreg(llim_ht)); 385 | box_putf(box, "\\h'-%su/2u'\\v'%su'%s\\v'-%su'\\h'-%su/2u'", 386 | nreg(llim_wd), nreg(llim_fall), box_toreg(llim), 387 | nreg(llim_fall), nreg(llim_wd)); 388 | } 389 | box_putf(box, "\\h'%su/2u'", nreg(all_wd)); 390 | box_afterput(box, T_BIGOP); 391 | nregrm(lim_wd); 392 | nregrm(lim_ht); 393 | nregrm(lim_dp); 394 | nregrm(llim_wd); 395 | nregrm(ulim_wd); 396 | nregrm(ulim_dp); 397 | nregrm(llim_ht); 398 | nregrm(ulim_rise); 399 | nregrm(llim_fall); 400 | nregrm(all_wd); 401 | } 402 | 403 | /* return the width of s; len is the height plus depth */ 404 | static void tok_len(char *s, int wd, int len, int ht, int dp) 405 | { 406 | printf(".nr %s 0\\w'%s'\n", nregname(wd), s); 407 | if (len) 408 | printf(".nr %s 0\\n[bblly]-\\n[bbury]-2\n", nregname(len)); 409 | if (dp) 410 | printf(".nr %s 0\\n[bblly]-1\n", nregname(dp)); 411 | if (ht) 412 | printf(".nr %s 0-\\n[bbury]-1\n", nregname(ht)); 413 | } 414 | 415 | /* len[0]: width, len[1]: vertical length, len[2]: height, len[3]: depth */ 416 | static void blen_mk(char *s, int len[4]) 417 | { 418 | int i; 419 | for (i = 0; i < 4; i++) 420 | len[i] = nregmk(); 421 | tok_len(s, len[0], len[1], len[2], len[3]); 422 | } 423 | 424 | /* free the registers allocated with blen_mk() */ 425 | static void blen_rm(int len[4]) 426 | { 427 | int i; 428 | for (i = 0; i < 4; i++) 429 | nregrm(len[i]); 430 | } 431 | 432 | /* build a fraction; the correct font should be set up beforehand */ 433 | void box_over(struct box *box, struct box *num, struct box *den) 434 | { 435 | int num_wd = nregmk(); 436 | int num_dp = nregmk(); 437 | int den_wd = nregmk(); 438 | int den_ht = nregmk(); 439 | int all_wd = nregmk(); 440 | int num_rise = nregmk(); 441 | int den_fall = nregmk(); 442 | int bar_wd = nregmk(); 443 | int bar_dp = nregmk(); 444 | int bar_ht = nregmk(); 445 | int bar_fall = nregmk(); 446 | int tmp_15d = nregmk(); 447 | int bargap = (TS_DX(box->style) ? 7 : 3) * e_rulethickness / 2; 448 | box_beforeput(box, T_INNER, 0); 449 | box_italiccorrection(num); 450 | box_italiccorrection(den); 451 | tok_dim(box_toreg(num), num_wd, 0, num_dp); 452 | tok_dim(box_toreg(den), den_wd, den_ht, 0); 453 | roff_max(all_wd, num_wd, den_wd); 454 | printf(".ps %s\n", nreg(box->szreg)); 455 | tok_len("\\(ru", bar_wd, 0, bar_ht, bar_dp); 456 | /* 15b */ 457 | printf(".nr %s 0%dm/100u\n", 458 | nregname(num_rise), TS_DX(box->style) ? e_num1 : e_num2); 459 | printf(".nr %s 0%dm/100u\n", 460 | nregname(den_fall), TS_DX(box->style) ? e_denom1 : e_denom2); 461 | /* 15d */ 462 | printf(".nr %s (%s-%s)-((%dm/100u)+(%dm/100u/2))\n", 463 | nregname(tmp_15d), nreg(num_rise), nreg(num_dp), 464 | e_axisheight, e_rulethickness); 465 | printf(".if %s<(%dm/100u) .nr %s +(%dm/100u)-%s\n", 466 | nreg(tmp_15d), bargap, nregname(num_rise), 467 | bargap, nreg(tmp_15d)); 468 | printf(".nr %s ((%dm/100u)-(%dm/100u/2))-(%s-%s)\n", 469 | nregname(tmp_15d), e_axisheight, e_rulethickness, 470 | nreg(den_ht), nreg(den_fall)); 471 | printf(".if %s<(%dm/100u) .nr %s +(%dm/100u)-%s\n", 472 | nreg(tmp_15d), bargap, nregname(den_fall), 473 | bargap, nreg(tmp_15d)); 474 | /* calculating the vertical position of the bar */ 475 | printf(".nr %s 0-%s+%s/2-(%dm/100u)\n", 476 | nregname(bar_fall), nreg(bar_dp), 477 | nreg(bar_ht), e_axisheight); 478 | /* making the bar longer */ 479 | printf(".nr %s +2*(%dm/100u)\n", 480 | nregname(all_wd), e_overhang); 481 | /* null delimiter space */ 482 | box_putf(box, "\\h'%sp*%du/100u'",nreg(box->szreg), e_nulldelim); 483 | /* drawing the bar */ 484 | box_putf(box, "\\v'%su'\\f[\\n(.f]\\s[%s]\\l'%su'\\v'-%su'\\h'-%su/2u'", 485 | nreg(bar_fall), nreg(box->szreg), nreg(all_wd), 486 | nreg(bar_fall), nreg(all_wd)); 487 | /* output the numerator */ 488 | box_putf(box, "\\h'-%su/2u'", nreg(num_wd)); 489 | box_putf(box, "\\v'-%su'%s\\v'%su'", 490 | nreg(num_rise), box_toreg(num), nreg(num_rise)); 491 | box_putf(box, "\\h'-%su/2u'", nreg(num_wd)); 492 | /* output the denominator */ 493 | box_putf(box, "\\h'-%su/2u'", nreg(den_wd)); 494 | box_putf(box, "\\v'%su'%s\\v'-%su'", 495 | nreg(den_fall), box_toreg(den), nreg(den_fall)); 496 | box_putf(box, "\\h'(-%su+%su)/2u'", nreg(den_wd), nreg(all_wd)); 497 | box_putf(box, "\\h'%sp*%du/100u'",nreg(box->szreg), e_nulldelim); 498 | box_afterput(box, T_INNER); 499 | box_toreg(box); 500 | nregrm(num_wd); 501 | nregrm(num_dp); 502 | nregrm(den_wd); 503 | nregrm(den_ht); 504 | nregrm(all_wd); 505 | nregrm(num_rise); 506 | nregrm(den_fall); 507 | nregrm(bar_wd); 508 | nregrm(bar_dp); 509 | nregrm(bar_ht); 510 | nregrm(bar_fall); 511 | nregrm(tmp_15d); 512 | } 513 | 514 | /* choose the smallest bracket among br[], large enough for \n(ht+\n(dp */ 515 | static void box_bracketsel(int dst, int ht, int dp, char **br, int any, int both) 516 | { 517 | int i; 518 | for (i = 0; br[i]; i++) { 519 | printf(".if '%s'' ", sreg(dst)); 520 | printf(".if \\w'%s' ", br[i]); /* is this bracket available? */ 521 | if (both) { /* check both the height and the depth */ 522 | printf(".if (%s-(%dm/100)*2)<=(-\\n[bbury]+\\n[bblly]+(%dm/100*2)) ", 523 | nreg(ht), e_rulethickness, e_axisheight); 524 | printf(".if (%s*2)<=(-\\n[bbury]+\\n[bblly]-(%dm/100*2)) ", 525 | nreg(dp), e_axisheight); 526 | } else { 527 | printf(".if (%s+%s)<=(-\\n[bbury]+\\n[bblly]) ", nreg(ht), nreg(dp)); 528 | } 529 | printf(".ds %s \"%s\n", sregname(dst), br[i]); 530 | } 531 | if (any) /* choose the largest bracket, if any is 1 */ 532 | while (--i >= 0) 533 | printf(".if '%s'' .if \\w'%s' .ds %s \"%s\n", 534 | sreg(dst), br[i], sregname(dst), br[i]); 535 | } 536 | 537 | /* build a bracket using the provided pieces */ 538 | static void box_bracketmk(int dst, int len, 539 | char *top, char *mid, char *bot, char *cen) 540 | { 541 | int toplen[4]; 542 | int midlen[4]; 543 | int botlen[4]; 544 | int cenlen[4]; 545 | int mid_cnt = nregmk(); /* number of mid glyphs to insert */ 546 | int mid_cur = nregmk(); /* the number of mid glyphs inserted */ 547 | int cen_pos = nregmk(); 548 | int buildmacro = sregmk(); 549 | blen_mk(top, toplen); 550 | blen_mk(mid, midlen); 551 | blen_mk(bot, botlen); 552 | if (cen) 553 | blen_mk(cen, cenlen); 554 | /* the number of mid tokens necessary to cover sub */ 555 | if (!cen) { 556 | printf(".nr %s %s*2-%s-%s*11/10/%s\n", 557 | nregname(mid_cnt), nreg(len), 558 | nreg(toplen[1]), nreg(botlen[1]), nreg(midlen[1])); 559 | printf(".if %s<0 .nr %s 0\n", nreg(mid_cnt), nregname(mid_cnt)); 560 | } else { /* for brackets with a center like { */ 561 | printf(".nr %s %s-(%s+%s+%s/2)*11/10/%s\n", 562 | nregname(cen_pos), nreg(len), nreg(cenlen[1]), 563 | nreg(toplen[1]), nreg(botlen[1]), nreg(midlen[1])); 564 | printf(".if %s<0 .nr %s 0\n", nreg(cen_pos), nregname(cen_pos)); 565 | printf(".nr %s 0%s*2\n", nregname(mid_cnt), nreg(cen_pos)); 566 | } 567 | /* the macro to create the bracket; escaping backslashes */ 568 | printf(".de %s\n", sregname(buildmacro)); 569 | if (cen) /* inserting cen */ 570 | printf(".if \\%s=\\%s .as %s \"\\v'-\\%su'%s\\h'-\\%su'\\v'-\\%su'\n", 571 | nreg(mid_cur), nreg(cen_pos), sregname(dst), 572 | nreg(cenlen[3]), cen, nreg(cenlen[0]), nreg(cenlen[2])); 573 | printf(".if \\%s<\\%s .as %s \"\\v'-\\%su'%s\\h'-\\%su'\\v'-\\%su'\n", 574 | nreg(mid_cur), nreg(mid_cnt), 575 | sregname(dst), nreg(midlen[3]), 576 | mid, nreg(midlen[0]), nreg(midlen[2])); 577 | printf(".if \\\\n+%s<\\%s .%s\n", 578 | escarg(nregname(mid_cur)), nreg(mid_cnt), sregname(buildmacro)); 579 | printf("..\n"); 580 | /* constructing the bracket */ 581 | printf(".ds %s \"\\v'-%su'%s\\h'-%su'\\v'-%su'\n", 582 | sregname(dst), nreg(botlen[3]), 583 | bot, nreg(botlen[0]), nreg(botlen[2])); 584 | printf(".nr %s 0 1\n", nregname(mid_cur)); 585 | printf(".%s\n", sregname(buildmacro)); 586 | printf(".as %s \"\\v'-%su'%s\\h'-%su'\\v'-%su'\n", 587 | sregname(dst), nreg(toplen[3]), top, 588 | nreg(toplen[0]), nreg(toplen[2])); 589 | /* moving back vertically */ 590 | printf(".as %s \"\\v'%su*%su+%su+%su+%su'\n", 591 | sregname(dst), nreg(mid_cnt), nreg(midlen[1]), nreg(botlen[1]), 592 | nreg(toplen[1]), cen ? nreg(cenlen[1]) : "0"); 593 | /* moving right */ 594 | printf(".as %s \"\\h'%su'\n", 595 | sregname(dst), cen ? nreg(cenlen[0]) : nreg(midlen[0])); 596 | blen_rm(toplen); 597 | blen_rm(midlen); 598 | blen_rm(botlen); 599 | if (cen) 600 | blen_rm(cenlen); 601 | nregrm(mid_cnt); 602 | nregrm(mid_cur); 603 | nregrm(cen_pos); 604 | sregrm(buildmacro); 605 | } 606 | 607 | static void box_bracket(struct box *box, char *brac, int ht, int dp) 608 | { 609 | char *sizes[NSIZES] = {NULL}; 610 | char *top = NULL, *mid = NULL, *bot = NULL, *cen = NULL; 611 | int dst = sregmk(); 612 | int len = nregmk(); 613 | int fall = nregmk(); 614 | int parlen[4]; 615 | roff_max(len, ht, dp); 616 | def_sizes(brac, sizes); 617 | printf(".ds %s \"\n", sregname(dst)); 618 | def_pieces(brac, &top, &mid, &bot, &cen); 619 | box_bracketsel(dst, ht, dp, sizes, !mid, 1); 620 | if (mid) { 621 | printf(".if '%s'' \\{\\\n", sreg(dst)); 622 | box_bracketmk(dst, len, top, mid, bot, cen); 623 | printf(". \\}\n"); 624 | } 625 | /* calculating the total vertical length of the bracket */ 626 | blen_mk(sreg(dst), parlen); 627 | /* calculating the amount the bracket should be moved downwards */ 628 | printf(".nr %s 0-%s+%s/2-(%sp*%du/100u)\n", nregname(fall), 629 | nreg(parlen[3]), nreg(parlen[2]), nreg(box->szreg), e_axisheight); 630 | /* printing the output */ 631 | box_putf(box, "\\f[\\n(.f]\\s[\\n(.s]\\v'%su'%s\\v'-%su'", 632 | nreg(fall), sreg(dst), nreg(fall)); 633 | box_toreg(box); 634 | blen_rm(parlen); 635 | sregrm(dst); 636 | nregrm(len); 637 | nregrm(fall); 638 | } 639 | 640 | static char *bracsign(char *brac, int left) 641 | { 642 | if (brac[0] == 'c' && !strncmp("ceiling", brac, strlen(brac))) 643 | return left ? "\\(lc" : "\\(rc"; 644 | if (brac[0] == 'f' && !strncmp("floor", brac, strlen(brac))) 645 | return left ? "\\(lf" : "\\(rf"; 646 | if (brac[0] == '<' && brac[1] == '\0') 647 | return "\\(la"; 648 | if (brac[0] == '>' && brac[1] == '\0') 649 | return "\\(ra"; 650 | return brac; 651 | } 652 | 653 | /* build large brackets; the correct font should be set up beforehand */ 654 | void box_wrap(struct box *box, struct box *sub, char *left, char *right) 655 | { 656 | int sublen[4]; 657 | blen_mk(box_toreg(sub), sublen); 658 | printf(".ps %s\n", nreg(box->szreg)); 659 | if (left) { 660 | box_beforeput(box, T_LEFT, 0); 661 | box_bracket(box, bracsign(left, 1), sublen[2], sublen[3]); 662 | box_afterput(box, T_LEFT); 663 | } 664 | box_merge(box, sub, 0); 665 | if (right) { 666 | box_beforeput(box, T_RIGHT, 0); 667 | box_bracket(box, bracsign(right, 0), sublen[2], sublen[3]); 668 | box_afterput(box, T_RIGHT); 669 | } 670 | blen_rm(sublen); 671 | } 672 | 673 | /* construct a radical with height at least len and width wd in dst register */ 674 | static void sqrt_rad(int dst, int len, int wd) 675 | { 676 | char *sizes[NSIZES] = {NULL}; 677 | int srlen[4]; 678 | int rnlen[4]; 679 | int sr_sz = nregmk(); 680 | int wd_diff = nregmk(); /* if wd is shorter than \(rn */ 681 | int sr_rx = nregmk(); /* the right-most horizontal position of \(sr */ 682 | int rn_dx = nregmk(); /* horizontal displacement necessary for \(rn */ 683 | int len2 = nregmk(); 684 | int rad = sregmk(); 685 | char *top = NULL, *mid = NULL, *bot = NULL, *cen; 686 | printf(".nr %s 0%s/2*11/10\n", nregname(len2), nreg(len)); 687 | printf(".ds %s \"\n", sregname(rad)); 688 | /* selecting a radical of the appropriate size */ 689 | def_pieces("\\(sr", &top, &mid, &bot, &cen); 690 | def_sizes("\\(sr", sizes); 691 | box_bracketsel(rad, len2, len2, sizes, 0, 0); 692 | /* constructing the bracket if needed */ 693 | if (mid) { 694 | printf(".if \\w'%s' ", mid); 695 | printf(".if '%s'' \\{\\\n", sreg(rad)); 696 | box_bracketmk(rad, len2, top, mid, bot, NULL); 697 | printf(". \\}\n"); 698 | } 699 | /* enlarging \(sr if no suitable glyph was found */ 700 | printf(".if '%s'' \\{\\\n", sreg(rad)); 701 | blen_mk("\\(sr", srlen); 702 | printf(".ie %s<(%s+%s) .nr %s 0\\n(.s\n", 703 | nreg(len), nreg(srlen[2]), nreg(srlen[3]), nregname(sr_sz)); 704 | printf(".el .nr %s 0%s*\\n(.s/(%s+%s-(%dm/100u))+1\n", 705 | nregname(sr_sz), nreg(len), 706 | nreg(srlen[2]), nreg(srlen[3]), e_rulethickness); 707 | printf(".ps %s\n", nreg(sr_sz)); 708 | printf(".ds %s \"\\(sr\n", sregname(rad)); 709 | blen_rm(srlen); 710 | printf(". \\}\n"); 711 | /* adding the handle */ 712 | blen_mk(sreg(rad), srlen); 713 | printf(".nr %s \\n[bburx]\n", nregname(sr_rx)); 714 | blen_mk("\\(rn", rnlen); 715 | printf(".nr %s 0%s-\\n[bbllx]-(%dm/100u)\n", 716 | nregname(rn_dx), nreg(sr_rx), e_rulethickness); 717 | printf(".nr %s 0\n", nregname(wd_diff)); 718 | printf(".if %s<%s .nr %s 0%s-%s\n", 719 | nreg(wd), nreg(rnlen[0]), 720 | nregname(wd_diff), nreg(rnlen[0]), nreg(wd)); 721 | /* output the radical; align the top of the radical to the baseline */ 722 | printf(".ds %s \"\\s[\\n(.s]\\f[\\n(.f]" 723 | "\\v'%su'\\h'%su'\\l'%su+%su\\(rn'\\h'-%su'\\v'-%su'" 724 | "\\h'-%su-%su'\\v'%su'%s\\v'-%su'\\h'%su+%su'\n", 725 | nregname(dst), 726 | nreg(rnlen[2]), nreg(rn_dx), nreg(wd), nreg(wd_diff), 727 | nreg(rn_dx), nreg(rnlen[2]), nreg(wd), nreg(wd_diff), 728 | nreg(srlen[2]), sreg(rad), nreg(srlen[2]), nreg(wd), nreg(wd_diff)); 729 | blen_rm(srlen); 730 | blen_rm(rnlen); 731 | nregrm(sr_sz); 732 | nregrm(wd_diff); 733 | nregrm(sr_rx); 734 | nregrm(rn_dx); 735 | sregrm(rad); 736 | } 737 | 738 | void box_sqrt(struct box *box, struct box *sub) 739 | { 740 | int sublen[4]; 741 | int radlen[4]; 742 | int rad = sregmk(); 743 | int rad_rise = nregmk(); 744 | int min_ht = nregmk(); 745 | box_italiccorrection(sub); 746 | box_beforeput(box, T_ORD, 0); 747 | blen_mk(box_toreg(sub), sublen); 748 | printf(".ps %s\n", nreg(box->szreg)); 749 | /* 11 */ 750 | printf(".nr %s 0%s+%s+(2*%dm/100u)+(%dm/100u/4)\n", 751 | nregname(min_ht), nreg(sublen[2]), nreg(sublen[3]), 752 | e_rulethickness, 753 | TS_DX(box->style) ? e_xheight : e_rulethickness); 754 | sqrt_rad(rad, min_ht, sublen[0]); 755 | blen_mk(sreg(rad), radlen); 756 | printf(".nr %s 0(%dm/100u)+(%dm/100u/4)\n", 757 | nregname(rad_rise), e_rulethickness, 758 | TS_DX(box->style) ? e_xheight : e_rulethickness); 759 | printf(".if %s>(%s+%s+%s) .nr %s (%s+%s-%s-%s)/2\n", 760 | nreg(radlen[3]), nreg(sublen[2]), nreg(sublen[3]), 761 | nreg(rad_rise), nregname(rad_rise), 762 | nreg(rad_rise), nreg(radlen[3]), nreg(sublen[2]), 763 | nreg(sublen[3])); 764 | printf(".nr %s +%s\n", nregname(rad_rise), nreg(sublen[2])); 765 | /* output the radical */ 766 | box_putf(box, "\\v'-%su'%s\\v'%su'\\h'-%su'%s", 767 | nreg(rad_rise), sreg(rad), nreg(rad_rise), 768 | nreg(sublen[0]), box_toreg(sub)); 769 | box_afterput(box, T_ORD); 770 | box_toreg(box); 771 | blen_rm(sublen); 772 | blen_rm(radlen); 773 | sregrm(rad); 774 | nregrm(rad_rise); 775 | nregrm(min_ht); 776 | } 777 | 778 | void box_bar(struct box *box) 779 | { 780 | int box_wd = nregmk(); 781 | int box_ht = nregmk(); 782 | int bar_wd = nregmk(); 783 | int bar_dp = nregmk(); 784 | int bar_rise = nregmk(); 785 | box_italiccorrection(box); 786 | printf(".ps %s\n", nreg(box->szreg)); 787 | tok_len("\\(ru", bar_wd, 0, 0, bar_dp); 788 | tok_dim(box_toreg(box), box_wd, box_ht, 0); 789 | printf(".if %su<(%dm/100u) .nr %s 0%dm/100u\n", 790 | nreg(box_ht), e_xheight, nregname(box_ht), e_xheight); 791 | printf(".nr %s 0%su+%su+(3*%dm/100u)\n", 792 | nregname(bar_rise), nreg(box_ht), 793 | nreg(bar_dp), e_rulethickness); 794 | box_putf(box, "\\v'-%su'\\s%s\\f[\\n(.f]\\l'-%su\\(ru'\\v'%su'", 795 | nreg(bar_rise), escarg(nreg(box->szreg)), 796 | nreg(box_wd), nreg(bar_rise)); 797 | nregrm(box_wd); 798 | nregrm(box_ht); 799 | nregrm(bar_wd); 800 | nregrm(bar_dp); 801 | nregrm(bar_rise); 802 | } 803 | 804 | void box_accent(struct box *box, char *c) 805 | { 806 | int box_wd = nregmk(); 807 | int box_ht = nregmk(); 808 | int ac_rise = nregmk(); 809 | int ac_wd = nregmk(); 810 | int ac_dp = nregmk(); 811 | box_italiccorrection(box); 812 | printf(".ps %s\n", nreg(box->szreg)); 813 | tok_len(c, ac_wd, 0, 0, ac_dp); 814 | tok_dim(box_toreg(box), box_wd, box_ht, 0); 815 | printf(".if %su<(%dm/100u) .nr %s 0%dm/100u\n", 816 | nreg(box_ht), e_xheight, nregname(box_ht), e_xheight); 817 | printf(".nr %s 0%su+%su+(%sp*10u/100u)\n", 818 | nregname(ac_rise), nreg(box_ht), 819 | nreg(ac_dp), nreg(box->szreg)); 820 | box_putf(box, "\\v'-%su'\\h'-%su-%su/2u'\\s%s\\f[\\n(.f]%s\\h'%su-%su/2u'\\v'%su'", 821 | nreg(ac_rise), nreg(box_wd), nreg(ac_wd), 822 | escarg(nreg(box->szreg)), c, nreg(box_wd), 823 | nreg(ac_wd), nreg(ac_rise)); 824 | nregrm(box_wd); 825 | nregrm(box_ht); 826 | nregrm(ac_rise); 827 | nregrm(ac_wd); 828 | nregrm(ac_dp); 829 | } 830 | 831 | void box_under(struct box *box) 832 | { 833 | int box_wd = nregmk(); 834 | int box_dp = nregmk(); 835 | int bar_wd = nregmk(); 836 | int bar_ht = nregmk(); 837 | int bar_fall = nregmk(); 838 | box_italiccorrection(box); 839 | printf(".ps %s\n", nreg(box->szreg)); 840 | tok_len("\\(ul", bar_wd, 0, bar_ht, 0); 841 | tok_dim(box_toreg(box), box_wd, 0, box_dp); 842 | printf(".if %s<0 .nr %s 0\n", nreg(box_dp), nregname(box_dp)); 843 | printf(".nr %s 0%su+%su+(3*%dm/100u)\n", 844 | nregname(bar_fall), nreg(box_dp), 845 | nreg(bar_ht), e_rulethickness); 846 | box_putf(box, "\\v'%su'\\s%s\\f[\\n(.f]\\l'-%su\\(ul'\\v'-%su'", 847 | nreg(bar_fall), escarg(nreg(box->szreg)), 848 | nreg(box_wd), nreg(bar_fall)); 849 | nregrm(box_wd); 850 | nregrm(box_dp); 851 | nregrm(bar_wd); 852 | nregrm(bar_ht); 853 | nregrm(bar_fall); 854 | } 855 | 856 | char *box_toreg(struct box *box) 857 | { 858 | if (!box->reg) { 859 | box->reg = sregmk(); 860 | printf(".ds %s \"%s\n", sregname(box->reg), box_buf(box)); 861 | } 862 | return sreg(box->reg); 863 | } 864 | 865 | int box_empty(struct box *box) 866 | { 867 | return !strlen(box_buf(box)); 868 | } 869 | 870 | void box_vcenter(struct box *box, struct box *sub) 871 | { 872 | int wd = nregmk(); 873 | int ht = nregmk(); 874 | int dp = nregmk(); 875 | int fall = nregmk(); 876 | box_beforeput(box, sub->tbeg, 0); 877 | tok_dim(box_toreg(sub), wd, ht, dp); 878 | printf(".nr %s 0-%s+%s/2-(%sp*%du/100u)\n", nregname(fall), 879 | nreg(dp), nreg(ht), nreg(box->szreg), e_axisheight); 880 | box_putf(box, "\\v'%su'%s\\v'-%su'", 881 | nreg(fall), box_toreg(sub), nreg(fall)); 882 | box_toreg(box); 883 | box_afterput(box, sub->tcur); 884 | nregrm(wd); 885 | nregrm(ht); 886 | nregrm(dp); 887 | nregrm(fall); 888 | } 889 | 890 | /* include line-space requests */ 891 | void box_vertspace(struct box *box) 892 | { 893 | int box_wd = nregmk(); 894 | int htroom = nregmk(); 895 | int dproom = nregmk(); 896 | box_italiccorrection(box); 897 | /* amount of room available before and after this line */ 898 | printf(".nr %s 0+\\n(.vu-%sp+(%sp*%du/100u)\n", 899 | nregname(htroom), nreg(box->szreg), 900 | nreg(box->szreg), e_bodyheight); 901 | printf(".nr %s 0+\\n(.vu-%sp+(%sp*%du/100u)\n", 902 | nregname(dproom), nreg(box->szreg), 903 | nreg(box->szreg), e_bodydepth); 904 | /* appending \x requests */ 905 | tok_dim(box_toreg(box), box_wd, 0, 0); 906 | printf(".if -\\n[bbury]>%s .as %s \"\\x'\\n[bbury]u+%su'\n", 907 | nreg(htroom), sregname(box->reg), nreg(htroom)); 908 | printf(".if \\n[bblly]>%s .as %s \"\\x'\\n[bblly]u-%su'\n", 909 | nreg(dproom), sregname(box->reg), nreg(dproom)); 910 | nregrm(box_wd); 911 | nregrm(htroom); 912 | nregrm(dproom); 913 | } 914 | 915 | /* put the current width to the given number register */ 916 | void box_markpos(struct box *box, char *reg) 917 | { 918 | box->tomark = reg; 919 | } 920 | 921 | /* initialize the length of a pile or column of a matrix */ 922 | static void box_colinit(struct box **pile, int n, 923 | int plen[][4], int wd, int ht) 924 | { 925 | int i; 926 | for (i = 0; i < n; i++) 927 | if (pile[i]) 928 | box_italiccorrection(pile[i]); 929 | for (i = 0; i < n; i++) 930 | blen_mk(pile[i] ? box_toreg(pile[i]) : "", plen[i]); 931 | printf(".nr %s 0%s\n", nregname(wd), nreg(plen[0][0])); 932 | printf(".nr %s 0%s\n", nregname(ht), nreg(plen[0][2])); 933 | /* finding the maximum width */ 934 | for (i = 1; i < n; i++) { 935 | printf(".if %s>%s .nr %s 0+%s\n", 936 | nreg(plen[i][0]), nreg(wd), 937 | nregname(wd), nreg(plen[i][0])); 938 | } 939 | /* finding the maximum height (vertical length) */ 940 | for (i = 1; i < n; i++) { 941 | printf(".if %s+%s>%s .nr %s 0+%s+%s\n", 942 | nreg(plen[i - 1][3]), nreg(plen[i][2]), nreg(ht), 943 | nregname(ht), nreg(plen[i - 1][3]), nreg(plen[i][2])); 944 | } 945 | /* maximum height and the depth of the last row */ 946 | printf(".if %s>%s .nr %s 0+%s\n", 947 | nreg(plen[n - 1][3]), nreg(ht), 948 | nregname(ht), nreg(plen[n - 1][3])); 949 | } 950 | 951 | /* append the give pile to box */ 952 | static void box_colput(struct box **pile, int n, struct box *box, 953 | int adj, int plen[][4], int wd, int ht) 954 | { 955 | int i; 956 | box_putf(box, "\\v'-%du*%su/2u'", n - 1, nreg(ht)); 957 | /* adding the entries */ 958 | for (i = 0; i < n; i++) { 959 | if (adj == 'c') 960 | box_putf(box, "\\h'%su-%su/2u'", 961 | nreg(wd), nreg(plen[i][0])); 962 | if (adj == 'r') 963 | box_putf(box, "\\h'%su-%su'", 964 | nreg(wd), nreg(plen[i][0])); 965 | box_putf(box, "\\v'%su'%s", i ? nreg(ht) : "0", 966 | pile[i] ? box_toreg(pile[i]) : ""); 967 | if (adj == 'l') 968 | box_putf(box, "\\h'-%su'", nreg(plen[i][0])); 969 | if (adj == 'c') 970 | box_putf(box, "\\h'-%su+(%su-%su/2u)'", 971 | nreg(wd), nreg(wd), nreg(plen[i][0])); 972 | if (adj == 'r') 973 | box_putf(box, "\\h'-%su'", nreg(wd)); 974 | } 975 | box_putf(box, "\\v'-%du*%su/2u'\\h'%su'", n - 1, nreg(ht), nreg(wd)); 976 | } 977 | 978 | /* free the registers allocated for this pile */ 979 | static void box_coldone(struct box **pile, int n, int plen[][4]) 980 | { 981 | int i; 982 | for (i = 0; i < n; i++) 983 | blen_rm(plen[i]); 984 | } 985 | 986 | /* calculate the number of entries in the given pile */ 987 | static int box_colnrows(struct box *cols[]) 988 | { 989 | int n = 0; 990 | while (n < NPILES && cols[n]) 991 | n++; 992 | return n; 993 | } 994 | 995 | void box_pile(struct box *box, struct box **pile, int adj, int rowspace) 996 | { 997 | int plen[NPILES][4]; 998 | int max_wd = nregmk(); 999 | int max_ht = nregmk(); 1000 | int n = box_colnrows(pile); 1001 | box_beforeput(box, T_INNER, 0); 1002 | box_colinit(pile, n, plen, max_wd, max_ht); 1003 | /* inserting spaces between entries */ 1004 | printf(".if %s<(%sp*%du/100u) .nr %s (%sp*%du/100u)\n", 1005 | nreg(max_ht), nreg(box->szreg), e_baselinesep, 1006 | nregname(max_ht), nreg(box->szreg), e_baselinesep); 1007 | if (rowspace) 1008 | printf(".nr %s +(%sp*%du/100u)\n", 1009 | nregname(max_ht), nreg(box->szreg), rowspace); 1010 | /* adding the entries */ 1011 | box_colput(pile, n, box, adj, plen, max_wd, max_ht); 1012 | box_coldone(pile, n, plen); 1013 | box_afterput(box, T_INNER); 1014 | box_toreg(box); 1015 | nregrm(max_wd); 1016 | nregrm(max_ht); 1017 | } 1018 | 1019 | void box_matrix(struct box *box, int ncols, struct box *cols[][NPILES], 1020 | int *adj, int colspace, int rowspace) 1021 | { 1022 | int plen[NPILES][NPILES][4]; 1023 | int wd[NPILES]; 1024 | int ht[NPILES]; 1025 | int max_ht = nregmk(); 1026 | int max_wd = nregmk(); 1027 | int nrows = 0; 1028 | int i; 1029 | box_beforeput(box, T_INNER, 0); 1030 | for (i = 0; i < ncols; i++) 1031 | if (box_colnrows(cols[i]) > nrows) 1032 | nrows = box_colnrows(cols[i]); 1033 | for (i = 0; i < ncols; i++) 1034 | wd[i] = nregmk(); 1035 | for (i = 0; i < ncols; i++) 1036 | ht[i] = nregmk(); 1037 | /* initializing the columns */ 1038 | for (i = 0; i < ncols; i++) 1039 | box_colinit(cols[i], nrows, plen[i], wd[i], ht[i]); 1040 | /* finding the maximum width and height */ 1041 | printf(".nr %s 0%s\n", nregname(max_wd), nreg(wd[0])); 1042 | printf(".nr %s 0%s\n", nregname(max_ht), nreg(ht[0])); 1043 | for (i = 1; i < ncols; i++) { 1044 | printf(".if %s>%s .nr %s 0+%s\n", 1045 | nreg(wd[i]), nreg(max_wd), 1046 | nregname(max_wd), nreg(wd[i])); 1047 | } 1048 | for (i = 1; i < ncols; i++) { 1049 | printf(".if %s>%s .nr %s 0+%s\n", 1050 | nreg(ht[i]), nreg(max_ht), 1051 | nregname(max_ht), nreg(ht[i])); 1052 | } 1053 | /* inserting spaces between rows */ 1054 | printf(".if %s<(%sp*%du/100u) .nr %s (%sp*%du/100u)\n", 1055 | nreg(max_ht), nreg(box->szreg), e_baselinesep, 1056 | nregname(max_ht), nreg(box->szreg), e_baselinesep); 1057 | if (rowspace) 1058 | printf(".nr %s +(%sp*%du/100u)\n", 1059 | nregname(max_ht), nreg(box->szreg), rowspace); 1060 | /* printing the columns */ 1061 | for (i = 0; i < ncols; i++) { 1062 | if (i) /* space between columns */ 1063 | box_putf(box, "\\h'%sp*%du/100u'", 1064 | nreg(box->szreg), e_columnsep + colspace); 1065 | box_colput(cols[i], nrows, box, adj[i], 1066 | plen[i], max_wd, max_ht); 1067 | } 1068 | box_afterput(box, T_INNER); 1069 | box_toreg(box); 1070 | for (i = 0; i < ncols; i++) 1071 | box_coldone(cols[i], nrows, plen[i]); 1072 | for (i = 0; i < ncols; i++) 1073 | nregrm(ht[i]); 1074 | for (i = 0; i < ncols; i++) 1075 | nregrm(wd[i]); 1076 | nregrm(max_wd); 1077 | nregrm(max_ht); 1078 | } 1079 | -------------------------------------------------------------------------------- /def.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "eqn.h" 4 | 5 | /* null-terminated list of default macros */ 6 | char *def_macros[][2] = { 7 | {"<-", "\\(<-"}, 8 | {"<=", "\\(<="}, 9 | {">=", "\\(>="}, 10 | {"==", "\\(=="}, 11 | {"->", "\\(->"}, 12 | {"!=", "\\(!="}, 13 | {"+-", "\\(+-"}, 14 | {"...", "vcenter roman \"\\ .\\ .\\ .\\ \""}, 15 | {",...,", "roman \",\\ .\\ .\\ .\\ ,\\|\""}, 16 | {"ALPHA", "\\(*A"}, 17 | {"BETA", "\\(*B"}, 18 | {"CHI", "\\(*X"}, 19 | {"DELTA", "\\(*D"}, 20 | {"EPSILON", "\\(*E"}, 21 | {"ETA", "\\(*Y"}, 22 | {"GAMMA", "\\(*G"}, 23 | {"IOTA", "\\(*I"}, 24 | {"KAPPA", "\\(*K"}, 25 | {"LAMBDA", "\\(*L"}, 26 | {"MU", "\\(*M"}, 27 | {"NU", "\\(*N"}, 28 | {"OMEGA", "\\(*W"}, 29 | {"OMICRON", "\\(*O"}, 30 | {"PHI", "\\(*F"}, 31 | {"PI", "\\(*P"}, 32 | {"PSI", "\\(*Q"}, 33 | {"RHO", "\\(*R"}, 34 | {"SIGMA", "\\(*S"}, 35 | {"TAU", "\\(*T"}, 36 | {"THETA", "\\(*H"}, 37 | {"UPSILON", "\\(*U"}, 38 | {"XI", "\\(*C"}, 39 | {"ZETA", "\\(*Z"}, 40 | {"alpha", "\\(*a"}, 41 | {"beta", "\\(*b"}, 42 | {"chi", "\\(*x"}, 43 | {"delta", "\\(*d"}, 44 | {"epsilon", "\\(*e"}, 45 | {"eta", "\\(*y"}, 46 | {"gamma", "\\(*g"}, 47 | {"iota", "\\(*i"}, 48 | {"kappa", "\\(*k"}, 49 | {"lambda", "\\(*l"}, 50 | {"mu", "\\(*m"}, 51 | {"nu", "\\(*n"}, 52 | {"omega", "\\(*w"}, 53 | {"omicron", "\\(*o"}, 54 | {"phi", "\\(*f"}, 55 | {"pi", "\\(*p"}, 56 | {"psi", "\\(*q"}, 57 | {"rho", "\\(*r"}, 58 | {"sigma", "\\(*s"}, 59 | {"tau", "\\(*t"}, 60 | {"theta", "\\(*h"}, 61 | {"upsilon", "\\(*u"}, 62 | {"xi", "\\(*c"}, 63 | {"zeta", "\\(*z"}, 64 | {"Im", "roman \"Im\""}, 65 | {"Re", "roman \"Re\""}, 66 | {"and", "roman \"and\""}, 67 | {"approx", "\"\\v'-.2m'\\z\\(ap\\v'.25m'\\(ap\\v'-.05m'\""}, 68 | {"arc", "roman \"arc\""}, 69 | {"cdot", "\\(c."}, 70 | {"cos", "roman \"cos\""}, 71 | {"cosh", "roman \"cosh\""}, 72 | {"coth", "roman \"coth\""}, 73 | {"del", "\\(gr"}, 74 | {"det", "roman \"det\""}, 75 | {"dollar", "roman $"}, 76 | {"exp", "roman \"exp\""}, 77 | {"for", "roman \"for\""}, 78 | {"grad", "\\(gr"}, 79 | {"half", "roman \\(12"}, 80 | {"if", "roman \"if\""}, 81 | {"inf", "\\(if"}, 82 | {"infinity", "\\(if"}, 83 | {"int", "{vcenter roman size +2 \\(is}"}, 84 | {"inter", "roman size +2 \\(ca"}, 85 | {"lim", "roman \"lim\""}, 86 | {"ln", "roman \"ln\""}, 87 | {"log", "roman \"log\""}, 88 | {"max", "roman \"max\""}, 89 | {"min", "roman \"min\""}, 90 | {"nothing", ""}, 91 | {"partial", "\\(pd"}, 92 | {"prime", "roman \\(fm"}, 93 | {"prod", "{vcenter roman size +2 \\(pr}"}, 94 | {"sin", "roman \"sin\""}, 95 | {"sinh", "roman \"sinh\""}, 96 | {"sum", "{vcenter roman size +2 \\(su}"}, 97 | {"tan", "roman \"tan\""}, 98 | {"tanh", "roman \"tanh\""}, 99 | {"times", "\\(mu"}, 100 | {"union", "roman size +2 \\(cu"}, 101 | {NULL, NULL} 102 | }; 103 | 104 | /* list of binary operations */ 105 | static char *binops[] = { 106 | "+", "\\(pl", 107 | "−", "-", "\\(mi", 108 | "÷", "\\(-:", "\\(di", 109 | "×", "xx", "\\(mu", 110 | "±", "\\(+-", 111 | "⊗", "\\(Ox", 112 | "⊕", "\\(O+", 113 | "∧", "\\(l&", 114 | "∨", "\\(l|", 115 | "∩", "\\(ca", 116 | "∪", "\\(cu", 117 | "⋅", "\\(c.", 118 | }; 119 | 120 | /* list of relations */ 121 | static char *relops[] = { 122 | "<", ">", ":=", 123 | "=", "\\(eq", 124 | "≅", "\\(cg", 125 | "≤", "\\(<=", 126 | "≥", "\\(>=", 127 | "≠", "\\(!=", 128 | "≡", "\\(==", 129 | "≈", "\\(~~", 130 | "⊃", "\\(sp", 131 | "⊇", "\\(ip", 132 | "⊄", "\\(!b", 133 | "⊂", "\\(sb", 134 | "⊆", "\\(ib", 135 | "∈", "\\(mo", 136 | "∉", "\\(!m", 137 | "↔", "\\(ab", 138 | "←", "\\(<-", 139 | "↑", "\\(ua", 140 | "→", "\\(->", 141 | "↓", "\\(da", 142 | }; 143 | 144 | /* list of punctuations */ 145 | static char *puncs[] = {".", ",", ";", ":", "!"}; 146 | 147 | /* left and right brackets */ 148 | static char *bracketleft[] = {"(", "[", "{", "\\(lc", "\\(lf", "\\(la"}; 149 | static char *bracketright[] = {")", "]", "}", "\\(rc", "\\(rf", "\\(ra"}; 150 | 151 | /* glyphs for different bracket sizes */ 152 | static char bracketsizes[32][NSIZES][BRLEN] = { 153 | {"(", "(", "\\N'parenleftbig'", "\\N'parenleftBig'", 154 | "\\N'parenleftbigg'", "\\N'parenleftBigg'"}, 155 | {")", ")", "\\N'parenrightbig'", "\\N'parenrightBig'", 156 | "\\N'parenrightbigg'", "\\N'parenrightBigg'"}, 157 | {"[", "[", "\\N'bracketleftbig'", "\\N'bracketleftBig'", 158 | "\\N'bracketleftbigg'", "\\N'bracketleftBigg'"}, 159 | {"]", "]", "\\N'bracketrightbig'", "\\N'bracketrightBig'", 160 | "\\N'bracketrightbigg'", "\\N'bracketrightBigg'"}, 161 | {"{", "{", "\\N'braceleftbig'", "\\N'braceleftBig'", 162 | "\\N'braceleftbigg'", "\\N'braceleftBigg'"}, 163 | {"}", "}", "\\N'bracerightbig'", "\\N'bracerightBig'", 164 | "\\N'bracerightbigg'", "\\N'bracerightBigg'"}, 165 | {"\\(lc", "\\N'ceilingleft'", "\\N'ceilingleftbig'", "\\N'ceilingleftBig'", 166 | "\\N'ceilingleftbigg'", "\\N'ceilingleftBigg'", "\\(lc"}, 167 | {"\\(rc", "\\N'ceilingright'", "\\N'ceilingrightbig'", "\\N'ceilingrightBig'", 168 | "\\N'ceilingrightbigg'", "\\N'ceilingrightBigg'", "\\(rc"}, 169 | {"\\(lf", "\\N'floorleft'", "\\N'floorleftbig'", "\\N'floorleftBig'", 170 | "\\N'floorleftbigg'", "\\N'floorleftBigg'", "\\(lf"}, 171 | {"\\(rf", "\\N'floorright'", "\\N'floorrightbig'", "\\N'floorrightBig'", 172 | "\\N'floorrightbigg'", "\\N'floorrightBigg'", "\\(rf"}, 173 | {"\\(la", "\\(la", "\\N'angbracketleft'", "\\N'angbracketleftbig'", 174 | "\\N'angbracketleftBig'", "\\N'angbracketleftbigg'", "\\N'angbracketleftBigg'"}, 175 | {"\\(ra", "\\(ra", "\\N'angbracketright'", "\\N'angbracketrightbig'", 176 | "\\N'angbracketrightBig'", "\\N'angbracketrightbigg'", "\\N'angbracketrightBigg'"}, 177 | {"|", "|", "|", "|", "|"}, 178 | {"\\(sr", "\\(sr", "\\N'radical'", "\\N'radicalbig'", "\\N'radicalBig'", 179 | "\\N'radicalbigg'", "\\N'radicalBigg'"}, 180 | }; 181 | 182 | /* large glyph pieces: name, top, mid, bot, centre */ 183 | static char bracketpieces[32][8][BRLEN] = { 184 | {"(", "\\(LT", "\\(LX", "\\(LB"}, 185 | {")", "\\(RT", "\\(RX", "\\(RB"}, 186 | {"[", "\\(lc", "\\(lx", "\\(lf"}, 187 | {"]", "\\(rc", "\\(rx", "\\(rf"}, 188 | {"{", "\\(lt", "\\(bv", "\\(lb", "\\(lk"}, 189 | {"}", "\\(rt", "\\(bv", "\\(rb", "\\(rk"}, 190 | {"\\(lc", "\\(lc", "\\(lx", "\\(lx"}, 191 | {"\\(rc", "\\(rc", "\\(rx", "\\(rx"}, 192 | {"\\(lf", "\\(lx", "\\(lx", "\\(lf"}, 193 | {"\\(rf", "\\(rx", "\\(rx", "\\(rf"}, 194 | {"|", "|", "|", "|"}, 195 | {"\\(sr", "\\N'radicaltp'", "\\N'radicalvertex'", "\\N'radicalbt'"}, 196 | }; 197 | 198 | /* custom glyph types */ 199 | static struct gtype { 200 | char g[GNLEN]; 201 | int type; 202 | } gtypes[128]; 203 | 204 | void def_typeput(char *s, int type) 205 | { 206 | int i; 207 | for (i = 0; i < LEN(gtypes) && gtypes[i].g[0]; i++) 208 | if (!strcmp(s, gtypes[i].g)) 209 | break; 210 | if (i < LEN(gtypes)) { 211 | strcpy(gtypes[i].g, s); 212 | gtypes[i].type = type; 213 | } 214 | } 215 | 216 | /* find an entry in an array */ 217 | static char *alookup(char **a, int len, char *s) 218 | { 219 | int i; 220 | for (i = 0; i < len; i++) 221 | if (!strcmp(s, a[i])) 222 | return a[i]; 223 | return NULL; 224 | } 225 | 226 | int def_type(char *s) 227 | { 228 | int i; 229 | for (i = 0; i < LEN(gtypes) && gtypes[i].g[0]; i++) 230 | if (!strcmp(s, gtypes[i].g)) 231 | return gtypes[i].type; 232 | if (alookup(puncs, LEN(puncs), s)) 233 | return T_PUNC; 234 | if (alookup(binops, LEN(binops), s)) 235 | return T_BINOP; 236 | if (alookup(relops, LEN(relops), s)) 237 | return T_RELOP; 238 | if (alookup(bracketleft, LEN(bracketleft), s)) 239 | return T_LEFT; 240 | if (alookup(bracketright, LEN(bracketright), s)) 241 | return T_RIGHT; 242 | return -1; 243 | } 244 | 245 | static int pieces_find(char *sign) 246 | { 247 | int i; 248 | for (i = 0; i < LEN(bracketpieces); i++) 249 | if (!strcmp(bracketpieces[i][0], sign)) 250 | return i; 251 | return -1; 252 | } 253 | 254 | /* find the pieces for creating the given bracket */ 255 | void def_pieces(char *sign, char **top, char **mid, char **bot, char **cen) 256 | { 257 | int i = pieces_find(sign); 258 | if (i >= 0) { 259 | *top = bracketpieces[i][1][0] ? bracketpieces[i][1] : NULL; 260 | *mid = bracketpieces[i][2][0] ? bracketpieces[i][2] : NULL; 261 | *bot = bracketpieces[i][3][0] ? bracketpieces[i][3] : NULL; 262 | *cen = bracketpieces[i][4][0] ? bracketpieces[i][4] : NULL; 263 | } 264 | } 265 | 266 | void def_piecesput(char *sign, char *top, char *mid, char *bot, char *cen) 267 | { 268 | int i = pieces_find(sign); 269 | if (i < 0 && (i = pieces_find("")) < 0) 270 | return; 271 | snprintf(bracketpieces[i][0], sizeof(bracketpieces[i][0]), "%s", sign); 272 | snprintf(bracketpieces[i][1], sizeof(bracketpieces[i][1]), "%s", top); 273 | snprintf(bracketpieces[i][2], sizeof(bracketpieces[i][2]), "%s", mid); 274 | snprintf(bracketpieces[i][3], sizeof(bracketpieces[i][3]), "%s", bot); 275 | snprintf(bracketpieces[i][4], sizeof(bracketpieces[i][4]), "%s", cen); 276 | } 277 | 278 | static int sizes_find(char *sign) 279 | { 280 | int i; 281 | for (i = 0; i < LEN(bracketsizes); i++) 282 | if (!strcmp(bracketsizes[i][0], sign)) 283 | return i; 284 | return -1; 285 | } 286 | 287 | /* return different sizes of the given bracket */ 288 | void def_sizes(char *sign, char *sizes[]) 289 | { 290 | int idx = sizes_find(sign); 291 | int i; 292 | sizes[0] = sign; 293 | for (i = 1; idx >= 0 && i < NSIZES; i++) 294 | sizes[i - 1] = bracketsizes[idx][i][0] ? bracketsizes[idx][i] : NULL; 295 | } 296 | 297 | void def_sizesput(char *sign, char *sizes[]) 298 | { 299 | int idx = sizes_find(sign); 300 | int i; 301 | if (idx < 0 && (idx = sizes_find("")) < 0) 302 | return; 303 | snprintf(bracketsizes[idx][0], sizeof(bracketsizes[idx][0]), "%s", sign); 304 | for (i = 1; i < NSIZES; i++) 305 | snprintf(bracketsizes[idx][i], sizeof(bracketsizes[idx][i]), 306 | "%s", sizes[i - 1] ? sizes[i - 1] : ""); 307 | } 308 | 309 | /* global variables */ 310 | int e_axisheight = 23; /* axis height */ 311 | int e_minimumsize = 5; /* minimum size */ 312 | int e_overhang = 7; 313 | int e_nulldelim = 12; 314 | int e_scriptspace = 12; 315 | int e_thinspace = 17; 316 | int e_mediumspace = 22; 317 | int e_thickspace = 28; 318 | int e_num1 = 70; /* minimum numerator rise */ 319 | int e_num2 = 40; 320 | int e_denom1 = 70; /* minimum denominator fall */ 321 | int e_denom2 = 36; 322 | int e_sup1 = 42; 323 | int e_sup2 = 37; 324 | int e_sup3 = 28; 325 | int e_sub1 = 20; 326 | int e_sub2 = 23; 327 | int e_supdrop = 38; 328 | int e_subdrop = 5; 329 | int e_xheight = 45; 330 | int e_rulethickness = 4; 331 | int e_bigopspacing1 = 11; 332 | int e_bigopspacing2 = 17; 333 | int e_bigopspacing3 = 20; 334 | int e_bigopspacing4 = 60; 335 | int e_bigopspacing5 = 10; 336 | int e_columnsep = 100; 337 | int e_baselinesep = 140; 338 | int e_bodyheight = 70; 339 | int e_bodydepth = 25; 340 | 341 | static struct gvar { 342 | char *name; 343 | int *ref; 344 | } gvars[] = { 345 | {"axis_height", &e_axisheight}, 346 | {"minimum_size", &e_minimumsize}, 347 | {"over_hang", &e_overhang}, 348 | {"null_delimiter_space", &e_nulldelim}, 349 | {"script_space", &e_scriptspace}, 350 | {"thin_space", &e_thinspace}, 351 | {"medium_space", &e_mediumspace}, 352 | {"thick_space", &e_thickspace}, 353 | {"num1", &e_num1}, 354 | {"num2", &e_num2}, 355 | {"denom1", &e_denom1}, 356 | {"denom2", &e_denom2}, 357 | {"sup1", &e_sup1}, 358 | {"sup2", &e_sup2}, 359 | {"sup3", &e_sup3}, 360 | {"sub1", &e_sub1}, 361 | {"sub2", &e_sub2}, 362 | {"sup_drop", &e_supdrop}, 363 | {"sub_drop", &e_subdrop}, 364 | {"x_height", &e_xheight}, 365 | {"default_rule_thickness", &e_rulethickness}, 366 | {"big_op_spacing1", &e_bigopspacing1}, 367 | {"big_op_spacing2", &e_bigopspacing2}, 368 | {"big_op_spacing3", &e_bigopspacing3}, 369 | {"big_op_spacing4", &e_bigopspacing4}, 370 | {"big_op_spacing5", &e_bigopspacing5}, 371 | {"column_sep", &e_columnsep}, 372 | {"baseline_sep", &e_baselinesep}, 373 | {"body_height", &e_bodyheight}, 374 | {"body_depth", &e_bodydepth}, 375 | }; 376 | 377 | void def_set(char *name, int val) 378 | { 379 | int i; 380 | for (i = 0; i < LEN(gvars); i++) 381 | if (!strcmp(gvars[i].name, name)) 382 | *gvars[i].ref = val; 383 | } 384 | 385 | /* superscript style */ 386 | int ts_sup(int style) 387 | { 388 | int sz = MIN(2, TS_SZ(style) + 1); 389 | return TS_MK(sz, TS_0(style)); 390 | } 391 | 392 | /* subscript style */ 393 | int ts_sub(int style) 394 | { 395 | int sz = MIN(2, TS_SZ(style) + 1); 396 | return TS_MK(sz, 1); 397 | } 398 | 399 | /* numerator style */ 400 | int ts_num(int style) 401 | { 402 | int sz; 403 | if (TS_DX(style)) 404 | return TS_0(style) ? TS_T0 : TS_T; 405 | sz = MIN(2, TS_SZ(style) + 1); 406 | return TS_MK(sz, TS_0(style)); 407 | } 408 | 409 | /* denominator style */ 410 | int ts_denom(int style) 411 | { 412 | int sz; 413 | if (TS_DX(style)) 414 | return TS_T0; 415 | sz = MIN(2, TS_SZ(style) + 1); 416 | return TS_MK(sz, 1); 417 | } 418 | 419 | /* extra line-break cost */ 420 | static int brcost_type[32]; 421 | static int brcost_cost[32]; 422 | static int brcost_n; 423 | 424 | int def_brcost(int type) 425 | { 426 | int i; 427 | for (i = 0; i < brcost_n; i++) 428 | if (brcost_type[i] == type && brcost_cost[i] > 0) 429 | return brcost_cost[i]; 430 | return 100000; 431 | } 432 | 433 | void def_brcostput(int type, int cost) 434 | { 435 | int i; 436 | if (type == 0) 437 | brcost_n = 0; 438 | for (i = 0; i < brcost_n; i++) 439 | if (brcost_type[i] == type) 440 | break; 441 | if (type <= 0 || i + (i >= brcost_n) >= LEN(brcost_type)) 442 | return; 443 | brcost_type[i] = type; 444 | brcost_cost[i] = cost; 445 | if (i >= brcost_n) 446 | brcost_n = i + 1; 447 | } 448 | 449 | /* at which characters equations are chopped */ 450 | static char chopped[256] = "^~\"\t"; 451 | 452 | int def_chopped(int c) 453 | { 454 | return strchr("\n {}", c) != NULL || strchr(chopped, c) != NULL; 455 | } 456 | 457 | void def_choppedset(char *c) 458 | { 459 | strcpy(chopped, c); 460 | } 461 | -------------------------------------------------------------------------------- /eqn.c: -------------------------------------------------------------------------------- 1 | /* 2 | * NEATEQN NEATROFF PREPROCESSOR 3 | * 4 | * Copyright (C) 2014-2017 Ali Gholami Rudi 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "eqn.h" 23 | 24 | /* flags passed to eqn_box() */ 25 | #define EQN_TSMASK 0x00ffff /* style mask */ 26 | #define EQN_SUB 0x020000 /* this is a subscript */ 27 | #define EQN_FROM 0x040000 /* this is a from block */ 28 | 29 | static char gfont[FNLEN] = "2"; 30 | static char grfont[FNLEN] = "1"; 31 | static char gbfont[FNLEN] = "3"; 32 | static char gsize[FNLEN] = "\\n[" EQNSZ "]"; 33 | static char eqn_lineup[128]; /* the lineup horizontal request */ 34 | static int eqn_lineupreg; /* the number register holding lineup width */ 35 | static int eqn_mk; /* the value of MK */ 36 | 37 | static struct box *eqn_box(int flg, struct box *pre, int sz0, char *fn0); 38 | 39 | /* read equations until delim is read */ 40 | static int eqn_boxuntil(struct box *box, int sz0, char *fn0, char *delim) 41 | { 42 | struct box *sub = NULL; 43 | while (tok_get() && tok_jmp(delim)) { 44 | if (!strcmp("}", tok_get())) 45 | return 1; 46 | sub = eqn_box(box->style, sub ? box : NULL, sz0, fn0); 47 | box_merge(box, sub, 0); 48 | box_free(sub); 49 | } 50 | return 0; 51 | } 52 | 53 | /* subscript size */ 54 | static void sizesub(int dst, int src, int style, int src_style) 55 | { 56 | if (TS_SZ(style) > TS_SZ(src_style)) { 57 | printf(".nr %s %s*7/10\n", nregname(dst), nreg(src)); 58 | printf(".if %s<%d .nr %s %d\n", 59 | nreg(dst), e_minimumsize, 60 | nregname(dst), e_minimumsize); 61 | } else { 62 | printf(".nr %s %s\n", nregname(dst), nreg(src)); 63 | } 64 | } 65 | 66 | static char *tok_quotes(char *s) 67 | { 68 | if (s && s[0] == '"') { 69 | s[strlen(s) - 1] = '\0'; 70 | return s + 1; 71 | } 72 | return s ? s : ""; 73 | } 74 | 75 | static char *tok_improve(char *s) 76 | { 77 | if (s && s[0] == '-' && s[1] == '\0') 78 | return "\\(mi"; 79 | if (s && s[0] == '+' && s[1] == '\0') 80 | return "\\(pl"; 81 | if (s && s[0] == '\'' && s[1] == '\0') 82 | return "\\(fm"; 83 | return tok_quotes(s); 84 | } 85 | 86 | static void eqn_bracketsizes(void) 87 | { 88 | char sign[BRLEN]; 89 | char bufs[NSIZES][BRLEN]; 90 | char *sizes[NSIZES] = {NULL}; 91 | int n, i; 92 | snprintf(sign, sizeof(sign), "%s", tok_quotes(tok_poptext(1))); 93 | n = atoi(tok_poptext(1)); 94 | for (i = 0; i < n; i++) { 95 | char *size = tok_quotes(tok_poptext(1)); 96 | if (i < NSIZES) { 97 | snprintf(bufs[i], sizeof(bufs[i]), "%s", size); 98 | sizes[i] = bufs[i]; 99 | } 100 | } 101 | def_sizesput(sign, sizes); 102 | } 103 | 104 | static void eqn_bracketpieces(void) 105 | { 106 | char sign[BRLEN], top[BRLEN], mid[BRLEN], bot[BRLEN], cen[BRLEN]; 107 | snprintf(sign, sizeof(sign), "%s", tok_quotes(tok_poptext(1))); 108 | snprintf(top, sizeof(top), "%s", tok_quotes(tok_poptext(1))); 109 | snprintf(mid, sizeof(mid), "%s", tok_quotes(tok_poptext(1))); 110 | snprintf(bot, sizeof(bot), "%s", tok_quotes(tok_poptext(1))); 111 | snprintf(cen, sizeof(cen), "%s", tok_quotes(tok_poptext(1))); 112 | def_piecesput(sign, top, mid, bot, cen); 113 | } 114 | 115 | static int typenum(char *s) 116 | { 117 | if (!strcmp("ord", s) || !strcmp("ordinary", s)) 118 | return T_ORD; 119 | if (!strcmp("op", s) || !strcmp("operator", s)) 120 | return T_BIGOP; 121 | if (!strcmp("bin", s) || !strcmp("binary", s)) 122 | return T_BINOP; 123 | if (!strcmp("rel", s) || !strcmp("relation", s)) 124 | return T_RELOP; 125 | if (!strcmp("open", s) || !strcmp("opening", s)) 126 | return T_LEFT; 127 | if (!strcmp("close", s) || !strcmp("closing", s)) 128 | return T_RIGHT; 129 | if (!strcmp("punct", s) || !strcmp("punctuation", s)) 130 | return T_PUNC; 131 | if (!strcmp("inner", s)) 132 | return T_INNER; 133 | return T_ORD; 134 | } 135 | 136 | /* read chartype command arguments and perform it */ 137 | static void eqn_chartype(void) 138 | { 139 | char gl[GNLEN], type[NMLEN]; 140 | snprintf(type, sizeof(type), "%s", tok_quotes(tok_poptext(1))); 141 | snprintf(gl, sizeof(gl), "%s", tok_quotes(tok_poptext(1))); 142 | if (typenum(type) >= 0) 143 | def_typeput(gl, typenum(type)); 144 | } 145 | 146 | /* read breakcost command arguments and perform it */ 147 | static void eqn_breakcost(void) 148 | { 149 | char tok[NMLEN]; 150 | int cost, type; 151 | snprintf(tok, sizeof(tok), "%s", tok_quotes(tok_poptext(1))); 152 | cost = atoi(tok_poptext(1)); 153 | type = !strcmp("any", tok) ? 0 : typenum(tok); 154 | if (type >= 0) 155 | def_brcostput(type, cost); 156 | } 157 | 158 | /* read general eqn commands */ 159 | static int eqn_commands(void) 160 | { 161 | char var[LNLEN]; 162 | char *sz; 163 | if (!tok_jmp("delim")) { 164 | tok_delim(); 165 | return 0; 166 | } 167 | if (!tok_jmp("define")) { 168 | tok_macro(); 169 | return 0; 170 | } 171 | if (!tok_jmp("gfont")) { 172 | strcpy(gfont, tok_quotes(tok_poptext(1))); 173 | return 0; 174 | } 175 | if (!tok_jmp("grfont")) { 176 | strcpy(grfont, tok_quotes(tok_poptext(1))); 177 | return 0; 178 | } 179 | if (!tok_jmp("gbfont")) { 180 | strcpy(gbfont, tok_quotes(tok_poptext(1))); 181 | return 0; 182 | } 183 | if (!tok_jmp("gsize")) { 184 | sz = tok_quotes(tok_poptext(1)); 185 | if (sz[0] == '-' || sz[0] == '+') 186 | sprintf(gsize, "\\n%s%s", escarg(EQNSZ), sz); 187 | else 188 | strcpy(gsize, sz); 189 | return 0; 190 | } 191 | if (!tok_jmp("set")) { 192 | strcpy(var, tok_poptext(1)); 193 | def_set(var, atoi(tok_poptext(1))); 194 | return 0; 195 | } 196 | if (!tok_jmp("bracketsizes")) { 197 | eqn_bracketsizes(); 198 | return 0; 199 | } 200 | if (!tok_jmp("bracketpieces")) { 201 | eqn_bracketpieces(); 202 | return 0; 203 | } 204 | if (!tok_jmp("chartype")) { 205 | eqn_chartype(); 206 | return 0; 207 | } 208 | if (!tok_jmp("breakcost")) { 209 | eqn_breakcost(); 210 | return 0; 211 | } 212 | return 1; 213 | } 214 | 215 | /* read user-specified spaces */ 216 | static int eqn_gaps(struct box *box, int szreg) 217 | { 218 | if (!tok_jmp("~")) { 219 | box_puttext(box, T_GAP, "\\h'%du*%sp/100u'", 220 | S_S3, nreg(szreg)); 221 | return 0; 222 | } 223 | if (!tok_jmp("^")) { 224 | box_puttext(box, T_GAP, "\\h'%du*%sp/100u'", 225 | S_S1, nreg(szreg)); 226 | return 0; 227 | } 228 | if (!tok_jmp("\t")) { 229 | box_puttext(box, T_GAP, "\t"); 230 | return 0; 231 | } 232 | return 1; 233 | } 234 | 235 | /* return the font of the given token type */ 236 | static char *tok_font(int tok, char *fn) 237 | { 238 | if (fn && fn[0]) 239 | return fn; 240 | if (tok == T_LETTER || tok == T_STRING) 241 | return gfont; 242 | return grfont; 243 | } 244 | 245 | /* check the next token */ 246 | static void tok_expect(char *s) 247 | { 248 | if (tok_jmp(s)) { 249 | fprintf(stderr, "neateqn: expected %s bot got %s\n", 250 | s, tok_get()); 251 | exit(1); 252 | } 253 | } 254 | 255 | /* read pile command */ 256 | static void eqn_pile(struct box *box, int sz0, char *fn0, int adj) 257 | { 258 | struct box *pile[NPILES] = {NULL}; 259 | int i; 260 | int n = 0; 261 | int rowspace = 0; 262 | if (tok_jmp("{")) { 263 | rowspace = atoi(tok_poptext(1)); 264 | tok_expect("{"); 265 | } 266 | do { 267 | if (!tok_get()) 268 | errdie("neateqn: unterminated pile\n"); 269 | pile[n++] = box_alloc(sz0, 0, box->style); 270 | } while (!eqn_boxuntil(pile[n - 1], sz0, fn0, "above")); 271 | tok_expect("}"); 272 | box_pile(box, pile, adj, rowspace); 273 | for (i = 0; i < n; i++) 274 | box_free(pile[i]); 275 | } 276 | 277 | /* read matrix command */ 278 | static void eqn_matrix(struct box *box, int sz0, char *fn0) 279 | { 280 | struct box *cols[NPILES][NPILES] = {{NULL}}; 281 | int adj[NPILES]; 282 | int nrows; 283 | int ncols = 0; 284 | int colspace = 0; 285 | int rowspace = 0; 286 | int i, j; 287 | if (tok_jmp("{")) { 288 | colspace = atoi(tok_poptext(1)); 289 | tok_expect("{"); 290 | } 291 | while (1) { 292 | if (!tok_jmp("col") || !tok_jmp("ccol")) 293 | adj[ncols] = 'c'; 294 | else if (!tok_jmp("lcol")) 295 | adj[ncols] = 'l'; 296 | else if (!tok_jmp("rcol")) 297 | adj[ncols] = 'r'; 298 | else 299 | break; 300 | nrows = 0; 301 | if (tok_jmp("{")) { 302 | i = atoi(tok_poptext(1)); 303 | if (i > rowspace) 304 | rowspace = i; 305 | tok_expect("{"); 306 | } 307 | do { 308 | if (!tok_get()) 309 | errdie("neateqn: unterminated matrix col\n"); 310 | cols[ncols][nrows++] = box_alloc(sz0, 0, box->style); 311 | } while (!eqn_boxuntil(cols[ncols][nrows - 1], 312 | sz0, fn0, "above")); 313 | tok_expect("}"); 314 | ncols++; 315 | } 316 | tok_expect("}"); 317 | box_matrix(box, ncols, cols, adj, colspace, rowspace); 318 | for (i = 0; i < ncols; i++) 319 | for (j = 0; j < NPILES; j++) 320 | if (cols[i][j]) 321 | box_free(cols[i][j]); 322 | } 323 | 324 | /* return nonzero if fn is italic */ 325 | static int italic(char *fn) 326 | { 327 | return (!strcmp("I", fn) || !strcmp("2", fn) || 328 | gfont == fn || !strcmp(gfont, fn)) ? T_ITALIC : 0; 329 | } 330 | 331 | /* read a box without fractions */ 332 | static struct box *eqn_left(int flg, struct box *pre, int sz0, char *fn0) 333 | { 334 | struct box *box = NULL; 335 | struct box *sub_sub = NULL, *sub_sup = NULL; 336 | struct box *sub_from = NULL, *sub_to = NULL; 337 | struct box *sqrt, *inner; 338 | char left[NMLEN] = "", right[NMLEN] = ""; 339 | char fn[FNLEN] = ""; 340 | int sz = sz0; 341 | int subsz; 342 | int type, dx = 0, dy = 0; 343 | int style = EQN_TSMASK & flg; 344 | if (fn0) 345 | strcpy(fn, fn0); 346 | while (!eqn_commands()) 347 | ; 348 | box = box_alloc(sz, pre ? pre->tcur : 0, style); 349 | if (!eqn_gaps(box, sz)) { 350 | while (!eqn_gaps(box, sz)) 351 | ; 352 | return box; 353 | } 354 | while (1) { 355 | if (!tok_jmp("fat")) { 356 | } else if (!tok_jmp("roman")) { 357 | strcpy(fn, grfont); 358 | } else if (!tok_jmp("italic")) { 359 | strcpy(fn, gfont); 360 | } else if (!tok_jmp("bold")) { 361 | strcpy(fn, gbfont); 362 | } else if (!tok_jmp("font")) { 363 | strcpy(fn, tok_poptext(1)); 364 | } else if (!tok_jmp("size")) { 365 | sz = box_size(box, tok_poptext(1)); 366 | } else if (!tok_jmp("fwd")) { 367 | dx += atoi(tok_poptext(1)); 368 | } else if (!tok_jmp("back")) { 369 | dx -= atoi(tok_poptext(1)); 370 | } else if (!tok_jmp("down")) { 371 | dy += atoi(tok_poptext(1)); 372 | } else if (!tok_jmp("up")) { 373 | dy -= atoi(tok_poptext(1)); 374 | } else { 375 | break; 376 | } 377 | } 378 | if (!tok_jmp("sqrt")) { 379 | sqrt = eqn_left(TS_MK0(style), NULL, sz, fn); 380 | printf(".ft %s\n", grfont); 381 | box_sqrt(box, sqrt); 382 | box_free(sqrt); 383 | } else if (!tok_jmp("type")) { 384 | type = typenum(tok_poptext(1)); 385 | inner = eqn_left(flg, pre, sz, fn); 386 | inner->tbeg = inner->tcur = type; 387 | box_merge(box, inner, 1); 388 | box_free(inner); 389 | box->tcur = type; 390 | } else if (!tok_jmp("pile") || !tok_jmp("cpile")) { 391 | eqn_pile(box, sz, fn, 'c'); 392 | } else if (!tok_jmp("lpile")) { 393 | eqn_pile(box, sz, fn, 'l'); 394 | } else if (!tok_jmp("rpile")) { 395 | eqn_pile(box, sz, fn, 'r'); 396 | } else if (!tok_jmp("matrix")) { 397 | eqn_matrix(box, sz, fn); 398 | } else if (!tok_jmp("vcenter")) { 399 | inner = eqn_left(flg, pre, sz, fn); 400 | box_vcenter(box, inner); 401 | box_free(inner); 402 | } else if (!tok_jmp("{")) { 403 | eqn_boxuntil(box, sz, fn, "}"); 404 | } else if (!tok_jmp("left")) { 405 | inner = box_alloc(sz, 0, style); 406 | snprintf(left, sizeof(left), "%s", tok_quotes(tok_poptext(0))); 407 | eqn_boxuntil(inner, sz, fn, "right"); 408 | snprintf(right, sizeof(right), "%s", tok_quotes(tok_poptext(0))); 409 | printf(".ft %s\n", grfont); 410 | box_wrap(box, inner, left[0] ? left : NULL, 411 | right[0] ? right : NULL); 412 | box_free(inner); 413 | } else if (tok_get() && tok_type() != T_KEYWORD) { 414 | if (dx || dy) 415 | box_move(box, dy, dx); 416 | box_putf(box, "\\s%s", escarg(nreg(sz))); 417 | do { 418 | char *cfn = tok_font(tok_type(), fn); 419 | int chops; 420 | box_puttext(box, tok_type() | italic(cfn), "\\f%s%s", 421 | escarg(cfn), tok_improve(tok_get())); 422 | chops = tok_chops(0); 423 | tok_pop(); 424 | if (chops) /* what we read was a splitting */ 425 | break; 426 | } while (!tok_chops(0)); /* the next token is splitting */ 427 | if (dx || dy) 428 | box_move(box, -dy, -dx); 429 | } 430 | while (tok_get()) { 431 | if (!tok_jmp("dyad")) { 432 | printf(".ft %s\n", grfont); 433 | box_accent(box, "\\(ab"); 434 | } else if (!tok_jmp("bar")) { 435 | printf(".ft %s\n", grfont); 436 | box_bar(box); 437 | } else if (!tok_jmp("under")) { 438 | printf(".ft %s\n", grfont); 439 | box_under(box); 440 | } else if (!tok_jmp("vec")) { 441 | printf(".ft %s\n", grfont); 442 | box_accent(box, "\\s[\\n(.s/2u]\\(->\\s0"); 443 | } else if (!tok_jmp("tilde")) { 444 | printf(".ft %s\n", grfont); 445 | box_accent(box, "\\s[\\n(.s*3u/4u]\\(ap\\s0"); 446 | } else if (!tok_jmp("hat")) { 447 | printf(".ft %s\n", grfont); 448 | box_accent(box, "ˆ"); 449 | } else if (!tok_jmp("dot")) { 450 | printf(".ft %s\n", grfont); 451 | box_accent(box, "."); 452 | } else if (!tok_jmp("dotdot")) { 453 | printf(".ft %s\n", grfont); 454 | box_accent(box, ".."); 455 | } else { 456 | break; 457 | } 458 | } 459 | subsz = nregmk(); 460 | if (!tok_jmp("sub")) { 461 | sizesub(subsz, sz0, ts_sup(style), style); 462 | sub_sub = eqn_left(ts_sup(style) | EQN_SUB, NULL, subsz, fn0); 463 | } 464 | if ((sub_sub || !(flg & EQN_SUB)) && !tok_jmp("sup")) { 465 | sizesub(subsz, sz0, ts_sub(style), style); 466 | sub_sup = eqn_left(ts_sub(style), NULL, subsz, fn0); 467 | } 468 | if (sub_sub || sub_sup) 469 | box_sub(box, sub_sub, sub_sup); 470 | if (!tok_jmp("from")) { 471 | sizesub(subsz, sz0, ts_sub(style), style); 472 | sub_from = eqn_left(ts_sub(style) | EQN_FROM, NULL, subsz, fn0); 473 | } 474 | if ((sub_from || !(flg & EQN_FROM)) && !tok_jmp("to")) { 475 | sizesub(subsz, sz0, ts_sup(style), style); 476 | sub_to = eqn_left(ts_sup(style), NULL, subsz, fn0); 477 | } 478 | if (sub_from || sub_to) { 479 | inner = box_alloc(sz0, 0, style); 480 | box_from(inner, box, sub_from, sub_to); 481 | box_free(box); 482 | box = inner; 483 | } 484 | nregrm(subsz); 485 | if (sub_sub) 486 | box_free(sub_sub); 487 | if (sub_sup) 488 | box_free(sub_sup); 489 | if (sub_from) 490 | box_free(sub_from); 491 | if (sub_to) 492 | box_free(sub_to); 493 | return box; 494 | } 495 | 496 | /* read a box */ 497 | static struct box *eqn_box(int flg, struct box *pre, int sz0, char *fn0) 498 | { 499 | struct box *box; 500 | struct box *sub_num = NULL, *sub_den = NULL; 501 | int style = flg & EQN_TSMASK; 502 | box = eqn_left(flg, pre, sz0, fn0); 503 | while (!tok_jmp("over")) { 504 | sub_num = box; 505 | sub_den = eqn_left(TS_MK0(style), NULL, sz0, fn0); 506 | box = box_alloc(sz0, pre ? pre->tcur : 0, style); 507 | printf(".ft %s\n", grfont); 508 | box_over(box, sub_num, sub_den); 509 | box_free(sub_num); 510 | box_free(sub_den); 511 | } 512 | return box; 513 | } 514 | 515 | /* read an equation, either inline or block */ 516 | static struct box *eqn_read(int style) 517 | { 518 | struct box *box, *sub; 519 | int szreg = nregmk(); 520 | printf(".nr %s %s\n", nregname(szreg), gsize); 521 | box = box_alloc(szreg, 0, style); 522 | while (tok_get()) { 523 | if (!tok_jmp("mark")) { 524 | eqn_mk = !eqn_mk ? 1 : eqn_mk; 525 | box_markpos(box, EQNMK); 526 | continue; 527 | } 528 | if (!tok_jmp("lineup")) { 529 | eqn_mk = 2; 530 | box_markpos(box, nregname(eqn_lineupreg)); 531 | sprintf(eqn_lineup, "\\h'\\n%su-%su'", 532 | escarg(EQNMK), nreg(eqn_lineupreg)); 533 | continue; 534 | } 535 | sub = eqn_box(style, box, szreg, NULL); 536 | box_merge(box, sub, 1); 537 | box_free(sub); 538 | } 539 | box_vertspace(box); 540 | nregrm(szreg); 541 | return box; 542 | } 543 | 544 | void errdie(char *msg) 545 | { 546 | fprintf(stderr, "%s", msg); 547 | exit(1); 548 | } 549 | 550 | int main(int argc, char **argv) 551 | { 552 | struct box *box; 553 | char eqnblk[128]; 554 | int i; 555 | for (i = 1; i < argc; i++) { 556 | if (argv[i][0] != '-' || !argv[i][1]) 557 | break; 558 | if (argv[i][1] == 'c') { 559 | def_choppedset(argv[i][2] ? argv[i] + 2 : argv[++i]); 560 | } else { 561 | fprintf(stderr, "Usage: neateqn [options] output\n\n" 562 | "Options:\n" 563 | " -c chars \tcharacters that chop equations\n"); 564 | return 1; 565 | } 566 | } 567 | for (i = 0; def_macros[i][0]; i++) 568 | src_define(def_macros[i][0], def_macros[i][1]); 569 | while (!tok_eqn()) { 570 | reg_reset(); 571 | eqn_mk = 0; 572 | tok_pop(); 573 | printf(".nr %s \\n(.s\n", EQNSZ); 574 | printf(".nr %s \\n(.f\n", EQNFN); 575 | eqn_lineupreg = nregmk(); 576 | box = eqn_read(tok_inline() ? TS_T : TS_D); 577 | printf(".nr MK %d\n", eqn_mk); 578 | if (!box_empty(box)) { 579 | sprintf(eqnblk, "%s%s", eqn_lineup, box_toreg(box)); 580 | tok_eqnout(eqnblk); 581 | printf(".ps \\n%s\n", escarg(EQNSZ)); 582 | printf(".ft \\n%s\n", escarg(EQNFN)); 583 | } 584 | printf(".lf %d\n", src_lineget()); 585 | eqn_lineup[0] = '\0'; 586 | nregrm(eqn_lineupreg); 587 | box_free(box); 588 | } 589 | src_done(); 590 | return 0; 591 | } 592 | -------------------------------------------------------------------------------- /eqn.h: -------------------------------------------------------------------------------- 1 | /* 2 | * NEATEQN MAIN HEADER 3 | * 4 | * In Neateqn equations are recursively decomposed into boxes. eqn.c 5 | * reads the input and makes eqn boxes by calling appropriate functions 6 | * from box.c. 7 | */ 8 | /* predefined array sizes */ 9 | #define FNLEN 32 /* font name length */ 10 | #define SZLEN 32 /* point size length */ 11 | #define LNLEN 1000 /* line length */ 12 | #define NMLEN 32 /* macro name length */ 13 | #define RLEN 8 /* register name length */ 14 | #define NPILES 32 /* number of piled items */ 15 | #define NSIZES 8 /* number of bracket sizes */ 16 | #define GNLEN 32 /* glyph name length */ 17 | #define BRLEN 64 /* bracket definition length */ 18 | 19 | /* registers used by neateqn */ 20 | #define EQNSZ ".eqnsz" /* register for surrounding point size */ 21 | #define EQNFN ".eqnfn" /* register for surrounding font */ 22 | #define EQNS "10" /* eqn-collected line register */ 23 | #define EQNMK ".eqnmk" /* eqn horizontal mark */ 24 | 25 | /* helpers */ 26 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 27 | #define MAX(a, b) ((a) < (b) ? (b) : (a)) 28 | #define LEN(a) (sizeof(a) / sizeof((a)[0])) 29 | 30 | /* token and atom types */ 31 | #define T_TOK(t) ((t) & 0x00ff) 32 | #define T_ATOM(t) ((t) & 0x00f0) 33 | #define T_FONT(t) ((t) & T_ITALIC) 34 | #define T_ATOMIDX(t) ((((t) >> 4) & 15) - 1) 35 | 36 | #define T_EOF 0x0000 /* parser-specific */ 37 | #define T_SPACE 0x0001 38 | #define T_TAB 0x0002 39 | #define T_KEYWORD 0x0003 40 | #define T_ORD 0x0010 /* ordinary */ 41 | #define T_LETTER 0x0011 42 | #define T_NUMBER 0x0012 43 | #define T_STRING 0x0013 44 | #define T_GAP 0x0014 45 | #define T_BIGOP 0x0020 /* big operators */ 46 | #define T_BINOP 0x0030 /* binary operators */ 47 | #define T_RELOP 0x0040 /* relational symbol */ 48 | #define T_LEFT 0x0050 /* opening token */ 49 | #define T_RIGHT 0x0060 /* closing token */ 50 | #define T_PUNC 0x0070 /* punctuation symbol */ 51 | #define T_INNER 0x0080 /* like fractions */ 52 | 53 | #define T_ITALIC 0x0100 /* atom with italic font */ 54 | 55 | /* spaces in hundredths of em */ 56 | #define S_S1 e_thinspace /* thin space */ 57 | #define S_S2 e_mediumspace /* medium space */ 58 | #define S_S3 e_thickspace /* thick space */ 59 | 60 | /* small helper functions */ 61 | void errdie(char *msg); 62 | 63 | /* reading the source */ 64 | int src_next(void); 65 | void src_back(int c); 66 | void src_define(char *name, char *def); 67 | int src_expand(char *name, char **args); 68 | int src_macro(char *name); 69 | int src_arg(int i); 70 | int src_top(void); 71 | int src_lineget(void); 72 | void src_lineset(int n); 73 | void src_done(void); 74 | 75 | /* tokenizer */ 76 | int tok_eqn(void); 77 | void tok_eqnout(char *s); 78 | char *tok_get(void); 79 | char *tok_pop(void); 80 | char *tok_poptext(int sep); 81 | int tok_jmp(char *kwd); 82 | int tok_type(void); 83 | int tok_chops(int soft); 84 | void tok_delim(void); 85 | void tok_macro(void); 86 | int tok_inline(void); 87 | 88 | /* default definitions and operators */ 89 | int def_type(char *s); 90 | void def_typeput(char *s, int type); 91 | int def_chopped(int c); 92 | void def_choppedset(char *s); 93 | void def_pieces(char *sign, char **top, char **mid, char **bot, char **cen); 94 | void def_sizes(char *sign, char *sizes[]); 95 | int def_brcost(int type); 96 | void def_piecesput(char *sign, char *top, char *mid, char *bot, char *cen); 97 | void def_sizesput(char *sign, char *sizes[]); 98 | void def_brcostput(int type, int cost); 99 | extern char *def_macros[][2]; 100 | 101 | /* variable length string buffer */ 102 | struct sbuf { 103 | char *s; /* allocated buffer */ 104 | int sz; /* buffer size */ 105 | int n; /* length of the string stored in s */ 106 | }; 107 | 108 | void sbuf_init(struct sbuf *sbuf); 109 | void sbuf_done(struct sbuf *sbuf); 110 | char *sbuf_buf(struct sbuf *sbuf); 111 | void sbuf_add(struct sbuf *sbuf, int c); 112 | void sbuf_append(struct sbuf *sbuf, char *s); 113 | void sbuf_printf(struct sbuf *sbuf, char *s, ...); 114 | void sbuf_cut(struct sbuf *sbuf, int n); 115 | int sbuf_len(struct sbuf *sbuf); 116 | int sbuf_empty(struct sbuf *sbuf); 117 | 118 | /* tex styles */ 119 | #define TS_D 0x00 120 | #define TS_D0 0x01 121 | #define TS_T 0x02 122 | #define TS_T0 0x03 123 | #define TS_S 0x10 124 | #define TS_S0 0x11 125 | #define TS_SS 0x20 126 | #define TS_SS0 0x21 127 | 128 | #define TS_MK(s, p) (((s) << 4) | (p)) 129 | #define TS_0(s) ((s) & 0x01) /* primed variant */ 130 | #define TS_MK0(s) ((s) | 0x01) /* make style primed */ 131 | #define TS_SZ(s) ((s) >> 4) /* character size */ 132 | #define TS_DX(s) ((s) == TS_D || (s) == TS_D0) 133 | #define TS_TXT(s) ((s) & 0x02) /* text style */ 134 | 135 | int ts_sup(int style); 136 | int ts_sub(int style); 137 | int ts_denom(int style); 138 | int ts_num(int style); 139 | 140 | /* equations */ 141 | struct box { 142 | struct sbuf raw; /* the contents */ 143 | int szreg, szown; /* number register holding box size */ 144 | int reg; /* register holding the contents */ 145 | int atoms; /* the number of atoms inserted */ 146 | int tbeg, tcur; /* type of the first and the last atoms */ 147 | int style; /* tex style (TS_*) */ 148 | char *tomark; /* register for saving box width */ 149 | }; 150 | 151 | struct box *box_alloc(int szreg, int at_pre, int style); 152 | void box_free(struct box *box); 153 | void box_puttext(struct box *box, int type, char *s, ...); 154 | void box_putf(struct box *box, char *s, ...); 155 | int box_size(struct box *box, char *val); 156 | void box_merge(struct box *box, struct box *sub, int breakable); 157 | void box_sub(struct box *box, struct box *sub, struct box *sup); 158 | void box_from(struct box *box, struct box *lim, struct box *llim, struct box *ulim); 159 | void box_over(struct box *box, struct box *num, struct box *denom); 160 | void box_sqrt(struct box *box, struct box *sub); 161 | void box_bar(struct box *box); 162 | void box_under(struct box *box); 163 | void box_accent(struct box *box, char *c); 164 | void box_wrap(struct box *box, struct box *sub, char *left, char *right); 165 | void box_move(struct box *box, int dy, int dx); 166 | void box_gap(struct box *box, int n); 167 | char *box_buf(struct box *box); 168 | char *box_toreg(struct box *box); 169 | void box_vertspace(struct box *box); 170 | int box_empty(struct box *box); 171 | void box_markpos(struct box *box, char *regname); 172 | void box_vcenter(struct box *box, struct box *sub); 173 | void box_pile(struct box *box, struct box **pile, int adj, int rowspace); 174 | void box_matrix(struct box *box, int ncols, struct box *cols[][NPILES], 175 | int *adj, int colspace, int rowspace); 176 | 177 | /* managing registers */ 178 | char *escarg(char *arg); 179 | int sregmk(void); 180 | void sregrm(int id); 181 | int nregmk(void); 182 | void nregrm(int id); 183 | char *nreg(int id); 184 | char *sreg(int id); 185 | char *nregname(int id); 186 | char *sregname(int id); 187 | void reg_reset(void); 188 | 189 | /* eqn global variables */ 190 | extern int e_axisheight; 191 | extern int e_minimumsize; 192 | extern int e_overhang; 193 | extern int e_nulldelim; 194 | extern int e_scriptspace; 195 | extern int e_thinspace; 196 | extern int e_mediumspace; 197 | extern int e_thickspace; 198 | extern int e_num1; 199 | extern int e_num2; 200 | extern int e_denom1; 201 | extern int e_denom2; 202 | extern int e_sup1; 203 | extern int e_sup2; 204 | extern int e_sup3; 205 | extern int e_sub1; 206 | extern int e_sub2; 207 | extern int e_supdrop; 208 | extern int e_subdrop; 209 | extern int e_xheight; 210 | extern int e_rulethickness; 211 | extern int e_bigopspacing1; 212 | extern int e_bigopspacing2; 213 | extern int e_bigopspacing3; 214 | extern int e_bigopspacing4; 215 | extern int e_bigopspacing5; 216 | extern int e_columnsep; 217 | extern int e_baselinesep; 218 | extern int e_bodyheight; 219 | extern int e_bodydepth; 220 | void def_set(char *name, int val); 221 | -------------------------------------------------------------------------------- /reg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "eqn.h" 5 | 6 | #define NREGS 2048 7 | #define EPREFIX "" 8 | 9 | static int sreg_max; /* maximum allocated string register */ 10 | static int sreg_free[NREGS]; /* free string registers */ 11 | static int sreg_n; /* number of items in sreg_free[] */ 12 | static char sreg_name[NREGS][RLEN]; 13 | static char sreg_read[NREGS][RLEN]; 14 | 15 | static int nreg_max; 16 | static int nreg_free[NREGS]; 17 | static int nreg_n; 18 | static char nreg_name[NREGS][RLEN]; 19 | static char nreg_read[NREGS][RLEN]; 20 | 21 | /* allocate a troff string register */ 22 | int sregmk(void) 23 | { 24 | int id = sreg_n ? sreg_free[--sreg_n] : ++sreg_max; 25 | sprintf(sreg_name[id], "%s%02d", EPREFIX, id); 26 | sprintf(sreg_read[id], "\\*%s", escarg(sreg_name[id])); 27 | return id; 28 | } 29 | 30 | /* free a troff string register */ 31 | void sregrm(int id) 32 | { 33 | sreg_free[sreg_n++] = id; 34 | } 35 | 36 | char *sregname(int id) 37 | { 38 | return sreg_name[id]; 39 | } 40 | 41 | char *sreg(int id) 42 | { 43 | return sreg_read[id]; 44 | } 45 | 46 | /* allocate a troff number register */ 47 | int nregmk(void) 48 | { 49 | int id = nreg_n ? nreg_free[--nreg_n] : ++nreg_max; 50 | sprintf(nreg_name[id], "%s%02d", EPREFIX, id); 51 | sprintf(nreg_read[id], "\\n%s", escarg(nreg_name[id])); 52 | return id; 53 | } 54 | 55 | /* free a troff number register */ 56 | void nregrm(int id) 57 | { 58 | nreg_free[nreg_n++] = id; 59 | } 60 | 61 | char *nregname(int id) 62 | { 63 | return nreg_name[id]; 64 | } 65 | 66 | char *nreg(int id) 67 | { 68 | return nreg_read[id]; 69 | } 70 | 71 | /* free all allocated registers */ 72 | void reg_reset(void) 73 | { 74 | nreg_max = 0; 75 | nreg_n = 0; 76 | sreg_max = 11; 77 | sreg_n = 0; 78 | } 79 | 80 | /* format the argument of a troff escape like \s or \f */ 81 | char *escarg(char *arg) 82 | { 83 | static char buf[256]; 84 | if (!arg[1]) 85 | sprintf(buf, "%c", arg[0]); 86 | else if (!arg[2]) 87 | sprintf(buf, "(%c%c", arg[0], arg[1]); 88 | else 89 | sprintf(buf, "[%s]", arg); 90 | return buf; 91 | } 92 | -------------------------------------------------------------------------------- /sbuf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "eqn.h" 6 | 7 | #define SBUF_SZ 512 8 | 9 | static void sbuf_extend(struct sbuf *sbuf, int amount) 10 | { 11 | char *s = sbuf->s; 12 | sbuf->sz = (MAX(1, amount) + SBUF_SZ - 1) & ~(SBUF_SZ - 1); 13 | sbuf->s = malloc(sbuf->sz); 14 | if (sbuf->n) 15 | memcpy(sbuf->s, s, sbuf->n); 16 | free(s); 17 | } 18 | 19 | void sbuf_init(struct sbuf *sbuf) 20 | { 21 | memset(sbuf, 0, sizeof(*sbuf)); 22 | sbuf_extend(sbuf, SBUF_SZ); 23 | } 24 | 25 | void sbuf_add(struct sbuf *sbuf, int c) 26 | { 27 | if (sbuf->n + 2 >= sbuf->sz) 28 | sbuf_extend(sbuf, sbuf->sz * 2); 29 | sbuf->s[sbuf->n++] = c; 30 | } 31 | 32 | void sbuf_append(struct sbuf *sbuf, char *s) 33 | { 34 | int len = strlen(s); 35 | if (sbuf->n + len + 1 >= sbuf->sz) 36 | sbuf_extend(sbuf, sbuf->n + len + 1); 37 | memcpy(sbuf->s + sbuf->n, s, len); 38 | sbuf->n += len; 39 | } 40 | 41 | void sbuf_printf(struct sbuf *sbuf, char *s, ...) 42 | { 43 | char buf[LNLEN]; 44 | va_list ap; 45 | va_start(ap, s); 46 | vsprintf(buf, s, ap); 47 | va_end(ap); 48 | sbuf_append(sbuf, buf); 49 | } 50 | 51 | int sbuf_empty(struct sbuf *sbuf) 52 | { 53 | return !sbuf->n; 54 | } 55 | 56 | char *sbuf_buf(struct sbuf *sbuf) 57 | { 58 | sbuf->s[sbuf->n] = '\0'; 59 | return sbuf->s; 60 | } 61 | 62 | int sbuf_len(struct sbuf *sbuf) 63 | { 64 | return sbuf->n; 65 | } 66 | 67 | /* shorten the sbuf */ 68 | void sbuf_cut(struct sbuf *sbuf, int n) 69 | { 70 | if (sbuf->n > n) 71 | sbuf->n = n; 72 | } 73 | 74 | void sbuf_done(struct sbuf *sbuf) 75 | { 76 | free(sbuf->s); 77 | } 78 | -------------------------------------------------------------------------------- /src.c: -------------------------------------------------------------------------------- 1 | /* reading input */ 2 | #include 3 | #include 4 | #include 5 | #include "eqn.h" 6 | 7 | #define NARGS 10 /* number of arguments */ 8 | #define NMACROS 512 /* number of macros */ 9 | #define NSRCDEP 512 /* maximum esrc_depth */ 10 | 11 | /* eqn input stream */ 12 | struct esrc { 13 | struct esrc *prev; /* previous buffer */ 14 | char *buf; /* input buffer; NULL for stdin */ 15 | int pos; /* current position in buf */ 16 | int unbuf[LNLEN]; /* push-back buffer */ 17 | int uncnt; 18 | char *args[NARGS]; /* macro arguments */ 19 | int call; /* is a macro call */ 20 | }; 21 | 22 | static struct esrc esrc_stdin; /* the default input stream */ 23 | static struct esrc *esrc = &esrc_stdin; 24 | static int lineno = 1; /* current line number */ 25 | static int esrc_depth; /* the length of esrc chain */ 26 | 27 | static char *src_strdup(char *s) 28 | { 29 | char *d = malloc(strlen(s) + 1); 30 | strcpy(d, s); 31 | return d; 32 | } 33 | 34 | /* push buf in the input stream; this is a macro call if args is not NULL */ 35 | static void src_push(char *buf, char **args) 36 | { 37 | struct esrc *next; 38 | int i; 39 | if (esrc_depth > NSRCDEP) 40 | errdie("neateqn: macro recursion limit reached\n"); 41 | next = malloc(sizeof(*next)); 42 | memset(next, 0, sizeof(*next)); 43 | next->prev = esrc; 44 | next->buf = src_strdup(buf); 45 | next->call = args != NULL; 46 | if (args) 47 | for (i = 0; i < NARGS; i++) 48 | next->args[i] = args[i] ? src_strdup(args[i]) : NULL; 49 | esrc = next; 50 | esrc_depth++; 51 | } 52 | 53 | /* back to the previous esrc buffer */ 54 | static void src_pop(void) 55 | { 56 | struct esrc *prev = esrc->prev; 57 | int i; 58 | if (prev) { 59 | for (i = 0; i < NARGS; i++) 60 | free(esrc->args[i]); 61 | free(esrc->buf); 62 | free(esrc); 63 | esrc = prev; 64 | esrc_depth--; 65 | } 66 | } 67 | 68 | static int src_stdin(void) 69 | { 70 | int c = fgetc(stdin); 71 | if (c == '\n') 72 | lineno++; 73 | return c; 74 | } 75 | 76 | /* read the next character */ 77 | int src_next(void) 78 | { 79 | while (1) { 80 | if (esrc->uncnt) 81 | return esrc->unbuf[--esrc->uncnt]; 82 | if (!esrc->prev) 83 | return src_stdin(); 84 | if (esrc->buf[esrc->pos]) 85 | return (unsigned char) esrc->buf[esrc->pos++]; 86 | src_pop(); 87 | } 88 | return 0; 89 | } 90 | 91 | /* push back c */ 92 | void src_back(int c) 93 | { 94 | if (c > 0) 95 | esrc->unbuf[esrc->uncnt++] = c; 96 | } 97 | 98 | int src_lineget(void) 99 | { 100 | return lineno; 101 | } 102 | 103 | void src_lineset(int n) 104 | { 105 | lineno = n; 106 | } 107 | 108 | /* eqn macros */ 109 | struct macro { 110 | char name[NMLEN]; 111 | char *def; 112 | }; 113 | static struct macro macros[NMACROS]; 114 | static int nmacros; 115 | 116 | static int src_findmacro(char *name) 117 | { 118 | int i; 119 | for (i = 0; i < nmacros; i++) 120 | if (!strcmp(macros[i].name, name)) 121 | return i; 122 | return -1; 123 | } 124 | 125 | /* return nonzero if name is a macro */ 126 | int src_macro(char *name) 127 | { 128 | return src_findmacro(name) >= 0; 129 | } 130 | 131 | /* define a macro */ 132 | void src_define(char *name, char *def) 133 | { 134 | int idx = src_findmacro(name); 135 | if (idx < 0 && nmacros < NMACROS) 136 | idx = nmacros++; 137 | if (idx >= 0) { 138 | strcpy(macros[idx].name, name); 139 | free(macros[idx].def); 140 | macros[idx].def = src_strdup(def); 141 | } 142 | } 143 | 144 | void src_done(void) 145 | { 146 | int i; 147 | for (i = 0; i < nmacros; i++) 148 | free(macros[i].def); 149 | } 150 | 151 | /* expand macro */ 152 | int src_expand(char *name, char **args) 153 | { 154 | int i = src_findmacro(name); 155 | if (i >= 0) 156 | src_push(macros[i].def, args); 157 | return i < 0; 158 | } 159 | 160 | /* expand argument */ 161 | int src_arg(int i) 162 | { 163 | int call = esrc->call; 164 | if (call && esrc->args[i - 1]) 165 | src_push(esrc->args[i - 1], NULL); 166 | return call ? 0 : 1; 167 | } 168 | 169 | /* return one if not reading macros and their arguments */ 170 | int src_top(void) 171 | { 172 | return !esrc->prev; 173 | } 174 | -------------------------------------------------------------------------------- /tok.c: -------------------------------------------------------------------------------- 1 | /* the preprocessor and tokenizer */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "eqn.h" 7 | 8 | #define T_BIN(c1, c2) (((c1) << 8) | (c2)) 9 | #define T_SOFTSEP ("^~{}(),\"\n\t =:|.+-*/\\,()[]<>!") 10 | #define ESAVE "\\E*[.eqnbeg]\\R'" EQNFN "0 \\En(.f'\\R'" EQNSZ "0 \\En(.s'" 11 | #define ELOAD "\\f[\\En[" EQNFN "0]]\\s[\\En[" EQNSZ "0]]\\E*[.eqnend]" 12 | 13 | static char *kwds[] = { 14 | "fwd", "down", "back", "up", 15 | "bold", "italic", "roman", "font", "fat", "size", "type", 16 | "bar", "dot", "dotdot", "dyad", "hat", "under", "vec", "tilde", 17 | "sub", "sup", "from", "to", "vcenter", 18 | "left", "right", "over", "sqrt", 19 | "pile", "lpile", "cpile", "rpile", "above", 20 | "matrix", "col", "ccol", "lcol", "rcol", 21 | "delim", "define", 22 | "gfont", "grfont", "gbfont", "gsize", "set", "chartype", 23 | "mark", "lineup", "bracketsizes", "bracketpieces", "breakcost", 24 | }; 25 | 26 | static int tok_eqen; /* non-zero if inside .EQ/.EN */ 27 | static int tok_line; /* inside inline eqn block */ 28 | static int tok_part; /* partial line with inline eqn blocks */ 29 | static char tok[LNLEN]; /* current token */ 30 | static char tok_prev[LNLEN]; /* previous token */ 31 | static int tok_curtype; /* type of current token */ 32 | static int tok_cursep; /* current character is a separator */ 33 | static int tok_prevsep; /* previous character was a separator */ 34 | static int eqn_beg, eqn_end; /* inline eqn delimiters */ 35 | 36 | /* return zero if troff request .ab is read */ 37 | static int tok_req(int a, int b) 38 | { 39 | int eqln[LNLEN]; 40 | int i = 0; 41 | int ret = 0; 42 | eqln[i++] = src_next(); 43 | if (eqln[i - 1] != '.') 44 | goto failed; 45 | eqln[i++] = src_next(); 46 | while (eqln[i - 1] == ' ' && i < sizeof(eqln) - 4) 47 | eqln[i++] = src_next(); 48 | if (eqln[i - 1] != a) 49 | goto failed; 50 | eqln[i++] = src_next(); 51 | if (eqln[i - 1] != b) 52 | goto failed; 53 | ret = 1; 54 | failed: 55 | while (i > 0) 56 | src_back(eqln[--i]); 57 | return ret; 58 | } 59 | 60 | /* read .EN */ 61 | static int tok_en(void) 62 | { 63 | return tok_req('E', 'N'); 64 | } 65 | 66 | /* does the line start with eq */ 67 | static int tok_eq(char *s) 68 | { 69 | if (*s++ != '.') 70 | return 0; 71 | while (isspace((unsigned char) *s)) 72 | s++; 73 | return s[0] == 'E' && s[1] == 'Q'; 74 | } 75 | 76 | /* read an lf request */ 77 | static int tok_lf(char *s) 78 | { 79 | if (*s++ != '.') 80 | return 0; 81 | while (isspace((unsigned char) *s)) 82 | s++; 83 | if (*s++ != 'l' || *s++ != 'f') 84 | return 0; 85 | while (isspace((unsigned char) *s)) 86 | s++; 87 | if (isdigit((unsigned char) *s)) 88 | src_lineset(atoi(s)); 89 | return 1; 90 | } 91 | 92 | /* read the next input character */ 93 | static int tok_next(void) 94 | { 95 | int c; 96 | if (!tok_eqen && !tok_line) 97 | return 0; 98 | c = src_next(); 99 | if (tok_eqen && c == '\n' && tok_en()) 100 | tok_eqen = 0; 101 | if (tok_line && (src_top() && c == eqn_end)) { 102 | tok_line = 0; 103 | return 0; 104 | } 105 | return c; 106 | } 107 | 108 | /* push back the last character read */ 109 | static void tok_back(int c) 110 | { 111 | if (tok_eqen || tok_line) 112 | src_back(c); 113 | } 114 | 115 | static int readchar(char *dst) 116 | { 117 | int c = src_next(); 118 | dst[0] = c; 119 | if (c == '\\') { 120 | c = src_next(); 121 | dst[1] = c; 122 | if (c == '(') { 123 | dst[2] = src_next(); 124 | dst[3] = src_next(); 125 | return 4; 126 | } 127 | if (c == '[') { 128 | int n = 2; 129 | while (c > 0 && c != ']') { 130 | c = src_next(); 131 | dst[n++] = c; 132 | } 133 | return n; 134 | } 135 | return 2; 136 | } 137 | return c > 0; 138 | } 139 | 140 | /* read the next word; if opstop, stop at open parenthesis */ 141 | static void tok_preview(char *s, int opstop) 142 | { 143 | int c = src_next(); 144 | int n = 0; 145 | if (c > 0 && def_chopped(c)) { 146 | src_back(c); 147 | n = readchar(s); 148 | } else { 149 | while (c > 0 && (!def_chopped(c) && (!opstop || c != '(')) && 150 | (!tok_line || (!src_top() || c != eqn_end))) { 151 | src_back(c); 152 | n += readchar(s + n); 153 | c = src_next(); 154 | } 155 | src_back(c); 156 | } 157 | s[n] = '\0'; 158 | } 159 | 160 | /* push back the given word */ 161 | static void tok_unpreview(char *s) 162 | { 163 | int n = strlen(s); 164 | while (n > 0) 165 | src_back((unsigned char) s[--n]); 166 | } 167 | 168 | /* read a keyword; return zero on success */ 169 | static int tok_keyword(void) 170 | { 171 | int i; 172 | tok_preview(tok, 0); 173 | for (i = 0; i < LEN(kwds); i++) 174 | if (!strcmp(kwds[i], tok)) 175 | return 0; 176 | tok_unpreview(tok); 177 | return 1; 178 | } 179 | 180 | /* read the next argument of a macro call; return zero if read a ',' */ 181 | static int tok_readarg(struct sbuf *sbuf) 182 | { 183 | int c = src_next(); 184 | int pdepth = 0; /* number of nested parenthesis */ 185 | int quotes = 0; /* inside double quotes */ 186 | while (c > 0 && (pdepth || quotes || (c != ',' && c != ')'))) { 187 | sbuf_add(sbuf, c); 188 | if (!quotes && c == ')') 189 | pdepth++; 190 | if (!quotes && c == '(') 191 | pdepth--; 192 | if (c == '"') 193 | quotes = 1 - quotes; 194 | if (c == '\\') { 195 | sbuf_add(sbuf, c = src_next()); 196 | if (c == '*' || c == 'n') 197 | sbuf_add(sbuf, c = src_next()); 198 | if (c == '(') { 199 | sbuf_add(sbuf, c = src_next()); 200 | sbuf_add(sbuf, c = src_next()); 201 | } else if (c == '[') { 202 | while (c > 0 && c != ']') 203 | sbuf_add(sbuf, c = src_next()); 204 | } 205 | } 206 | c = src_next(); 207 | } 208 | return c == ',' ? 0 : 1; 209 | } 210 | 211 | /* expand a macro; return zero on success */ 212 | static int tok_expand(void) 213 | { 214 | char *args[10] = {NULL}; 215 | struct sbuf sbufs[10]; 216 | int i, n = 0; 217 | tok_preview(tok, 1); 218 | if (src_macro(tok)) { 219 | int c = src_next(); 220 | src_back(c); 221 | if (c == '(') { /* macro arguments follow */ 222 | src_next(); 223 | while (n <= 9) { 224 | sbuf_init(&sbufs[n]); 225 | if (tok_readarg(&sbufs[n++])) 226 | break; 227 | } 228 | } 229 | for (i = 0; i < n; i++) 230 | args[i] = sbuf_buf(&sbufs[i]); 231 | src_expand(tok, args); 232 | for (i = 0; i < n; i++) 233 | sbuf_done(&sbufs[i]); 234 | return 0; 235 | } 236 | tok_unpreview(tok); 237 | return 1; 238 | } 239 | 240 | /* read until .EQ or eqn_beg */ 241 | int tok_eqn(void) 242 | { 243 | struct sbuf ln; 244 | int c; 245 | tok_cursep = 1; 246 | sbuf_init(&ln); 247 | while ((c = src_next()) > 0) { 248 | if (c == eqn_beg) { 249 | printf(".eo\n"); 250 | printf(".%s %s \"%s\n", 251 | tok_part ? "as" : "ds", EQNS, sbuf_buf(&ln)); 252 | sbuf_done(&ln); 253 | printf(".ec\n"); 254 | tok_part = 1; 255 | tok_line = 1; 256 | return 0; 257 | } 258 | sbuf_add(&ln, c); 259 | if (c == '\n' && !tok_part) { 260 | printf("%s", sbuf_buf(&ln)); 261 | tok_lf(sbuf_buf(&ln)); 262 | if (tok_eq(sbuf_buf(&ln)) && !tok_en()) { 263 | tok_eqen = 1; 264 | sbuf_done(&ln); 265 | return 0; 266 | } 267 | } 268 | if (c == '\n' && tok_part) { 269 | printf(".lf %d\n", src_lineget()); 270 | printf("\\*%s%s", escarg(EQNS), sbuf_buf(&ln)); 271 | tok_part = 0; 272 | } 273 | if (c == '\n') 274 | sbuf_cut(&ln, 0); 275 | } 276 | sbuf_done(&ln); 277 | return 1; 278 | } 279 | 280 | /* collect the output of this eqn block */ 281 | void tok_eqnout(char *s) 282 | { 283 | if (!tok_part) { 284 | printf(".ds %s \"%s%s%s\n", EQNS, ESAVE, s, ELOAD); 285 | printf(".lf %d\n", src_lineget() - 1); 286 | printf("\\&\\*%s\n", escarg(EQNS)); 287 | } else { 288 | printf(".as %s \"%s%s%s\n", EQNS, ESAVE, s, ELOAD); 289 | } 290 | } 291 | 292 | /* return the length of a utf-8 character based on its first byte */ 293 | static int utf8len(int c) 294 | { 295 | if (~c & 0x80) 296 | return c > 0; 297 | if (~c & 0x40) 298 | return 1; 299 | if (~c & 0x20) 300 | return 2; 301 | if (~c & 0x10) 302 | return 3; 303 | if (~c & 0x08) 304 | return 4; 305 | return 1; 306 | } 307 | 308 | /* return the type of a token */ 309 | static int char_type(char *s) 310 | { 311 | int c = (unsigned char) s[0]; 312 | int t; 313 | if (isdigit(c)) 314 | return T_NUMBER; 315 | if (c == '"') 316 | return T_STRING; 317 | if ((t = def_type(s)) >= 0) 318 | return t; 319 | if (c == '~' || c == '^') 320 | return T_GAP; 321 | if (ispunct(c) && (c != '\\' || !s[1])) 322 | return T_ORD; 323 | return T_LETTER; 324 | } 325 | 326 | /* read the next token */ 327 | static int tok_read(void) 328 | { 329 | char *s = tok; 330 | char *e = tok + sizeof(tok) - 2; 331 | int c, c2; 332 | int i; 333 | *s = '\0'; 334 | c = tok_next(); 335 | if (c <= 0) 336 | return 1; 337 | tok_prevsep = tok_cursep; 338 | tok_cursep = def_chopped(c); 339 | if (tok_cursep) 340 | tok_prevsep = 1; 341 | if (c == ' ' || c == '\n') { 342 | while (c > 0 && (c == ' ' || c == '\n')) 343 | c = tok_next(); 344 | tok_back(c); 345 | *s++ = ' '; 346 | *s = '\0'; 347 | tok_curtype = T_SPACE; 348 | return 0; 349 | } 350 | if (c == '\t') { 351 | *s++ = '\t'; 352 | *s = '\0'; 353 | tok_curtype = T_TAB; 354 | return 0; 355 | } 356 | if (tok_prevsep) { 357 | if (c == '$') { 358 | c2 = tok_next(); 359 | if (c2 >= '1' && c2 <= '9' && !src_arg(c2 - '0')) { 360 | tok_cursep = 1; 361 | return tok_read(); 362 | } 363 | tok_back(c2); 364 | } 365 | tok_back(c); 366 | if (!tok_keyword()) { 367 | tok_curtype = T_KEYWORD; 368 | tok_cursep = 1; 369 | return 0; 370 | } 371 | if (!tok_expand()) { 372 | tok_cursep = 1; 373 | return tok_read(); 374 | } 375 | c = tok_next(); 376 | } 377 | if (strchr(T_SOFTSEP, c)) { 378 | *s++ = c; 379 | if (c == '\\') { 380 | c = tok_next(); 381 | if (c == '(') { 382 | *s++ = c; 383 | *s++ = tok_next(); 384 | *s++ = tok_next(); 385 | } else if (c == '[') { 386 | while (c && c != ']') { 387 | if (s < e) 388 | *s++ = c; 389 | c = tok_next(); 390 | } 391 | *s++ = ']'; 392 | } 393 | } else if (c == '"') { 394 | c = tok_next(); 395 | while (c > 0 && c != '"') { 396 | if (c == '\\') { 397 | c2 = tok_next(); 398 | if (c2 == '"') 399 | c = '"'; 400 | else 401 | tok_back(c2); 402 | } 403 | if (s < e) 404 | *s++ = c; 405 | c = tok_next(); 406 | } 407 | *s++ = '"'; 408 | } else { 409 | /* two-character operators */ 410 | c2 = tok_next(); 411 | switch (T_BIN(c, c2)) { 412 | case T_BIN('<', '='): 413 | case T_BIN('>', '='): 414 | case T_BIN('=', '='): 415 | case T_BIN('!', '='): 416 | case T_BIN('>', '>'): 417 | case T_BIN('<', '<'): 418 | case T_BIN(':', '='): 419 | case T_BIN('-', '>'): 420 | case T_BIN('<', '-'): 421 | case T_BIN('-', '+'): 422 | *s++ = c2; 423 | break; 424 | default: 425 | tok_back(c2); 426 | } 427 | } 428 | *s = '\0'; 429 | tok_curtype = char_type(tok); 430 | return 0; 431 | } 432 | *s++ = c; 433 | i = utf8len(c); 434 | while (--i > 0 && s < e) 435 | *s++ = tok_next(); 436 | *s = '\0'; 437 | tok_curtype = char_type(tok); 438 | return 0; 439 | } 440 | 441 | /* current token */ 442 | char *tok_get(void) 443 | { 444 | return tok[0] ? tok : NULL; 445 | } 446 | 447 | /* current token type */ 448 | int tok_type(void) 449 | { 450 | return tok[0] ? tok_curtype : 0; 451 | } 452 | 453 | /* return nonzero if current token chops the equation */ 454 | int tok_chops(int soft) 455 | { 456 | if (!tok_get() || tok_curtype == T_KEYWORD) 457 | return 1; 458 | if (soft) 459 | return strchr(T_SOFTSEP, (unsigned char) tok_get()[0]) != NULL; 460 | return def_chopped((unsigned char) tok_get()[0]); 461 | } 462 | 463 | /* read the next token, return the previous */ 464 | char *tok_pop(void) 465 | { 466 | strcpy(tok_prev, tok); 467 | tok_read(); 468 | return tok_prev[0] ? tok_prev : NULL; 469 | } 470 | 471 | /* like tok_pop() but ignore T_SPACE tokens; if sep, read until chopped */ 472 | char *tok_poptext(int sep) 473 | { 474 | while (tok_type() == T_SPACE) 475 | tok_read(); 476 | tok_prev[0] = '\0'; 477 | do { 478 | strcat(tok_prev, tok); 479 | tok_read(); 480 | } while (tok[0] && !tok_chops(!sep)); 481 | return tok_prev[0] ? tok_prev : NULL; 482 | } 483 | 484 | /* skip spaces */ 485 | static void tok_blanks(void) 486 | { 487 | while (tok_type() == T_SPACE) 488 | tok_pop(); 489 | } 490 | 491 | /* if the next token is s, return zero and skip it */ 492 | int tok_jmp(char *s) 493 | { 494 | tok_blanks(); 495 | if (tok_get() && !s[1] && strchr("{}~^\t", s[0]) && !strcmp(s, tok_get())) { 496 | tok_pop(); 497 | return 0; 498 | } 499 | if (tok_type() != T_KEYWORD || !tok_get() || strcmp(s, tok_get())) 500 | return 1; 501 | tok_pop(); 502 | return 0; 503 | } 504 | 505 | /* read delim command */ 506 | void tok_delim(void) 507 | { 508 | char delim[NMLEN]; 509 | tok_preview(delim, 0); 510 | if (!strcmp("off", delim)) { 511 | eqn_beg = 0; 512 | eqn_end = 0; 513 | } else { 514 | eqn_beg = delim[0]; 515 | eqn_end = delim[1]; 516 | } 517 | } 518 | 519 | /* read macro definition */ 520 | static void tok_macrodef(struct sbuf *def) 521 | { 522 | int c; 523 | int delim; 524 | c = src_next(); 525 | while (c > 0 && isspace(c)) 526 | c = src_next(); 527 | delim = c; 528 | c = src_next(); 529 | while (c > 0 && c != delim) { 530 | sbuf_add(def, c); 531 | c = src_next(); 532 | } 533 | } 534 | 535 | /* read the next macro command */ 536 | void tok_macro(void) 537 | { 538 | char name[NMLEN]; 539 | struct sbuf def; 540 | tok_preview(name, 0); 541 | sbuf_init(&def); 542 | tok_macrodef(&def); 543 | src_define(name, sbuf_buf(&def)); 544 | sbuf_done(&def); 545 | } 546 | 547 | /* return 1 if inside inline equations */ 548 | int tok_inline(void) 549 | { 550 | return tok_line; 551 | } 552 | --------------------------------------------------------------------------------