* Makefile.am: Remove definition of CONFDIR, it's not used.
[midnight-commander.git] / src / man2hlp.c
blob0942468f19678351dc568c421d8f1da976603486
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 = "Topics:";
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 Topics = strdup (buffer);
377 } else {
378 /* Other commands are ignored */
382 static void
383 handle_link (char *buffer)
385 static char old[80];
386 int len;
388 switch (link_flag) {
389 case 1:
390 /* Old format link, not supported */
391 break;
392 case 2:
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);
398 link_flag = 3;
399 break;
400 case 3:
401 /* Second part of new format link */
402 if (buffer[0] == '.')
403 buffer++;
404 if (buffer[0] == '\\')
405 buffer++;
406 if (buffer[0] == '"')
407 buffer++;
408 len = strlen (buffer);
409 if (len && buffer[len - 1] == '"') {
410 buffer[--len] = 0;
412 printf_string ("%c%s%c%s%c\n", CHAR_LINK_START, old,
413 CHAR_LINK_POINTER, buffer, CHAR_LINK_END);
414 link_flag = 0;
415 break;
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 */
429 char *node = NULL;
431 long cont_start, file_end;
433 /* Validity check for arguments */
434 if ((argc != 5) || ((width = atoi (argv[1])) <= 10)) {
435 fprintf (stderr,
436 "Usage: man2hlp width file.man template_file helpfile\n");
437 return 3;
440 c_man = argv[2];
441 c_tmpl = argv[3];
442 c_out = argv[4];
444 /* Open the input file (manual) */
445 f_man = fopen (c_man, "r");
446 if (f_man == NULL) {
447 sprintf (buffer, "man2hlp: Cannot open file \"%s\"", c_man);
448 perror (buffer);
449 return 3;
452 f_out = fopen (c_out, "w");
453 if (f_out == NULL) {
454 sprintf (buffer, "man2hlp: Cannot open file \"%s\"", c_out);
455 perror (buffer);
456 return 3;
459 /* Repeat for each input line */
460 while (!feof (f_man)) {
461 /* Read a line */
462 if (!fgets (buffer2, BUFFER_SIZE, f_man)) {
463 break;
465 if (buffer2[0] == '\\' && buffer2[1] == '&')
466 buffer = buffer2 + 2;
467 else
468 buffer = buffer2;
469 in_row++;
470 len = strlen (buffer);
471 /* Remove terminating newline */
472 if (buffer[len - 1] == '\n') {
473 len--;
474 buffer[len] = 0;
476 if (verbatim_flag) {
477 /* Copy the line verbatim */
478 if (strcmp (buffer, ".fi") == 0) {
479 verbatim_flag = 0;
480 } else {
481 print_string (buffer);
482 newline ();
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);
490 else {
491 /* A normal line, just output it */
492 print_string (buffer);
496 /* All done */
497 newline ();
498 fclose (f_man);
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);
504 perror (buffer);
505 return 3;
508 /* Repeat for each input line */
509 while (!feof (f_tmpl)) {
510 /* Read a line */
511 if (!fgets (buffer2, BUFFER_SIZE, f_tmpl)) {
512 break;
514 if (node) {
515 if (*buffer2 && *buffer2 != '\n') {
516 cnode->lname = strdup (buffer2);
517 node = strchr (cnode->lname, '\n');
518 if (node)
519 *node = 0;
521 node = NULL;
522 } else {
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
527 && node[6] == ']') {
528 node = 0;
529 } else {
530 if (!cnode) {
531 cnode = &nodes;
532 cnode->next = NULL;
533 } else {
534 cnode->next = malloc (sizeof (nodes));
535 cnode = cnode->next;
537 cnode->node = strdup (node + 2);
538 cnode->node[p - node - 2] = 0;
539 cnode->lname = NULL;
541 } else
542 node = NULL;
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 == ' ') {
556 heading_level++;
557 node++;
559 if (*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");
564 free (cnode->node);
565 if (cnode->lname)
566 free (cnode->lname);
567 if (cnode != &nodes)
568 free (cnode);
569 cnode = next;
572 file_end = ftell (f_out);
573 fclose (f_out);
575 if (file_end <= 0) {
576 perror (c_out);
577 return 1;
580 Topics = malloc (file_end);
581 if (!Topics)
582 return 1;
584 f_out = fopen (c_out, "r");
585 if (!f_out) {
586 perror (c_out);
587 return 1;
590 if (persistent_fread (Topics, file_end, f_out) < 0) {
591 perror (c_out);
592 return 1;
595 if (fclose (f_out) != 0) {
596 perror (c_out);
597 return 1;
600 f_out = fopen (c_out, "w");
601 if (!f_out) {
602 perror (c_out);
603 return 1;
606 if (persistent_fwrite
607 (Topics + cont_start, file_end - cont_start, f_out)
608 < 0) {
609 perror (c_out);
610 return 1;
613 if (persistent_fwrite (Topics, cont_start, f_out) < 0) {
614 perror (c_out);
615 return 1;
618 free (Topics);
620 if (fclose (f_out) != 0) {
621 perror (c_out);
622 return 1;
625 return 0;