OSDN Git Service

3bb824968261b412d38838c45e62c23f89549b4b
[pf3gnuchains/sourceware.git] / tcl / tools / man2tcl.c
1 /* 
2  * man2tcl.c --
3  *
4  *      This file contains a program that turns a man page of the
5  *      form used for Tcl and Tk into a Tcl script that invokes
6  *      a Tcl command for each construct in the man page.  The
7  *      script can then be eval'ed to translate the manual entry
8  *      into some other format such as MIF or HTML.
9  *
10  * Usage:
11  *
12  *      man2tcl ?fileName?
13  *
14  * Copyright (c) 1995 Sun Microsystems, Inc.
15  *
16  * See the file "license.terms" for information on usage and redistribution
17  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
18  *
19  * RCS: @(#) $Id$
20  */
21
22 static char sccsid[] = "@(#) man2tcl.c 1.3 95/08/12 17:34:08";
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <ctype.h>
27 #ifndef NO_ERRNO_H
28 #include <errno.h>
29 #endif
30
31 /*
32  * Imported things that aren't defined in header files:
33  */
34
35 extern int errno;
36
37 /*
38  * Current line number, used for error messages.
39  */
40
41 static int lineNumber;
42
43 /*
44  * The variable below is set to 1 if an error occurs anywhere
45  * while reading in the file.
46  */
47
48 static int status;
49
50 /*
51  * The variable below is set to 1 if output should be generated.
52  * If it's 0, it means we're doing a pre-pass to make sure that
53  * the file can be properly parsed.
54  */
55
56 static int writeOutput;
57
58 /*
59  * Prototypes for procedures defined in this file:
60  */
61
62 static void             DoMacro(char *line);
63 static void             DoText(char *line);
64 static void             QuoteText(char *string, int count);
65 \f
66 /*
67  *----------------------------------------------------------------------
68  *
69  * main --
70  *
71  *      This procedure is the main program, which does all of the work
72  *      of the program.
73  *
74  * Results:
75  *      None: exits with a 0 return status to indicate success, or
76  *      1 to indicate that there were problems in the translation.
77  *
78  * Side effects:
79  *      A Tcl script is output to standard output.  Error messages may
80  *      be output on standard error.
81  *
82  *----------------------------------------------------------------------
83  */
84
85 int
86 main(argc, argv)
87     int argc;                   /* Number of command-line arguments. */
88     char **argv;                /* Values of command-line arguments. */
89 {
90     FILE *f;
91 #define MAX_LINE_SIZE 500
92     char line[MAX_LINE_SIZE];
93     char *p;
94
95     /*
96      * Find the file to read, and open it if it isn't stdin.
97      */
98
99     if (argc == 1) {
100         f = stdin;
101     } else if (argc == 2) {
102         f = fopen(argv[1], "r");
103         if (f == NULL) {
104             fprintf(stderr, "Couldn't read \"%s\": %s\n", argv[1],
105                     strerror(errno));
106             exit(1);
107         }
108     } else {
109         fprintf(stderr, "Usage: %s ?fileName?\n", argv[0]);
110     }
111
112     /*
113      * Make two passes over the file.  In the first pass, just check
114      * to make sure we can handle everything.  If there are problems,
115      * generate output and stop.  If everything is OK, make a second
116      * pass to actually generate output.
117      */
118
119     for (writeOutput = 0; writeOutput < 2; writeOutput++) {
120         lineNumber = 0;
121         status = 0;
122         while (fgets(line, MAX_LINE_SIZE, f) != NULL) {
123             for (p = line; *p != 0; p++) {
124                 if (*p == '\n') {
125                     *p = 0;
126                     break;
127                 }
128             }
129             lineNumber++;
130     
131             if ((line[0] == '\'') && (line[1] == '\\') && (line[2] == '\"')) {
132                 /* 
133                  * This line is a comment.  Ignore it.
134                  */
135     
136                 continue;
137             }
138     
139             if ((line[0] == '.') || (line[0] == '\'')) {
140                 /*
141                  * This line is a macro invocation.
142                  */
143     
144                 DoMacro(line);
145             } else {
146                 /*
147                  * This line is text, possibly with formatting characters
148                  * embedded in it.
149                  */
150     
151                 DoText(line);
152             }
153         }
154         if (status != 0) {
155             break;
156         }
157         fseek(f, 0, SEEK_SET);
158     }
159     exit(status);
160 }
161 \f
162 /*
163  *----------------------------------------------------------------------
164  *
165  * DoMacro --
166  *
167  *      This procedure is called to handle a macro invocation.
168  *      It parses the arguments to the macro and generates a
169  *      Tcl command to handle the invocation.
170  *
171  * Results:
172  *      None.
173  *
174  * Side effects:
175  *      A Tcl command is written to stdout.
176  *
177  *----------------------------------------------------------------------
178  */
179
180 static void
181 DoMacro(line)
182     char *line;                 /* The line of text that contains the
183                                  * macro invocation. */
184 {
185     char *p, *end;
186
187     /*
188      * If there is no macro name, then just skip the whole line.
189      */
190
191     if ((line[1] == 0) || (isspace(line[1]))) {
192         return;
193     }
194
195     if (writeOutput) {
196         printf("macro");
197     }
198     if (*line != '.') {
199         if (writeOutput) {
200             printf("2");
201         }
202     }
203
204     /*
205      * Parse the arguments to the macro (including the name), in order.
206      */
207
208     p = line+1;
209     while (1) {
210         if (writeOutput) {
211             putc(' ', stdout);
212         }
213         if (*p == '"')  {
214             /*
215              * The argument is delimited by quotes.
216              */
217
218             for (end = p+1; *end != '"'; end++) {
219                 if (*end == 0) {
220                     fprintf(stderr,
221                         "Unclosed quote in macro call on line %d.\n",
222                         lineNumber);
223                     status = 1;
224                     break;
225                 }
226             }
227             QuoteText(p+1, (end-(p+1)));
228         } else {
229             for (end = p+1;  (*end != 0) && !isspace(*end); end++) {
230                 /* Empty loop body. */
231             }
232             QuoteText(p, end-p);
233         }
234         if (*end == 0) {
235             break;
236         }
237         p = end+1;
238         while (isspace(*p)) {
239             /*
240              * Skip empty space before next argument.
241              */
242
243             p++;
244         }
245         if (*p == 0) {
246             break;
247         }
248     }
249     if (writeOutput) {
250         putc('\n', stdout);
251     }
252 }
253 \f
254 /*
255  *----------------------------------------------------------------------
256  *
257  * DoText --
258  *
259  *      This procedure is called to handle a line of troff text.
260  *      It parses the text, generating Tcl commands for text and
261  *      for formatting stuff such as font changes.
262  *
263  * Results:
264  *      None.
265  *
266  * Side effects:
267  *      Tcl commands are written to stdout.
268  *
269  *----------------------------------------------------------------------
270  */
271
272 static void
273 DoText(line)
274     char *line;                 /* The line of text. */
275 {
276     char *p, *end;
277
278     /*
279      * Divide the line up into pieces consisting of backslash sequences,
280      * tabs, and other text.
281      */
282
283     p = line;
284     while (*p != 0) {
285         if (*p == '\t') {
286             if (writeOutput) {
287                 printf("tab\n");
288             }
289             p++;
290         } else if (*p != '\\') {
291             /*
292              * Ordinary text.
293              */
294
295             for (end = p+1; (*end != '\\') && (*end != 0); end++) {
296                 /* Empty loop body. */
297             }
298             if (writeOutput) {
299                 printf("text ");
300             }
301             QuoteText(p, end-p);
302             if (writeOutput) {
303                 putc('\n', stdout);
304             }
305             p = end;
306         } else {
307             /*
308              * A backslash sequence.  There are particular ones
309              * that we understand;  output an error message for
310              * anything else and just ignore the backslash.
311              */
312
313             p++;
314             if (*p == 'f') {
315                 /*
316                  * Font change.
317                  */
318
319                 if (writeOutput) {
320                     printf("font %c\n", p[1]);
321                 }
322                 p += 2;
323             } else if (*p == '-') {
324                 if (writeOutput) {
325                     printf("dash\n");
326                 }
327                 p++;
328             } else if (*p == 'e') {
329                 if (writeOutput) {
330                     printf("text \\\\\n");
331                 }
332                 p++;
333             } else if (*p == '.') {
334                 if (writeOutput) {
335                     printf("text .\n");
336                 }
337                 p++;
338             } else if (*p == '&') {
339                 p++;
340             } else if (*p == '(') {
341                 if ((p[1] == 0) || (p[2] == 0)) {
342                     fprintf(stderr, "Bad \\( sequence on line %d.\n",
343                             lineNumber);
344                     status = 1;
345                 } else {
346                     if (writeOutput) {
347                         printf("char {\\(%c%c}\n", p[1], p[2]);
348                     }
349                     p += 3;
350                 }
351             } else if (*p != 0) {
352                 if (writeOutput) {
353                     printf("char {\\%c}\n", *p);
354                 }
355                 p++;
356             }
357         }
358     }
359     if (writeOutput) {
360         printf("newline\n");
361     }
362 }
363 \f
364 /*
365  *----------------------------------------------------------------------
366  *
367  * QuoteText --
368  *
369  *      Copy the "string" argument to stdout, adding quote characters
370  *      around any special Tcl characters so that they'll just be treated
371  *      as ordinary text.
372  *
373  * Results:
374  *      None.
375  *
376  * Side effects:
377  *      Text is written to stdout.
378  *
379  *----------------------------------------------------------------------
380  */
381
382 static void
383 QuoteText(string, count)
384     char *string;               /* The line of text. */
385     int count;                  /* Number of characters to write from string. */
386 {
387     if (count == 0) {
388         if (writeOutput) {
389             printf("{}");
390         }
391         return;
392     }
393     for ( ; count > 0; string++, count--) {
394         if ((*string == '$') || (*string == '[') || (*string == '{')
395                 || (*string == ' ') || (*string == ';') || (*string == '\\')
396                 || (*string == '"') || (*string == '\t')) {
397             if (writeOutput) {
398                 putc('\\', stdout);
399             }
400         }
401         if (writeOutput) {
402             putc(*string, stdout);
403         }
404     }
405 }