PageRenderTime 68ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/groff/src/roff/troff/env.cpp

https://bitbucket.org/freebsd/freebsd-head/
C++ | 3893 lines | 3496 code | 263 blank | 134 comment | 897 complexity | c07b344fa412b4f06e5daa000c8d6c87 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, BSD-3-Clause, LGPL-2.0, LGPL-2.1, BSD-2-Clause, 0BSD, JSON, AGPL-1.0, GPL-2.0
  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
  3. Free Software Foundation, Inc.
  4. Written by James Clark (jjc@jclark.com)
  5. This file is part of groff.
  6. groff is free software; you can redistribute it and/or modify it under
  7. the terms of the GNU General Public License as published by the Free
  8. Software Foundation; either version 2, or (at your option) any later
  9. version.
  10. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  13. for more details.
  14. You should have received a copy of the GNU General Public License along
  15. with groff; see the file COPYING. If not, write to the Free Software
  16. Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
  17. #include "troff.h"
  18. #include "dictionary.h"
  19. #include "hvunits.h"
  20. #include "stringclass.h"
  21. #include "mtsm.h"
  22. #include "env.h"
  23. #include "request.h"
  24. #include "node.h"
  25. #include "token.h"
  26. #include "div.h"
  27. #include "reg.h"
  28. #include "charinfo.h"
  29. #include "macropath.h"
  30. #include "input.h"
  31. #include <math.h>
  32. symbol default_family("T");
  33. enum { ADJUST_LEFT = 0, ADJUST_BOTH = 1, ADJUST_CENTER = 3, ADJUST_RIGHT = 5 };
  34. enum { HYPHEN_LAST_LINE = 2, HYPHEN_LAST_CHARS = 4, HYPHEN_FIRST_CHARS = 8 };
  35. struct env_list {
  36. environment *env;
  37. env_list *next;
  38. env_list(environment *e, env_list *p) : env(e), next(p) {}
  39. };
  40. env_list *env_stack;
  41. const int NENVIRONMENTS = 10;
  42. environment *env_table[NENVIRONMENTS];
  43. dictionary env_dictionary(10);
  44. environment *curenv;
  45. static int next_line_number = 0;
  46. extern int suppress_push;
  47. extern statem *get_diversion_state();
  48. charinfo *field_delimiter_char;
  49. charinfo *padding_indicator_char;
  50. int translate_space_to_dummy = 0;
  51. class pending_output_line {
  52. node *nd;
  53. int no_fill;
  54. int was_centered;
  55. vunits vs;
  56. vunits post_vs;
  57. hunits width;
  58. #ifdef WIDOW_CONTROL
  59. int last_line; // Is it the last line of the paragraph?
  60. #endif /* WIDOW_CONTROL */
  61. public:
  62. pending_output_line *next;
  63. pending_output_line(node *, int, vunits, vunits, hunits, int,
  64. pending_output_line * = 0);
  65. ~pending_output_line();
  66. int output();
  67. #ifdef WIDOW_CONTROL
  68. friend void environment::mark_last_line();
  69. friend void environment::output(node *, int, vunits, vunits, hunits, int);
  70. #endif /* WIDOW_CONTROL */
  71. };
  72. pending_output_line::pending_output_line(node *n, int nf, vunits v, vunits pv,
  73. hunits w, int ce,
  74. pending_output_line *p)
  75. : nd(n), no_fill(nf), was_centered(ce), vs(v), post_vs(pv), width(w),
  76. #ifdef WIDOW_CONTROL
  77. last_line(0),
  78. #endif /* WIDOW_CONTROL */
  79. next(p)
  80. {
  81. }
  82. pending_output_line::~pending_output_line()
  83. {
  84. delete_node_list(nd);
  85. }
  86. int pending_output_line::output()
  87. {
  88. if (trap_sprung_flag)
  89. return 0;
  90. #ifdef WIDOW_CONTROL
  91. if (next && next->last_line && !no_fill) {
  92. curdiv->need(vs + post_vs + vunits(vresolution));
  93. if (trap_sprung_flag) {
  94. next->last_line = 0; // Try to avoid infinite loops.
  95. return 0;
  96. }
  97. }
  98. #endif
  99. curenv->construct_format_state(nd, was_centered, !no_fill);
  100. curdiv->output(nd, no_fill, vs, post_vs, width);
  101. nd = 0;
  102. return 1;
  103. }
  104. void environment::output(node *nd, int no_fill_flag,
  105. vunits vs, vunits post_vs,
  106. hunits width, int was_centered)
  107. {
  108. #ifdef WIDOW_CONTROL
  109. while (pending_lines) {
  110. if (widow_control && !pending_lines->no_fill && !pending_lines->next)
  111. break;
  112. if (!pending_lines->output())
  113. break;
  114. pending_output_line *tem = pending_lines;
  115. pending_lines = pending_lines->next;
  116. delete tem;
  117. }
  118. #else /* WIDOW_CONTROL */
  119. output_pending_lines();
  120. #endif /* WIDOW_CONTROL */
  121. if (!trap_sprung_flag && !pending_lines
  122. #ifdef WIDOW_CONTROL
  123. && (!widow_control || no_fill_flag)
  124. #endif /* WIDOW_CONTROL */
  125. ) {
  126. curenv->construct_format_state(nd, was_centered, !no_fill_flag);
  127. curdiv->output(nd, no_fill_flag, vs, post_vs, width);
  128. } else {
  129. pending_output_line **p;
  130. for (p = &pending_lines; *p; p = &(*p)->next)
  131. ;
  132. *p = new pending_output_line(nd, no_fill_flag, vs, post_vs, width,
  133. was_centered);
  134. }
  135. }
  136. // a line from .tl goes at the head of the queue
  137. void environment::output_title(node *nd, int no_fill_flag,
  138. vunits vs, vunits post_vs,
  139. hunits width)
  140. {
  141. if (!trap_sprung_flag)
  142. curdiv->output(nd, no_fill_flag, vs, post_vs, width);
  143. else
  144. pending_lines = new pending_output_line(nd, no_fill_flag, vs, post_vs,
  145. width, 0, pending_lines);
  146. }
  147. void environment::output_pending_lines()
  148. {
  149. while (pending_lines && pending_lines->output()) {
  150. pending_output_line *tem = pending_lines;
  151. pending_lines = pending_lines->next;
  152. delete tem;
  153. }
  154. }
  155. #ifdef WIDOW_CONTROL
  156. void environment::mark_last_line()
  157. {
  158. if (!widow_control || !pending_lines)
  159. return;
  160. pending_output_line *p;
  161. for (p = pending_lines; p->next; p = p->next)
  162. ;
  163. if (!p->no_fill)
  164. p->last_line = 1;
  165. }
  166. void widow_control_request()
  167. {
  168. int n;
  169. if (has_arg() && get_integer(&n))
  170. curenv->widow_control = n != 0;
  171. else
  172. curenv->widow_control = 1;
  173. skip_line();
  174. }
  175. #endif /* WIDOW_CONTROL */
  176. /* font_size functions */
  177. size_range *font_size::size_table = 0;
  178. int font_size::nranges = 0;
  179. extern "C" {
  180. int compare_ranges(const void *p1, const void *p2)
  181. {
  182. return ((size_range *)p1)->min - ((size_range *)p2)->min;
  183. }
  184. }
  185. void font_size::init_size_table(int *sizes)
  186. {
  187. nranges = 0;
  188. while (sizes[nranges*2] != 0)
  189. nranges++;
  190. assert(nranges > 0);
  191. size_table = new size_range[nranges];
  192. for (int i = 0; i < nranges; i++) {
  193. size_table[i].min = sizes[i*2];
  194. size_table[i].max = sizes[i*2 + 1];
  195. }
  196. qsort(size_table, nranges, sizeof(size_range), compare_ranges);
  197. }
  198. font_size::font_size(int sp)
  199. {
  200. for (int i = 0; i < nranges; i++) {
  201. if (sp < size_table[i].min) {
  202. if (i > 0 && size_table[i].min - sp >= sp - size_table[i - 1].max)
  203. p = size_table[i - 1].max;
  204. else
  205. p = size_table[i].min;
  206. return;
  207. }
  208. if (sp <= size_table[i].max) {
  209. p = sp;
  210. return;
  211. }
  212. }
  213. p = size_table[nranges - 1].max;
  214. }
  215. int font_size::to_units()
  216. {
  217. return scale(p, units_per_inch, sizescale*72);
  218. }
  219. // we can't do this in a static constructor because various dictionaries
  220. // have to get initialized first
  221. void init_environments()
  222. {
  223. curenv = env_table[0] = new environment("0");
  224. }
  225. void tab_character()
  226. {
  227. curenv->tab_char = get_optional_char();
  228. skip_line();
  229. }
  230. void leader_character()
  231. {
  232. curenv->leader_char = get_optional_char();
  233. skip_line();
  234. }
  235. void environment::add_char(charinfo *ci)
  236. {
  237. int s;
  238. node *gc_np = 0;
  239. if (interrupted)
  240. ;
  241. // don't allow fields in dummy environments
  242. else if (ci == field_delimiter_char && !dummy) {
  243. if (current_field)
  244. wrap_up_field();
  245. else
  246. start_field();
  247. }
  248. else if (current_field && ci == padding_indicator_char)
  249. add_padding();
  250. else if (current_tab) {
  251. if (tab_contents == 0)
  252. tab_contents = new line_start_node;
  253. if (ci != hyphen_indicator_char)
  254. tab_contents = tab_contents->add_char(ci, this, &tab_width, &s, &gc_np);
  255. else
  256. tab_contents = tab_contents->add_discretionary_hyphen();
  257. }
  258. else {
  259. if (line == 0)
  260. start_line();
  261. #if 0
  262. fprintf(stderr, "current line is\n");
  263. line->debug_node_list();
  264. #endif
  265. if (ci != hyphen_indicator_char)
  266. line = line->add_char(ci, this, &width_total, &space_total, &gc_np);
  267. else
  268. line = line->add_discretionary_hyphen();
  269. }
  270. #if 0
  271. fprintf(stderr, "now after we have added character the line is\n");
  272. line->debug_node_list();
  273. #endif
  274. if ((!suppress_push) && gc_np) {
  275. if (gc_np && (gc_np->state == 0)) {
  276. gc_np->state = construct_state(0);
  277. gc_np->push_state = get_diversion_state();
  278. }
  279. else if (line && (line->state == 0)) {
  280. line->state = construct_state(0);
  281. line->push_state = get_diversion_state();
  282. }
  283. }
  284. #if 0
  285. fprintf(stderr, "now we have possibly added the state the line is\n");
  286. line->debug_node_list();
  287. #endif
  288. }
  289. node *environment::make_char_node(charinfo *ci)
  290. {
  291. return make_node(ci, this);
  292. }
  293. void environment::add_node(node *n)
  294. {
  295. if (n == 0)
  296. return;
  297. if (!suppress_push) {
  298. if (n->is_special && n->state == NULL)
  299. n->state = construct_state(0);
  300. n->push_state = get_diversion_state();
  301. }
  302. if (current_tab || current_field)
  303. n->freeze_space();
  304. if (interrupted) {
  305. delete n;
  306. }
  307. else if (current_tab) {
  308. n->next = tab_contents;
  309. tab_contents = n;
  310. tab_width += n->width();
  311. }
  312. else {
  313. if (line == 0) {
  314. if (discarding && n->discardable()) {
  315. // XXX possibly: input_line_start -= n->width();
  316. delete n;
  317. return;
  318. }
  319. start_line();
  320. }
  321. width_total += n->width();
  322. space_total += n->nspaces();
  323. n->next = line;
  324. line = n;
  325. construct_new_line_state(line);
  326. }
  327. }
  328. void environment::add_hyphen_indicator()
  329. {
  330. if (current_tab || interrupted || current_field
  331. || hyphen_indicator_char != 0)
  332. return;
  333. if (line == 0)
  334. start_line();
  335. line = line->add_discretionary_hyphen();
  336. }
  337. int environment::get_hyphenation_flags()
  338. {
  339. return hyphenation_flags;
  340. }
  341. int environment::get_hyphen_line_max()
  342. {
  343. return hyphen_line_max;
  344. }
  345. int environment::get_hyphen_line_count()
  346. {
  347. return hyphen_line_count;
  348. }
  349. int environment::get_center_lines()
  350. {
  351. return center_lines;
  352. }
  353. int environment::get_right_justify_lines()
  354. {
  355. return right_justify_lines;
  356. }
  357. void environment::add_italic_correction()
  358. {
  359. if (current_tab) {
  360. if (tab_contents)
  361. tab_contents = tab_contents->add_italic_correction(&tab_width);
  362. }
  363. else if (line)
  364. line = line->add_italic_correction(&width_total);
  365. }
  366. void environment::space_newline()
  367. {
  368. assert(!current_tab && !current_field);
  369. if (interrupted)
  370. return;
  371. hunits x = H0;
  372. hunits sw = env_space_width(this);
  373. hunits ssw = env_sentence_space_width(this);
  374. if (!translate_space_to_dummy) {
  375. x = sw;
  376. if (node_list_ends_sentence(line) == 1)
  377. x += ssw;
  378. }
  379. width_list *w = new width_list(sw, ssw);
  380. if (node_list_ends_sentence(line) == 1)
  381. w->next = new width_list(sw, ssw);
  382. if (line != 0 && line->merge_space(x, sw, ssw)) {
  383. width_total += x;
  384. return;
  385. }
  386. add_node(new word_space_node(x, get_fill_color(), w));
  387. possibly_break_line(0, spread_flag);
  388. spread_flag = 0;
  389. }
  390. void environment::space()
  391. {
  392. space(env_space_width(this), env_sentence_space_width(this));
  393. }
  394. void environment::space(hunits space_width, hunits sentence_space_width)
  395. {
  396. if (interrupted)
  397. return;
  398. if (current_field && padding_indicator_char == 0) {
  399. add_padding();
  400. return;
  401. }
  402. hunits x = translate_space_to_dummy ? H0 : space_width;
  403. node *p = current_tab ? tab_contents : line;
  404. hunits *tp = current_tab ? &tab_width : &width_total;
  405. if (p && p->nspaces() == 1 && p->width() == x
  406. && node_list_ends_sentence(p->next) == 1) {
  407. hunits xx = translate_space_to_dummy ? H0 : sentence_space_width;
  408. if (p->merge_space(xx, space_width, sentence_space_width)) {
  409. *tp += xx;
  410. return;
  411. }
  412. }
  413. if (p && p->merge_space(x, space_width, sentence_space_width)) {
  414. *tp += x;
  415. return;
  416. }
  417. add_node(new word_space_node(x,
  418. get_fill_color(),
  419. new width_list(space_width,
  420. sentence_space_width)));
  421. possibly_break_line(0, spread_flag);
  422. spread_flag = 0;
  423. }
  424. node *do_underline_special(int);
  425. void environment::set_font(symbol nm)
  426. {
  427. if (interrupted)
  428. return;
  429. if (nm == symbol("P") || nm.is_empty()) {
  430. if (family->make_definite(prev_fontno) < 0)
  431. return;
  432. int tem = fontno;
  433. fontno = prev_fontno;
  434. prev_fontno = tem;
  435. }
  436. else {
  437. prev_fontno = fontno;
  438. int n = symbol_fontno(nm);
  439. if (n < 0) {
  440. n = next_available_font_position();
  441. if (!mount_font(n, nm))
  442. return;
  443. }
  444. if (family->make_definite(n) < 0)
  445. return;
  446. fontno = n;
  447. }
  448. if (underline_spaces && fontno != prev_fontno) {
  449. if (fontno == get_underline_fontno())
  450. add_node(do_underline_special(1));
  451. if (prev_fontno == get_underline_fontno())
  452. add_node(do_underline_special(0));
  453. }
  454. }
  455. void environment::set_font(int n)
  456. {
  457. if (interrupted)
  458. return;
  459. if (is_good_fontno(n)) {
  460. prev_fontno = fontno;
  461. fontno = n;
  462. }
  463. else
  464. warning(WARN_FONT, "bad font number");
  465. }
  466. void environment::set_family(symbol fam)
  467. {
  468. if (interrupted)
  469. return;
  470. if (fam.is_null() || fam.is_empty()) {
  471. if (prev_family->make_definite(fontno) < 0)
  472. return;
  473. font_family *tem = family;
  474. family = prev_family;
  475. prev_family = tem;
  476. }
  477. else {
  478. font_family *f = lookup_family(fam);
  479. if (f->make_definite(fontno) < 0)
  480. return;
  481. prev_family = family;
  482. family = f;
  483. }
  484. }
  485. void environment::set_size(int n)
  486. {
  487. if (interrupted)
  488. return;
  489. if (n == 0) {
  490. font_size temp = prev_size;
  491. prev_size = size;
  492. size = temp;
  493. int temp2 = prev_requested_size;
  494. prev_requested_size = requested_size;
  495. requested_size = temp2;
  496. }
  497. else {
  498. prev_size = size;
  499. size = font_size(n);
  500. prev_requested_size = requested_size;
  501. requested_size = n;
  502. }
  503. }
  504. void environment::set_char_height(int n)
  505. {
  506. if (interrupted)
  507. return;
  508. if (n == requested_size || n <= 0)
  509. char_height = 0;
  510. else
  511. char_height = n;
  512. }
  513. void environment::set_char_slant(int n)
  514. {
  515. if (interrupted)
  516. return;
  517. char_slant = n;
  518. }
  519. color *environment::get_prev_glyph_color()
  520. {
  521. return prev_glyph_color;
  522. }
  523. color *environment::get_glyph_color()
  524. {
  525. return glyph_color;
  526. }
  527. color *environment::get_prev_fill_color()
  528. {
  529. return prev_fill_color;
  530. }
  531. color *environment::get_fill_color()
  532. {
  533. return fill_color;
  534. }
  535. void environment::set_glyph_color(color *c)
  536. {
  537. if (interrupted)
  538. return;
  539. curenv->prev_glyph_color = curenv->glyph_color;
  540. curenv->glyph_color = c;
  541. }
  542. void environment::set_fill_color(color *c)
  543. {
  544. if (interrupted)
  545. return;
  546. curenv->prev_fill_color = curenv->fill_color;
  547. curenv->fill_color = c;
  548. }
  549. environment::environment(symbol nm)
  550. : dummy(0),
  551. prev_line_length((units_per_inch*13)/2),
  552. line_length((units_per_inch*13)/2),
  553. prev_title_length((units_per_inch*13)/2),
  554. title_length((units_per_inch*13)/2),
  555. prev_size(sizescale*10),
  556. size(sizescale*10),
  557. requested_size(sizescale*10),
  558. prev_requested_size(sizescale*10),
  559. char_height(0),
  560. char_slant(0),
  561. space_size(12),
  562. sentence_space_size(12),
  563. adjust_mode(ADJUST_BOTH),
  564. fill(1),
  565. interrupted(0),
  566. prev_line_interrupted(0),
  567. center_lines(0),
  568. right_justify_lines(0),
  569. prev_vertical_spacing(points_to_units(12)),
  570. vertical_spacing(points_to_units(12)),
  571. prev_post_vertical_spacing(0),
  572. post_vertical_spacing(0),
  573. prev_line_spacing(1),
  574. line_spacing(1),
  575. prev_indent(0),
  576. indent(0),
  577. temporary_indent(0),
  578. have_temporary_indent(0),
  579. underline_lines(0),
  580. underline_spaces(0),
  581. input_trap_count(0),
  582. continued_input_trap(0),
  583. line(0),
  584. prev_text_length(0),
  585. width_total(0),
  586. space_total(0),
  587. input_line_start(0),
  588. line_tabs(0),
  589. current_tab(TAB_NONE),
  590. leader_node(0),
  591. tab_char(0),
  592. leader_char(charset_table['.']),
  593. current_field(0),
  594. discarding(0),
  595. spread_flag(0),
  596. margin_character_flags(0),
  597. margin_character_node(0),
  598. margin_character_distance(points_to_units(10)),
  599. numbering_nodes(0),
  600. number_text_separation(1),
  601. line_number_indent(0),
  602. line_number_multiple(1),
  603. no_number_count(0),
  604. hyphenation_flags(1),
  605. hyphen_line_count(0),
  606. hyphen_line_max(-1),
  607. hyphenation_space(H0),
  608. hyphenation_margin(H0),
  609. composite(0),
  610. pending_lines(0),
  611. #ifdef WIDOW_CONTROL
  612. widow_control(0),
  613. #endif /* WIDOW_CONTROL */
  614. glyph_color(&default_color),
  615. prev_glyph_color(&default_color),
  616. fill_color(&default_color),
  617. prev_fill_color(&default_color),
  618. seen_space(0),
  619. seen_eol(0),
  620. suppress_next_eol(0),
  621. seen_break(0),
  622. tabs(units_per_inch/2, TAB_LEFT),
  623. name(nm),
  624. control_char('.'),
  625. no_break_control_char('\''),
  626. hyphen_indicator_char(0)
  627. {
  628. prev_family = family = lookup_family(default_family);
  629. prev_fontno = fontno = 1;
  630. if (!is_good_fontno(1))
  631. fatal("font number 1 not a valid font");
  632. if (family->make_definite(1) < 0)
  633. fatal("invalid default family `%1'", default_family.contents());
  634. prev_fontno = fontno;
  635. }
  636. environment::environment(const environment *e)
  637. : dummy(1),
  638. prev_line_length(e->prev_line_length),
  639. line_length(e->line_length),
  640. prev_title_length(e->prev_title_length),
  641. title_length(e->title_length),
  642. prev_size(e->prev_size),
  643. size(e->size),
  644. requested_size(e->requested_size),
  645. prev_requested_size(e->prev_requested_size),
  646. char_height(e->char_height),
  647. char_slant(e->char_slant),
  648. prev_fontno(e->prev_fontno),
  649. fontno(e->fontno),
  650. prev_family(e->prev_family),
  651. family(e->family),
  652. space_size(e->space_size),
  653. sentence_space_size(e->sentence_space_size),
  654. adjust_mode(e->adjust_mode),
  655. fill(e->fill),
  656. interrupted(0),
  657. prev_line_interrupted(0),
  658. center_lines(0),
  659. right_justify_lines(0),
  660. prev_vertical_spacing(e->prev_vertical_spacing),
  661. vertical_spacing(e->vertical_spacing),
  662. prev_post_vertical_spacing(e->prev_post_vertical_spacing),
  663. post_vertical_spacing(e->post_vertical_spacing),
  664. prev_line_spacing(e->prev_line_spacing),
  665. line_spacing(e->line_spacing),
  666. prev_indent(e->prev_indent),
  667. indent(e->indent),
  668. temporary_indent(0),
  669. have_temporary_indent(0),
  670. underline_lines(0),
  671. underline_spaces(0),
  672. input_trap_count(0),
  673. continued_input_trap(0),
  674. line(0),
  675. prev_text_length(e->prev_text_length),
  676. width_total(0),
  677. space_total(0),
  678. input_line_start(0),
  679. line_tabs(e->line_tabs),
  680. current_tab(TAB_NONE),
  681. leader_node(0),
  682. tab_char(e->tab_char),
  683. leader_char(e->leader_char),
  684. current_field(0),
  685. discarding(0),
  686. spread_flag(0),
  687. margin_character_flags(e->margin_character_flags),
  688. margin_character_node(e->margin_character_node),
  689. margin_character_distance(e->margin_character_distance),
  690. numbering_nodes(0),
  691. number_text_separation(e->number_text_separation),
  692. line_number_indent(e->line_number_indent),
  693. line_number_multiple(e->line_number_multiple),
  694. no_number_count(e->no_number_count),
  695. hyphenation_flags(e->hyphenation_flags),
  696. hyphen_line_count(0),
  697. hyphen_line_max(e->hyphen_line_max),
  698. hyphenation_space(e->hyphenation_space),
  699. hyphenation_margin(e->hyphenation_margin),
  700. composite(0),
  701. pending_lines(0),
  702. #ifdef WIDOW_CONTROL
  703. widow_control(e->widow_control),
  704. #endif /* WIDOW_CONTROL */
  705. glyph_color(e->glyph_color),
  706. prev_glyph_color(e->prev_glyph_color),
  707. fill_color(e->fill_color),
  708. prev_fill_color(e->prev_fill_color),
  709. seen_space(e->seen_space),
  710. seen_eol(e->seen_eol),
  711. suppress_next_eol(e->suppress_next_eol),
  712. seen_break(e->seen_break),
  713. tabs(e->tabs),
  714. name(e->name), // so that eg `.if "\n[.ev]"0"' works
  715. control_char(e->control_char),
  716. no_break_control_char(e->no_break_control_char),
  717. hyphen_indicator_char(e->hyphen_indicator_char)
  718. {
  719. }
  720. void environment::copy(const environment *e)
  721. {
  722. prev_line_length = e->prev_line_length;
  723. line_length = e->line_length;
  724. prev_title_length = e->prev_title_length;
  725. title_length = e->title_length;
  726. prev_size = e->prev_size;
  727. size = e->size;
  728. prev_requested_size = e->prev_requested_size;
  729. requested_size = e->requested_size;
  730. char_height = e->char_height;
  731. char_slant = e->char_slant;
  732. space_size = e->space_size;
  733. sentence_space_size = e->sentence_space_size;
  734. adjust_mode = e->adjust_mode;
  735. fill = e->fill;
  736. interrupted = 0;
  737. prev_line_interrupted = 0;
  738. center_lines = 0;
  739. right_justify_lines = 0;
  740. prev_vertical_spacing = e->prev_vertical_spacing;
  741. vertical_spacing = e->vertical_spacing;
  742. prev_post_vertical_spacing = e->prev_post_vertical_spacing,
  743. post_vertical_spacing = e->post_vertical_spacing,
  744. prev_line_spacing = e->prev_line_spacing;
  745. line_spacing = e->line_spacing;
  746. prev_indent = e->prev_indent;
  747. indent = e->indent;
  748. have_temporary_indent = 0;
  749. temporary_indent = 0;
  750. underline_lines = 0;
  751. underline_spaces = 0;
  752. input_trap_count = 0;
  753. continued_input_trap = 0;
  754. prev_text_length = e->prev_text_length;
  755. width_total = 0;
  756. space_total = 0;
  757. input_line_start = 0;
  758. control_char = e->control_char;
  759. no_break_control_char = e->no_break_control_char;
  760. hyphen_indicator_char = e->hyphen_indicator_char;
  761. spread_flag = 0;
  762. line = 0;
  763. pending_lines = 0;
  764. discarding = 0;
  765. tabs = e->tabs;
  766. line_tabs = e->line_tabs;
  767. current_tab = TAB_NONE;
  768. current_field = 0;
  769. margin_character_flags = e->margin_character_flags;
  770. margin_character_node = e->margin_character_node;
  771. margin_character_distance = e->margin_character_distance;
  772. numbering_nodes = 0;
  773. number_text_separation = e->number_text_separation;
  774. line_number_multiple = e->line_number_multiple;
  775. line_number_indent = e->line_number_indent;
  776. no_number_count = e->no_number_count;
  777. tab_char = e->tab_char;
  778. leader_char = e->leader_char;
  779. hyphenation_flags = e->hyphenation_flags;
  780. fontno = e->fontno;
  781. prev_fontno = e->prev_fontno;
  782. dummy = e->dummy;
  783. family = e->family;
  784. prev_family = e->prev_family;
  785. leader_node = 0;
  786. #ifdef WIDOW_CONTROL
  787. widow_control = e->widow_control;
  788. #endif /* WIDOW_CONTROL */
  789. hyphen_line_max = e->hyphen_line_max;
  790. hyphen_line_count = 0;
  791. hyphenation_space = e->hyphenation_space;
  792. hyphenation_margin = e->hyphenation_margin;
  793. composite = 0;
  794. glyph_color= e->glyph_color;
  795. prev_glyph_color = e->prev_glyph_color;
  796. fill_color = e->fill_color;
  797. prev_fill_color = e->prev_fill_color;
  798. }
  799. environment::~environment()
  800. {
  801. delete leader_node;
  802. delete_node_list(line);
  803. delete_node_list(numbering_nodes);
  804. }
  805. hunits environment::get_input_line_position()
  806. {
  807. hunits n;
  808. if (line == 0)
  809. n = -input_line_start;
  810. else
  811. n = width_total - input_line_start;
  812. if (current_tab)
  813. n += tab_width;
  814. return n;
  815. }
  816. void environment::set_input_line_position(hunits n)
  817. {
  818. input_line_start = line == 0 ? -n : width_total - n;
  819. if (current_tab)
  820. input_line_start += tab_width;
  821. }
  822. hunits environment::get_line_length()
  823. {
  824. return line_length;
  825. }
  826. hunits environment::get_saved_line_length()
  827. {
  828. if (line)
  829. return target_text_length + saved_indent;
  830. else
  831. return line_length;
  832. }
  833. vunits environment::get_vertical_spacing()
  834. {
  835. return vertical_spacing;
  836. }
  837. vunits environment::get_post_vertical_spacing()
  838. {
  839. return post_vertical_spacing;
  840. }
  841. int environment::get_line_spacing()
  842. {
  843. return line_spacing;
  844. }
  845. vunits environment::total_post_vertical_spacing()
  846. {
  847. vunits tem(post_vertical_spacing);
  848. if (line_spacing > 1)
  849. tem += (line_spacing - 1)*vertical_spacing;
  850. return tem;
  851. }
  852. int environment::get_bold()
  853. {
  854. return get_bold_fontno(fontno);
  855. }
  856. hunits environment::get_digit_width()
  857. {
  858. return env_digit_width(this);
  859. }
  860. int environment::get_adjust_mode()
  861. {
  862. return adjust_mode;
  863. }
  864. int environment::get_fill()
  865. {
  866. return fill;
  867. }
  868. hunits environment::get_indent()
  869. {
  870. return indent;
  871. }
  872. hunits environment::get_saved_indent()
  873. {
  874. if (line)
  875. return saved_indent;
  876. else if (have_temporary_indent)
  877. return temporary_indent;
  878. else
  879. return indent;
  880. }
  881. hunits environment::get_temporary_indent()
  882. {
  883. return temporary_indent;
  884. }
  885. hunits environment::get_title_length()
  886. {
  887. return title_length;
  888. }
  889. node *environment::get_prev_char()
  890. {
  891. for (node *n = current_tab ? tab_contents : line; n; n = n->next) {
  892. node *last = n->last_char_node();
  893. if (last)
  894. return last;
  895. }
  896. return 0;
  897. }
  898. hunits environment::get_prev_char_width()
  899. {
  900. node *last = get_prev_char();
  901. if (!last)
  902. return H0;
  903. return last->width();
  904. }
  905. hunits environment::get_prev_char_skew()
  906. {
  907. node *last = get_prev_char();
  908. if (!last)
  909. return H0;
  910. return last->skew();
  911. }
  912. vunits environment::get_prev_char_height()
  913. {
  914. node *last = get_prev_char();
  915. if (!last)
  916. return V0;
  917. vunits min, max;
  918. last->vertical_extent(&min, &max);
  919. return -min;
  920. }
  921. vunits environment::get_prev_char_depth()
  922. {
  923. node *last = get_prev_char();
  924. if (!last)
  925. return V0;
  926. vunits min, max;
  927. last->vertical_extent(&min, &max);
  928. return max;
  929. }
  930. hunits environment::get_text_length()
  931. {
  932. hunits n = line == 0 ? H0 : width_total;
  933. if (current_tab)
  934. n += tab_width;
  935. return n;
  936. }
  937. hunits environment::get_prev_text_length()
  938. {
  939. return prev_text_length;
  940. }
  941. static int sb_reg_contents = 0;
  942. static int st_reg_contents = 0;
  943. static int ct_reg_contents = 0;
  944. static int rsb_reg_contents = 0;
  945. static int rst_reg_contents = 0;
  946. static int skw_reg_contents = 0;
  947. static int ssc_reg_contents = 0;
  948. void environment::width_registers()
  949. {
  950. // this is used to implement \w; it sets the st, sb, ct registers
  951. vunits min = 0, max = 0, cur = 0;
  952. int character_type = 0;
  953. ssc_reg_contents = line ? line->subscript_correction().to_units() : 0;
  954. skw_reg_contents = line ? line->skew().to_units() : 0;
  955. line = reverse_node_list(line);
  956. vunits real_min = V0;
  957. vunits real_max = V0;
  958. vunits v1, v2;
  959. for (node *tem = line; tem; tem = tem->next) {
  960. tem->vertical_extent(&v1, &v2);
  961. v1 += cur;
  962. if (v1 < real_min)
  963. real_min = v1;
  964. v2 += cur;
  965. if (v2 > real_max)
  966. real_max = v2;
  967. if ((cur += tem->vertical_width()) < min)
  968. min = cur;
  969. else if (cur > max)
  970. max = cur;
  971. character_type |= tem->character_type();
  972. }
  973. line = reverse_node_list(line);
  974. st_reg_contents = -min.to_units();
  975. sb_reg_contents = -max.to_units();
  976. rst_reg_contents = -real_min.to_units();
  977. rsb_reg_contents = -real_max.to_units();
  978. ct_reg_contents = character_type;
  979. }
  980. node *environment::extract_output_line()
  981. {
  982. if (current_tab)
  983. wrap_up_tab();
  984. node *n = line;
  985. line = 0;
  986. return n;
  987. }
  988. /* environment related requests */
  989. void environment_switch()
  990. {
  991. int pop = 0; // 1 means pop, 2 means pop but no error message on underflow
  992. if (curenv->is_dummy())
  993. error("can't switch environments when current environment is dummy");
  994. else if (!has_arg())
  995. pop = 1;
  996. else {
  997. symbol nm;
  998. if (!tok.delimiter()) {
  999. // It looks like a number.
  1000. int n;
  1001. if (get_integer(&n)) {
  1002. if (n >= 0 && n < NENVIRONMENTS) {
  1003. env_stack = new env_list(curenv, env_stack);
  1004. if (env_table[n] == 0)
  1005. env_table[n] = new environment(i_to_a(n));
  1006. curenv = env_table[n];
  1007. }
  1008. else
  1009. nm = i_to_a(n);
  1010. }
  1011. else
  1012. pop = 2;
  1013. }
  1014. else {
  1015. nm = get_long_name(1);
  1016. if (nm.is_null())
  1017. pop = 2;
  1018. }
  1019. if (!nm.is_null()) {
  1020. environment *e = (environment *)env_dictionary.lookup(nm);
  1021. if (!e) {
  1022. e = new environment(nm);
  1023. (void)env_dictionary.lookup(nm, e);
  1024. }
  1025. env_stack = new env_list(curenv, env_stack);
  1026. curenv = e;
  1027. }
  1028. }
  1029. if (pop) {
  1030. if (env_stack == 0) {
  1031. if (pop == 1)
  1032. error("environment stack underflow");
  1033. }
  1034. else {
  1035. int seen_space = curenv->seen_space;
  1036. int seen_eol = curenv->seen_eol;
  1037. int suppress_next_eol = curenv->suppress_next_eol;
  1038. curenv = env_stack->env;
  1039. curenv->seen_space = seen_space;
  1040. curenv->seen_eol = seen_eol;
  1041. curenv->suppress_next_eol = suppress_next_eol;
  1042. env_list *tem = env_stack;
  1043. env_stack = env_stack->next;
  1044. delete tem;
  1045. }
  1046. }
  1047. skip_line();
  1048. }
  1049. void environment_copy()
  1050. {
  1051. symbol nm;
  1052. environment *e=0;
  1053. tok.skip();
  1054. if (!tok.delimiter()) {
  1055. // It looks like a number.
  1056. int n;
  1057. if (get_integer(&n)) {
  1058. if (n >= 0 && n < NENVIRONMENTS)
  1059. e = env_table[n];
  1060. else
  1061. nm = i_to_a(n);
  1062. }
  1063. }
  1064. else
  1065. nm = get_long_name(1);
  1066. if (!e && !nm.is_null())
  1067. e = (environment *)env_dictionary.lookup(nm);
  1068. if (e == 0) {
  1069. error("No environment to copy from");
  1070. return;
  1071. }
  1072. else
  1073. curenv->copy(e);
  1074. skip_line();
  1075. }
  1076. void fill_color_change()
  1077. {
  1078. symbol s = get_name();
  1079. if (s.is_null())
  1080. curenv->set_fill_color(curenv->get_prev_fill_color());
  1081. else
  1082. do_fill_color(s);
  1083. skip_line();
  1084. }
  1085. void glyph_color_change()
  1086. {
  1087. symbol s = get_name();
  1088. if (s.is_null())
  1089. curenv->set_glyph_color(curenv->get_prev_glyph_color());
  1090. else
  1091. do_glyph_color(s);
  1092. skip_line();
  1093. }
  1094. static symbol P_symbol("P");
  1095. void font_change()
  1096. {
  1097. symbol s = get_name();
  1098. int is_number = 1;
  1099. if (s.is_null() || s == P_symbol) {
  1100. s = P_symbol;
  1101. is_number = 0;
  1102. }
  1103. else {
  1104. for (const char *p = s.contents(); p != 0 && *p != 0; p++)
  1105. if (!csdigit(*p)) {
  1106. is_number = 0;
  1107. break;
  1108. }
  1109. }
  1110. if (is_number)
  1111. curenv->set_font(atoi(s.contents()));
  1112. else
  1113. curenv->set_font(s);
  1114. skip_line();
  1115. }
  1116. void family_change()
  1117. {
  1118. symbol s = get_name();
  1119. curenv->set_family(s);
  1120. skip_line();
  1121. }
  1122. void point_size()
  1123. {
  1124. int n;
  1125. if (has_arg() && get_number(&n, 'z', curenv->get_requested_point_size())) {
  1126. if (n <= 0)
  1127. n = 1;
  1128. curenv->set_size(n);
  1129. }
  1130. else
  1131. curenv->set_size(0);
  1132. skip_line();
  1133. }
  1134. void override_sizes()
  1135. {
  1136. int n = 16;
  1137. int *sizes = new int[n];
  1138. int i = 0;
  1139. char *buf = read_string();
  1140. if (!buf)
  1141. return;
  1142. char *p = strtok(buf, " \t");
  1143. for (;;) {
  1144. if (!p)
  1145. break;
  1146. int lower, upper;
  1147. switch (sscanf(p, "%d-%d", &lower, &upper)) {
  1148. case 1:
  1149. upper = lower;
  1150. // fall through
  1151. case 2:
  1152. if (lower <= upper && lower >= 0)
  1153. break;
  1154. // fall through
  1155. default:
  1156. warning(WARN_RANGE, "bad size range `%1'", p);
  1157. return;
  1158. }
  1159. if (i + 2 > n) {
  1160. int *old_sizes = sizes;
  1161. sizes = new int[n*2];
  1162. memcpy(sizes, old_sizes, n*sizeof(int));
  1163. n *= 2;
  1164. a_delete old_sizes;
  1165. }
  1166. sizes[i++] = lower;
  1167. if (lower == 0)
  1168. break;
  1169. sizes[i++] = upper;
  1170. p = strtok(0, " \t");
  1171. }
  1172. font_size::init_size_table(sizes);
  1173. }
  1174. void space_size()
  1175. {
  1176. int n;
  1177. if (get_integer(&n)) {
  1178. curenv->space_size = n;
  1179. if (has_arg() && get_integer(&n))
  1180. curenv->sentence_space_size = n;
  1181. else
  1182. curenv->sentence_space_size = curenv->space_size;
  1183. }
  1184. skip_line();
  1185. }
  1186. void fill()
  1187. {
  1188. while (!tok.newline() && !tok.eof())
  1189. tok.next();
  1190. if (break_flag)
  1191. curenv->do_break();
  1192. curenv->fill = 1;
  1193. tok.next();
  1194. }
  1195. void no_fill()
  1196. {
  1197. while (!tok.newline() && !tok.eof())
  1198. tok.next();
  1199. if (break_flag)
  1200. curenv->do_break();
  1201. curenv->fill = 0;
  1202. curenv->suppress_next_eol = 1;
  1203. tok.next();
  1204. }
  1205. void center()
  1206. {
  1207. int n;
  1208. if (!has_arg() || !get_integer(&n))
  1209. n = 1;
  1210. else if (n < 0)
  1211. n = 0;
  1212. while (!tok.newline() && !tok.eof())
  1213. tok.next();
  1214. if (break_flag)
  1215. curenv->do_break();
  1216. curenv->right_justify_lines = 0;
  1217. curenv->center_lines = n;
  1218. curdiv->modified_tag.incl(MTSM_CE);
  1219. tok.next();
  1220. }
  1221. void right_justify()
  1222. {
  1223. int n;
  1224. if (!has_arg() || !get_integer(&n))
  1225. n = 1;
  1226. else if (n < 0)
  1227. n = 0;
  1228. while (!tok.newline() && !tok.eof())
  1229. tok.next();
  1230. if (break_flag)
  1231. curenv->do_break();
  1232. curenv->center_lines = 0;
  1233. curenv->right_justify_lines = n;
  1234. curdiv->modified_tag.incl(MTSM_RJ);
  1235. tok.next();
  1236. }
  1237. void line_length()
  1238. {
  1239. hunits temp;
  1240. if (has_arg() && get_hunits(&temp, 'm', curenv->line_length)) {
  1241. if (temp < H0) {
  1242. warning(WARN_RANGE, "bad line length %1u", temp.to_units());
  1243. temp = H0;
  1244. }
  1245. }
  1246. else
  1247. temp = curenv->prev_line_length;
  1248. curenv->prev_line_length = curenv->line_length;
  1249. curenv->line_length = temp;
  1250. curdiv->modified_tag.incl(MTSM_LL);
  1251. skip_line();
  1252. }
  1253. void title_length()
  1254. {
  1255. hunits temp;
  1256. if (has_arg() && get_hunits(&temp, 'm', curenv->title_length)) {
  1257. if (temp < H0) {
  1258. warning(WARN_RANGE, "bad title length %1u", temp.to_units());
  1259. temp = H0;
  1260. }
  1261. }
  1262. else
  1263. temp = curenv->prev_title_length;
  1264. curenv->prev_title_length = curenv->title_length;
  1265. curenv->title_length = temp;
  1266. skip_line();
  1267. }
  1268. void vertical_spacing()
  1269. {
  1270. vunits temp;
  1271. if (has_arg() && get_vunits(&temp, 'p', curenv->vertical_spacing)) {
  1272. if (temp < V0) {
  1273. warning(WARN_RANGE, "vertical spacing must not be negative");
  1274. temp = vresolution;
  1275. }
  1276. }
  1277. else
  1278. temp = curenv->prev_vertical_spacing;
  1279. curenv->prev_vertical_spacing = curenv->vertical_spacing;
  1280. curenv->vertical_spacing = temp;
  1281. skip_line();
  1282. }
  1283. void post_vertical_spacing()
  1284. {
  1285. vunits temp;
  1286. if (has_arg() && get_vunits(&temp, 'p', curenv->post_vertical_spacing)) {
  1287. if (temp < V0) {
  1288. warning(WARN_RANGE,
  1289. "post vertical spacing must be greater than or equal to 0");
  1290. temp = V0;
  1291. }
  1292. }
  1293. else
  1294. temp = curenv->prev_post_vertical_spacing;
  1295. curenv->prev_post_vertical_spacing = curenv->post_vertical_spacing;
  1296. curenv->post_vertical_spacing = temp;
  1297. skip_line();
  1298. }
  1299. void line_spacing()
  1300. {
  1301. int temp;
  1302. if (has_arg() && get_integer(&temp)) {
  1303. if (temp < 1) {
  1304. warning(WARN_RANGE, "value %1 out of range: interpreted as 1", temp);
  1305. temp = 1;
  1306. }
  1307. }
  1308. else
  1309. temp = curenv->prev_line_spacing;
  1310. curenv->prev_line_spacing = curenv->line_spacing;
  1311. curenv->line_spacing = temp;
  1312. skip_line();
  1313. }
  1314. void indent()
  1315. {
  1316. hunits temp;
  1317. if (has_arg() && get_hunits(&temp, 'm', curenv->indent)) {
  1318. if (temp < H0) {
  1319. warning(WARN_RANGE, "indent cannot be negative");
  1320. temp = H0;
  1321. }
  1322. }
  1323. else
  1324. temp = curenv->prev_indent;
  1325. while (!tok.newline() && !tok.eof())
  1326. tok.next();
  1327. if (break_flag)
  1328. curenv->do_break();
  1329. curenv->have_temporary_indent = 0;
  1330. curenv->prev_indent = curenv->indent;
  1331. curenv->indent = temp;
  1332. curdiv->modified_tag.incl(MTSM_IN);
  1333. tok.next();
  1334. }
  1335. void temporary_indent()
  1336. {
  1337. int err = 0;
  1338. hunits temp;
  1339. if (!get_hunits(&temp, 'm', curenv->get_indent()))
  1340. err = 1;
  1341. while (!tok.newline() && !tok.eof())
  1342. tok.next();
  1343. if (break_flag)
  1344. curenv->do_break();
  1345. if (temp < H0) {
  1346. warning(WARN_RANGE, "total indent cannot be negative");
  1347. temp = H0;
  1348. }
  1349. if (!err) {
  1350. curenv->temporary_indent = temp;
  1351. curenv->have_temporary_indent = 1;
  1352. curdiv->modified_tag.incl(MTSM_TI);
  1353. }
  1354. tok.next();
  1355. }
  1356. node *do_underline_special(int underline_spaces)
  1357. {
  1358. macro m;
  1359. m.append_str("x u ");
  1360. m.append(underline_spaces + '0');
  1361. return new special_node(m, 1);
  1362. }
  1363. void do_underline(int underline_spaces)
  1364. {
  1365. int n;
  1366. if (!has_arg() || !get_integer(&n))
  1367. n = 1;
  1368. if (n <= 0) {
  1369. if (curenv->underline_lines > 0) {
  1370. curenv->prev_fontno = curenv->fontno;
  1371. curenv->fontno = curenv->pre_underline_fontno;
  1372. if (underline_spaces) {
  1373. curenv->underline_spaces = 0;
  1374. curenv->add_node(do_underline_special(0));
  1375. }
  1376. }
  1377. curenv->underline_lines = 0;
  1378. }
  1379. else {
  1380. curenv->underline_lines = n;
  1381. curenv->pre_underline_fontno = curenv->fontno;
  1382. curenv->fontno = get_underline_fontno();
  1383. if (underline_spaces) {
  1384. curenv->underline_spaces = 1;
  1385. curenv->add_node(do_underline_special(1));
  1386. }
  1387. }
  1388. skip_line();
  1389. }
  1390. void continuous_underline()
  1391. {
  1392. do_underline(1);
  1393. }
  1394. void underline()
  1395. {
  1396. do_underline(0);
  1397. }
  1398. void control_char()
  1399. {
  1400. curenv->control_char = '.';
  1401. if (has_arg()) {
  1402. if (tok.ch() == 0)
  1403. error("bad control character");
  1404. else
  1405. curenv->control_char = tok.ch();
  1406. }
  1407. skip_line();
  1408. }
  1409. void no_break_control_char()
  1410. {
  1411. curenv->no_break_control_char = '\'';
  1412. if (has_arg()) {
  1413. if (tok.ch() == 0)
  1414. error("bad control character");
  1415. else
  1416. curenv->no_break_control_char = tok.ch();
  1417. }
  1418. skip_line();
  1419. }
  1420. void margin_character()
  1421. {
  1422. while (tok.space())
  1423. tok.next();
  1424. charinfo *ci = tok.get_char();
  1425. if (ci) {
  1426. // Call tok.next() only after making the node so that
  1427. // .mc \s+9\(br\s0 works.
  1428. node *nd = curenv->make_char_node(ci);
  1429. tok.next();
  1430. if (nd) {
  1431. delete curenv->margin_character_node;
  1432. curenv->margin_character_node = nd;
  1433. curenv->margin_character_flags = (MARGIN_CHARACTER_ON
  1434. |MARGIN_CHARACTER_NEXT);
  1435. hunits d;
  1436. if (has_arg() && get_hunits(&d, 'm'))
  1437. curenv->margin_character_distance = d;
  1438. }
  1439. }
  1440. else {
  1441. check_missing_character();
  1442. curenv->margin_character_flags &= ~MARGIN_CHARACTER_ON;
  1443. if (curenv->margin_character_flags == 0) {
  1444. delete curenv->margin_character_node;
  1445. curenv->margin_character_node = 0;
  1446. }
  1447. }
  1448. skip_line();
  1449. }
  1450. void number_lines()
  1451. {
  1452. delete_node_list(curenv->numbering_nodes);
  1453. curenv->numbering_nodes = 0;
  1454. if (has_arg()) {
  1455. node *nd = 0;
  1456. for (int i = '9'; i >= '0'; i--) {
  1457. node *tem = make_node(charset_table[i], curenv);
  1458. if (!tem) {
  1459. skip_line();
  1460. return;
  1461. }
  1462. tem->next = nd;
  1463. nd = tem;
  1464. }
  1465. curenv->numbering_nodes = nd;
  1466. curenv->line_number_digit_width = env_digit_width(curenv);
  1467. int n;
  1468. if (!tok.delimiter()) {
  1469. if (get_integer(&n, next_line_number)) {
  1470. next_line_number = n;
  1471. if (next_line_number < 0) {
  1472. warning(WARN_RANGE, "negative line number");
  1473. next_line_number = 0;
  1474. }
  1475. }
  1476. }
  1477. else
  1478. while (!tok.space() && !tok.newline() && !tok.eof())
  1479. tok.next();
  1480. if (has_arg()) {
  1481. if (!tok.delimiter()) {
  1482. if (get_integer(&n)) {
  1483. if (n <= 0) {
  1484. warning(WARN_RANGE, "negative or zero line number multiple");
  1485. }
  1486. else
  1487. curenv->line_number_multiple = n;
  1488. }
  1489. }
  1490. else
  1491. while (!tok.space() && !tok.newline() && !tok.eof())
  1492. tok.next();
  1493. if (has_arg()) {
  1494. if (!tok.delimiter()) {
  1495. if (get_integer(&n))
  1496. curenv->number_text_separation = n;
  1497. }
  1498. else
  1499. while (!tok.space() && !tok.newline() && !tok.eof())
  1500. tok.next();
  1501. if (has_arg() && !tok.delimiter() && get_integer(&n))
  1502. curenv->line_number_indent = n;
  1503. }
  1504. }
  1505. }
  1506. skip_line();
  1507. }
  1508. void no_number()
  1509. {
  1510. int n;
  1511. if (has_arg() && get_integer(&n))
  1512. curenv->no_number_count = n > 0 ? n : 0;
  1513. else
  1514. curenv->no_number_count = 1;
  1515. skip_line();
  1516. }
  1517. void no_hyphenate()
  1518. {
  1519. curenv->hyphenation_flags = 0;
  1520. skip_line();
  1521. }
  1522. void hyphenate_request()
  1523. {
  1524. int n;
  1525. if (has_arg() && get_integer(&n))
  1526. curenv->hyphenation_flags = n;
  1527. else
  1528. curenv->hyphenation_flags = 1;
  1529. skip_line();
  1530. }
  1531. void hyphen_char()
  1532. {
  1533. curenv->hyphen_indicator_char = get_optional_char();
  1534. skip_line();
  1535. }
  1536. void hyphen_line_max_request()
  1537. {
  1538. int n;
  1539. if (has_arg() && get_integer(&n))
  1540. curenv->hyphen_line_max = n;
  1541. else
  1542. curenv->hyphen_line_max = -1;
  1543. skip_line();
  1544. }
  1545. void environment::interrupt()
  1546. {
  1547. if (!dummy) {
  1548. add_node(new transparent_dummy_node);
  1549. interrupted = 1;
  1550. }
  1551. }
  1552. void environment::newline()
  1553. {
  1554. int was_centered = 0;
  1555. if (underline_lines > 0) {
  1556. if (--underline_lines == 0) {
  1557. prev_fontno = fontno;
  1558. fontno = pre_underline_fontno;
  1559. if (underline_spaces) {
  1560. underline_spaces = 0;
  1561. add_node(do_underline_special(0));
  1562. }
  1563. }
  1564. }
  1565. if (current_field)
  1566. wrap_up_field();
  1567. if (current_tab)
  1568. wrap_up_tab();
  1569. // strip trailing spaces
  1570. while (line != 0 && line->discardable()) {
  1571. width_total -= line->width();
  1572. space_total -= line->nspaces();
  1573. node *tem = line;
  1574. line = line->next;
  1575. delete tem;
  1576. }
  1577. node *to_be_output = 0;
  1578. hunits to_be_output_width;
  1579. prev_line_interrupted = 0;
  1580. if (dummy)
  1581. space_newline();
  1582. else if (interrupted) {
  1583. interrupted = 0;
  1584. // see environment::final_break
  1585. prev_line_interrupted = exit_started ? 2 : 1;
  1586. }
  1587. else if (center_lines > 0) {
  1588. --center_lines;
  1589. hunits x = target_text_length - width_total;
  1590. if (x > H0)
  1591. saved_indent += x/2;
  1592. to_be_output = line;
  1593. was_centered = 1;
  1594. to_be_output_width = width_total;
  1595. line = 0;
  1596. }
  1597. else if (right_justify_lines > 0) {
  1598. --right_justify_lines;
  1599. hunits x = target_text_length - width_total;
  1600. if (x > H0)
  1601. saved_indent += x;
  1602. to_be_output = line;
  1603. to_be_output_width = width_total;
  1604. line = 0;
  1605. }
  1606. else if (fill)
  1607. space_newline();
  1608. else {
  1609. to_be_output = line;
  1610. to_be_output_width = width_total;
  1611. line = 0;
  1612. }
  1613. input_line_start = line == 0 ? H0 : width_total;
  1614. if (to_be_output) {
  1615. if (is_html && !fill) {
  1616. curdiv->modified_tag.incl(MTSM_EOL);
  1617. if (suppress_next_eol)
  1618. suppress_next_eol = 0;
  1619. else
  1620. seen_eol = 1;
  1621. }
  1622. output_line(to_be_output, to_be_output_width, was_centered);
  1623. hyphen_line_count = 0;
  1624. }
  1625. if (input_trap_count > 0) {
  1626. if (!(continued_input_trap && prev_line_interrupted))
  1627. if (--input_trap_count == 0)
  1628. spring_trap(input_trap);
  1629. }
  1630. }
  1631. void environment::output_line(node *n, hunits width, int was_centered)
  1632. {
  1633. prev_text_length = width;
  1634. if (margin_character_flags) {
  1635. hunits d = line_length + margin_character_distance - saved_indent - width;
  1636. if (d > 0) {
  1637. n = new hmotion_node(d, get_fill_color(), n);
  1638. width += d;
  1639. }
  1640. margin_character_flags &= ~MARGIN_CHARACTER_NEXT;
  1641. node *tem;
  1642. if (!margin_character_flags) {
  1643. tem = margin_character_node;
  1644. margin_character_node = 0;
  1645. }
  1646. else
  1647. tem = margin_character_node->copy();
  1648. tem->next = n;
  1649. n = tem;
  1650. width += tem->width();
  1651. }
  1652. node *nn = 0;
  1653. while (n != 0) {
  1654. node *tem = n->next;
  1655. n->next = nn;
  1656. nn = n;
  1657. n = tem;
  1658. }
  1659. if (!saved_indent.is_zero())
  1660. nn = new hmotion_node(saved_indent, get_fill_color(), nn);
  1661. width += saved_indent;
  1662. if (no_number_count > 0)
  1663. --no_number_count;
  1664. else if (numbering_nodes) {
  1665. hunits w = (line_number_digit_width
  1666. *(3+line_number_indent+number_text_separation));
  1667. if (next_line_number % line_number_multiple != 0)
  1668. nn = new hmotion_node(w, get_fill_color(), nn);
  1669. else {
  1670. hunits x = w;
  1671. nn = new hmotion_node(number_text_separation * line_number_digit_width,
  1672. get_fill_color(), nn);
  1673. x -= number_text_separation*line_number_digit_width;
  1674. char buf[30];
  1675. sprintf(buf, "%3d", next_line_number);
  1676. for (char *p = strchr(buf, '\0') - 1; p >= buf && *p != ' '; --p) {
  1677. node *gn = numbering_nodes;
  1678. for (int count = *p - '0'; count > 0; count--)
  1679. gn = gn->next;
  1680. gn = gn->copy();
  1681. x -= gn->width();
  1682. gn->next = nn;
  1683. nn = gn;
  1684. }
  1685. nn = new hmotion_node(x, get_fill_color(), nn);
  1686. }
  1687. width += w;
  1688. ++next_line_number;
  1689. }
  1690. output(nn, !fill, vertical_spacing, total_post_vertical_spacing(), width,
  1691. was_centered);
  1692. }
  1693. void environment::start_line()
  1694. {
  1695. assert(line == 0);
  1696. discarding = 0;
  1697. line = new line_start_node;
  1698. if (have_temporary_indent) {
  1699. saved_indent = temporary_indent;
  1700. have_temporary_indent = 0;
  1701. }
  1702. else
  1703. saved_indent = indent;
  1704. target_text_length = line_length - saved_indent;
  1705. width_total = H0;
  1706. space_total = 0;
  1707. }
  1708. hunits environment::get_hyphenation_space()
  1709. {
  1710. return hyphenation_space;
  1711. }
  1712. void hyphenation_space_request()
  1713. {
  1714. hunits n;
  1715. if (get_hunits(&n, 'm')) {
  1716. if (n < H0) {
  1717. warning(WARN_RANGE, "hyphenation space cannot be negative");
  1718. n = H0;
  1719. }
  1720. curenv->hyphenation_space = n;
  1721. }
  1722. skip_line();
  1723. }
  1724. hunits environment::get_hyphenation_margin()
  1725. {
  1726. return hyphenation_margin;
  1727. }
  1728. void hyphenation_margin_request()
  1729. {
  1730. hunits n;
  1731. if (get_hunits(&n, 'm')) {
  1732. if (n < H0) {
  1733. warning(WARN_RANGE, "hyphenation margin cannot be negative");
  1734. n = H0;
  1735. }
  1736. curenv->hyphenation_margin = n;
  1737. }
  1738. skip_line();
  1739. }
  1740. breakpoint *environment::choose_breakpoint()
  1741. {
  1742. hunits x = width_total;
  1743. int s = space_total;
  1744. node *n = line;
  1745. breakpoint *best_bp = 0; // the best breakpoint so far
  1746. int best_bp_fits = 0;
  1747. while (n != 0) {
  1748. x -= n->width();
  1749. s -= n->nspaces();
  1750. breakpoint *bp = n->get_breakpoints(x, s);
  1751. while (bp != 0) {
  1752. if (bp->width <= target_text_length) {
  1753. if (!bp->hyphenated) {
  1754. breakpoint *tem = bp->next;
  1755. bp->next = 0;
  1756. while (tem != 0) {
  1757. breakpoint *tem1 = tem;
  1758. tem = tem->next;
  1759. delete tem1;
  1760. }
  1761. if (best_bp_fits
  1762. // Decide whether to use the hyphenated breakpoint.
  1763. && (hyphen_line_max < 0
  1764. // Only choose the hyphenated breakpoint if it would not
  1765. // exceed the maximum number of consecutive hyphenated
  1766. // lines.
  1767. || hyphen_line_count + 1 <= hyphen_line_max)
  1768. && !(adjust_mode == ADJUST_BOTH
  1769. // Don't choose the hyphenated breakpoint if the line
  1770. // can be justified by adding no more than
  1771. // hyphenation_space to any word space.
  1772. ? (bp->nspaces > 0
  1773. && (((target_text_length - bp->width
  1774. + (bp->nspaces - 1)*hresolution)/bp->nspaces)
  1775. <= hyphenation_space))
  1776. // Don't choose the hyphenated breakpoint if the line
  1777. // is no more than hyphenation_margin short.
  1778. : target_text_length - bp->width <= hyphenation_margin)) {
  1779. delete bp;
  1780. return best_bp;
  1781. }
  1782. if (best_bp)
  1783. delete best_bp;
  1784. return bp;
  1785. }
  1786. else {
  1787. if ((adjust_mode == ADJUST_BOTH
  1788. ? hyphenation_space == H0
  1789. : hyphenation_margin == H0)
  1790. && (hyphen_line_max < 0
  1791. || hyphen_line_count + 1 <= hyphen_line_max)) {
  1792. // No need to consider a non-hyphenated breakpoint.
  1793. if (best_bp)
  1794. delete best_bp;
  1795. breakpoint *tem = bp->next;
  1796. bp->next = 0;
  1797. while (tem != 0) {
  1798. breakpoint *tem1 = tem;
  1799. tem = tem->next;
  1800. delete tem1;
  1801. }
  1802. return bp;
  1803. }
  1804. // It fits but it's hyphenated.
  1805. if (!best_bp_fits) {
  1806. if (best_bp)
  1807. delete best_bp;
  1808. best_bp = bp;
  1809. bp = bp->next;
  1810. best_bp_fits = 1;
  1811. }
  1812. else {
  1813. breakpoint *tem = bp;
  1814. bp = bp->next;
  1815. delete tem;
  1816. }
  1817. }
  1818. }
  1819. else {
  1820. if (best_bp)
  1821. delete best_bp;
  1822. best_bp = bp;
  1823. bp = bp->next;
  1824. }
  1825. }
  1826. n = n->next;
  1827. }
  1828. if (best_bp) {
  1829. if (!best_bp_fits)
  1830. output_warning(WARN_BREAK, "can't break line");
  1831. return best_bp;
  1832. }
  1833. return 0;
  1834. }
  1835. void environment::hyphenate_line(int start_here)
  1836. {
  1837. assert(line != 0);
  1838. hyphenation_type prev_type = line->get_hyphenation_type();
  1839. node **startp;
  1840. if (start_here)
  1841. startp = &line;
  1842. else
  1843. for (startp = &line->next; *startp != 0; startp = &(*startp)->next) {
  1844. hyphenation_type this_type = (*startp)->get_hyphenation_type();
  1845. if (prev_type == HYPHEN_BOUNDARY && this_type == HYPHEN_MIDDLE)
  1846. break;
  1847. prev_type = this_type;
  1848. }
  1849. if (*startp == 0)
  1850. return;
  1851. node *tem = *startp;
  1852. do {
  1853. tem = tem->next;
  1854. } while (tem != 0 && tem->get_hyphenation_type() == HYPHEN_MIDDLE);
  1855. int inhibit = (tem != 0 && tem->get_hyphenation_type() == HYPHEN_INHIBIT);
  1856. node *end = tem;
  1857. hyphen_list *sl = 0;
  1858. tem = *startp;
  1859. node *forward = 0;
  1860. int i = 0;
  1861. while (tem != end) {
  1862. sl = tem->get_hyphen_list(sl, &i);
  1863. node *tem1 = tem;
  1864. tem = tem->next;
  1865. tem1->next = forward;
  1866. forward = tem1;
  1867. }
  1868. if (!inhibit) {
  1869. // this is for characters like hyphen and emdash
  1870. int prev_code = 0;
  1871. for (hyphen_list *h = sl; h; h = h->next) {
  1872. h->breakable = (prev_code != 0
  1873. && h->next != 0
  1874. && h->next->hyphenation_code != 0);
  1875. prev_code = h->hyphenation_code;
  1876. }
  1877. }
  1878. if (hyphenation_flags != 0
  1879. && !inhibit
  1880. // this may not be right if we have extra space on this line
  1881. && !((hyphenation_flags & HYPHEN_LAST_LINE)
  1882. && (curdiv->distance_to_next_trap()
  1883. <= vertical_spacing + total_post_vertical_spacing()))
  1884. && i >= 4)
  1885. hyphenate(sl, hyphenation_flags);
  1886. while (forward != 0) {
  1887. node *tem1 = forward;
  1888. forward = forward->next;
  1889. tem1->next = 0;
  1890. tem = tem1->add_self(tem, &sl);
  1891. }
  1892. *startp = tem;
  1893. }
  1894. static node *node_list_reverse(node *n)
  1895. {
  1896. node *res = 0;
  1897. while (n) {
  1898. node *tem = n;
  1899. n = n->next;
  1900. tem->next = res;
  1901. res = tem;
  1902. }
  1903. return res;
  1904. }
  1905. static void distribute_space(node *n, int nspaces, hunits desired_space,
  1906. int force_reverse = 0)
  1907. {
  1908. static int reverse = 0;
  1909. if (force_reverse || reverse)
  1910. n = node_list_reverse(n);
  1911. if (!force_reverse && nspaces > 0 && spread_limit >= 0
  1912. && desired_space.to_units() > 0) {
  1913. hunits em = curenv->get_size();
  1914. double Ems = (double)desired_space.to_units() / nspaces
  1915. / (em.is_zero() ? hresolution : em.to_units());
  1916. if (Ems > spread_limit)
  1917. output_warning(WARN_BREAK, "spreading %1m per space", Ems);
  1918. }
  1919. for (node *tem = n; tem; tem = tem->next)
  1920. tem->spread_space(&nspaces, &desired_space);
  1921. if (force_reverse || reverse)
  1922. (void)node_list_reverse(n);
  1923. if (!force_reverse)
  1924. reverse = !reverse;
  1925. assert(desired_space.is_zero() && nspaces == 0);
  1926. }
  1927. void environment::possibly_break_line(int start_here, int forced)
  1928. {
  1929. int was_centered = center_lines > 0;
  1930. if (!fill || current_tab || current_field || dummy)
  1931. return;
  1932. while (line != 0
  1933. && (forced
  1934. // When a macro follows a paragraph in fill mode, the
  1935. // current line should not be empty.
  1936. || (width_total - line->width()) > target_text_length)) {
  1937. hyphenate_line(start_here);
  1938. breakpoint *bp = choose_breakpoint();
  1939. if (bp == 0)
  1940. // we'll find one eventually
  1941. return;
  1942. node *pre, *post;
  1943. node **ndp = &line;
  1944. while (*ndp != bp->nd)
  1945. ndp = &(*ndp)->next;
  1946. bp->nd->split(bp->index, &pre, &post);
  1947. *ndp = post;
  1948. hunits extra_space_width = H0;
  1949. switch(adjust_mode) {
  1950. case ADJUST_BOTH:
  1951. if (bp->nspaces != 0)
  1952. extra_space_width = target_text_length - bp->width;
  1953. else if (bp->width > 0 && target_text_length > 0
  1954. && target_text_length > bp->width)
  1955. output_warning(WARN_BREAK, "cannot adjust line");
  1956. break;
  1957. case ADJUST_CENTER:
  1958. saved_indent += (target_text_length - bp->width)/2;
  1959. was_centered = 1;
  1960. break;
  1961. case ADJUST_RIGHT:
  1962. saved_indent += target_text_length - bp->width;
  1963. break;
  1964. }
  1965. distribute_space(pre, bp->nspaces, extra_space_width);
  1966. hunits output_width = bp->width + extra_space_width;
  1967. input_line_start -= output_width;
  1968. if (bp->hyphenated)
  1969. hyphen_line_count++;
  1970. else
  1971. hyphen_line_count = 0;
  1972. delete bp;
  1973. space_total = 0;
  1974. width_total = 0;
  1975. node *first_non_discardable = 0;
  1976. node *tem;
  1977. for (tem = line; tem != 0; tem = tem->next)
  1978. if (!tem->discardable())
  1979. first_non_discardable = tem;
  1980. node *to_be_discarded;
  1981. if (first_non_discardable) {
  1982. to_be_discarded = first_non_discardable->next;
  1983. first_non_discardable->next = 0;
  1984. for (tem = line; tem != 0; tem = tem->next) {
  1985. width_total += tem->width();
  1986. space_total += tem->nspaces();
  1987. }
  1988. discarding = 0;
  1989. }
  1990. else {
  1991. discarding = 1;
  1992. to_be_discarded = line;
  1993. line = 0;
  1994. }
  1995. // Do output_line() here so that line will be 0 iff the
  1996. // the environment will be empty.
  1997. output_line(pre, output_width, was_centered);
  1998. while (to_be_discarded != 0) {
  1999. tem = to_be_discarded;
  2000. to_be_discarded = to_be_discarded->next;
  2001. input_line_start -= tem->width();
  2002. delete tem;
  2003. }
  2004. if (line != 0) {
  2005. if (have_temporary_indent) {
  2006. saved_indent = temporary_indent;
  2007. have_temporary_indent = 0;
  2008. }
  2009. else
  2010. saved_indent = indent;
  2011. target_text_length = line_length - saved_indent;
  2012. }
  2013. }
  2014. }
  2015. /*
  2016. Do the break at the end of input after the end macro (if any).
  2017. Unix troff behaves as follows: if the last line is
  2018. foo bar\c
  2019. it will output foo on the current page, and bar on the next page;
  2020. if the last line is
  2021. foo\c
  2022. or
  2023. foo bar
  2024. everything will be output on the current page. This behaviour must be
  2025. considered a bug.
  2026. The problem is that some macro packages rely on this. For example,
  2027. the ATK macros have an end macro that emits \c if it needs to print a
  2028. table of contents but doesn't do a 'bp in the end macro; instead the
  2029. 'bp is done in the bottom of page trap. This works with Unix troff,
  2030. provided that the current environment is not empty at the end of the
  2031. input file.
  2032. The following will make macro packages that do that sort of thing work
  2033. even if the current environment is empty at the end of the input file.
  2034. If the last input line used \c and this line occurred in the end macro,
  2035. then we'll force everything out on the current page, but we'll make
  2036. sure that the environment isn't empty so that we won't exit at the
  2037. bottom of this page.
  2038. */
  2039. void environment::final_break()
  2040. {
  2041. if (prev_line_interrupted == 2) {
  2042. do_break();
  2043. add_node(new transparent_dummy_node);
  2044. }
  2045. else
  2046. do_break();
  2047. }
  2048. node *environment::make_tag(const char *nm, int i)
  2049. {
  2050. if (is_html) {
  2051. /*
  2052. * need to emit tag for post-grohtml
  2053. * but we check to see whether we can emit specials
  2054. */
  2055. if (curdiv == topdiv && topdiv->before_first_page)
  2056. topdiv->begin_page();
  2057. macro *m = new macro;
  2058. m->append_str("devtag:");
  2059. for (const char *p = nm; *p; p++)
  2060. if (!invalid_input_char((unsigned char)*p))
  2061. m->append(*p);
  2062. m->append(' ');
  2063. m->append_int(i);
  2064. return new special_node(*m);
  2065. }
  2066. return 0;
  2067. }
  2068. void environment::dump_troff_state()
  2069. {
  2070. #define SPACES " "
  2071. fprintf(stderr, SPACES "register `in' = %d\n", curenv->indent.to_units());
  2072. if (curenv->have_temporary_indent)
  2073. fprintf(stderr, SPACES "register `ti' = %d\n",
  2074. curenv->temporary_indent.to_units());
  2075. fprintf(stderr, SPACES "centered lines `ce' = %d\n", curenv->center_lines);
  2076. fprintf(stderr, SPACES "register `ll' = %d\n",
  2077. curenv->line_length.to_units());
  2078. fprintf(stderr, SPACES "fill `fi=1/nf=0' = %d\n", curenv->fill);
  2079. fprintf(stderr, SPACES "page offset `po' = %d\n",
  2080. topdiv->get_page_offset().to_units());
  2081. fprintf(stderr, SPACES "seen_break = %d\n", curenv->seen_break);
  2082. fprintf(stderr, SPACES "seen_space = %d\n", curenv->seen_space);
  2083. fflush(stderr);
  2084. #undef SPACES
  2085. }
  2086. statem *environment::construct_state(int only_eol)
  2087. {
  2088. if (is_html) {
  2089. statem *s = new statem();
  2090. if (!only_eol) {
  2091. s->add_tag(MTSM_IN, indent);
  2092. s->add_tag(MTSM_LL, line_length);
  2093. s->add_tag(MTSM_PO, topdiv->get_page_offset().to_units());
  2094. s->add_tag(MTSM_RJ, right_justify_lines);
  2095. if (have_temporary_indent)
  2096. s->add_tag(MTSM_TI, temporary_indent);
  2097. s->add_tag_ta();
  2098. if (seen_break)
  2099. s->add_tag(MTSM_BR);
  2100. if (seen_space != 0)
  2101. s->add_tag(MTSM_SP, seen_space);
  2102. seen_break = 0;
  2103. seen_space = 0;
  2104. }
  2105. if (seen_eol) {
  2106. s->add_tag(MTSM_EOL);
  2107. s->add_tag(MTSM_CE, center_lines);
  2108. }
  2109. seen_eol = 0;
  2110. return s;
  2111. }
  2112. else
  2113. return NULL;
  2114. }
  2115. void environment::construct_format_state(node *n, int was_centered,
  2116. int filling)
  2117. {
  2118. if (is_html) {
  2119. // find first glyph node which has a state.
  2120. while (n != 0 && n->state == 0)
  2121. n = n->next;
  2122. if (n == 0 || (n->state == 0))
  2123. return;
  2124. if (seen_space != 0)
  2125. n->state->add_tag(MTSM_SP, seen_space);
  2126. if (seen_eol && topdiv == curdiv)
  2127. n->state->add_tag(MTSM_EOL);
  2128. seen_space = 0;
  2129. seen_eol = 0;
  2130. if (was_centered)
  2131. n->state->add_tag(MTSM_CE, center_lines+1);
  2132. else
  2133. n->state->add_tag_if_unknown(MTSM_CE, 0);
  2134. n->state->add_tag_if_unknown(MTSM_FI, filling);
  2135. n = n->next;
  2136. while (n != 0) {
  2137. if (n->state != 0) {
  2138. n->state->sub_tag_ce();
  2139. n->state->add_tag_if_unknown(MTSM_FI, filling);
  2140. }
  2141. n = n->next;
  2142. }
  2143. }
  2144. }
  2145. void environment::construct_new_line_state(node *n)
  2146. {
  2147. if (is_html) {
  2148. // find first glyph node which has a state.
  2149. while (n != 0 && n->state == 0)
  2150. n = n->next;
  2151. if (n == 0 || n->state == 0)
  2152. return;
  2153. if (seen_space != 0)
  2154. n->state->add_tag(MTSM_SP, seen_space);
  2155. if (seen_eol && topdiv == curdiv)
  2156. n->state->add_tag(MTSM_EOL);
  2157. seen_space = 0;
  2158. seen_eol = 0;
  2159. }
  2160. }
  2161. extern int global_diverted_space;
  2162. void environment::do_break(int do_spread)
  2163. {
  2164. int was_centered = 0;
  2165. if (curdiv == topdiv && topdiv->before_first_page) {
  2166. topdiv->begin_page();
  2167. return;
  2168. }
  2169. if (current_tab)
  2170. wrap_up_tab();
  2171. if (line) {
  2172. // this is so that hyphenation works
  2173. line = new space_node(H0, get_fill_color(), line);
  2174. space_total++;
  2175. possibly_break_line(0, do_spread);
  2176. }
  2177. while (line != 0 && line->discardable()) {
  2178. width_total -= line->width();
  2179. space_total -= line->nspaces();
  2180. node *tem = line;
  2181. line = line->next;
  2182. delete tem;
  2183. }
  2184. discarding = 0;
  2185. input_line_start = H0;
  2186. if (line != 0) {
  2187. if (fill) {
  2188. switch (adjust_mode) {
  2189. case ADJUST_CENTER:
  2190. saved_indent += (target_text_length - width_total)/2;
  2191. was_centered = 1;
  2192. break;
  2193. case ADJUST_RIGHT:
  2194. saved_indent += target_text_length - width_total;
  2195. break;
  2196. }
  2197. }
  2198. node *tem = line;
  2199. line = 0;
  2200. output_line(tem, width_total, was_centered);
  2201. hyphen_line_count = 0;
  2202. }
  2203. prev_line_interrupted = 0;
  2204. #ifdef WIDOW_CONTROL
  2205. mark_last_line();
  2206. output_pending_lines();
  2207. #endif /* WIDOW_CONTROL */
  2208. if (!global_diverted_space) {
  2209. curdiv->modified_tag.incl(MTSM_BR);
  2210. seen_break = 1;
  2211. }
  2212. }
  2213. int environment::is_empty()
  2214. {
  2215. return !current_tab && line == 0 && pending_lines == 0;
  2216. }
  2217. void do_break_request(int spread)
  2218. {
  2219. while (!tok.newline() && !tok.eof())
  2220. tok.next();
  2221. if (break_flag)
  2222. curenv->do_break(spread);
  2223. tok.next();
  2224. }
  2225. void break_request()
  2226. {
  2227. do_break_request(0);
  2228. }
  2229. void break_spread_request()
  2230. {
  2231. do_break_request(1);
  2232. }
  2233. void title()
  2234. {
  2235. if (curdiv == topdiv && topdiv->before_first_page) {
  2236. handle_initial_title();
  2237. return;
  2238. }
  2239. node *part[3];
  2240. hunits part_width[3];
  2241. part[0] = part[1] = part[2] = 0;
  2242. environment env(curenv);
  2243. environment *oldenv = curenv;
  2244. curenv = &env;
  2245. read_title_parts(part, part_width);
  2246. curenv = oldenv;
  2247. curenv->size = env.size;
  2248. curenv->prev_size = env.prev_size;
  2249. curenv->requested_size = env.requested_size;
  2250. curenv->prev_requested_size = env.prev_requested_size;
  2251. curenv->char_height = env.char_height;
  2252. curenv->char_slant = env.char_slant;
  2253. curenv->fontno = env.fontno;
  2254. curenv->prev_fontno = env.prev_fontno;
  2255. curenv->glyph_color = env.glyph_color;
  2256. curenv->prev_glyph_color = env.prev_glyph_color;
  2257. curenv->fill_color = env.fill_color;
  2258. curenv->prev_fill_color = env.prev_fill_color;
  2259. node *n = 0;
  2260. node *p = part[2];
  2261. while (p != 0) {
  2262. node *tem = p;
  2263. p = p->next;
  2264. tem->next = n;
  2265. n = tem;
  2266. }
  2267. hunits length_title(curenv->title_length);
  2268. hunits f = length_title - part_width[1];
  2269. hunits f2 = f/2;
  2270. n = new hmotion_node(f2 - part_width[2], curenv->get_fill_color(), n);
  2271. p = part[1];
  2272. while (p != 0) {
  2273. node *tem = p;
  2274. p = p->next;
  2275. tem->next = n;
  2276. n = tem;
  2277. }
  2278. n = new hmotion_node(f - f2 - part_width[0], curenv->get_fill_color(), n);
  2279. p = part[0];
  2280. while (p != 0) {
  2281. node *tem = p;
  2282. p = p->next;
  2283. tem->next = n;
  2284. n = tem;
  2285. }
  2286. curenv->output_title(n, !curenv->fill, curenv->vertical_spacing,
  2287. curenv->total_post_vertical_spacing(), length_title);
  2288. curenv->hyphen_line_count = 0;
  2289. tok.next();
  2290. }
  2291. void adjust()
  2292. {
  2293. curenv->adjust_mode |= 1;
  2294. if (has_arg()) {
  2295. switch (tok.ch()) {
  2296. case 'l':
  2297. curenv->adjust_mode = ADJUST_LEFT;
  2298. break;
  2299. case 'r':
  2300. curenv->adjust_mode = ADJUST_RIGHT;
  2301. break;
  2302. case 'c':
  2303. curenv->adjust_mode = ADJUST_CENTER;
  2304. break;
  2305. case 'b':
  2306. case 'n':
  2307. curenv->adjust_mode = ADJUST_BOTH;
  2308. break;
  2309. default:
  2310. int n;
  2311. if (get_integer(&n)) {
  2312. if (n < 0)
  2313. warning(WARN_RANGE, "negative adjustment mode");
  2314. else if (n > 5) {
  2315. curenv->adjust_mode = 5;
  2316. warning(WARN_RANGE, "adjustment mode `%1' out of range", n);
  2317. }
  2318. else
  2319. curenv->adjust_mode = n;
  2320. }
  2321. }
  2322. }
  2323. skip_line();
  2324. }
  2325. void no_adjust()
  2326. {
  2327. curenv->adjust_mode &= ~1;
  2328. skip_line();
  2329. }
  2330. void do_input_trap(int continued)
  2331. {
  2332. curenv->input_trap_count = 0;
  2333. if (continued)
  2334. curenv->continued_input_trap = 1;
  2335. int n;
  2336. if (has_arg() && get_integer(&n)) {
  2337. if (n <= 0)
  2338. warning(WARN_RANGE,
  2339. "number of lines for input trap must be greater than zero");
  2340. else {
  2341. symbol s = get_name(1);
  2342. if (!s.is_null()) {
  2343. curenv->input_trap_count = n;
  2344. curenv->input_trap = s;
  2345. }
  2346. }
  2347. }
  2348. skip_line();
  2349. }
  2350. void input_trap()
  2351. {
  2352. do_input_trap(0);
  2353. }
  2354. void input_trap_continued()
  2355. {
  2356. do_input_trap(1);
  2357. }
  2358. /* tabs */
  2359. // must not be R or C or L or a legitimate part of a number expression
  2360. const char TAB_REPEAT_CHAR = 'T';
  2361. struct tab {
  2362. tab *next;
  2363. hunits pos;
  2364. tab_type type;
  2365. tab(hunits, tab_type);
  2366. enum { BLOCK = 1024 };
  2367. static tab *free_list;
  2368. void *operator new(size_t);
  2369. void operator delete(void *);
  2370. };
  2371. tab *tab::free_list = 0;
  2372. void *tab::operator new(size_t n)
  2373. {
  2374. assert(n == sizeof(tab));
  2375. if (!free_list) {
  2376. free_list = (tab *)new char[sizeof(tab)*BLOCK];
  2377. for (int i = 0; i < BLOCK - 1; i++)
  2378. free_list[i].next = free_list + i + 1;
  2379. free_list[BLOCK-1].next = 0;
  2380. }
  2381. tab *p = free_list;
  2382. free_list = (tab *)(free_list->next);
  2383. p->next = 0;
  2384. return p;
  2385. }
  2386. #ifdef __GNUG__
  2387. /* cfront can't cope with this. */
  2388. inline
  2389. #endif
  2390. void tab::operator delete(void *p)
  2391. {
  2392. if (p) {
  2393. ((tab *)p)->next = free_list;
  2394. free_list = (tab *)p;
  2395. }
  2396. }
  2397. tab::tab(hunits x, tab_type t) : next(0), pos(x), type(t)
  2398. {
  2399. }
  2400. tab_stops::tab_stops(hunits distance, tab_type type)
  2401. : initial_list(0)
  2402. {
  2403. repeated_list = new tab(distance, type);
  2404. }
  2405. tab_stops::~tab_stops()
  2406. {
  2407. clear();
  2408. }
  2409. tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance)
  2410. {
  2411. hunits nextpos;
  2412. return distance_to_next_tab(curpos, distance, &nextpos);
  2413. }
  2414. tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance,
  2415. hunits *nextpos)
  2416. {
  2417. hunits lastpos = 0;
  2418. tab *tem;
  2419. for (tem = initial_list; tem && tem->pos <= curpos; tem = tem->next)
  2420. lastpos = tem->pos;
  2421. if (tem) {
  2422. *distance = tem->pos - curpos;
  2423. *nextpos = tem->pos;
  2424. return tem->type;
  2425. }
  2426. if (repeated_list == 0)
  2427. return TAB_NONE;
  2428. hunits base = lastpos;
  2429. for (;;) {
  2430. for (tem = repeated_list; tem && tem->pos + base <= curpos; tem = tem->next)
  2431. lastpos = tem->pos;
  2432. if (tem) {
  2433. *distance = tem->pos + base - curpos;
  2434. *nextpos = tem->pos + base;
  2435. return tem->type;
  2436. }
  2437. assert(lastpos > 0);
  2438. base += lastpos;
  2439. }
  2440. return TAB_NONE;
  2441. }
  2442. const char *tab_stops::to_string()
  2443. {
  2444. static char *buf = 0;
  2445. static int buf_size = 0;
  2446. // figure out a maximum on the amount of space we can need
  2447. int count = 0;
  2448. tab *p;
  2449. for (p = initial_list; p; p = p->next)
  2450. ++count;
  2451. for (p = repeated_list; p; p = p->next)
  2452. ++count;
  2453. // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0'
  2454. int need = count*12 + 3;
  2455. if (buf == 0 || need > buf_size) {
  2456. if (buf)
  2457. a_delete buf;
  2458. buf_size = need;
  2459. buf = new char[buf_size];
  2460. }
  2461. char *ptr = buf;
  2462. for (p = initial_list; p; p = p->next) {
  2463. strcpy(ptr, i_to_a(p->pos.to_units()));
  2464. ptr = strchr(ptr, '\0');
  2465. *ptr++ = 'u';
  2466. *ptr = '\0';
  2467. switch (p->type) {
  2468. case TAB_LEFT:
  2469. break;
  2470. case TAB_RIGHT:
  2471. *ptr++ = 'R';
  2472. break;
  2473. case TAB_CENTER:
  2474. *ptr++ = 'C';
  2475. break;
  2476. case TAB_NONE:
  2477. default:
  2478. assert(0);
  2479. }
  2480. }
  2481. if (repeated_list)
  2482. *ptr++ = TAB_REPEAT_CHAR;
  2483. for (p = repeated_list; p; p = p->next) {
  2484. strcpy(ptr, i_to_a(p->pos.to_units()));
  2485. ptr = strchr(ptr, '\0');
  2486. *ptr++ = 'u';
  2487. *ptr = '\0';
  2488. switch (p->type) {
  2489. case TAB_LEFT:
  2490. break;
  2491. case TAB_RIGHT:
  2492. *ptr++ = 'R';
  2493. break;
  2494. case TAB_CENTER:
  2495. *ptr++ = 'C';
  2496. break;
  2497. case TAB_NONE:
  2498. default:
  2499. assert(0);
  2500. }
  2501. }
  2502. *ptr++ = '\0';
  2503. return buf;
  2504. }
  2505. tab_stops::tab_stops() : initial_list(0), repeated_list(0)
  2506. {
  2507. }
  2508. tab_stops::tab_stops(const tab_stops &ts)
  2509. : initial_list(0), repeated_list(0)
  2510. {
  2511. tab **p = &initial_list;
  2512. tab *t = ts.initial_list;
  2513. while (t) {
  2514. *p = new tab(t->pos, t->type);
  2515. t = t->next;
  2516. p = &(*p)->next;
  2517. }
  2518. p = &repeated_list;
  2519. t = ts.repeated_list;
  2520. while (t) {
  2521. *p = new tab(t->pos, t->type);
  2522. t = t->next;
  2523. p = &(*p)->next;
  2524. }
  2525. }
  2526. void tab_stops::clear()
  2527. {
  2528. while (initial_list) {
  2529. tab *tem = initial_list;
  2530. initial_list = initial_list->next;
  2531. delete tem;
  2532. }
  2533. while (repeated_list) {
  2534. tab *tem = repeated_list;
  2535. repeated_list = repeated_list->next;
  2536. delete tem;
  2537. }
  2538. }
  2539. void tab_stops::add_tab(hunits pos, tab_type type, int repeated)
  2540. {
  2541. tab **p;
  2542. for (p = repeated ? &repeated_list : &initial_list; *p; p = &(*p)->next)
  2543. ;
  2544. *p = new tab(pos, type);
  2545. }
  2546. void tab_stops::operator=(const tab_stops &ts)
  2547. {
  2548. clear();
  2549. tab **p = &initial_list;
  2550. tab *t = ts.initial_list;
  2551. while (t) {
  2552. *p = new tab(t->pos, t->type);
  2553. t = t->next;
  2554. p = &(*p)->next;
  2555. }
  2556. p = &repeated_list;
  2557. t = ts.repeated_list;
  2558. while (t) {
  2559. *p = new tab(t->pos, t->type);
  2560. t = t->next;
  2561. p = &(*p)->next;
  2562. }
  2563. }
  2564. void set_tabs()
  2565. {
  2566. hunits pos;
  2567. hunits prev_pos = 0;
  2568. int first = 1;
  2569. int repeated = 0;
  2570. tab_stops tabs;
  2571. while (has_arg()) {
  2572. if (tok.ch() == TAB_REPEAT_CHAR) {
  2573. tok.next();
  2574. repeated = 1;
  2575. prev_pos = 0;
  2576. }
  2577. if (!get_hunits(&pos, 'm', prev_pos))
  2578. break;
  2579. tab_type type = TAB_LEFT;
  2580. if (tok.ch() == 'C') {
  2581. tok.next();
  2582. type = TAB_CENTER;
  2583. }
  2584. else if (tok.ch() == 'R') {
  2585. tok.next();
  2586. type = TAB_RIGHT;
  2587. }
  2588. else if (tok.ch() == 'L') {
  2589. tok.next();
  2590. }
  2591. if (pos <= prev_pos && !first)
  2592. warning(WARN_RANGE,
  2593. "positions of tab stops must be strictly increasing");
  2594. else {
  2595. tabs.add_tab(pos, type, repeated);
  2596. prev_pos = pos;
  2597. first = 0;
  2598. }
  2599. }
  2600. curenv->tabs = tabs;
  2601. curdiv->modified_tag.incl(MTSM_TA);
  2602. skip_line();
  2603. }
  2604. const char *environment::get_tabs()
  2605. {
  2606. return tabs.to_string();
  2607. }
  2608. tab_type environment::distance_to_next_tab(hunits *distance)
  2609. {
  2610. return line_tabs
  2611. ? curenv->tabs.distance_to_next_tab(get_text_length(), distance)
  2612. : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance);
  2613. }
  2614. tab_type environment::distance_to_next_tab(hunits *distance, hunits *leftpos)
  2615. {
  2616. return line_tabs
  2617. ? curenv->tabs.distance_to_next_tab(get_text_length(), distance, leftpos)
  2618. : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance,
  2619. leftpos);
  2620. }
  2621. void field_characters()
  2622. {
  2623. field_delimiter_char = get_optional_char();
  2624. if (field_delimiter_char)
  2625. padding_indicator_char = get_optional_char();
  2626. else
  2627. padding_indicator_char = 0;
  2628. skip_line();
  2629. }
  2630. void line_tabs_request()
  2631. {
  2632. int n;
  2633. if (has_arg() && get_integer(&n))
  2634. curenv->line_tabs = n != 0;
  2635. else
  2636. curenv->line_tabs = 1;
  2637. skip_line();
  2638. }
  2639. int environment::get_line_tabs()
  2640. {
  2641. return line_tabs;
  2642. }
  2643. void environment::wrap_up_tab()
  2644. {
  2645. if (!current_tab)
  2646. return;
  2647. if (line == 0)
  2648. start_line();
  2649. hunits tab_amount;
  2650. switch (current_tab) {
  2651. case TAB_RIGHT:
  2652. tab_amount = tab_distance - tab_width;
  2653. line = make_tab_node(tab_amount, line);
  2654. break;
  2655. case TAB_CENTER:
  2656. tab_amount = tab_distance - tab_width/2;
  2657. line = make_tab_node(tab_amount, line);
  2658. break;
  2659. case TAB_NONE:
  2660. case TAB_LEFT:
  2661. default:
  2662. assert(0);
  2663. }
  2664. width_total += tab_amount;
  2665. width_total += tab_width;
  2666. if (current_field) {
  2667. if (tab_precedes_field) {
  2668. pre_field_width += tab_amount;
  2669. tab_precedes_field = 0;
  2670. }
  2671. field_distance -= tab_amount;
  2672. field_spaces += tab_field_spaces;
  2673. }
  2674. if (tab_contents != 0) {
  2675. node *tem;
  2676. for (tem = tab_contents; tem->next != 0; tem = tem->next)
  2677. ;
  2678. tem->next = line;
  2679. line = tab_contents;
  2680. }
  2681. tab_field_spaces = 0;
  2682. tab_contents = 0;
  2683. tab_width = H0;
  2684. tab_distance = H0;
  2685. current_tab = TAB_NONE;
  2686. }
  2687. node *environment::make_tab_node(hunits d, node *next)
  2688. {
  2689. if (leader_node != 0 && d < 0) {
  2690. error("motion generated by leader cannot be negative");
  2691. delete leader_node;
  2692. leader_node = 0;
  2693. }
  2694. if (!leader_node)
  2695. return new hmotion_node(d, 1, 0, get_fill_color(), next);
  2696. node *n = new hline_node(d, leader_node, next);
  2697. leader_node = 0;
  2698. return n;
  2699. }
  2700. void environment::handle_tab(int is_leader)
  2701. {
  2702. hunits d;
  2703. hunits absolute;
  2704. if (current_tab)
  2705. wrap_up_tab();
  2706. charinfo *ci = is_leader ? leader_char : tab_char;
  2707. delete leader_node;
  2708. leader_node = ci ? make_char_node(ci) : 0;
  2709. tab_type t = distance_to_next_tab(&d, &absolute);
  2710. switch (t) {
  2711. case TAB_NONE:
  2712. return;
  2713. case TAB_LEFT:
  2714. add_node(make_tag("tab L", absolute.to_units()));
  2715. add_node(make_tab_node(d));
  2716. return;
  2717. case TAB_RIGHT:
  2718. add_node(make_tag("tab R", absolute.to_units()));
  2719. break;
  2720. case TAB_CENTER:
  2721. add_node(make_tag("tab C", absolute.to_units()));
  2722. break;
  2723. default:
  2724. assert(0);
  2725. }
  2726. tab_width = 0;
  2727. tab_distance = d;
  2728. tab_contents = 0;
  2729. current_tab = t;
  2730. tab_field_spaces = 0;
  2731. }
  2732. void environment::start_field()
  2733. {
  2734. assert(!current_field);
  2735. hunits d;
  2736. if (distance_to_next_tab(&d) != TAB_NONE) {
  2737. pre_field_width = get_text_length();
  2738. field_distance = d;
  2739. current_field = 1;
  2740. field_spaces = 0;
  2741. tab_field_spaces = 0;
  2742. for (node *p = line; p; p = p->next)
  2743. if (p->nspaces()) {
  2744. p->freeze_space();
  2745. space_total--;
  2746. }
  2747. tab_precedes_field = current_tab != TAB_NONE;
  2748. }
  2749. else
  2750. error("zero field width");
  2751. }
  2752. void environment::wrap_up_field()
  2753. {
  2754. if (!current_tab && field_spaces == 0)
  2755. add_padding();
  2756. hunits padding = field_distance - (get_text_length() - pre_field_width);
  2757. if (current_tab && tab_field_spaces != 0) {
  2758. hunits tab_padding = scale(padding,
  2759. tab_field_spaces,
  2760. field_spaces + tab_field_spaces);
  2761. padding -= tab_padding;
  2762. distribute_space(tab_contents, tab_field_spaces, tab_padding, 1);
  2763. tab_field_spaces = 0;
  2764. tab_width += tab_padding;
  2765. }
  2766. if (field_spaces != 0) {
  2767. distribute_space(line, field_spaces, padding, 1);
  2768. width_total += padding;
  2769. if (current_tab) {
  2770. // the start of the tab has been moved to the right by padding, so
  2771. tab_distance -= padding;
  2772. if (tab_distance <= H0) {
  2773. // use the next tab stop instead
  2774. current_tab = tabs.distance_to_next_tab(get_input_line_position()
  2775. - tab_width,
  2776. &tab_distance);
  2777. if (current_tab == TAB_NONE || current_tab == TAB_LEFT) {
  2778. width_total += tab_width;
  2779. if (current_tab == TAB_LEFT) {
  2780. line = make_tab_node(tab_distance, line);
  2781. width_total += tab_distance;
  2782. current_tab = TAB_NONE;
  2783. }
  2784. if (tab_contents != 0) {
  2785. node *tem;
  2786. for (tem = tab_contents; tem->next != 0; tem = tem->next)
  2787. ;
  2788. tem->next = line;
  2789. line = tab_contents;
  2790. tab_contents = 0;
  2791. }
  2792. tab_width = H0;
  2793. tab_distance = H0;
  2794. }
  2795. }
  2796. }
  2797. }
  2798. current_field = 0;
  2799. }
  2800. void environment::add_padding()
  2801. {
  2802. if (current_tab) {
  2803. tab_contents = new space_node(H0, get_fill_color(), tab_contents);
  2804. tab_field_spaces++;
  2805. }
  2806. else {
  2807. if (line == 0)
  2808. start_line();
  2809. line = new space_node(H0, get_fill_color(), line);
  2810. field_spaces++;
  2811. }
  2812. }
  2813. typedef int (environment::*INT_FUNCP)();
  2814. typedef vunits (environment::*VUNITS_FUNCP)();
  2815. typedef hunits (environment::*HUNITS_FUNCP)();
  2816. typedef const char *(environment::*STRING_FUNCP)();
  2817. class int_env_reg : public reg {
  2818. INT_FUNCP func;
  2819. public:
  2820. int_env_reg(INT_FUNCP);
  2821. const char *get_string();
  2822. int get_value(units *val);
  2823. };
  2824. class vunits_env_reg : public reg {
  2825. VUNITS_FUNCP func;
  2826. public:
  2827. vunits_env_reg(VUNITS_FUNCP f);
  2828. const char *get_string();
  2829. int get_value(units *val);
  2830. };
  2831. class hunits_env_reg : public reg {
  2832. HUNITS_FUNCP func;
  2833. public:
  2834. hunits_env_reg(HUNITS_FUNCP f);
  2835. const char *get_string();
  2836. int get_value(units *val);
  2837. };
  2838. class string_env_reg : public reg {
  2839. STRING_FUNCP func;
  2840. public:
  2841. string_env_reg(STRING_FUNCP);
  2842. const char *get_string();
  2843. };
  2844. int_env_reg::int_env_reg(INT_FUNCP f) : func(f)
  2845. {
  2846. }
  2847. int int_env_reg::get_value(units *val)
  2848. {
  2849. *val = (curenv->*func)();
  2850. return 1;
  2851. }
  2852. const char *int_env_reg::get_string()
  2853. {
  2854. return i_to_a((curenv->*func)());
  2855. }
  2856. vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f) : func(f)
  2857. {
  2858. }
  2859. int vunits_env_reg::get_value(units *val)
  2860. {
  2861. *val = (curenv->*func)().to_units();
  2862. return 1;
  2863. }
  2864. const char *vunits_env_reg::get_string()
  2865. {
  2866. return i_to_a((curenv->*func)().to_units());
  2867. }
  2868. hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f) : func(f)
  2869. {
  2870. }
  2871. int hunits_env_reg::get_value(units *val)
  2872. {
  2873. *val = (curenv->*func)().to_units();
  2874. return 1;
  2875. }
  2876. const char *hunits_env_reg::get_string()
  2877. {
  2878. return i_to_a((curenv->*func)().to_units());
  2879. }
  2880. string_env_reg::string_env_reg(STRING_FUNCP f) : func(f)
  2881. {
  2882. }
  2883. const char *string_env_reg::get_string()
  2884. {
  2885. return (curenv->*func)();
  2886. }
  2887. class horizontal_place_reg : public general_reg {
  2888. public:
  2889. horizontal_place_reg();
  2890. int get_value(units *);
  2891. void set_value(units);
  2892. };
  2893. horizontal_place_reg::horizontal_place_reg()
  2894. {
  2895. }
  2896. int horizontal_place_reg::get_value(units *res)
  2897. {
  2898. *res = curenv->get_input_line_position().to_units();
  2899. return 1;
  2900. }
  2901. void horizontal_place_reg::set_value(units n)
  2902. {
  2903. curenv->set_input_line_position(hunits(n));
  2904. }
  2905. const char *environment::get_font_family_string()
  2906. {
  2907. return family->nm.contents();
  2908. }
  2909. const char *environment::get_glyph_color_string()
  2910. {
  2911. return glyph_color->nm.contents();
  2912. }
  2913. const char *environment::get_fill_color_string()
  2914. {
  2915. return fill_color->nm.contents();
  2916. }
  2917. const char *environment::get_font_name_string()
  2918. {
  2919. symbol f = get_font_name(fontno, this);
  2920. return f.contents();
  2921. }
  2922. const char *environment::get_style_name_string()
  2923. {
  2924. symbol f = get_style_name(fontno);
  2925. return f.contents();
  2926. }
  2927. const char *environment::get_name_string()
  2928. {
  2929. return name.contents();
  2930. }
  2931. // Convert a quantity in scaled points to ascii decimal fraction.
  2932. const char *sptoa(int sp)
  2933. {
  2934. assert(sp > 0);
  2935. assert(sizescale > 0);
  2936. if (sizescale == 1)
  2937. return i_to_a(sp);
  2938. if (sp % sizescale == 0)
  2939. return i_to_a(sp/sizescale);
  2940. // See if 1/sizescale is exactly representable as a decimal fraction,
  2941. // ie its only prime factors are 2 and 5.
  2942. int n = sizescale;
  2943. int power2 = 0;
  2944. while ((n & 1) == 0) {
  2945. n >>= 1;
  2946. power2++;
  2947. }
  2948. int power5 = 0;
  2949. while ((n % 5) == 0) {
  2950. n /= 5;
  2951. power5++;
  2952. }
  2953. if (n == 1) {
  2954. int decimal_point = power5 > power2 ? power5 : power2;
  2955. if (decimal_point <= 10) {
  2956. int factor = 1;
  2957. int t;
  2958. for (t = decimal_point - power2; --t >= 0;)
  2959. factor *= 2;
  2960. for (t = decimal_point - power5; --t >= 0;)
  2961. factor *= 5;
  2962. if (factor == 1 || sp <= INT_MAX/factor)
  2963. return if_to_a(sp*factor, decimal_point);
  2964. }
  2965. }
  2966. double s = double(sp)/double(sizescale);
  2967. double factor = 10.0;
  2968. double val = s;
  2969. int decimal_point = 0;
  2970. do {
  2971. double v = ceil(s*factor);
  2972. if (v > INT_MAX)
  2973. break;
  2974. val = v;
  2975. factor *= 10.0;
  2976. } while (++decimal_point < 10);
  2977. return if_to_a(int(val), decimal_point);
  2978. }
  2979. const char *environment::get_point_size_string()
  2980. {
  2981. return sptoa(curenv->get_point_size());
  2982. }
  2983. const char *environment::get_requested_point_size_string()
  2984. {
  2985. return sptoa(curenv->get_requested_point_size());
  2986. }
  2987. #define init_int_env_reg(name, func) \
  2988. number_reg_dictionary.define(name, new int_env_reg(&environment::func))
  2989. #define init_vunits_env_reg(name, func) \
  2990. number_reg_dictionary.define(name, new vunits_env_reg(&environment::func))
  2991. #define init_hunits_env_reg(name, func) \
  2992. number_reg_dictionary.define(name, new hunits_env_reg(&environment::func))
  2993. #define init_string_env_reg(name, func) \
  2994. number_reg_dictionary.define(name, new string_env_reg(&environment::func))
  2995. void init_env_requests()
  2996. {
  2997. init_request("ad", adjust);
  2998. init_request("br", break_request);
  2999. init_request("brp", break_spread_request);
  3000. init_request("c2", no_break_control_char);
  3001. init_request("cc", control_char);
  3002. init_request("ce", center);
  3003. init_request("cu", continuous_underline);
  3004. init_request("ev", environment_switch);
  3005. init_request("evc", environment_copy);
  3006. init_request("fam", family_change);
  3007. init_request("fc", field_characters);
  3008. init_request("fi", fill);
  3009. init_request("fcolor", fill_color_change);
  3010. init_request("ft", font_change);
  3011. init_request("gcolor", glyph_color_change);
  3012. init_request("hc", hyphen_char);
  3013. init_request("hlm", hyphen_line_max_request);
  3014. init_request("hy", hyphenate_request);
  3015. init_request("hym", hyphenation_margin_request);
  3016. init_request("hys", hyphenation_space_request);
  3017. init_request("in", indent);
  3018. init_request("it", input_trap);
  3019. init_request("itc", input_trap_continued);
  3020. init_request("lc", leader_character);
  3021. init_request("linetabs", line_tabs_request);
  3022. init_request("ll", line_length);
  3023. init_request("ls", line_spacing);
  3024. init_request("lt", title_length);
  3025. init_request("mc", margin_character);
  3026. init_request("na", no_adjust);
  3027. init_request("nf", no_fill);
  3028. init_request("nh", no_hyphenate);
  3029. init_request("nm", number_lines);
  3030. init_request("nn", no_number);
  3031. init_request("ps", point_size);
  3032. init_request("pvs", post_vertical_spacing);
  3033. init_request("rj", right_justify);
  3034. init_request("sizes", override_sizes);
  3035. init_request("ss", space_size);
  3036. init_request("ta", set_tabs);
  3037. init_request("ti", temporary_indent);
  3038. init_request("tc", tab_character);
  3039. init_request("tl", title);
  3040. init_request("ul", underline);
  3041. init_request("vs", vertical_spacing);
  3042. #ifdef WIDOW_CONTROL
  3043. init_request("wdc", widow_control_request);
  3044. #endif /* WIDOW_CONTROL */
  3045. init_int_env_reg(".b", get_bold);
  3046. init_vunits_env_reg(".cdp", get_prev_char_depth);
  3047. init_int_env_reg(".ce", get_center_lines);
  3048. init_vunits_env_reg(".cht", get_prev_char_height);
  3049. init_hunits_env_reg(".csk", get_prev_char_skew);
  3050. init_string_env_reg(".ev", get_name_string);
  3051. init_int_env_reg(".f", get_font);
  3052. init_string_env_reg(".fam", get_font_family_string);
  3053. init_string_env_reg(".fn", get_font_name_string);
  3054. init_int_env_reg(".height", get_char_height);
  3055. init_int_env_reg(".hlc", get_hyphen_line_count);
  3056. init_int_env_reg(".hlm", get_hyphen_line_max);
  3057. init_int_env_reg(".hy", get_hyphenation_flags);
  3058. init_hunits_env_reg(".hym", get_hyphenation_margin);
  3059. init_hunits_env_reg(".hys", get_hyphenation_space);
  3060. init_hunits_env_reg(".i", get_indent);
  3061. init_hunits_env_reg(".in", get_saved_indent);
  3062. init_int_env_reg(".int", get_prev_line_interrupted);
  3063. init_int_env_reg(".linetabs", get_line_tabs);
  3064. init_hunits_env_reg(".lt", get_title_length);
  3065. init_int_env_reg(".j", get_adjust_mode);
  3066. init_hunits_env_reg(".k", get_text_length);
  3067. init_int_env_reg(".L", get_line_spacing);
  3068. init_hunits_env_reg(".l", get_line_length);
  3069. init_hunits_env_reg(".ll", get_saved_line_length);
  3070. init_string_env_reg(".M", get_fill_color_string);
  3071. init_string_env_reg(".m", get_glyph_color_string);
  3072. init_hunits_env_reg(".n", get_prev_text_length);
  3073. init_int_env_reg(".ps", get_point_size);
  3074. init_int_env_reg(".psr", get_requested_point_size);
  3075. init_vunits_env_reg(".pvs", get_post_vertical_spacing);
  3076. init_int_env_reg(".rj", get_right_justify_lines);
  3077. init_string_env_reg(".s", get_point_size_string);
  3078. init_int_env_reg(".slant", get_char_slant);
  3079. init_int_env_reg(".ss", get_space_size);
  3080. init_int_env_reg(".sss", get_sentence_space_size);
  3081. init_string_env_reg(".sr", get_requested_point_size_string);
  3082. init_string_env_reg(".sty", get_style_name_string);
  3083. init_string_env_reg(".tabs", get_tabs);
  3084. init_int_env_reg(".u", get_fill);
  3085. init_vunits_env_reg(".v", get_vertical_spacing);
  3086. init_hunits_env_reg(".w", get_prev_char_width);
  3087. number_reg_dictionary.define("ct", new variable_reg(&ct_reg_contents));
  3088. number_reg_dictionary.define("hp", new horizontal_place_reg);
  3089. number_reg_dictionary.define("ln", new variable_reg(&next_line_number));
  3090. number_reg_dictionary.define("rsb", new variable_reg(&rsb_reg_contents));
  3091. number_reg_dictionary.define("rst", new variable_reg(&rst_reg_contents));
  3092. number_reg_dictionary.define("sb", new variable_reg(&sb_reg_contents));
  3093. number_reg_dictionary.define("skw", new variable_reg(&skw_reg_contents));
  3094. number_reg_dictionary.define("ssc", new variable_reg(&ssc_reg_contents));
  3095. number_reg_dictionary.define("st", new variable_reg(&st_reg_contents));
  3096. }
  3097. // Hyphenation - TeX's hyphenation algorithm with a less fancy implementation.
  3098. struct trie_node;
  3099. class trie {
  3100. trie_node *tp;
  3101. virtual void do_match(int len, void *val) = 0;
  3102. virtual void do_delete(void *) = 0;
  3103. void delete_trie_node(trie_node *);
  3104. public:
  3105. trie() : tp(0) {}
  3106. virtual ~trie(); // virtual to shut up g++
  3107. void insert(const char *, int, void *);
  3108. // find calls do_match for each match it finds
  3109. void find(const char *pat, int patlen);
  3110. void clear();
  3111. };
  3112. class hyphen_trie : private trie {
  3113. int *h;
  3114. void do_match(int i, void *v);
  3115. void do_delete(void *v);
  3116. void insert_pattern(const char *pat, int patlen, int *num);
  3117. void insert_hyphenation(dictionary *ex, const char *pat, int patlen);
  3118. int hpf_getc(FILE *f);
  3119. public:
  3120. hyphen_trie() {}
  3121. ~hyphen_trie() {}
  3122. void hyphenate(const char *word, int len, int *hyphens);
  3123. void read_patterns_file(const char *name, int append, dictionary *ex);
  3124. };
  3125. struct hyphenation_language {
  3126. symbol name;
  3127. dictionary exceptions;
  3128. hyphen_trie patterns;
  3129. hyphenation_language(symbol nm) : name(nm), exceptions(501) {}
  3130. ~hyphenation_language() { }
  3131. };
  3132. dictionary language_dictionary(5);
  3133. hyphenation_language *current_language = 0;
  3134. static void set_hyphenation_language()
  3135. {
  3136. symbol nm = get_name(1);
  3137. if (!nm.is_null()) {
  3138. current_language = (hyphenation_language *)language_dictionary.lookup(nm);
  3139. if (!current_language) {
  3140. current_language = new hyphenation_language(nm);
  3141. (void)language_dictionary.lookup(nm, (void *)current_language);
  3142. }
  3143. }
  3144. skip_line();
  3145. }
  3146. const int WORD_MAX = 256; // we use unsigned char for offsets in
  3147. // hyphenation exceptions
  3148. static void hyphen_word()
  3149. {
  3150. if (!current_language) {
  3151. error("no current hyphenation language");
  3152. skip_line();
  3153. return;
  3154. }
  3155. char buf[WORD_MAX + 1];
  3156. unsigned char pos[WORD_MAX + 2];
  3157. for (;;) {
  3158. tok.skip();
  3159. if (tok.newline() || tok.eof())
  3160. break;
  3161. int i = 0;
  3162. int npos = 0;
  3163. while (i < WORD_MAX && !tok.space() && !tok.newline() && !tok.eof()) {
  3164. charinfo *ci = tok.get_char(1);
  3165. if (ci == 0) {
  3166. skip_line();
  3167. return;
  3168. }
  3169. tok.next();
  3170. if (ci->get_ascii_code() == '-') {
  3171. if (i > 0 && (npos == 0 || pos[npos - 1] != i))
  3172. pos[npos++] = i;
  3173. }
  3174. else {
  3175. unsigned char c = ci->get_hyphenation_code();
  3176. if (c == 0)
  3177. break;
  3178. buf[i++] = c;
  3179. }
  3180. }
  3181. if (i > 0) {
  3182. pos[npos] = 0;
  3183. buf[i] = 0;
  3184. unsigned char *tem = new unsigned char[npos + 1];
  3185. memcpy(tem, pos, npos + 1);
  3186. tem = (unsigned char *)current_language->exceptions.lookup(symbol(buf),
  3187. tem);
  3188. if (tem)
  3189. a_delete tem;
  3190. }
  3191. }
  3192. skip_line();
  3193. }
  3194. struct trie_node {
  3195. char c;
  3196. trie_node *down;
  3197. trie_node *right;
  3198. void *val;
  3199. trie_node(char, trie_node *);
  3200. };
  3201. trie_node::trie_node(char ch, trie_node *p)
  3202. : c(ch), down(0), right(p), val(0)
  3203. {
  3204. }
  3205. trie::~trie()
  3206. {
  3207. clear();
  3208. }
  3209. void trie::clear()
  3210. {
  3211. delete_trie_node(tp);
  3212. tp = 0;
  3213. }
  3214. void trie::delete_trie_node(trie_node *p)
  3215. {
  3216. if (p) {
  3217. delete_trie_node(p->down);
  3218. delete_trie_node(p->right);
  3219. if (p->val)
  3220. do_delete(p->val);
  3221. delete p;
  3222. }
  3223. }
  3224. void trie::insert(const char *pat, int patlen, void *val)
  3225. {
  3226. trie_node **p = &tp;
  3227. assert(patlen > 0 && pat != 0);
  3228. for (;;) {
  3229. while (*p != 0 && (*p)->c < pat[0])
  3230. p = &((*p)->right);
  3231. if (*p == 0 || (*p)->c != pat[0])
  3232. *p = new trie_node(pat[0], *p);
  3233. if (--patlen == 0) {
  3234. (*p)->val = val;
  3235. break;
  3236. }
  3237. ++pat;
  3238. p = &((*p)->down);
  3239. }
  3240. }
  3241. void trie::find(const char *pat, int patlen)
  3242. {
  3243. trie_node *p = tp;
  3244. for (int i = 0; p != 0 && i < patlen; i++) {
  3245. while (p != 0 && p->c < pat[i])
  3246. p = p->right;
  3247. if (p != 0 && p->c == pat[i]) {
  3248. if (p->val != 0)
  3249. do_match(i+1, p->val);
  3250. p = p->down;
  3251. }
  3252. else
  3253. break;
  3254. }
  3255. }
  3256. struct operation {
  3257. operation *next;
  3258. short distance;
  3259. short num;
  3260. operation(int, int, operation *);
  3261. };
  3262. operation::operation(int i, int j, operation *op)
  3263. : next(op), distance(j), num(i)
  3264. {
  3265. }
  3266. void hyphen_trie::insert_pattern(const char *pat, int patlen, int *num)
  3267. {
  3268. operation *op = 0;
  3269. for (int i = 0; i < patlen+1; i++)
  3270. if (num[i] != 0)
  3271. op = new operation(num[i], patlen - i, op);
  3272. insert(pat, patlen, op);
  3273. }
  3274. void hyphen_trie::insert_hyphenation(dictionary *ex, const char *pat,
  3275. int patlen)
  3276. {
  3277. char buf[WORD_MAX + 1];
  3278. unsigned char pos[WORD_MAX + 2];
  3279. int i = 0, j = 0;
  3280. int npos = 0;
  3281. while (j < patlen) {
  3282. unsigned char c = pat[j++];
  3283. if (c == '-') {
  3284. if (i > 0 && (npos == 0 || pos[npos - 1] != i))
  3285. pos[npos++] = i;
  3286. }
  3287. else
  3288. buf[i++] = hpf_code_table[c];
  3289. }
  3290. if (i > 0) {
  3291. pos[npos] = 0;
  3292. buf[i] = 0;
  3293. unsigned char *tem = new unsigned char[npos + 1];
  3294. memcpy(tem, pos, npos + 1);
  3295. tem = (unsigned char *)ex->lookup(symbol(buf), tem);
  3296. if (tem)
  3297. a_delete tem;
  3298. }
  3299. }
  3300. void hyphen_trie::hyphenate(const char *word, int len, int *hyphens)
  3301. {
  3302. int j;
  3303. for (j = 0; j < len + 1; j++)
  3304. hyphens[j] = 0;
  3305. for (j = 0; j < len - 1; j++) {
  3306. h = hyphens + j;
  3307. find(word + j, len - j);
  3308. }
  3309. }
  3310. inline int max(int m, int n)
  3311. {
  3312. return m > n ? m : n;
  3313. }
  3314. void hyphen_trie::do_match(int i, void *v)
  3315. {
  3316. operation *op = (operation *)v;
  3317. while (op != 0) {
  3318. h[i - op->distance] = max(h[i - op->distance], op->num);
  3319. op = op->next;
  3320. }
  3321. }
  3322. void hyphen_trie::do_delete(void *v)
  3323. {
  3324. operation *op = (operation *)v;
  3325. while (op) {
  3326. operation *tem = op;
  3327. op = tem->next;
  3328. delete tem;
  3329. }
  3330. }
  3331. /* We use very simple rules to parse TeX's hyphenation patterns.
  3332. . `%' starts a comment even if preceded by `\'.
  3333. . No support for digraphs and like `\$'.
  3334. . `^^xx' (`x' is 0-9 or a-f), and `^^x' (character code of `x' in the
  3335. range 0-127) are recognized; other use of `^' causes an error.
  3336. . No macro expansion.
  3337. . We check for the expression `\patterns{...}' (possibly with
  3338. whitespace before and after the braces). Everything between the
  3339. braces is taken as hyphenation patterns. Consequently, `{' and `}'
  3340. are not allowed in patterns.
  3341. . Similarly, `\hyphenation{...}' gives a list of hyphenation
  3342. exceptions.
  3343. . `\endinput' is recognized also.
  3344. . For backwards compatibility, if `\patterns' is missing, the
  3345. whole file is treated as a list of hyphenation patterns (only
  3346. recognizing `%' as the start of a comment.
  3347. */
  3348. int hyphen_trie::hpf_getc(FILE *f)
  3349. {
  3350. int c = getc(f);
  3351. int c1;
  3352. int cc = 0;
  3353. if (c != '^')
  3354. return c;
  3355. c = getc(f);
  3356. if (c != '^')
  3357. goto fail;
  3358. c = getc(f);
  3359. c1 = getc(f);
  3360. if (((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))
  3361. && ((c1 >= '0' && c1 <= '9') || (c1 >= 'a' && c1 <= 'f'))) {
  3362. if (c >= '0' && c <= '9')
  3363. c -= '0';
  3364. else
  3365. c = c - 'a' + 10;
  3366. if (c1 >= '0' && c1 <= '9')
  3367. c1 -= '0';
  3368. else
  3369. c1 = c1 - 'a' + 10;
  3370. cc = c * 16 + c1;
  3371. }
  3372. else {
  3373. ungetc(c1, f);
  3374. if (c >= 0 && c <= 63)
  3375. cc = c + 64;
  3376. else if (c >= 64 && c <= 127)
  3377. cc = c - 64;
  3378. else
  3379. goto fail;
  3380. }
  3381. return cc;
  3382. fail:
  3383. error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file");
  3384. return c;
  3385. }
  3386. void hyphen_trie::read_patterns_file(const char *name, int append,
  3387. dictionary *ex)
  3388. {
  3389. if (!append)
  3390. clear();
  3391. char buf[WORD_MAX];
  3392. for (int i = 0; i < WORD_MAX; i++)
  3393. buf[i] = 0;
  3394. int num[WORD_MAX+1];
  3395. errno = 0;
  3396. char *path = 0;
  3397. FILE *fp = mac_path->open_file(name, &path);
  3398. if (fp == 0) {
  3399. error("can't find hyphenation patterns file `%1'", name);
  3400. return;
  3401. }
  3402. int c = hpf_getc(fp);
  3403. int have_patterns = 0; // we've seen \patterns
  3404. int final_pattern = 0; // 1 if we have a trailing closing brace
  3405. int have_hyphenation = 0; // we've seen \hyphenation
  3406. int final_hyphenation = 0; // 1 if we have a trailing closing brace
  3407. int have_keyword = 0; // we've seen either \patterns or \hyphenation
  3408. int traditional = 0; // don't handle \patterns
  3409. for (;;) {
  3410. for (;;) {
  3411. if (c == '%') { // skip comments
  3412. do {
  3413. c = getc(fp);
  3414. } while (c != EOF && c != '\n');
  3415. }
  3416. if (c == EOF || !csspace(c))
  3417. break;
  3418. c = hpf_getc(fp);
  3419. }
  3420. if (c == EOF) {
  3421. if (have_keyword || traditional) // we are done
  3422. break;
  3423. else { // rescan file in `traditional' mode
  3424. rewind(fp);
  3425. traditional = 1;
  3426. c = hpf_getc(fp);
  3427. continue;
  3428. }
  3429. }
  3430. int i = 0;
  3431. num[0] = 0;
  3432. if (!(c == '{' || c == '}')) { // skip braces at line start
  3433. do { // scan patterns
  3434. if (csdigit(c))
  3435. num[i] = c - '0';
  3436. else {
  3437. buf[i++] = c;
  3438. num[i] = 0;
  3439. }
  3440. c = hpf_getc(fp);
  3441. } while (i < WORD_MAX && c != EOF && !csspace(c)
  3442. && c != '%' && c != '{' && c != '}');
  3443. }
  3444. if (!traditional) {
  3445. if (i >= 9 && !strncmp(buf + i - 9, "\\patterns", 9)) {
  3446. while (csspace(c))
  3447. c = hpf_getc(fp);
  3448. if (c == '{') {
  3449. if (have_patterns || have_hyphenation)
  3450. error("\\patterns not allowed inside of %1 group",
  3451. have_patterns ? "\\patterns" : "\\hyphenation");
  3452. else {
  3453. have_patterns = 1;
  3454. have_keyword = 1;
  3455. }
  3456. c = hpf_getc(fp);
  3457. continue;
  3458. }
  3459. }
  3460. else if (i >= 12 && !strncmp(buf + i - 12, "\\hyphenation", 12)) {
  3461. while (csspace(c))
  3462. c = hpf_getc(fp);
  3463. if (c == '{') {
  3464. if (have_patterns || have_hyphenation)
  3465. error("\\hyphenation not allowed inside of %1 group",
  3466. have_patterns ? "\\patterns" : "\\hyphenation");
  3467. else {
  3468. have_hyphenation = 1;
  3469. have_keyword = 1;
  3470. }
  3471. c = hpf_getc(fp);
  3472. continue;
  3473. }
  3474. }
  3475. else if (strstr(buf, "\\endinput")) {
  3476. if (have_patterns || have_hyphenation)
  3477. error("found \\endinput inside of %1 group",
  3478. have_patterns ? "\\patterns" : "\\hyphenation");
  3479. break;
  3480. }
  3481. else if (c == '}') {
  3482. if (have_patterns) {
  3483. have_patterns = 0;
  3484. if (i > 0)
  3485. final_pattern = 1;
  3486. }
  3487. else if (have_hyphenation) {
  3488. have_hyphenation = 0;
  3489. if (i > 0)
  3490. final_hyphenation = 1;
  3491. }
  3492. c = hpf_getc(fp);
  3493. }
  3494. else if (c == '{') {
  3495. if (have_patterns || have_hyphenation)
  3496. error("`{' not allowed within %1 group",
  3497. have_patterns ? "\\patterns" : "\\hyphenation");
  3498. c = hpf_getc(fp); // skipped if not starting \patterns
  3499. // or \hyphenation
  3500. }
  3501. }
  3502. else {
  3503. if (c == '{' || c == '}')
  3504. c = hpf_getc(fp);
  3505. }
  3506. if (i > 0) {
  3507. if (have_patterns || final_pattern || traditional) {
  3508. for (int j = 0; j < i; j++)
  3509. buf[j] = hpf_code_table[(unsigned char)buf[j]];
  3510. insert_pattern(buf, i, num);
  3511. final_pattern = 0;
  3512. }
  3513. else if (have_hyphenation || final_hyphenation) {
  3514. insert_hyphenation(ex, buf, i);
  3515. final_hyphenation = 0;
  3516. }
  3517. }
  3518. }
  3519. fclose(fp);
  3520. a_delete path;
  3521. return;
  3522. }
  3523. void hyphenate(hyphen_list *h, unsigned flags)
  3524. {
  3525. if (!current_language)
  3526. return;
  3527. while (h) {
  3528. while (h && h->hyphenation_code == 0)
  3529. h = h->next;
  3530. int len = 0;
  3531. char hbuf[WORD_MAX+2];
  3532. char *buf = hbuf + 1;
  3533. hyphen_list *tem;
  3534. for (tem = h; tem && len < WORD_MAX; tem = tem->next) {
  3535. if (tem->hyphenation_code != 0)
  3536. buf[len++] = tem->hyphenation_code;
  3537. else
  3538. break;
  3539. }
  3540. hyphen_list *nexth = tem;
  3541. if (len > 2) {
  3542. buf[len] = 0;
  3543. unsigned char *pos
  3544. = (unsigned char *)current_language->exceptions.lookup(buf);
  3545. if (pos != 0) {
  3546. int j = 0;
  3547. int i = 1;
  3548. for (tem = h; tem != 0; tem = tem->next, i++)
  3549. if (pos[j] == i) {
  3550. tem->hyphen = 1;
  3551. j++;
  3552. }
  3553. }
  3554. else {
  3555. hbuf[0] = hbuf[len+1] = '.';
  3556. int num[WORD_MAX+3];
  3557. current_language->patterns.hyphenate(hbuf, len+2, num);
  3558. int i;
  3559. num[2] = 0;
  3560. if (flags & 8)
  3561. num[3] = 0;
  3562. if (flags & 4)
  3563. --len;
  3564. for (i = 2, tem = h; i < len && tem; tem = tem->next, i++)
  3565. if (num[i] & 1)
  3566. tem->hyphen = 1;
  3567. }
  3568. }
  3569. h = nexth;
  3570. }
  3571. }
  3572. static void do_hyphenation_patterns_file(int append)
  3573. {
  3574. symbol name = get_long_name(1);
  3575. if (!name.is_null()) {
  3576. if (!current_language)
  3577. error("no current hyphenation language");
  3578. else
  3579. current_language->patterns.read_patterns_file(
  3580. name.contents(), append,
  3581. &current_language->exceptions);
  3582. }
  3583. skip_line();
  3584. }
  3585. static void hyphenation_patterns_file()
  3586. {
  3587. do_hyphenation_patterns_file(0);
  3588. }
  3589. static void hyphenation_patterns_file_append()
  3590. {
  3591. do_hyphenation_patterns_file(1);
  3592. }
  3593. class hyphenation_language_reg : public reg {
  3594. public:
  3595. const char *get_string();
  3596. };
  3597. const char *hyphenation_language_reg::get_string()
  3598. {
  3599. return current_language ? current_language->name.contents() : "";
  3600. }
  3601. void init_hyphen_requests()
  3602. {
  3603. init_request("hw", hyphen_word);
  3604. init_request("hla", set_hyphenation_language);
  3605. init_request("hpf", hyphenation_patterns_file);
  3606. init_request("hpfa", hyphenation_patterns_file_append);
  3607. number_reg_dictionary.define(".hla", new hyphenation_language_reg);
  3608. }