*** empty log message ***
[midnight-commander.git] / src / man2hlp.c
blob66123213c7ca59d7e0e6bb1ae5f17e38be394c63
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. */
18 #include <config.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdarg.h>
22 #include <string.h>
24 #include "help.h"
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.
34 0 = don't skip,
35 1 = skipping title,
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 = NULL;
46 static struct node {
47 char *node;
48 char *lname;
49 struct node *next;
50 } nodes, *cnode;
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.
58 static size_t
59 persistent_fread (void *data, size_t len, FILE * stream)
61 size_t count;
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);
72 if (count <= 0)
73 return -1;
75 bytes_done += count;
76 ptr += count;
79 return bytes_done;
83 * Write in blocks of reasonable size and make sure we write everything.
84 * Failure to write everything is an error.
86 static size_t
87 persistent_fwrite (const void *data, size_t len, FILE * stream)
89 size_t count;
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);
100 if (count <= 0)
101 return -1;
103 bytes_done += count;
104 ptr += count;
107 return bytes_done;
110 /* Report error in input */
111 static void
112 print_error (char *message)
114 fprintf (stderr, "man2hlp: %s in file \"%s\" at row %d\n", message,
115 c_out, in_row);
118 /* Change output line */
119 static void
120 newline (void)
122 out_row++;
123 col = 0;
124 fprintf (f_out, "\n");
127 /* Calculate the length of string */
128 static int
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 */
137 while (*(buffer)) {
138 c = *buffer++;
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 */
145 anchor_flag = 1;
146 /* Ugly hack to prevent loss of one space */
147 len++;
149 /* Don't add control characters to the length */
150 if (c >= 0 && c < 32)
151 continue;
152 /* Attempt to handle backslash quoting */
153 if (c == '\\' && !backslash_flag) {
154 backslash_flag = 1;
155 continue;
157 backslash_flag = 0;
158 /* Increase length if not inside anchor name or link target name */
159 if (!anchor_flag && !link_flag)
160 len++;
161 if (anchor_flag && c == ']') {
162 /* Node anchor name ends */
163 anchor_flag = 0;
166 return len;
169 /* Output the string */
170 static void
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? */
178 if (skip_flag)
179 return;
180 /* Copying verbatim? */
181 if (verbatim_flag) {
182 /* Attempt to handle backslash quoting */
183 while (*(buffer)) {
184 c = *buffer++;
185 if (c == '\\' && !backslash_flag) {
186 backslash_flag = 1;
187 continue;
189 backslash_flag = 0;
190 fprintf (f_out, "%c", c);
192 } else {
193 /* Split into words */
194 buffer = strtok (buffer, " \t\n");
195 /* Repeat for each word */
196 while (buffer) {
197 /* Skip empty strings */
198 if (*(buffer)) {
199 len = string_len (buffer);
200 /* Change the line if about to break the right margin */
201 if (col + len >= width)
202 newline ();
203 /* Words are separated by spaces */
204 if (col > 0) {
205 fprintf (f_out, " ");
206 col++;
208 /* Attempt to handle backslash quoting */
209 while (*(buffer)) {
210 c = *buffer++;
211 if (c == '\\' && !backslash_flag) {
212 backslash_flag = 1;
213 continue;
215 backslash_flag = 0;
216 fprintf (f_out, "%c", c);
218 /* Increase column */
219 col += len;
221 /* Get the next word */
222 buffer = strtok (NULL, " \t\n");
223 } /* while */
227 /* Like print_string but with printf-like syntax */
228 static void
229 printf_string (char *format, ...)
231 va_list args;
232 char buffer[BUFFER_SIZE];
234 va_start (args, format);
235 vsprintf (buffer, format, args);
236 va_end (args);
237 print_string (buffer);
240 /* Handle all the roff dot commands */
241 static void
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) {
253 skip_flag = 0;
255 /* Get the command parameters */
256 buffer = strtok (NULL, "");
257 if (buffer == NULL) {
258 print_error ("Syntax error: .SH: no title");
259 return;
260 } else {
261 /* Remove quotes */
262 if (buffer[0] == '"') {
263 buffer++;
264 len = strlen (buffer);
265 if (buffer[len - 1] == '"') {
266 len--;
267 buffer[len] = 0;
270 /* Calculate heading level */
271 heading_level = 0;
272 while (buffer[heading_level] == ' ')
273 heading_level++;
274 /* Heading level must be even */
275 if (heading_level & 1)
276 print_error ("Syntax error: .SH: odd heading level");
277 if (no_split_flag) {
278 /* Don't start a new section */
279 newline ();
280 print_string (buffer);
281 newline ();
282 newline ();
283 no_split_flag = 0;
284 } else if (skip_flag) {
285 /* Skipping title and marking text for skipping */
286 skip_flag = 2;
287 } else {
288 if (!SH || !node) {
289 /* Start a new section */
290 fprintf (f_out, "%c[%s]", CHAR_NODE_END,
291 buffer + heading_level);
292 if (!cnode) {
293 cnode = &nodes;
294 cnode->next = NULL;
295 } else {
296 cnode->next = malloc (sizeof (nodes));
297 cnode = cnode->next;
299 cnode->node = strdup (buffer);
300 cnode->lname = NULL;
301 col++;
302 newline ();
304 if (SH) {
305 /* print_string() strtok()es buffer, so */
306 cnode->lname = strdup (buffer + heading_level);
307 print_string (buffer + heading_level);
308 newline ();
309 newline ();
311 } /* Start new section */
312 } /* Has parameters */
313 node = !SH;
314 } /* Command .SH */
315 else if (strcmp (buffer, ".\\\"DONT_SPLIT\"") == 0) {
316 no_split_flag = 1;
317 } else if (strcmp (buffer, ".\\\"SKIP_SECTION\"") == 0) {
318 skip_flag = 1;
319 } else if (strcmp (buffer, ".\\\"LINK2\"") == 0) {
320 /* Next two input lines form a link */
321 link_flag = 2;
322 } else if (strcmp (buffer, ".PP") == 0) {
323 /* End of paragraph */
324 if (col > 0)
325 newline ();
326 newline ();
327 } else if (strcmp (buffer, ".nf") == 0) {
328 /* Following input lines are to be handled verbatim */
329 verbatim_flag = 1;
330 if (col > 0)
331 newline ();
332 } else if (strcmp (buffer, ".I") == 0 || strcmp (buffer, ".B") == 0) {
333 /* Bold text or italics text */
334 char type = buffer[1];
335 char *p;
336 char *w = buffer;
337 int backslash_flag = 0;
339 buffer = strtok (NULL, "");
340 if (buffer == NULL) {
341 print_error ("Syntax error: .I / .B: no text");
342 return;
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) {
350 backslash_flag = 1;
351 continue;
353 backslash_flag = 0;
354 *w++ = *p;
357 *w++ = CHAR_BOLD_OFF;
358 *w = 0;
359 print_string (buffer);
360 } else if (strcmp (buffer, ".TP") == 0) {
361 /* End of paragraph? */
362 if (col > 0)
363 newline ();
364 newline ();
365 } else if (strcmp (buffer, ".\\\"TOPICS") == 0) {
366 if (out_row > 1) {
367 print_error
368 ("Syntax error: .\\\"TOPICS must be first command");
369 return;
371 buffer = strtok (NULL, "");
372 if (buffer == NULL) {
373 print_error ("Syntax error: .\\\"TOPICS: no text");
374 return;
376 /* Remove quotes */
377 if (buffer[0] == '"') {
378 buffer++;
379 len = strlen (buffer);
380 if (buffer[len - 1] == '"') {
381 len--;
382 buffer[len] = 0;
385 Topics = strdup (buffer);
386 } else {
387 /* Other commands are ignored */
391 static void
392 handle_link (char *buffer)
394 static char old[80];
395 int len;
397 switch (link_flag) {
398 case 1:
399 /* Old format link, not supported */
400 break;
401 case 2:
402 /* First part of new format link */
403 /* Bold text or italics text */
404 if (buffer[0] == '.' && (buffer[1] == 'I' || buffer[1] == 'B'))
405 for (buffer += 2; *buffer == ' ' || *buffer == '\t'; buffer++);
406 strcpy (old, buffer);
407 link_flag = 3;
408 break;
409 case 3:
410 /* Second part of new format link */
411 if (buffer[0] == '.')
412 buffer++;
413 if (buffer[0] == '\\')
414 buffer++;
415 if (buffer[0] == '"')
416 buffer++;
417 len = strlen (buffer);
418 if (len && buffer[len - 1] == '"') {
419 buffer[--len] = 0;
421 printf_string ("%c%s%c%s%c\n", CHAR_LINK_START, old,
422 CHAR_LINK_POINTER, buffer, CHAR_LINK_END);
423 link_flag = 0;
424 break;
429 main (int argc, char **argv)
431 int len; /* Length of input line */
432 const char *c_man; /* Manual filename */
433 const char *c_tmpl; /* Template filename */
434 FILE *f_man; /* Manual file */
435 FILE *f_tmpl; /* Template file */
436 char buffer2[BUFFER_SIZE]; /* Temp input line */
437 char *buffer = buffer2; /* Input line */
438 char *node = NULL;
440 long cont_start, file_end;
442 /* Validity check for arguments */
443 if ((argc != 5) || ((width = atoi (argv[1])) <= 10)) {
444 fprintf (stderr,
445 "Usage: man2hlp width file.man template_file helpfile\n");
446 return 3;
449 c_man = argv[2];
450 c_tmpl = argv[3];
451 c_out = argv[4];
453 /* Open the input file (manual) */
454 f_man = fopen (c_man, "r");
455 if (f_man == NULL) {
456 sprintf (buffer, "man2hlp: Cannot open file \"%s\"", c_man);
457 perror (buffer);
458 return 3;
461 f_out = fopen (c_out, "w");
462 if (f_out == NULL) {
463 sprintf (buffer, "man2hlp: Cannot open file \"%s\"", c_out);
464 perror (buffer);
465 return 3;
468 /* Repeat for each input line */
469 while (!feof (f_man)) {
470 /* Read a line */
471 if (!fgets (buffer2, BUFFER_SIZE, f_man)) {
472 break;
474 if (buffer2[0] == '\\' && buffer2[1] == '&')
475 buffer = buffer2 + 2;
476 else
477 buffer = buffer2;
478 in_row++;
479 len = strlen (buffer);
480 /* Remove terminating newline */
481 if (buffer[len - 1] == '\n') {
482 len--;
483 buffer[len] = 0;
485 if (verbatim_flag) {
486 /* Copy the line verbatim */
487 if (strcmp (buffer, ".fi") == 0) {
488 verbatim_flag = 0;
489 } else {
490 print_string (buffer);
491 newline ();
493 } else if (link_flag)
494 /* The line is a link */
495 handle_link (buffer);
496 else if (buffer[0] == '.')
497 /* The line is a roff command */
498 handle_command (buffer);
499 else {
500 /* A normal line, just output it */
501 print_string (buffer);
505 /* All done */
506 newline ();
507 fclose (f_man);
509 /* Open the template file */
510 f_tmpl = fopen (c_tmpl, "r");
511 if (f_tmpl == NULL) {
512 sprintf (buffer, "man2hlp: Cannot open file \"%s\"", c_tmpl);
513 perror (buffer);
514 return 3;
517 /* Repeat for each input line */
518 while (!feof (f_tmpl)) {
519 /* Read a line */
520 if (!fgets (buffer2, BUFFER_SIZE, f_tmpl)) {
521 break;
523 if (node) {
524 if (*buffer2 && *buffer2 != '\n') {
525 cnode->lname = strdup (buffer2);
526 node = strchr (cnode->lname, '\n');
527 if (node)
528 *node = 0;
530 node = NULL;
531 } else {
532 node = strchr (buffer, CHAR_NODE_END);
533 if (node && (node[1] == '[')) {
534 char *p = strrchr (node, ']');
535 if (p && strncmp (node + 2, "main", 4) == 0
536 && node[6] == ']') {
537 node = 0;
538 } else {
539 if (!cnode) {
540 cnode = &nodes;
541 cnode->next = NULL;
542 } else {
543 cnode->next = malloc (sizeof (nodes));
544 cnode = cnode->next;
546 cnode->node = strdup (node + 2);
547 cnode->node[p - node - 2] = 0;
548 cnode->lname = NULL;
550 } else
551 node = NULL;
553 fputs (buffer, f_out);
556 cont_start = ftell (f_out);
557 if (Topics)
558 fprintf (f_out, "\004[Contents]\n%s\n\n", Topics);
559 else
560 fprintf (f_out, "\004[Contents]\n");
562 for (cnode = &nodes; cnode && cnode->node;) {
563 char *node = cnode->node;
564 int heading_level = 0;
565 struct node *next = cnode->next;
567 while (*node == ' ') {
568 heading_level++;
569 node++;
571 if (*node)
572 fprintf (f_out, " %*s\001 %s \002%s\003", heading_level, "",
573 cnode->lname ? cnode->lname : node, node);
574 fprintf (f_out, "\n");
576 free (cnode->node);
577 if (cnode->lname)
578 free (cnode->lname);
579 if (cnode != &nodes)
580 free (cnode);
581 cnode = next;
584 file_end = ftell (f_out);
585 fclose (f_out);
587 if (file_end <= 0) {
588 perror (c_out);
589 return 1;
592 Topics = malloc (file_end);
593 if (!Topics)
594 return 1;
596 f_out = fopen (c_out, "r");
597 if (!f_out) {
598 perror (c_out);
599 return 1;
602 if (persistent_fread (Topics, file_end, f_out) < 0) {
603 perror (c_out);
604 return 1;
607 if (fclose (f_out) != 0) {
608 perror (c_out);
609 return 1;
612 f_out = fopen (c_out, "w");
613 if (!f_out) {
614 perror (c_out);
615 return 1;
618 if (persistent_fwrite
619 (Topics + cont_start, file_end - cont_start, f_out)
620 < 0) {
621 perror (c_out);
622 return 1;
625 if (persistent_fwrite (Topics, cont_start, f_out) < 0) {
626 perror (c_out);
627 return 1;
630 free (Topics);
632 if (fclose (f_out) != 0) {
633 perror (c_out);
634 return 1;
637 return 0;