1 /* Man page to help file converter
2 Copyright (C) 1994, 1995 Janne Kukonlehto
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
26 #define BUFFER_SIZE 256
28 static int width
; /* Output width in characters */
29 static int col
= 0; /* Current output column */
30 static int out_row
= 1; /* Current output row */
31 static int in_row
= 0; /* Current input row */
32 static int no_split_flag
= 0; /* Flag: Don't split section on next ".SH" */
33 static int skip_flag
= 0; /* Flag: Skip this section.
36 2 = title skipped, skipping text */
37 static int link_flag
= 0; /* Flag: Next line is a link */
38 static int verbatim_flag
= 0; /* Flag: Copy input to output verbatim */
39 static int node
= 0; /* Flag: This line is an original ".SH" */
41 static const char *c_out
; /* Output filename */
42 static FILE *f_out
; /* Output file */
44 static char *Topics
= "Topics:";
52 #define MAX_STREAM_BLOCK 8192
55 * Read in blocks of reasonable size and make sure we read everything.
56 * Failure to read everything is an error.
59 persistent_fread (void *data
, size_t len
, FILE * stream
)
62 size_t bytes_done
= 0;
63 char *ptr
= (char *) data
;
65 while (bytes_done
< len
) {
66 count
= len
- bytes_done
;
67 if (count
> MAX_STREAM_BLOCK
)
68 count
= MAX_STREAM_BLOCK
;
70 count
= fread (ptr
, 1, count
, stream
);
83 * Write in blocks of reasonable size and make sure we write everything.
84 * Failure to write everything is an error.
87 persistent_fwrite (const void *data
, size_t len
, FILE * stream
)
90 size_t bytes_done
= 0;
91 const char *ptr
= (const char *) data
;
93 while (bytes_done
< len
) {
94 count
= len
- bytes_done
;
95 if (count
> MAX_STREAM_BLOCK
)
96 count
= MAX_STREAM_BLOCK
;
98 count
= fwrite (ptr
, 1, count
, stream
);
110 /* Report error in input */
112 print_error (char *message
)
114 fprintf (stderr
, "man2hlp: %s in file \"%s\" at row %d\n", message
,
118 /* Change output line */
124 fprintf (f_out
, "\n");
127 /* Calculate the length of string */
129 string_len (char *buffer
)
131 static int anchor_flag
= 0; /* Flag: Inside hypertext anchor name */
132 static int link_flag
= 0; /* Flag: Inside hypertext link target name */
133 int backslash_flag
= 0; /* Flag: Backslash quoting */
134 int c
; /* Current character */
135 int len
= 0; /* Result: the length of the string */
139 if (c
== CHAR_LINK_POINTER
)
140 link_flag
= 1; /* Link target name starts */
141 else if (c
== CHAR_LINK_END
)
142 link_flag
= 0; /* Link target name ends */
143 else if (c
== CHAR_NODE_END
) {
144 /* Node anchor name starts */
146 /* Ugly hack to prevent loss of one space */
149 /* Don't add control characters to the length */
150 if (c
>= 0 && c
< 32)
152 /* Attempt to handle backslash quoting */
153 if (c
== '\\' && !backslash_flag
) {
158 /* Increase length if not inside anchor name or link target name */
159 if (!anchor_flag
&& !link_flag
)
161 if (anchor_flag
&& c
== ']') {
162 /* Node anchor name ends */
169 /* Output the string */
171 print_string (char *buffer
)
173 int len
; /* The length of current word */
174 int c
; /* Current character */
175 int backslash_flag
= 0;
177 /* Skipping lines? */
180 /* Copying verbatim? */
182 /* Attempt to handle backslash quoting */
185 if (c
== '\\' && !backslash_flag
) {
190 fprintf (f_out
, "%c", c
);
193 /* Split into words */
194 buffer
= strtok (buffer
, " \t\n");
195 /* Repeat for each word */
197 /* Skip empty strings */
199 len
= string_len (buffer
);
200 /* Change the line if about to break the right margin */
201 if (col
+ len
>= width
)
203 /* Words are separated by spaces */
205 fprintf (f_out
, " ");
208 /* Attempt to handle backslash quoting */
211 if (c
== '\\' && !backslash_flag
) {
216 fprintf (f_out
, "%c", c
);
218 /* Increase column */
221 /* Get the next word */
222 buffer
= strtok (NULL
, " \t\n");
227 /* Like print_string but with printf-like syntax */
229 printf_string (char *format
, ...)
232 char buffer
[BUFFER_SIZE
];
234 va_start (args
, format
);
235 vsprintf (buffer
, format
, args
);
237 print_string (buffer
);
240 /* Handle all the roff dot commands */
242 handle_command (char *buffer
)
244 int len
, heading_level
;
246 /* Get the command name */
247 strtok (buffer
, " \t");
248 if ((strcmp (buffer
, ".SH") == 0)
249 || (strcmp (buffer
, ".\\\"NODE") == 0)) {
250 int SH
= (strcmp (buffer
, ".SH") == 0);
251 /* If we already skipped a section, don't skip another */
252 if (skip_flag
== 2) {
255 /* Get the command parameters */
256 buffer
= strtok (NULL
, "");
257 if (buffer
== NULL
) {
258 print_error ("Syntax error: .SH: no title");
262 if (buffer
[0] == '"') {
264 len
= strlen (buffer
);
265 if (buffer
[len
- 1] == '"') {
270 /* Calculate heading level */
272 while (buffer
[heading_level
] == ' ')
274 /* Heading level must be even */
275 if (heading_level
& 1)
276 print_error ("Syntax error: .SH: odd heading level");
278 /* Don't start a new section */
280 print_string (buffer
);
284 } else if (skip_flag
) {
285 /* Skipping title and marking text for skipping */
289 /* Start a new section */
290 fprintf (f_out
, "%c[%s]", CHAR_NODE_END
,
291 buffer
+ heading_level
);
296 cnode
->next
= malloc (sizeof (nodes
));
299 cnode
->node
= strdup (buffer
);
305 /* print_string() strtok()es buffer, so */
306 cnode
->lname
= strdup (buffer
+ heading_level
);
307 print_string (buffer
+ heading_level
);
311 } /* Start new section */
312 } /* Has parameters */
315 else if (strcmp (buffer
, ".\\\"DONT_SPLIT\"") == 0) {
317 } else if (strcmp (buffer
, ".\\\"SKIP_SECTION\"") == 0) {
319 } else if (strcmp (buffer
, ".\\\"LINK2\"") == 0) {
320 /* Next two input lines form a link */
322 } else if (strcmp (buffer
, ".PP") == 0) {
323 /* End of paragraph */
327 } else if (strcmp (buffer
, ".nf") == 0) {
328 /* Following input lines are to be handled verbatim */
332 } else if (strcmp (buffer
, ".I") == 0 || strcmp (buffer
, ".B") == 0) {
333 /* Bold text or italics text */
334 char type
= buffer
[1];
337 int backslash_flag
= 0;
339 buffer
= strtok (NULL
, "");
340 if (buffer
== NULL
) {
341 print_error ("Syntax error: .I / .B: no text");
345 *w
= (type
== 'I') ? CHAR_ITALIC_ON
: CHAR_BOLD_ON
;
347 /* Attempt to handle backslash quoting */
348 for (p
= buffer
, buffer
= w
++; *p
; p
++) {
349 if (*p
== '\\' && !backslash_flag
) {
357 *w
++ = CHAR_BOLD_OFF
;
359 print_string (buffer
);
360 } else if (strcmp (buffer
, ".TP") == 0) {
361 /* End of paragraph? */
365 } else if (strcmp (buffer
, ".\\\"TOPICS") == 0) {
368 ("Syntax error: .\\\"TOPICS must be first command");
371 buffer
= strtok (NULL
, "");
372 if (buffer
== NULL
) {
373 print_error ("Syntax error: .\\\"TOPICS: no text");
376 Topics
= strdup (buffer
);
378 /* Other commands are ignored */
383 handle_link (char *buffer
)
390 /* Old format link, not supported */
393 /* First part of new format link */
394 /* Bold text or italics text */
395 if (buffer
[0] == '.' && (buffer
[1] == 'I' || buffer
[1] == 'B'))
396 for (buffer
+= 2; *buffer
== ' ' || *buffer
== '\t'; buffer
++);
397 strcpy (old
, buffer
);
401 /* Second part of new format link */
402 if (buffer
[0] == '.')
404 if (buffer
[0] == '\\')
406 if (buffer
[0] == '"')
408 len
= strlen (buffer
);
409 if (len
&& buffer
[len
- 1] == '"') {
412 printf_string ("%c%s%c%s%c\n", CHAR_LINK_START
, old
,
413 CHAR_LINK_POINTER
, buffer
, CHAR_LINK_END
);
420 main (int argc
, char **argv
)
422 int len
; /* Length of input line */
423 const char *c_man
; /* Manual filename */
424 const char *c_tmpl
; /* Template filename */
425 FILE *f_man
; /* Manual file */
426 FILE *f_tmpl
; /* Template file */
427 char buffer2
[BUFFER_SIZE
]; /* Temp input line */
428 char *buffer
= buffer2
; /* Input line */
431 long cont_start
, file_end
;
433 /* Validity check for arguments */
434 if ((argc
!= 5) || ((width
= atoi (argv
[1])) <= 10)) {
436 "Usage: man2hlp width file.man template_file helpfile\n");
444 /* Open the input file (manual) */
445 f_man
= fopen (c_man
, "r");
447 sprintf (buffer
, "man2hlp: Cannot open file \"%s\"", c_man
);
452 f_out
= fopen (c_out
, "w");
454 sprintf (buffer
, "man2hlp: Cannot open file \"%s\"", c_out
);
459 /* Repeat for each input line */
460 while (!feof (f_man
)) {
462 if (!fgets (buffer2
, BUFFER_SIZE
, f_man
)) {
465 if (buffer2
[0] == '\\' && buffer2
[1] == '&')
466 buffer
= buffer2
+ 2;
470 len
= strlen (buffer
);
471 /* Remove terminating newline */
472 if (buffer
[len
- 1] == '\n') {
477 /* Copy the line verbatim */
478 if (strcmp (buffer
, ".fi") == 0) {
481 print_string (buffer
);
484 } else if (link_flag
)
485 /* The line is a link */
486 handle_link (buffer
);
487 else if (buffer
[0] == '.')
488 /* The line is a roff command */
489 handle_command (buffer
);
491 /* A normal line, just output it */
492 print_string (buffer
);
500 /* Open the template file */
501 f_tmpl
= fopen (c_tmpl
, "r");
502 if (f_tmpl
== NULL
) {
503 sprintf (buffer
, "man2hlp: Cannot open file \"%s\"", c_tmpl
);
508 /* Repeat for each input line */
509 while (!feof (f_tmpl
)) {
511 if (!fgets (buffer2
, BUFFER_SIZE
, f_tmpl
)) {
515 if (*buffer2
&& *buffer2
!= '\n') {
516 cnode
->lname
= strdup (buffer2
);
517 node
= strchr (cnode
->lname
, '\n');
523 node
= strchr (buffer
, CHAR_NODE_END
);
524 if (node
&& (node
[1] == '[')) {
525 char *p
= strrchr (node
, ']');
526 if (p
&& strncmp (node
+ 2, "main", 4) == 0
534 cnode
->next
= malloc (sizeof (nodes
));
537 cnode
->node
= strdup (node
+ 2);
538 cnode
->node
[p
- node
- 2] = 0;
544 fputs (buffer
, f_out
);
547 cont_start
= ftell (f_out
);
548 fprintf (f_out
, "\004[Contents]\n%s\n\n", Topics
);
550 for (cnode
= &nodes
; cnode
&& cnode
->node
;) {
551 char *node
= cnode
->node
;
552 int heading_level
= 0;
553 struct node
*next
= cnode
->next
;
555 while (*node
== ' ') {
560 fprintf (f_out
, " %*s\001 %s \002%s\003", heading_level
, "",
561 cnode
->lname
? cnode
->lname
: node
, node
);
562 fprintf (f_out
, "\n");
572 file_end
= ftell (f_out
);
580 Topics
= malloc (file_end
);
584 f_out
= fopen (c_out
, "r");
590 if (persistent_fread (Topics
, file_end
, f_out
) < 0) {
595 if (fclose (f_out
) != 0) {
600 f_out
= fopen (c_out
, "w");
606 if (persistent_fwrite
607 (Topics
+ cont_start
, file_end
- cont_start
, f_out
)
613 if (persistent_fwrite (Topics
, cont_start
, f_out
) < 0) {
620 if (fclose (f_out
) != 0) {