Bug Summary

File:src/gnu/usr.bin/texinfo/makeinfo/html.c
Warning:line 781, column 7
Null pointer passed as 2nd argument to string concatenation function

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name html.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/gnu/usr.bin/texinfo/obj/makeinfo -resource-dir /usr/local/lib/clang/13.0.0 -D HAVE_CONFIG_H -I . -I /usr/src/gnu/usr.bin/texinfo/makeinfo -I .. -I /usr/src/gnu/usr.bin/texinfo/lib -I ../intl -D LOCALEDIR="/usr/share/locale" -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/gnu/usr.bin/texinfo/obj/makeinfo -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/gnu/usr.bin/texinfo/makeinfo/html.c
1/* html.c -- html-related utilities.
2 $Id: html.c,v 1.3 2010/06/06 12:31:09 fgsch Exp $
3
4 Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software
5 Foundation, Inc.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software Foundation,
19 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20
21#include "system.h"
22#include "cmds.h"
23#include "files.h"
24#include "html.h"
25#include "lang.h"
26#include "makeinfo.h"
27#include "node.h"
28#include "sectioning.h"
29
30
31/* Append CHAR to BUFFER, (re)allocating as necessary. We don't handle
32 null characters. */
33
34typedef struct
35{
36 unsigned size; /* allocated */
37 unsigned length; /* used */
38 char *buffer;
39} buffer_type;
40
41static buffer_type *
42init_buffer (void)
43{
44 buffer_type *buf = xmalloc (sizeof (buffer_type));
45 buf->length = 0;
46 buf->size = 0;
47 buf->buffer = NULL((void *)0);
48
49 return buf;
50}
51
52static void
53append_char (buffer_type *buf, int c)
54{
55 buf->length++;
56 if (buf->length >= buf->size)
57 {
58 buf->size += 100;
59 buf->buffer = xrealloc (buf->buffer, buf->size);
60 }
61 buf->buffer[buf->length - 1] = c;
62 buf->buffer[buf->length] = 0;
63}
64
65/* Read the cascading style-sheet file FILENAME. Write out any @import
66 commands, which must come first, by the definition of css. If the
67 file contains any actual css code following the @imports, return it;
68 else return NULL. */
69static char *
70process_css_file (char *filename)
71{
72 int c;
73 int lastchar = 0;
74 FILE *f;
75 buffer_type *import_text = init_buffer ();
76 buffer_type *inline_text = init_buffer ();
77 unsigned lineno = 1;
78 enum { null_state, comment_state, import_state, inline_state } state
79 = null_state, prev_state;
80
81 prev_state = null_state;
82
83 /* read from stdin if `-' is the filename. */
84 f = STREQ (filename, "-")(strcmp (filename, "-") == 0) ? stdin(&__sF[0]) : fopen (filename, "r");
85 if (!f)
86 {
87 error (_("%s: could not open --css-file: %s")((const char *) ("%s: could not open --css-file: %s")), progname, filename);
88 return NULL((void *)0);
89 }
90
91 /* Read the file. The @import statements must come at the beginning,
92 with only whitespace and comments allowed before any inline css code. */
93 while ((c = getc (f)(!__isthreaded ? (--(f)->_r < 0 ? __srget(f) : (int)(*(
f)->_p++)) : (getc)(f))
) >= 0)
94 {
95 if (c == '\n')
96 lineno++;
97
98 switch (state)
99 {
100 case null_state: /* between things */
101 if (c == '@')
102 { /* Only @import and @charset should switch into
103 import_state, other @-commands, such as @media, should
104 put us into inline_state. I don't think any other css
105 @-commands start with `i' or `c', although of course
106 this will break when such a command is defined. */
107 int nextchar = getc (f)(!__isthreaded ? (--(f)->_r < 0 ? __srget(f) : (int)(*(
f)->_p++)) : (getc)(f))
;
108 if (nextchar == 'i' || nextchar == 'c')
109 {
110 append_char (import_text, c);
111 state = import_state;
112 }
113 else
114 {
115 ungetc (nextchar, f); /* wasn't an @import */
116 state = inline_state;
117 }
118 }
119 else if (c == '/')
120 { /* possible start of a comment */
121 int nextchar = getc (f)(!__isthreaded ? (--(f)->_r < 0 ? __srget(f) : (int)(*(
f)->_p++)) : (getc)(f))
;
122 if (nextchar == '*')
123 state = comment_state;
124 else
125 {
126 ungetc (nextchar, f); /* wasn't a comment */
127 state = inline_state;
128 }
129 }
130 else if (isspace (c))
131 ; /* skip whitespace; maybe should use c_isspace? */
132
133 else
134 /* not an @import, not a comment, not whitespace: we must
135 have started the inline text. */
136 state = inline_state;
137
138 if (state == inline_state)
139 append_char (inline_text, c);
140
141 if (state != null_state)
142 prev_state = null_state;
143 break;
144
145 case comment_state:
146 if (c == '/' && lastchar == '*')
147 state = prev_state; /* end of comment */
148 break; /* else ignore this comment char */
149
150 case import_state:
151 append_char (import_text, c); /* include this import char */
152 if (c == ';')
153 { /* done with @import */
154 append_char (import_text, '\n'); /* make the output nice */
155 state = null_state;
156 prev_state = import_state;
157 }
158 break;
159
160 case inline_state:
161 /* No harm in writing out comments, so don't bother parsing
162 them out, just append everything. */
163 append_char (inline_text, c);
164 break;
165 }
166
167 lastchar = c;
168 }
169
170 fclose (f); /* Even closing stdin should be ok, can't read it more
171 than once? */
172
173 /* Reached the end of the file. We should not be still in a comment. */
174 if (state == comment_state)
175 warning (_("%s:%d: --css-file ended in comment")((const char *) ("%s:%d: --css-file ended in comment")), filename, lineno);
176
177 /* Write the @import text, if any. */
178 if (import_text->buffer)
179 {
180 add_word (import_text->buffer);
181 free (import_text->buffer);
182 free (import_text);
183 }
184
185 /* We're wasting the buffer struct memory, but so what. */
186 return inline_text->buffer;
187}
188
189HSTACK *htmlstack = NULL((void *)0);
190
191/* See html.h. */
192int html_output_head_p = 0;
193int html_title_written = 0;
194
195void
196html_output_head (void)
197{
198 static const char *html_title = NULL((void *)0);
199 char *encoding;
200
201 if (html_output_head_p)
202 return;
203 html_output_head_p = 1;
204
205 encoding = current_document_encoding ();
206
207 /* The <title> should not have markup, so use text_expansion. */
208 if (!html_title)
209 html_title = escape_string (title ?
210 text_expansion (title) : (char *) _("Untitled")((const char *) ("Untitled")));
211
212 /* Make sure this is the very first string of the output document. */
213 output_paragraph_offset = 0;
214
215 add_html_block_elt_args ("<html lang=\"%s\">\n<head>\n",
216 language_table[language_code].abbrev);
217
218 /* When splitting, add current node's name to title if it's available and not
219 Top. */
220 if (splitting && current_node && !STREQ (current_node, "Top")(strcmp (current_node, "Top") == 0))
221 add_word_args ("<title>%s - %s</title>\n",
222 escape_string (xstrdup (current_node)), html_title);
223 else
224 add_word_args ("<title>%s</title>\n", html_title);
225
226 add_word ("<meta http-equiv=\"Content-Type\" content=\"text/html");
227 if (encoding && *encoding)
228 add_word_args ("; charset=%s", encoding);
229
230 add_word ("\">\n");
231
232 if (!document_description)
233 document_description = html_title;
234
235 add_word_args ("<meta name=\"description\" content=\"%s\">\n",
236 document_description);
237 add_word_args ("<meta name=\"generator\" content=\"makeinfo %s\">\n",
238 VERSION"4.8");
239
240 /* Navigation bar links. */
241 if (!splitting)
242 add_word ("<link title=\"Top\" rel=\"top\" href=\"#Top\">\n");
243 else if (tag_table)
244 {
245 /* Always put a top link. */
246 add_word ("<link title=\"Top\" rel=\"start\" href=\"index.html#Top\">\n");
247
248 /* We already have a top link, avoid duplication. */
249 if (tag_table->up && !STREQ (tag_table->up, "Top")(strcmp (tag_table->up, "Top") == 0))
250 add_link (tag_table->up, "rel=\"up\"");
251
252 if (tag_table->prev)
253 add_link (tag_table->prev, "rel=\"prev\"");
254
255 if (tag_table->next)
256 add_link (tag_table->next, "rel=\"next\"");
257
258 /* fixxme: Look for a way to put links to various indices in the
259 document. Also possible candidates to be added here are First and
260 Last links. */
261 }
262 else
263 {
264 /* We are splitting, but we neither have a tag_table. So this must be
265 index.html. So put a link to Top. */
266 add_word ("<link title=\"Top\" rel=\"start\" href=\"#Top\">\n");
267 }
268
269 add_word ("<link href=\"http://www.gnu.org/software/texinfo/\" \
270rel=\"generator-home\" title=\"Texinfo Homepage\">\n");
271
272 if (copying_text)
273 { /* It is not ideal that we include the html markup here within
274 <head>, so we use text_expansion. */
275 insert_string ("<!--\n");
276 insert_string (text_expansion (copying_text));
277 insert_string ("-->\n");
278 }
279
280 /* Put the style definitions in a comment for the sake of browsers
281 that don't support <style>. */
282 add_word ("<meta http-equiv=\"Content-Style-Type\" content=\"text/css\">\n");
283 add_word ("<style type=\"text/css\"><!--\n");
284
285 {
286 char *css_inline = NULL((void *)0);
287
288 if (css_include)
289 /* This writes out any @import commands from the --css-file,
290 and returns any actual css code following the imports. */
291 css_inline = process_css_file (css_include);
292
293 /* This seems cleaner than adding <br>'s at the end of each line for
294 these "roman" displays. It's hardly the end of the world if the
295 browser doesn't do <style>s, in any case; they'll just come out in
296 typewriter. */
297#define CSS_FONT_INHERIT"font-family:inherit" "font-family:inherit"
298 add_word_args (" pre.display { %s }\n", CSS_FONT_INHERIT"font-family:inherit");
299 add_word_args (" pre.format { %s }\n", CSS_FONT_INHERIT"font-family:inherit");
300
301 /* Alternatively, we could do <font size=-1> in insertion.c, but this
302 way makes it easier to override. */
303#define CSS_FONT_SMALLER"font-size:smaller" "font-size:smaller"
304 add_word_args (" pre.smalldisplay { %s; %s }\n", CSS_FONT_INHERIT"font-family:inherit",
305 CSS_FONT_SMALLER"font-size:smaller");
306 add_word_args (" pre.smallformat { %s; %s }\n", CSS_FONT_INHERIT"font-family:inherit",
307 CSS_FONT_SMALLER"font-size:smaller");
308 add_word_args (" pre.smallexample { %s }\n", CSS_FONT_SMALLER"font-size:smaller");
309 add_word_args (" pre.smalllisp { %s }\n", CSS_FONT_SMALLER"font-size:smaller");
310
311 /* Since HTML doesn't have a sc element, we use span with a bit of
312 CSS spice instead. */
313#define CSS_FONT_SMALL_CAPS"font-variant:small-caps" "font-variant:small-caps"
314 add_word_args (" span.sc { %s }\n", CSS_FONT_SMALL_CAPS"font-variant:small-caps");
315
316 /* Roman (default) font class, closest we can come. */
317#define CSS_FONT_ROMAN"font-family:serif; font-weight:normal;" "font-family:serif; font-weight:normal;"
318 add_word_args (" span.roman { %s } \n", CSS_FONT_ROMAN"font-family:serif; font-weight:normal;");
319
320 /* Sans serif font class. */
321#define CSS_FONT_SANSSERIF"font-family:sans-serif; font-weight:normal;" "font-family:sans-serif; font-weight:normal;"
322 add_word_args (" span.sansserif { %s } \n", CSS_FONT_SANSSERIF"font-family:sans-serif; font-weight:normal;");
323
324 /* Write out any css code from the user's --css-file. */
325 if (css_inline)
326 insert_string (css_inline);
327
328 add_word ("--></style>\n");
329 }
330
331 add_word ("</head>\n<body>\n");
332
333 if (title && !html_title_written && titlepage_cmd_present)
334 {
335 add_word_args ("<h1 class=\"settitle\">%s</h1>\n", html_title);
336 html_title_written = 1;
337 }
338
339 free (encoding);
340}
341
342/* Escape HTML special characters in the string if necessary,
343 returning a pointer to a possibly newly-allocated one. */
344char *
345escape_string (char *string)
346{
347 char *newstring;
348 int i = 0, newlen = 0;
349
350 do
351 {
352 /* Find how much to allocate. */
353 switch (string[i])
354 {
355 case '"':
356 newlen += 6; /* `&quot;' */
357 break;
358 case '&':
359 newlen += 5; /* `&amp;' */
360 break;
361 case '<':
362 case '>':
363 newlen += 4; /* `&lt;', `&gt;' */
364 break;
365 default:
366 newlen++;
367 }
368 }
369 while (string[i++]);
370
371 if (newlen == i) return string; /* Already OK. */
372
373 newstring = xmalloc (newlen);
374 i = 0;
375 do
376 {
377 switch (string[i])
378 {
379 case '"':
380 strcpy (newstring, "&quot;");
381 newstring += 6;
382 break;
383 case '&':
384 strcpy (newstring, "&amp;");
385 newstring += 5;
386 break;
387 case '<':
388 strcpy (newstring, "&lt;");
389 newstring += 4;
390 break;
391 case '>':
392 strcpy (newstring, "&gt;");
393 newstring += 4;
394 break;
395 default:
396 newstring[0] = string[i];
397 newstring++;
398 }
399 }
400 while (string[i++]);
401 free (string);
402 return newstring - newlen;
403}
404
405/* Save current tag. */
406static void
407push_tag (char *tag, char *attribs)
408{
409 HSTACK *newstack = xmalloc (sizeof (HSTACK));
410
411 newstack->tag = tag;
412 newstack->attribs = xstrdup (attribs);
413 newstack->next = htmlstack;
414 htmlstack = newstack;
415}
416
417/* Get last tag. */
418static void
419pop_tag (void)
420{
421 HSTACK *tos = htmlstack;
422
423 if (!tos)
424 {
425 line_error (_("[unexpected] no html tag to pop")((const char *) ("[unexpected] no html tag to pop")));
426 return;
427 }
428
429 free (htmlstack->attribs);
430
431 htmlstack = htmlstack->next;
432 free (tos);
433}
434
435/* Check if tag is an empty or a whitespace only element.
436 If so, remove it, keeping whitespace intact. */
437int
438rollback_empty_tag (char *tag)
439{
440 int check_position = output_paragraph_offset;
441 int taglen = strlen (tag);
442 int rollback_happened = 0;
443 char *contents = "";
444 char *contents_canon_white = "";
445
446 /* If output_paragraph is empty, we cannot rollback :-\ */
447 if (output_paragraph_offset <= 0)
448 return 0;
449
450 /* Find the end of the previous tag. */
451 while (check_position > 0 && output_paragraph[check_position-1] != '>')
452 check_position--;
453
454 /* Save stuff between tag's end to output_paragraph's end. */
455 if (check_position != output_paragraph_offset)
456 {
457 contents = xmalloc (output_paragraph_offset - check_position + 1);
458 memcpy (contents, output_paragraph + check_position,
459 output_paragraph_offset - check_position);
460
461 contents[output_paragraph_offset - check_position] = '\0';
462
463 contents_canon_white = xstrdup (contents);
464 canon_white (contents_canon_white);
465 }
466
467 /* Find the start of the previous tag. */
468 while (check_position > 0 && output_paragraph[check_position-1] != '<')
469 check_position--;
470
471 /* Check to see if this is the tag. */
472 if (strncmp ((char *) output_paragraph + check_position, tag, taglen) == 0
473 && (whitespace (output_paragraph[check_position + taglen])((output_paragraph[check_position + taglen]) == '\t' || (output_paragraph
[check_position + taglen]) == ' ')
474 || output_paragraph[check_position + taglen] == '>'))
475 {
476 if (!contents_canon_white || !*contents_canon_white)
477 {
478 /* Empty content after whitespace removal, so roll it back. */
479 output_paragraph_offset = check_position - 1;
480 rollback_happened = 1;
481
482 /* Original contents may not be empty (whitespace.) */
483 if (contents && *contents)
484 {
485 insert_string (contents);
486 free (contents);
487 }
488 }
489 }
490
491 return rollback_happened;
492}
493
494/* Open or close TAG according to START_OR_END. */
495void
496#if defined (VA_FPRINTF) && __STDC__1
497insert_html_tag_with_attribute (int start_or_end, char *tag, char *format, ...)
498#else
499insert_html_tag_with_attribute (start_or_end, tag, format, va_alist)
500 int start_or_end;
501 char *tag;
502 char *format;
503 va_dcl
504#endif
505{
506 char *old_tag = NULL((void *)0);
507 char *old_attribs = NULL((void *)0);
508 char formatted_attribs[2000]; /* xx no fixed limits */
509 int do_return = 0;
510 extern int in_html_elt;
511
512 if (start_or_end != START0)
513 pop_tag ();
514
515 if (htmlstack)
516 {
517 old_tag = htmlstack->tag;
518 old_attribs = htmlstack->attribs;
519 }
520
521 if (format)
522 {
523#ifdef VA_SPRINTF
524 va_list ap;
525#endif
526
527 VA_START (ap, format)__builtin_va_start(ap, format);
528#ifdef VA_SPRINTF
529 VA_SPRINTF (formatted_attribs, format, ap)vsprintf (formatted_attribs, format, ap);
530#else
531 sprintf (formatted_attribs, format, a1, a2, a3, a4, a5, a6, a7, a8);
532#endif
533 va_end (ap)__builtin_va_end(ap);
534 }
535 else
536 formatted_attribs[0] = '\0';
537
538 /* Exception: can nest multiple spans. */
539 if (htmlstack
540 && STREQ (htmlstack->tag, tag)(strcmp (htmlstack->tag, tag) == 0)
541 && !(STREQ (tag, "span")(strcmp (tag, "span") == 0) && STREQ (old_attribs, formatted_attribs)(strcmp (old_attribs, formatted_attribs) == 0)))
542 do_return = 1;
543
544 if (start_or_end == START0)
545 push_tag (tag, formatted_attribs);
546
547 if (do_return)
548 return;
549
550 in_html_elt++;
551
552 /* texinfo.tex doesn't support more than one font attribute
553 at the same time. */
554 if ((start_or_end == START0) && old_tag && *old_tag
555 && !rollback_empty_tag (old_tag))
556 add_word_args ("</%s>", old_tag);
557
558 if (*tag)
559 {
560 if (start_or_end == START0)
561 add_word_args (format ? "<%s %s>" : "<%s>", tag, formatted_attribs);
562 else if (!rollback_empty_tag (tag))
563 /* Insert close tag only if we didn't rollback,
564 in which case the opening tag is removed. */
565 add_word_args ("</%s>", tag);
566 }
567
568 if ((start_or_end != START0) && old_tag && *old_tag)
569 add_word_args (strlen (old_attribs) > 0 ? "<%s %s>" : "<%s>",
570 old_tag, old_attribs);
571
572 in_html_elt--;
573}
574
575void
576insert_html_tag (int start_or_end, char *tag)
577{
578 insert_html_tag_with_attribute (start_or_end, tag, NULL((void *)0));
579}
580
581/* Output an HTML <link> to the filename for NODE, including the
582 other string as extra attributes. */
583void
584add_link (char *nodename, char *attributes)
585{
586 if (nodename)
587 {
588 add_html_elt ("<link ");
589 add_word_args ("%s", attributes);
590 add_word_args (" href=\"");
591 add_anchor_name (nodename, 1);
592 add_word_args ("\" title=\"%s\">\n", nodename);
593 }
594}
595
596/* Output NAME with characters escaped as appropriate for an anchor
597 name, i.e., escape URL special characters with our _00hh convention
598 if OLD is zero. (See the manual for details on the new scheme.)
599
600 If OLD is nonzero, generate the node name with the 4.6-and-earlier
601 convention of %hh (and more special characters output as-is, notably
602 - and *). This is only so that external references to old names can
603 still work with HTML generated by the new makeinfo; the gcc folks
604 needed this. Our own HTML does not refer to these names. */
605
606void
607add_escaped_anchor_name (char *name, int old)
608{
609 canon_white (name);
610
611 if (!old && !strchr ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
612 *name))
613 { /* XHTML does not allow anything but an ASCII letter to start an
614 identifier. Therefore kludge in this constant string if we
615 have a nonletter. */
616 add_word ("g_t");
617 }
618
619 for (; *name; name++)
620 {
621 if (cr_or_whitespace (*name)(((*name) == '\t' || (*name) == ' ') || (*name) == '\r' || (*
name) == '\n')
)
622 add_char ('-');
623
624 else if (!old && !URL_SAFE_CHAR (*name)(isalnum (*name)))
625 /* Cast so characters with the high bit set are treated as >128,
626 for example o-umlaut should be 246, not -10. */
627 add_word_args ("_00%x", (unsigned char) *name);
628
629 else if (old && !URL_SAFE_CHAR (*name)(isalnum (*name)) && !OLD_URL_SAFE_CHAR (*name)(strchr ("$-_.+!*'()", *name)))
630 /* Different output convention, but still cast as above. */
631 add_word_args ("%%%x", (unsigned char) *name);
632
633 else
634 add_char (*name);
635 }
636}
637
638/* Insert the text for the name of a reference in an HTML anchor
639 appropriate for NODENAME.
640
641 If HREF is zero, generate text for name= in the new node name
642 conversion convention.
643 If HREF is negative, generate text for name= in the old convention.
644 If HREF is positive, generate the name for an href= attribute, i.e.,
645 including the `#' if it's an internal reference. */
646void
647add_anchor_name (char *nodename, int href)
648{
649 if (href > 0)
650 {
651 if (splitting)
652 add_url_name (nodename, href);
653 add_char ('#');
654 }
655 /* Always add NODENAME, so that the reference would pinpoint the
656 exact node on its file. This is so several nodes could share the
657 same file, in case of file-name clashes, but also for more
658 accurate browser positioning. */
659 if (strcasecmp (nodename, "(dir)") == 0)
660 /* Strip the parens, but keep the original letter-case. */
661 add_word_args ("%.3s", nodename + 1);
662 else if (strcasecmp (nodename, "top") == 0)
663 add_word ("Top");
664 else
665 add_escaped_anchor_name (nodename, href < 0);
666}
667
668/* Insert the text for the name of a reference in an HTML url, aprropriate
669 for NODENAME */
670void
671add_url_name (char *nodename, int href)
672{
673 add_nodename_to_filename (nodename, href);
674}
675
676/* Convert non [A-Za-z0-9] to _00xx, where xx means the hexadecimal
677 representation of the ASCII character. Also convert spaces and
678 newlines to dashes. */
679static void
680fix_filename (char *filename)
681{
682 int i;
683 int len = strlen (filename);
684 char *oldname = xstrdup (filename);
685
686 *filename = '\0';
687
688 for (i = 0; i < len; i++)
689 {
690 if (cr_or_whitespace (oldname[i])(((oldname[i]) == '\t' || (oldname[i]) == ' ') || (oldname[i]
) == '\r' || (oldname[i]) == '\n')
)
691 strcat (filename, "-");
692 else if (URL_SAFE_CHAR (oldname[i])(isalnum (oldname[i])))
693 strncat (filename, (char *) oldname + i, 1);
694 else
695 {
696 char *hexchar = xmalloc (6 * sizeof (char));
697 sprintf (hexchar, "_00%x", (unsigned char) oldname[i]);
698 strcat (filename, hexchar);
699 free (hexchar);
700 }
701
702 /* Check if we are nearing boundaries. */
703 if (strlen (filename) >= PATH_MAX1024 - 20)
704 break;
705 }
706
707 free (oldname);
708}
709
710/* As we can't look-up a (forward-referenced) nodes' html filename
711 from the tentry, we take the easy way out. We assume that
712 nodenames are unique, and generate the html filename from the
713 nodename, that's always known. */
714static char *
715nodename_to_filename_1 (char *nodename, int href)
716{
717 char *p;
718 char *filename;
719 char dirname[PATH_MAX1024];
720
721 if (strcasecmp (nodename, "Top") == 0)
2
Assuming the condition is false
3
Taking false branch
722 {
723 /* We want to convert references to the Top node into
724 "index.html#Top". */
725 if (href)
726 filename = xstrdup ("index.html"); /* "#Top" is added by our callers */
727 else
728 filename = xstrdup ("Top");
729 }
730 else if (strcasecmp (nodename, "(dir)") == 0)
4
Assuming the condition is false
5
Taking false branch
731 /* We want to convert references to the (dir) node into
732 "../index.html". */
733 filename = xstrdup ("../index.html");
734 else
735 {
736 filename = xmalloc (PATH_MAX1024);
737 dirname[0] = '\0';
738 *filename = '\0';
739
740 /* Check for external reference: ``(info-document)node-name''
741 Assume this node lives at: ``../info-document/node-name.html''
742
743 We need to handle the special case (sigh): ``(info-document)'',
744 ie, an external top-node, which should translate to:
745 ``../info-document/info-document.html'' */
746
747 p = nodename;
748 if (*nodename == '(')
6
Assuming the condition is true
7
Taking true branch
749 {
750 int length;
751
752 p = strchr (nodename, ')');
753 if (p == NULL((void *)0))
8
Assuming 'p' is equal to NULL
9
Taking true branch
754 {
755 line_error (_("[unexpected] invalid node name: `%s'")((const char *) ("[unexpected] invalid node name: `%s'")), nodename);
756 xexit (1);
757 }
758
759 length = p - nodename - 1;
760 if (length > 5 &&
10
Assuming 'length' is <= 5
761 FILENAME_CMPNstrncmp (p - 5, ".info", 5) == 0)
762 length -= 5;
763 /* This is for DOS, and also for Windows and GNU/Linux
764 systems that might have Info files copied from a DOS 8+3
765 filesystem. */
766 if (length > 4 &&
11
Assuming 'length' is <= 4
767 FILENAME_CMPNstrncmp (p - 4, ".inf", 4) == 0)
768 length -= 4;
769 strcpy (filename, "../");
770 strncpy (dirname, nodename + 1, length);
771 *(dirname + length) = '\0';
772 fix_filename (dirname);
773 strcat (filename, dirname);
774 strcat (filename, "/");
775 p++;
12
Null pointer value stored to 'p'
776 }
777
778 /* In the case of just (info-document), there will be nothing
779 remaining, and we will refer to ../info-document/, which will
780 work fine. */
781 strcat (filename, p);
13
Null pointer passed as 2nd argument to string concatenation function
782 if (*p)
783 {
784 /* Hmm */
785 fix_filename (filename + strlen (filename) - strlen (p));
786 strcat (filename, ".html");
787 }
788 }
789
790 /* Produce a file name suitable for the underlying filesystem. */
791 normalize_filename (filename);
792
793#if 0
794 /* We add ``#Nodified-filename'' anchor to external references to be
795 prepared for non-split HTML support. Maybe drop this. */
796 if (href && *dirname)
797 {
798 strcat (filename, "#");
799 strcat (filename, p);
800 /* Hmm, again */
801 fix_filename (filename + strlen (filename) - strlen (p));
802 }
803#endif
804
805 return filename;
806}
807
808/* If necessary, ie, if current filename != filename of node, output
809 the node name. */
810void
811add_nodename_to_filename (char *nodename, int href)
812{
813 /* for now, don't check: always output filename */
814 char *filename = nodename_to_filename_1 (nodename, href);
815 add_word (filename);
816 free (filename);
817}
818
819char *
820nodename_to_filename (char *nodename)
821{
822 /* The callers of nodename_to_filename use the result to produce
823 <a href=, so call nodename_to_filename_1 with last arg non-zero. */
824 return nodename_to_filename_1 (nodename, 1);
1
Calling 'nodename_to_filename_1'
825}