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

https://bitbucket.org/freebsd/freebsd-head/ · C++ · 3893 lines · 3496 code · 263 blank · 134 comment · 898 complexity · c07b344fa412b4f06e5daa000c8d6c87 MD5 · raw file

Large files are truncated click here to view the full file

  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();