sl@0: /* sl@0: * man2tcl.c -- sl@0: * sl@0: * This file contains a program that turns a man page of the sl@0: * form used for Tcl and Tk into a Tcl script that invokes sl@0: * a Tcl command for each construct in the man page. The sl@0: * script can then be eval'ed to translate the manual entry sl@0: * into some other format such as MIF or HTML. sl@0: * sl@0: * Usage: sl@0: * sl@0: * man2tcl ?fileName? sl@0: * sl@0: * Copyright (c) 1995 Sun Microsystems, Inc. sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * RCS: @(#) $Id: man2tcl.c,v 1.7.2.1 2003/12/09 15:32:20 dkf Exp $ sl@0: */ sl@0: sl@0: static char sccsid[] = "@(#) man2tcl.c 1.3 95/08/12 17:34:08"; sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #ifndef NO_ERRNO_H sl@0: #include sl@0: #endif sl@0: sl@0: /* sl@0: * Imported things that aren't defined in header files: sl@0: */ sl@0: sl@0: /* sl@0: * Some define errno to be something complex and sl@0: * thread-aware; in that case we definitely do not want to declare sl@0: * errno ourselves! sl@0: */ sl@0: #ifndef errno sl@0: extern int errno; sl@0: #endif sl@0: sl@0: /* sl@0: * Current line number, used for error messages. sl@0: */ sl@0: sl@0: static int lineNumber; sl@0: sl@0: /* sl@0: * The variable below is set to 1 if an error occurs anywhere sl@0: * while reading in the file. sl@0: */ sl@0: sl@0: static int status; sl@0: sl@0: /* sl@0: * The variable below is set to 1 if output should be generated. sl@0: * If it's 0, it means we're doing a pre-pass to make sure that sl@0: * the file can be properly parsed. sl@0: */ sl@0: sl@0: static int writeOutput; sl@0: sl@0: /* sl@0: * Prototypes for procedures defined in this file: sl@0: */ sl@0: sl@0: static void DoMacro(char *line); sl@0: static void DoText(char *line); sl@0: static void QuoteText(char *string, int count); sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * main -- sl@0: * sl@0: * This procedure is the main program, which does all of the work sl@0: * of the program. sl@0: * sl@0: * Results: sl@0: * None: exits with a 0 return status to indicate success, or sl@0: * 1 to indicate that there were problems in the translation. sl@0: * sl@0: * Side effects: sl@0: * A Tcl script is output to standard output. Error messages may sl@0: * be output on standard error. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: main(argc, argv) sl@0: int argc; /* Number of command-line arguments. */ sl@0: char **argv; /* Values of command-line arguments. */ sl@0: { sl@0: FILE *f; sl@0: #define MAX_LINE_SIZE 1000 sl@0: char line[MAX_LINE_SIZE]; sl@0: char *p; sl@0: sl@0: /* sl@0: * Find the file to read, and open it if it isn't stdin. sl@0: */ sl@0: sl@0: if (argc == 1) { sl@0: f = stdin; sl@0: } else if (argc == 2) { sl@0: f = fopen(argv[1], "r"); sl@0: if (f == NULL) { sl@0: fprintf(stderr, "Couldn't read \"%s\": %s\n", argv[1], sl@0: strerror(errno)); sl@0: exit(1); sl@0: } sl@0: } else { sl@0: fprintf(stderr, "Usage: %s ?fileName?\n", argv[0]); sl@0: } sl@0: sl@0: /* sl@0: * Make two passes over the file. In the first pass, just check sl@0: * to make sure we can handle everything. If there are problems, sl@0: * generate output and stop. If everything is OK, make a second sl@0: * pass to actually generate output. sl@0: */ sl@0: sl@0: for (writeOutput = 0; writeOutput < 2; writeOutput++) { sl@0: lineNumber = 0; sl@0: status = 0; sl@0: while (fgets(line, MAX_LINE_SIZE, f) != NULL) { sl@0: for (p = line; *p != 0; p++) { sl@0: if (*p == '\n') { sl@0: *p = 0; sl@0: break; sl@0: } sl@0: } sl@0: lineNumber++; sl@0: sl@0: if ((line[0] == '\'') && (line[1] == '\\') && (line[2] == '\"')) { sl@0: /* sl@0: * This line is a comment. Ignore it. sl@0: */ sl@0: sl@0: continue; sl@0: } sl@0: sl@0: if (strlen(line) >= MAX_LINE_SIZE -1) { sl@0: fprintf(stderr, "Too long line. Max is %d chars.\n", sl@0: MAX_LINE_SIZE - 1); sl@0: exit(1); sl@0: } sl@0: sl@0: if ((line[0] == '.') || (line[0] == '\'')) { sl@0: /* sl@0: * This line is a macro invocation. sl@0: */ sl@0: sl@0: DoMacro(line); sl@0: } else { sl@0: /* sl@0: * This line is text, possibly with formatting characters sl@0: * embedded in it. sl@0: */ sl@0: sl@0: DoText(line); sl@0: } sl@0: } sl@0: if (status != 0) { sl@0: break; sl@0: } sl@0: fseek(f, 0, SEEK_SET); sl@0: } sl@0: exit(status); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * DoMacro -- sl@0: * sl@0: * This procedure is called to handle a macro invocation. sl@0: * It parses the arguments to the macro and generates a sl@0: * Tcl command to handle the invocation. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * A Tcl command is written to stdout. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: DoMacro(line) sl@0: char *line; /* The line of text that contains the sl@0: * macro invocation. */ sl@0: { sl@0: char *p, *end; sl@0: sl@0: /* sl@0: * If there is no macro name, then just skip the whole line. sl@0: */ sl@0: sl@0: if ((line[1] == 0) || (isspace(line[1]))) { sl@0: return; sl@0: } sl@0: sl@0: if (writeOutput) { sl@0: printf("macro"); sl@0: } sl@0: if (*line != '.') { sl@0: if (writeOutput) { sl@0: printf("2"); sl@0: } sl@0: } sl@0: sl@0: /* sl@0: * Parse the arguments to the macro (including the name), in order. sl@0: */ sl@0: sl@0: p = line+1; sl@0: while (1) { sl@0: if (writeOutput) { sl@0: putc(' ', stdout); sl@0: } sl@0: if (*p == '"') { sl@0: /* sl@0: * The argument is delimited by quotes. sl@0: */ sl@0: sl@0: for (end = p+1; *end != '"'; end++) { sl@0: if (*end == 0) { sl@0: fprintf(stderr, sl@0: "Unclosed quote in macro call on line %d.\n", sl@0: lineNumber); sl@0: status = 1; sl@0: break; sl@0: } sl@0: } sl@0: QuoteText(p+1, (end-(p+1))); sl@0: } else { sl@0: for (end = p+1; (*end != 0) && !isspace(*end); end++) { sl@0: /* Empty loop body. */ sl@0: } sl@0: QuoteText(p, end-p); sl@0: } sl@0: if (*end == 0) { sl@0: break; sl@0: } sl@0: p = end+1; sl@0: while (isspace(*p)) { sl@0: /* sl@0: * Skip empty space before next argument. sl@0: */ sl@0: sl@0: p++; sl@0: } sl@0: if (*p == 0) { sl@0: break; sl@0: } sl@0: } sl@0: if (writeOutput) { sl@0: putc('\n', stdout); sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * DoText -- sl@0: * sl@0: * This procedure is called to handle a line of troff text. sl@0: * It parses the text, generating Tcl commands for text and sl@0: * for formatting stuff such as font changes. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Tcl commands are written to stdout. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: DoText(line) sl@0: char *line; /* The line of text. */ sl@0: { sl@0: char *p, *end; sl@0: sl@0: /* sl@0: * Divide the line up into pieces consisting of backslash sequences, sl@0: * tabs, and other text. sl@0: */ sl@0: sl@0: p = line; sl@0: while (*p != 0) { sl@0: if (*p == '\t') { sl@0: if (writeOutput) { sl@0: printf("tab\n"); sl@0: } sl@0: p++; sl@0: } else if (*p != '\\') { sl@0: /* sl@0: * Ordinary text. sl@0: */ sl@0: sl@0: for (end = p+1; (*end != '\\') && (*end != 0); end++) { sl@0: /* Empty loop body. */ sl@0: } sl@0: if (writeOutput) { sl@0: printf("text "); sl@0: } sl@0: QuoteText(p, end-p); sl@0: if (writeOutput) { sl@0: putc('\n', stdout); sl@0: } sl@0: p = end; sl@0: } else { sl@0: /* sl@0: * A backslash sequence. There are particular ones sl@0: * that we understand; output an error message for sl@0: * anything else and just ignore the backslash. sl@0: */ sl@0: sl@0: p++; sl@0: if (*p == 'f') { sl@0: /* sl@0: * Font change. sl@0: */ sl@0: sl@0: if (writeOutput) { sl@0: printf("font %c\n", p[1]); sl@0: } sl@0: p += 2; sl@0: } else if (*p == '-') { sl@0: if (writeOutput) { sl@0: printf("dash\n"); sl@0: } sl@0: p++; sl@0: } else if (*p == 'e') { sl@0: if (writeOutput) { sl@0: printf("text \\\\\n"); sl@0: } sl@0: p++; sl@0: } else if (*p == '.') { sl@0: if (writeOutput) { sl@0: printf("text .\n"); sl@0: } sl@0: p++; sl@0: } else if (*p == '&') { sl@0: p++; sl@0: } else if (*p == '(') { sl@0: if ((p[1] == 0) || (p[2] == 0)) { sl@0: fprintf(stderr, "Bad \\( sequence on line %d.\n", sl@0: lineNumber); sl@0: status = 1; sl@0: } else { sl@0: if (writeOutput) { sl@0: printf("char {\\(%c%c}\n", p[1], p[2]); sl@0: } sl@0: p += 3; sl@0: } sl@0: } else if (*p != 0) { sl@0: if (writeOutput) { sl@0: printf("char {\\%c}\n", *p); sl@0: } sl@0: p++; sl@0: } sl@0: } sl@0: } sl@0: if (writeOutput) { sl@0: printf("newline\n"); sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * QuoteText -- sl@0: * sl@0: * Copy the "string" argument to stdout, adding quote characters sl@0: * around any special Tcl characters so that they'll just be treated sl@0: * as ordinary text. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Text is written to stdout. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: QuoteText(string, count) sl@0: char *string; /* The line of text. */ sl@0: int count; /* Number of characters to write from string. */ sl@0: { sl@0: if (count == 0) { sl@0: if (writeOutput) { sl@0: printf("{}"); sl@0: } sl@0: return; sl@0: } sl@0: for ( ; count > 0; string++, count--) { sl@0: if ((*string == '$') || (*string == '[') || (*string == '{') sl@0: || (*string == ' ') || (*string == ';') || (*string == '\\') sl@0: || (*string == '"') || (*string == '\t')) { sl@0: if (writeOutput) { sl@0: putc('\\', stdout); sl@0: } sl@0: } sl@0: if (writeOutput) { sl@0: putc(*string, stdout); sl@0: } sl@0: } sl@0: }