1 1.5 oster /* $NetBSD: sectioning.c,v 1.5 2025/12/31 22:18:50 oster Exp $ */ 2 1.1 christos 3 1.1 christos /* sectioning.c -- for @chapter, @section, ..., @contents ... 4 1.1 christos Id: sectioning.c,v 1.25 2004/07/05 22:23:23 karl Exp 5 1.1 christos 6 1.1 christos Copyright (C) 1999, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. 7 1.1 christos 8 1.1 christos This program is free software; you can redistribute it and/or modify 9 1.1 christos it under the terms of the GNU General Public License as published by 10 1.1 christos the Free Software Foundation; either version 2, or (at your option) 11 1.1 christos any later version. 12 1.1 christos 13 1.1 christos This program is distributed in the hope that it will be useful, 14 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of 15 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 1.1 christos GNU General Public License for more details. 17 1.1 christos 18 1.1 christos You should have received a copy of the GNU General Public License 19 1.1 christos along with this program; if not, write to the Free Software 20 1.1 christos Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21 1.1 christos 22 1.1 christos Originally written by Karl Heinz Marbaise <kama (at) hippo.fido.de>. */ 23 1.1 christos 24 1.1 christos #include "system.h" 25 1.1 christos #include "cmds.h" 26 1.1 christos #include "macro.h" 27 1.1 christos #include "makeinfo.h" 28 1.1 christos #include "node.h" 29 1.1 christos #include "toc.h" 30 1.1 christos #include "sectioning.h" 31 1.1 christos #include "xml.h" 32 1.1 christos 33 1.1 christos /* See comment in sectioning.h. */ 34 1.1 christos section_alist_type section_alist[] = { 35 1.1 christos { "unnumberedsubsubsec", 5, ENUM_SECT_NO, TOC_YES }, 36 1.1 christos { "unnumberedsubsec", 4, ENUM_SECT_NO, TOC_YES }, 37 1.1 christos { "unnumberedsec", 3, ENUM_SECT_NO, TOC_YES }, 38 1.1 christos { "unnumbered", 2, ENUM_SECT_NO, TOC_YES }, 39 1.1 christos { "centerchap", 2, ENUM_SECT_NO, TOC_YES }, 40 1.1 christos 41 1.1 christos { "appendixsubsubsec", 5, ENUM_SECT_APP, TOC_YES }, /* numbered like A.X.X.X */ 42 1.1 christos { "appendixsubsec", 4, ENUM_SECT_APP, TOC_YES }, 43 1.1 christos { "appendixsec", 3, ENUM_SECT_APP, TOC_YES }, 44 1.1 christos { "appendixsection", 3, ENUM_SECT_APP, TOC_YES }, 45 1.1 christos { "appendix", 2, ENUM_SECT_APP, TOC_YES }, 46 1.1 christos 47 1.1 christos { "subsubsec", 5, ENUM_SECT_YES, TOC_YES }, 48 1.1 christos { "subsubsection", 5, ENUM_SECT_YES, TOC_YES }, 49 1.1 christos { "subsection", 4, ENUM_SECT_YES, TOC_YES }, 50 1.1 christos { "section", 3, ENUM_SECT_YES, TOC_YES }, 51 1.1 christos { "chapter", 2, ENUM_SECT_YES, TOC_YES }, 52 1.1 christos 53 1.1 christos { "subsubheading", 5, ENUM_SECT_NO, TOC_NO }, 54 1.1 christos { "subheading", 4, ENUM_SECT_NO, TOC_NO }, 55 1.1 christos { "heading", 3, ENUM_SECT_NO, TOC_NO }, 56 1.1 christos { "chapheading", 2, ENUM_SECT_NO, TOC_NO }, 57 1.1 christos { "majorheading", 2, ENUM_SECT_NO, TOC_NO }, 58 1.1 christos 59 1.1 christos { "top", 1, ENUM_SECT_NO, TOC_YES }, 60 1.1 christos { NULL, 0, 0, 0 } 61 1.1 christos }; 62 1.1 christos 63 1.1 christos /* The argument of @settitle, used for HTML. */ 65 1.1 christos char *title = NULL; 66 1.1 christos 67 1.1 christos 68 1.1 christos #define APPENDIX_MAGIC 1024 69 1.1 christos #define UNNUMBERED_MAGIC 2048 70 1.1 christos 71 1.1 christos /* Number memory for every level @chapter, @section, 72 1.1 christos @subsection, @subsubsection. */ 73 1.1 christos static int numbers [] = { 0, 0, 0, 0 }; 74 1.1 christos 75 1.1 christos /* enum_marker == APPENDIX_MAGIC then we are counting appendencies 76 1.1 christos enum_marker == UNNUMBERED_MAGIC then we are within unnumbered area. 77 1.1 christos Handling situations like this: 78 1.1 christos @unnumbered .. 79 1.1 christos @section ... */ 80 1.1 christos static int enum_marker = 0; 81 1.1 christos 82 1.1 christos /* Organized by level commands. That is, "*" == chapter, "=" == section. */ 83 1.1 christos static char *scoring_characters = "*=-."; 84 1.1 christos 85 1.1 christos /* Amount to offset the name of sectioning commands to levels by. */ 86 1.1 christos static int section_alist_offset = 0; 87 1.1 christos 88 1.1 christos /* These two variables are for @float, @cindex like commands that need to know 89 1.1 christos in which section they are used. */ 90 1.1 christos /* Last value returned by get_sectioning_number. */ 91 1.1 christos static char *last_sectioning_number = ""; 92 1.1 christos /* Last title used by sectioning_underscore, etc. */ 93 1.1 christos static char *last_sectioning_title = ""; 94 1.1 christos 95 1.1 christos /* num == ENUM_SECT_NO means unnumbered (should never call this) 97 1.1 christos num == ENUM_SECT_YES means numbered 98 1.1 christos num == ENUM_SECT_APP means numbered like A.1 and so on */ 99 1.1 christos static char * 100 1.1 christos get_sectioning_number (int level, int num) 101 1.1 christos { 102 1.1 christos static char s[100]; /* should ever be enough for 99.99.99.99 103 1.1 christos Appendix A.1 */ 104 1.1 christos 105 1.1 christos char *p; 106 1.1 christos int i; 107 1.1 christos 108 1.1 christos s[0] = 0; 109 1.1 christos 110 1.1 christos /* create enumeration in front of chapter, section, subsection and so on. */ 111 1.1 christos for (i = 0; i < level; i++) 112 1.1 christos { 113 1.1 christos p = s + strlen (s); 114 1.1 christos if ((i == 0) && (enum_marker == APPENDIX_MAGIC)) 115 1.1 christos sprintf (p, "%c.", numbers[i] + 64); /* Should be changed to 116 1.1 christos be more portable */ 117 1.1 christos else 118 1.1 christos sprintf (p, "%d.", numbers[i]); 119 1.1 christos } 120 1.1 christos 121 1.1 christos /* the last number is never followed by a dot */ 122 1.1 christos p = s + strlen (s); 123 1.1 christos if ((num == ENUM_SECT_APP) 124 1.1 christos && (i == 0) 125 1.1 christos && (enum_marker == APPENDIX_MAGIC)) 126 1.1 christos sprintf (p, _("Appendix %c"), numbers[i] + 64); 127 1.1 christos else 128 1.1 christos sprintf (p, "%d", numbers[i]); 129 1.1 christos 130 1.1 christos /* Poor man's cache :-) */ 131 1.1 christos if (strlen (last_sectioning_number)) 132 1.1 christos free (last_sectioning_number); 133 1.1 christos last_sectioning_number = xstrdup (s); 134 1.1 christos 135 1.1 christos return s; 136 1.1 christos } 137 1.1 christos 138 1.1 christos 139 1.1 christos /* Set the level of @top to LEVEL. Return the old level of @top. */ 140 1.1 christos int 141 1.1 christos set_top_section_level (int level) 142 1.1 christos { 143 1.1 christos int i, result = -1; 144 1.1 christos 145 1.1 christos for (i = 0; section_alist[i].name; i++) 146 1.1 christos if (strcmp (section_alist[i].name, "top") == 0) 147 1.1 christos { 148 1.1 christos result = section_alist[i].level; 149 1.1 christos section_alist[i].level = level; 150 1.1 christos break; 151 1.1 christos } 152 1.1 christos return result; 153 1.1 christos } 154 1.1 christos 155 1.1 christos 156 1.1 christos /* return the index of the given sectioning command in section_alist */ 157 1.1 christos static int 158 1.1 christos search_sectioning (char *text) 159 1.1 christos { 160 1.1 christos int i; 161 1.1 christos char *t; 162 1.1 christos 163 1.1 christos /* ignore the optional command prefix */ 164 1.1 christos if (text[0] == COMMAND_PREFIX) 165 1.1 christos text++; 166 1.1 christos 167 1.1 christos for (i = 0; (t = section_alist[i].name); i++) 168 1.1 christos { 169 1.1 christos if (strcmp (t, text) == 0) 170 1.1 christos { 171 1.1 christos return i; 172 1.1 christos } 173 1.1 christos } 174 1.1 christos return -1; 175 1.1 christos } 176 1.1 christos 177 1.1 christos /* Return an integer which identifies the type of section present in 178 1.1 christos TEXT -- 1 for @top, 2 for chapters, ..., 5 for subsubsections (as 179 1.1 christos specified in section_alist). We take into account any @lowersections 180 1.1 christos and @raisesections. If SECNAME is non-NULL, also return the 181 1.1 christos corresponding section name. */ 182 1.1 christos int 183 1.1 christos what_section (char *text, char **secname) 184 1.1 christos { 185 1.1 christos int index, j; 186 1.1 christos char *temp; 187 1.1 christos int return_val; 188 1.1 christos 189 1.1 christos find_section_command: 190 1.1 christos for (j = 0; text[j] && cr_or_whitespace (text[j]); j++); 191 1.1 christos if (text[j] != COMMAND_PREFIX) 192 1.1 christos return -1; 193 1.1 christos 194 1.1 christos text = text + j + 1; 195 1.1 christos 196 1.1 christos /* We skip @c, @comment, and @?index commands. */ 197 1.1 christos if ((strncmp (text, "comment", strlen ("comment")) == 0) || 198 1.1 christos (text[0] == 'c' && cr_or_whitespace (text[1])) || 199 1.1 christos (strcmp (text + 1, "index") == 0)) 200 1.1 christos { 201 1.1 christos while (*text++ != '\n'); 202 1.1 christos goto find_section_command; 203 1.1 christos } 204 1.1 christos 205 1.1 christos /* Handle italicized sectioning commands. */ 206 1.1 christos if (*text == 'i') 207 1.1 christos text++; 208 1.1 christos 209 1.1 christos for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++); 210 1.1 christos 211 1.1 christos temp = xmalloc (1 + j); 212 1.1 christos strncpy (temp, text, j); 213 1.1 christos temp[j] = 0; 214 1.1 christos 215 1.1 christos index = search_sectioning (temp); 216 1.1 christos free (temp); 217 1.1 christos if (index >= 0) 218 1.1 christos { 219 1.1 christos return_val = section_alist[index].level + section_alist_offset; 220 1.1 christos if (return_val < 0) 221 1.1 christos return_val = 0; 222 1.1 christos else if (return_val > 5) 223 1.1 christos return_val = 5; 224 1.1 christos 225 1.1 christos if (secname) 226 1.1 christos { 227 1.1 christos int i; 228 1.1 christos int alist_size = sizeof (section_alist) / sizeof(section_alist_type); 229 1.1 christos /* Find location of offset sectioning entry, but don't go off 230 1.1 christos either end of the array. */ 231 1.1 christos int index_offset = MAX (index - section_alist_offset, 0); 232 1.1 christos index_offset = MIN (index_offset, alist_size - 1); 233 1.1 christos 234 1.1 christos /* Also make sure we don't go into the next "group" of 235 1.1 christos sectioning changes, e.g., change from an @appendix to an 236 1.1 christos @heading or some such. */ 237 1.1 christos #define SIGN(expr) ((expr) < 0 ? -1 : 1) 238 1.1 christos for (i = index; i != index_offset; i -= SIGN (section_alist_offset)) 239 1.1 christos { 240 1.1 christos /* As it happens, each group has unique .num/.toc values. */ 241 1.1 christos if (section_alist[i].num != section_alist[index_offset].num 242 1.1 christos || section_alist[i].toc != section_alist[index_offset].toc) 243 1.1 christos break; 244 1.1 christos } 245 1.1 christos *secname = section_alist[i].name; 246 1.1 christos } 247 1.1 christos return return_val; 248 1.1 christos } 249 1.1 christos return -1; 250 1.1 christos } 251 1.1 christos 252 1.1 christos /* Returns current top level division (ie. chapter, unnumbered) number. 253 1.1 christos - For chapters, returns the number. 254 1.1 christos - For unnumbered sections, returns empty string. 255 1.1 christos - For appendices, returns A, B, etc. */ 256 1.1 christos char * 257 1.1 christos current_chapter_number (void) 258 1.1 christos { 259 1.1 christos if (enum_marker == UNNUMBERED_MAGIC) 260 1.1 christos return xstrdup (""); 261 1.2 christos else if (enum_marker == APPENDIX_MAGIC) 262 1.1 christos { 263 1.1 christos char s[2] = { numbers[0] + 64, '\0' }; 264 1.1 christos return xstrdup (s); 265 1.1 christos } 266 1.2 christos else 267 1.2 christos { 268 1.1 christos char s[11]; 269 1.1 christos snprintf (s, sizeof(s), "%d", numbers[0]); 270 1.1 christos return xstrdup (s); 271 1.1 christos } 272 1.1 christos } 273 1.1 christos 274 1.1 christos /* Returns number of the last sectioning command used. */ 275 1.1 christos char * 276 1.1 christos current_sectioning_number (void) 277 1.1 christos { 278 1.1 christos if (enum_marker == UNNUMBERED_MAGIC || !number_sections) 279 1.1 christos return xstrdup (""); 280 1.1 christos else 281 1.1 christos return xstrdup (last_sectioning_number); 282 1.1 christos } 283 1.1 christos 284 1.1 christos /* Returns arguments of the last sectioning command used. */ 285 1.1 christos char * 286 1.1 christos current_sectioning_name (void) 287 1.1 christos { 288 1.1 christos return xstrdup (last_sectioning_title); 289 1.1 christos } 290 1.1 christos 291 1.1 christos /* insert_and_underscore, sectioning_underscore and sectioning_html call this. */ 292 1.1 christos 293 1.1 christos static char * 294 1.1 christos handle_enum_increment (int level, int index) 295 1.1 christos { 296 1.1 christos /* Here is how TeX handles enumeration: 297 1.1 christos - Anything starting with @unnumbered is not enumerated. 298 1.1 christos - @majorheading and the like are not enumberated. */ 299 1.1 christos int i; 300 1.1 christos 301 1.1 christos /* First constraint above. */ 302 1.1 christos if (enum_marker == UNNUMBERED_MAGIC && level == 0) 303 1.1 christos return xstrdup (""); 304 1.1 christos 305 1.1 christos /* Second constraint. */ 306 1.1 christos if (section_alist[index].num == ENUM_SECT_NO) 307 1.1 christos return xstrdup (""); 308 1.1 christos 309 1.1 christos /* reset all counters which are one level deeper */ 310 1.1 christos for (i = level; i < 3; i++) 311 1.1 christos numbers [i + 1] = 0; 312 1.1 christos 313 1.1 christos numbers[level]++; 314 1.1 christos if (section_alist[index].num == ENUM_SECT_NO || enum_marker == UNNUMBERED_MAGIC 315 1.1 christos || !number_sections) 316 1.1 christos return xstrdup (""); 317 1.1 christos else 318 1.1 christos return xstrdup (get_sectioning_number (level, section_alist[index].num)); 319 1.1 christos } 320 1.1 christos 321 1.1 christos 322 1.1 christos void 323 1.1 christos sectioning_underscore (char *cmd) 324 1.1 christos { 325 1.1 christos char *temp, *secname; 326 1.1 christos int level; 327 1.1 christos 328 1.1 christos /* If we're not indenting the first paragraph, we shall make it behave 329 1.5 oster like @noindent is called directly after the section heading. */ 330 1.1 christos if (! do_first_par_indent) 331 1.1 christos cm_noindent (0, 0, 0); 332 1.1 christos 333 1.1 christos temp = xmalloc (2 + strlen (cmd)); 334 1.1 christos temp[0] = COMMAND_PREFIX; 335 1.1 christos strcpy (&temp[1], cmd); 336 1.1 christos level = what_section (temp, &secname); 337 1.1 christos level -= 2; 338 1.1 christos if (level < 0) 339 1.1 christos level = 0; 340 1.1 christos free (temp); 341 1.1 christos 342 1.1 christos /* If the argument to @top is empty, we try using the one from @settitle. 343 1.1 christos Warn if both are unusable. */ 344 1.1 christos if (STREQ (command, "top")) 345 1.1 christos { 346 1.1 christos int save_input_text_offset = input_text_offset; 347 1.1 christos 348 1.1 christos get_rest_of_line (0, &temp); 349 1.1 christos 350 1.1 christos /* Due to get_rest_of_line ... */ 351 1.1 christos line_number--; 352 1.1 christos 353 1.1 christos if (strlen (temp) == 0 && (!title || strlen (title) == 0)) 354 1.1 christos warning ("Must specify a title with least one of @settitle or @top"); 355 1.1 christos 356 1.1 christos input_text_offset = save_input_text_offset; 357 1.1 christos } 358 1.1 christos 359 1.1 christos if (xml) 360 1.1 christos { 361 1.1 christos /* If the section appears in the toc, it means it's a real section 362 1.1 christos unlike majorheading, chapheading etc. */ 363 1.1 christos if (section_alist[search_sectioning (cmd)].toc == TOC_YES) 364 1.1 christos { 365 1.1 christos xml_close_sections (level); 366 1.1 christos /* Mark the beginning of the section 367 1.1 christos If the next command is printindex, we will remove 368 1.1 christos the section and put an Index instead */ 369 1.1 christos flush_output (); 370 1.1 christos xml_last_section_output_position = output_paragraph_offset; 371 1.1 christos 372 1.1 christos get_rest_of_line (0, &temp); 373 1.1 christos 374 1.1 christos /* Use @settitle value if @top parameter is empty. */ 375 1.1 christos if (STREQ (command, "top") && strlen(temp) == 0) 376 1.1 christos temp = xstrdup (title ? title : ""); 377 1.1 christos 378 1.1 christos /* Docbook does not support @unnumbered at all. So we provide numbers 379 1.1 christos that other formats use. @appendix seems to be fine though, so we let 380 1.1 christos Docbook handle that as usual. */ 381 1.1 christos if (docbook && enum_marker != APPENDIX_MAGIC) 382 1.1 christos { 383 1.1 christos if (section_alist[search_sectioning (cmd)].num == ENUM_SECT_NO 384 1.1 christos && section_alist[search_sectioning (cmd)].toc == TOC_YES) 385 1.1 christos xml_insert_element_with_attribute (xml_element (secname), 386 1.1 christos START, "label=\"%s\" xreflabel=\"%s\"", 387 1.1 christos handle_enum_increment (level, search_sectioning (cmd)), 388 1.1 christos text_expansion (temp)); 389 1.1 christos else 390 1.1 christos xml_insert_element_with_attribute (xml_element (secname), 391 1.1 christos START, "label=\"%s\"", 392 1.1 christos handle_enum_increment (level, search_sectioning (cmd))); 393 1.1 christos } 394 1.1 christos else 395 1.1 christos xml_insert_element (xml_element (secname), START); 396 1.1 christos 397 1.1 christos xml_insert_element (TITLE, START); 398 1.1 christos xml_open_section (level, secname); 399 1.1 christos execute_string ("%s", temp); 400 1.1 christos xml_insert_element (TITLE, END); 401 1.1 christos 402 1.1 christos free (temp); 403 1.1 christos } 404 1.1 christos else 405 1.1 christos { 406 1.1 christos if (docbook) 407 1.1 christos { 408 1.1 christos if (level > 0) 409 1.1 christos xml_insert_element_with_attribute (xml_element (secname), START, 410 1.1 christos "renderas=\"sect%d\"", level); 411 1.1 christos else 412 1.1 christos xml_insert_element_with_attribute (xml_element (secname), START, 413 1.1 christos "renderas=\"other\""); 414 1.1 christos } 415 1.1 christos else 416 1.1 christos xml_insert_element (xml_element (secname), START); 417 1.1 christos 418 1.1 christos get_rest_of_line (0, &temp); 419 1.1 christos execute_string ("%s", temp); 420 1.1 christos free (temp); 421 1.1 christos 422 1.1 christos xml_insert_element (xml_element (secname), END); 423 1.1 christos } 424 1.1 christos } 425 1.1 christos else if (html) 426 1.1 christos sectioning_html (level, secname); 427 1.1 christos else 428 1.1 christos insert_and_underscore (level, secname); 429 1.1 christos } 430 1.1 christos 431 1.1 christos 432 1.1 christos /* Insert the text following input_text_offset up to the end of the line 433 1.1 christos in a new, separate paragraph. Directly underneath it, insert a 434 1.1 christos line of WITH_CHAR, the same length of the inserted text. */ 435 1.1 christos void 436 1.1 christos insert_and_underscore (int level, char *cmd) 437 1.1 christos { 438 1.1 christos int i, len; 439 1.1 christos int index; 440 1.1 christos int old_no_indent; 441 1.1 christos unsigned char *starting_pos, *ending_pos; 442 1.1 christos char *temp; 443 1.1 christos char with_char = scoring_characters[level]; 444 1.1 christos 445 1.1 christos close_paragraph (); 446 1.1 christos filling_enabled = indented_fill = 0; 447 1.1 christos old_no_indent = no_indent; 448 1.1 christos no_indent = 1; 449 1.1 christos 450 1.1 christos if (macro_expansion_output_stream && !executing_string) 451 1.1 christos append_to_expansion_output (input_text_offset + 1); 452 1.1 christos 453 1.1 christos get_rest_of_line (0, &temp); 454 1.1 christos 455 1.1 christos /* Use @settitle value if @top parameter is empty. */ 456 1.1 christos if (STREQ (command, "top") && strlen(temp) == 0) 457 1.1 christos temp = xstrdup (title ? title : ""); 458 1.1 christos 459 1.1 christos starting_pos = output_paragraph + output_paragraph_offset; 460 1.1 christos 461 1.1 christos /* Poor man's cache for section title. */ 462 1.1 christos if (strlen (last_sectioning_title)) 463 1.1 christos free (last_sectioning_title); 464 1.1 christos last_sectioning_title = xstrdup (temp); 465 1.1 christos 466 1.1 christos index = search_sectioning (cmd); 467 1.1 christos if (index < 0) 468 1.1 christos { 469 1.1 christos /* should never happen, but a poor guy, named Murphy ... */ 470 1.1 christos warning (_("Internal error (search_sectioning) `%s'!"), cmd); 471 1.1 christos return; 472 1.1 christos } 473 1.1 christos 474 1.1 christos /* This is a bit tricky: we must produce "X.Y SECTION-NAME" in the 475 1.1 christos Info output and in TOC, but only SECTION-NAME in the macro-expanded 476 1.1 christos output. */ 477 1.1 christos 478 1.1 christos /* Step 1: produce "X.Y" and add it to Info output. */ 479 1.1 christos add_word_args ("%s ", handle_enum_increment (level, index)); 480 1.1 christos 481 1.1 christos /* Step 2: add "SECTION-NAME" to both Info and macro-expanded output. */ 482 1.1 christos if (macro_expansion_output_stream && !executing_string) 483 1.1 christos { 484 1.1 christos char *temp1 = xmalloc (2 + strlen (temp)); 485 1.1 christos sprintf (temp1, "%s\n", temp); 486 1.1 christos remember_itext (input_text, input_text_offset); 487 1.1 christos me_execute_string (temp1); 488 1.1 christos free (temp1); 489 1.1 christos } 490 1.1 christos else 491 1.1 christos execute_string ("%s\n", temp); 492 1.1 christos 493 1.1 christos /* Step 3: pluck "X.Y SECTION-NAME" from the output buffer and 494 1.1 christos insert it into the TOC. */ 495 1.1 christos ending_pos = output_paragraph + output_paragraph_offset; 496 1.1 christos if (section_alist[index].toc == TOC_YES) 497 1.1 christos toc_add_entry (substring (starting_pos, ending_pos - 1), 498 1.1 christos level, current_node, NULL); 499 1.1 christos 500 1.1 christos free (temp); 501 1.1 christos 502 1.1 christos len = (ending_pos - starting_pos) - 1; 503 1.1 christos for (i = 0; i < len; i++) 504 1.1 christos add_char (with_char); 505 1.1 christos insert ('\n'); 506 1.1 christos close_paragraph (); 507 1.1 christos filling_enabled = 1; 508 1.1 christos no_indent = old_no_indent; 509 1.1 christos } 510 1.1 christos 511 1.1 christos /* Insert the text following input_text_offset up to the end of the 512 1.1 christos line as an HTML heading element of the appropriate `level' and 513 1.1 christos tagged as an anchor for the current node.. */ 514 1.1 christos 515 1.1 christos void 516 1.1 christos sectioning_html (int level, char *cmd) 517 1.1 christos { 518 1.1 christos static int toc_ref_count = 0; 519 1.1 christos int index; 520 1.1 christos int old_no_indent; 521 1.1 christos unsigned char *starting_pos, *ending_pos; 522 1.1 christos char *temp, *toc_anchor = NULL; 523 1.1 christos 524 1.1 christos close_paragraph (); 525 1.1 christos filling_enabled = indented_fill = 0; 526 1.1 christos old_no_indent = no_indent; 527 1.1 christos no_indent = 1; 528 1.1 christos 529 1.1 christos /* level 0 (chapter) is <h2>, and we go down from there. */ 530 1.1 christos add_html_block_elt_args ("<h%d class=\"%s\">", level + 2, cmd); 531 1.1 christos 532 1.1 christos /* If we are outside of any node, produce an anchor that 533 1.1 christos the TOC could refer to. */ 534 1.1 christos if (!current_node || !*current_node) 535 1.1 christos { 536 1.1 christos static const char a_name[] = "<a name=\""; 537 1.1 christos 538 1.1 christos starting_pos = output_paragraph + output_paragraph_offset; 539 1.1 christos add_word_args ("%sTOC%d\">", a_name, toc_ref_count++); 540 1.1 christos toc_anchor = substring (starting_pos + sizeof (a_name) - 1, 541 1.1 christos output_paragraph + output_paragraph_offset); 542 1.1 christos /* This must be added after toc_anchor is extracted, since 543 1.1 christos toc_anchor cannot include the closing </a>. For details, 544 1.1 christos see toc.c:toc_add_entry and toc.c:contents_update_html. 545 1.1 christos 546 1.1 christos Also, the anchor close must be output before the section name 547 1.1 christos in case the name itself contains an anchor. */ 548 1.1 christos add_word ("</a>"); 549 1.1 christos } 550 1.1 christos starting_pos = output_paragraph + output_paragraph_offset; 551 1.1 christos 552 1.1 christos if (macro_expansion_output_stream && !executing_string) 553 1.1 christos append_to_expansion_output (input_text_offset + 1); 554 1.1 christos 555 1.1 christos get_rest_of_line (0, &temp); 556 1.1 christos 557 1.1 christos /* Use @settitle value if @top parameter is empty. */ 558 1.1 christos if (STREQ (command, "top") && strlen(temp) == 0) 559 1.1 christos temp = xstrdup (title ? title : ""); 560 1.1 christos 561 1.1 christos index = search_sectioning (cmd); 562 1.1 christos if (index < 0) 563 1.1 christos { 564 1.1 christos /* should never happen, but a poor guy, named Murphy ... */ 565 1.1 christos warning (_("Internal error (search_sectioning) \"%s\"!"), cmd); 566 1.1 christos return; 567 1.1 christos } 568 1.1 christos 569 1.1 christos /* Produce "X.Y" and add it to HTML output. */ 570 1.1 christos { 571 1.1 christos char *title_number = handle_enum_increment (level, index); 572 1.1 christos if (strlen (title_number) > 0) 573 1.1 christos add_word_args ("%s ", title_number); 574 1.1 christos } 575 1.1 christos 576 1.1 christos /* add the section name to both HTML and macro-expanded output. */ 577 1.1 christos if (macro_expansion_output_stream && !executing_string) 578 1.1 christos { 579 1.1 christos remember_itext (input_text, input_text_offset); 580 1.1 christos me_execute_string (temp); 581 1.1 christos write_region_to_macro_output ("\n", 0, 1); 582 1.1 christos } 583 1.1 christos else 584 1.1 christos execute_string ("%s", temp); 585 1.1 christos 586 1.1 christos ending_pos = output_paragraph + output_paragraph_offset; 587 1.1 christos 588 1.1 christos /* Pluck ``X.Y SECTION-NAME'' from the output buffer and insert it 589 1.1 christos into the TOC. */ 590 1.1 christos if (section_alist[index].toc == TOC_YES) 591 1.1 christos toc_add_entry (substring (starting_pos, ending_pos), 592 1.1 christos level, current_node, toc_anchor); 593 1.1 christos 594 1.1 christos free (temp); 595 1.1 christos 596 1.1 christos if (outstanding_node) 597 1.1 christos outstanding_node = 0; 598 1.1 christos 599 1.1 christos add_word_args ("</h%d>", level + 2); 600 1.1 christos close_paragraph(); 601 1.1 christos filling_enabled = 1; 602 1.1 christos no_indent = old_no_indent; 603 1.1 christos } 604 1.1 christos 605 1.1 christos 606 1.5 oster /* Shift the meaning of @section to @chapter. */ 608 1.1 christos void 609 1.1 christos cm_raisesections (int arg, int arg2, int arg3) 610 1.1 christos { 611 1.1 christos discard_until ("\n"); 612 1.1 christos section_alist_offset--; 613 1.1 christos } 614 1.5 oster 615 1.1 christos /* Shift the meaning of @chapter to @section. */ 616 1.1 christos void 617 1.1 christos cm_lowersections (int arg, int arg2, int arg3) 618 1.1 christos { 619 1.1 christos discard_until ("\n"); 620 1.1 christos section_alist_offset++; 621 1.1 christos } 622 1.1 christos 623 1.1 christos /* The command still works, but prints a warning message in addition. */ 624 1.1 christos void 625 1.1 christos cm_ideprecated (int arg, int start, int end) 626 1.1 christos { 627 1.1 christos warning (_("%c%s is obsolete; use %c%s instead"), 628 1.1 christos COMMAND_PREFIX, command, COMMAND_PREFIX, command + 1); 629 1.1 christos sectioning_underscore (command + 1); 630 1.1 christos } 631 1.1 christos 632 1.1 christos 633 1.5 oster /* Treat this just like @unnumbered. The only difference is 635 1.1 christos in node defaulting. */ 636 1.1 christos void 637 1.1 christos cm_top (int arg, int arg2, int arg3) 638 1.1 christos { 639 1.1 christos /* It is an error to have more than one @top. */ 640 1.1 christos if (top_node_seen && strcmp (current_node, "Top") != 0) 641 1.1 christos { 642 1.1 christos TAG_ENTRY *tag = tag_table; 643 1.1 christos 644 1.1 christos line_error (_("Node with %ctop as a section already exists"), 645 1.1 christos COMMAND_PREFIX); 646 1.1 christos 647 1.1 christos while (tag) 648 1.1 christos { 649 1.1 christos if (tag->flags & TAG_FLAG_IS_TOP) 650 1.1 christos { 651 1.1 christos file_line_error (tag->filename, tag->line_no, 652 1.1 christos _("Here is the %ctop node"), COMMAND_PREFIX); 653 1.1 christos return; 654 1.1 christos } 655 1.1 christos tag = tag->next_ent; 656 1.1 christos } 657 1.1 christos } 658 1.1 christos else 659 1.1 christos { 660 1.1 christos top_node_seen = 1; 661 1.1 christos 662 1.1 christos /* It is an error to use @top before using @node. */ 663 1.1 christos if (!tag_table) 664 1.1 christos { 665 1.1 christos char *top_name; 666 1.1 christos 667 1.1 christos get_rest_of_line (0, &top_name); 668 1.1 christos line_error (_("%ctop used before %cnode, defaulting to %s"), 669 1.1 christos COMMAND_PREFIX, COMMAND_PREFIX, top_name); 670 1.1 christos execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name); 671 1.5 oster free (top_name); 672 1.1 christos return; 673 1.1 christos } 674 1.1 christos 675 1.1 christos cm_unnumbered (0, 0, 0); 676 1.1 christos 677 1.1 christos /* The most recently defined node is the top node. */ 678 1.1 christos tag_table->flags |= TAG_FLAG_IS_TOP; 679 1.1 christos 680 1.1 christos /* Now set the logical hierarchical level of the Top node. */ 681 1.1 christos { 682 1.1 christos int orig_offset = input_text_offset; 683 1.1 christos 684 1.1 christos input_text_offset = search_forward (node_search_string, orig_offset); 685 1.1 christos 686 1.1 christos if (input_text_offset > 0) 687 1.1 christos { 688 1.1 christos int this_section; 689 1.1 christos 690 1.1 christos /* We have encountered a non-top node, so mark that one exists. */ 691 1.1 christos non_top_node_seen = 1; 692 1.1 christos 693 1.1 christos /* Move to the end of this line, and find out what the 694 1.1 christos sectioning command is here. */ 695 1.1 christos while (input_text[input_text_offset] != '\n') 696 1.1 christos input_text_offset++; 697 1.1 christos 698 1.1 christos if (input_text_offset < input_text_length) 699 1.1 christos input_text_offset++; 700 1.1 christos 701 1.1 christos this_section = what_section (input_text + input_text_offset, 702 1.1 christos NULL); 703 1.1 christos 704 1.1 christos /* If we found a sectioning command, then give the top section 705 1.1 christos a level of this section - 1. */ 706 1.1 christos if (this_section != -1) 707 1.1 christos set_top_section_level (this_section - 1); 708 1.1 christos } 709 1.1 christos input_text_offset = orig_offset; 710 1.1 christos } 711 1.1 christos } 712 1.5 oster } 713 1.1 christos 714 1.1 christos /* The remainder of the text on this line is a chapter heading. */ 715 1.1 christos void 716 1.1 christos cm_chapter (int arg, int arg2, int arg3) 717 1.1 christos { 718 1.1 christos enum_marker = 0; 719 1.1 christos sectioning_underscore ("chapter"); 720 1.5 oster } 721 1.1 christos 722 1.1 christos /* The remainder of the text on this line is a section heading. */ 723 1.1 christos void 724 1.1 christos cm_section (int arg, int arg2, int arg3) 725 1.1 christos { 726 1.1 christos sectioning_underscore ("section"); 727 1.5 oster } 728 1.1 christos 729 1.1 christos /* The remainder of the text on this line is a subsection heading. */ 730 1.1 christos void 731 1.1 christos cm_subsection (int arg, int arg2, int arg3) 732 1.1 christos { 733 1.1 christos sectioning_underscore ("subsection"); 734 1.5 oster } 735 1.1 christos 736 1.1 christos /* The remainder of the text on this line is a subsubsection heading. */ 737 1.1 christos void 738 1.1 christos cm_subsubsection (int arg, int arg2, int arg3) 739 1.1 christos { 740 1.1 christos sectioning_underscore ("subsubsection"); 741 1.5 oster } 742 1.1 christos 743 1.1 christos /* The remainder of the text on this line is an unnumbered heading. */ 744 1.1 christos void 745 1.1 christos cm_unnumbered (int arg, int arg2, int arg3) 746 1.1 christos { 747 1.1 christos enum_marker = UNNUMBERED_MAGIC; 748 1.1 christos sectioning_underscore ("unnumbered"); 749 1.5 oster } 750 1.1 christos 751 1.1 christos /* The remainder of the text on this line is an unnumbered section heading. */ 752 1.1 christos void 753 1.1 christos cm_unnumberedsec (int arg, int arg2, int arg3) 754 1.1 christos { 755 1.1 christos sectioning_underscore ("unnumberedsec"); 756 1.1 christos } 757 1.5 oster 758 1.1 christos /* The remainder of the text on this line is an unnumbered 759 1.1 christos subsection heading. */ 760 1.1 christos void 761 1.1 christos cm_unnumberedsubsec (int arg, int arg2, int arg3) 762 1.1 christos { 763 1.1 christos sectioning_underscore ("unnumberedsubsec"); 764 1.1 christos } 765 1.5 oster 766 1.1 christos /* The remainder of the text on this line is an unnumbered 767 1.1 christos subsubsection heading. */ 768 1.1 christos void 769 1.1 christos cm_unnumberedsubsubsec (int arg, int arg2, int arg3) 770 1.1 christos { 771 1.1 christos sectioning_underscore ("unnumberedsubsubsec"); 772 1.5 oster } 773 1.1 christos 774 1.1 christos /* The remainder of the text on this line is an appendix heading. */ 775 1.1 christos void 776 1.1 christos cm_appendix (int arg, int arg2, int arg3) 777 1.1 christos { 778 1.1 christos /* Reset top level number so we start from Appendix A */ 779 1.1 christos if (enum_marker != APPENDIX_MAGIC) 780 1.1 christos numbers [0] = 0; 781 1.1 christos enum_marker = APPENDIX_MAGIC; 782 1.1 christos sectioning_underscore ("appendix"); 783 1.5 oster } 784 1.1 christos 785 1.1 christos /* The remainder of the text on this line is an appendix section heading. */ 786 1.1 christos void 787 1.1 christos cm_appendixsec (int arg, int arg2, int arg3) 788 1.1 christos { 789 1.1 christos sectioning_underscore ("appendixsec"); 790 1.5 oster } 791 1.1 christos 792 1.1 christos /* The remainder of the text on this line is an appendix subsection heading. */ 793 1.1 christos void 794 1.1 christos cm_appendixsubsec (int arg, int arg2, int arg3) 795 1.1 christos { 796 1.1 christos sectioning_underscore ("appendixsubsec"); 797 1.1 christos } 798 1.5 oster 799 1.1 christos /* The remainder of the text on this line is an appendix 800 1.1 christos subsubsection heading. */ 801 1.1 christos void 802 1.1 christos cm_appendixsubsubsec (int arg, int arg2, int arg3) 803 1.1 christos { 804 1.1 christos sectioning_underscore ("appendixsubsubsec"); 805 1.5 oster } 806 1.1 christos 807 1.1 christos /* Compatibility functions substitute for chapter, section, etc. */ 808 1.1 christos void 809 1.1 christos cm_majorheading (int arg, int arg2, int arg3) 810 1.1 christos { 811 1.5 oster sectioning_underscore ("majorheading"); 812 1.1 christos } 813 1.1 christos 814 1.1 christos void 815 1.1 christos cm_chapheading (int arg, int arg2, int arg3) 816 1.1 christos { 817 1.5 oster sectioning_underscore ("chapheading"); 818 1.1 christos } 819 1.1 christos 820 1.1 christos void 821 1.1 christos cm_heading (int arg, int arg2, int arg3) 822 1.1 christos { 823 1.5 oster sectioning_underscore ("heading"); 824 1.1 christos } 825 1.1 christos 826 1.1 christos void 827 1.1 christos cm_subheading (int arg, int arg2, int arg3) 828 1.1 christos { 829 1.5 oster sectioning_underscore ("subheading"); 830 1.1 christos } 831 1.1 christos 832 1.1 christos void 833 cm_subsubheading (int arg, int arg2, int arg3) 834 { 835 sectioning_underscore ("subsubheading"); 836 } 837