% This is the cweb file c2cweb Version 1.5 10-Nov-1996 % % You should process this file with % % cweave +ai c2cweb.w \pageno=\contentspagenumber \advance\pageno by 1 \let\maybe=\iftrue \fullpageheight=240mm \pageheight=223mm \pagewidth=158mm \setpage \frenchspacing \def\in{\leavevmode\vrule width 0pt\nobreak\hskip 2em\hskip 0pt} % indentation \font\sixrm=cmr6 \def\tm{$^{\hbox{\sixrm TM}}$} % trademark \def\title{c2cweb (Version 1.5)} \def\topofcontents{% \null\vfill \centerline{\titlefont The {\ttitlefont c2cweb} program} \vskip 20pt \centerline{(Version 1.5)} \vfill} \def\botofcontents{% \vfill \noindent Copyright \copyright\ 1996 by Werner Lemberg \bigskip\noindent Permission is granted to make and distribute verbatim copies of this document provided that the copyright notice and this permission notice are preserved on all copies. \smallskip\noindent Permission is granted to copy and distribute modified versions of this document under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.} @* Introduction. This is the \.{c2cweb} program by Werner Lemberg (\.{a7971428@@unet.univie.ac.at}). The ``banner line'' defined here should be changed whenever \.{c2cweb} is modified. @d banner "\nThis is c2cweb Version 1.5 (c) 1996 by Werner Lemberg\n\n" @ \.{c2cweb} will transform ordinary \CEE/ or \CPLUSPLUS/ source code into \.{CWEB} formatted code. Three primary functions are performed: inserting \.{@@}-commands between code blocks, transforming comments into \TeX-text, and replacing offending characters like \.{\\}, \.{\_}, \.{\&} etc. with commands \TeX\ (and \.{CWEB}) can understand. The only changes the user has to do normally is to insert `\.{/*@@@@*/}' or `\.{/*@@*/}' \\{starting a line} outside of a comment or string (the rest of these lines will be ignored): \advance\leftskip by 3em \item{$\bullet$} \.{/*@@@@*/} starts a new section and switches the function block algorithm on (see below). \item{$\bullet$} \.{/*@@*/} starts a new section and switches the function block algorithm off. This is the default when \.{c2cweb} begins to scan a file. \advance\leftskip by -3em Both `commands' will be suppressed in the output (two additional commands are described in the `Hints and Tricks'-section). @ Normal \CEE/ code consists of two parts: the code before function blocks (\.{\#include} and \.{\#define} statements, prototypes, structure definitions, global variables, etc.) and the function blocks (i.e. \.{foo()\{ ... \}}) itselves (possibly mixed with global definitions of variables, structures etc.). The main reason to separate them are memory constraints of \.{CWEAVE}; inserting \.{/*@@@@*/} causes each function block to be written into an own section. In header files, nothing is to do because there are no function blocks. In \CEE/ code files, it's usually sufficient to insert \.{/*@@@@*/} once, but you can structure your code further by inserting \.{/*@@*/} (and \.{/*@@@@*/} if necessary). See also the example file delivered with this package. You will need a special \.{CWEAVE} executable which has among others two additional control codes sometimes needed. See also the section `Hints and Tricks'. From now on, the words \.{CWEB} and \.{CWEAVE} will be used mutually in spite of the fact that \.{CWEB} consists of both the programs \.{CWEAVE} and \.{CTANGLE}---\.{c2cweb} is intended to create a well printed listing, and the use of \.{CTANGLE} is of course possible but senseless. @* The program. The use of |_response()|, |_wildcard()|, and |_getname()| is compiler specific. If you don't use emx-gcc, it's likely that you have to use different functions. If you use this program under DOS, consider the \UNIX/-like behavior of the ``|*|''-wildcard character. After processing the options, the global variable |optind| (defined in \.{getopt.h}) is the index to the first file name. @d FALSE 0 @d TRUE 1 @d DONE 2 @d WAIT 3 @c @; @; @;@# void main(argc, argv) int argc; char *argv[]; {int i; char buffer[DIR_LENGTH + FILE_NAME_LENGTH + 1]; char *p, *q; printf(banner);@# #ifdef __EMX__ _response(&argc, &argv); /* to expand response files */ _wildcard(&argc, &argv); /* to expand wildcards */ #endif@# @;@# @; @;@# fclose(in); fclose(out); } @ \.{getopt.h} contains the \UNIX/-specific |getopt()|. If your system doesn't support this function, get the GNU \CEE/-library for an implementation. @= #include #include #include #include #include @ @= for (i = optind; i < argc - 1; i++) {printf(" processing %s\n", argv[i]);@# open_files(argv[i]); q = protect_underlines(_getname(argv[i])); if ((p = strrchr(q, '.')) != NULL) /* the macro \.{\\ZZZ} suppresses the final dot after the section title if the filename contains a dot */ fprintf(out,@/ "@@*{%s\\ZZZ{\\setbox0=\\hbox{%s}\\hskip-\\wd0}}.\n"@/ "\\ind=2\n\n", q, p); else fprintf(out, "@@*{%s}.\n"@/ "\\ind=2\n\n", q);@# handle_input(); fclose(in); fclose(out); } @ The last file will be the `master' file; you have to call \.{CWEAVE} with this file as the argument; all other files will be included. @= printf(" processing %s\n", argv[i]);@# open_files(argv[i]); @; q = protect_underlines(_getname(argv[i])); if ((p = strrchr(q, '.')) != NULL) fprintf(out,@/ "@@*{%s\\ZZZ{\\setbox0=\\hbox{%s}\\hskip-\\wd0}}.\n"@/ "\\ind=2\n\n", q, p); else fprintf(out, "@@*{%s}.\n"@/ "\\ind=2\n\n", q);@# handle_input(); @;@# strcpy(buffer, argv[argc - 1]); modify_filename(buffer);@# printf(@/ "\n You must now call CWEAVE with %s%s\n"@/ " as the argument to get a TeX output", outdir, _getname(buffer)); if (optind < argc - 1) printf(" of all processed files"); printf("\n"); @*1 The input switches. If the switch \.{-v} is set, all comments are written in typewriter type; comments starting a line will also start a line in the output. All tabs will be expanded, and the \.{-t} switch defines the tab length (default value is 4). The switch \.{-l} causes all linefeeds inside of \CEE/-text to be output explicitly by inserting a \.{@@/} command at the end of each code line. The output directory will be set with the \.{-o} option; this directory must already exist. Probably you have to adjust |PATH_SEPARATOR| and |pathsepchar[]| to make it work correctly under your operating system. To get a title, use the \.{-b} switch with the titlestring enclosed in double quotes; this string will be passed directly to \TeX. To include a format file (additionally to \.{compiler.w}), use the \.{-f} switch. One-sided output is enabled with the option \.{-1} set. The global variable |optarg| (defined in \.{getopt.h}) points to the option argument; the string |optswchar[]| is modified to allow |'-'| and |'/'| as characters which start options (not under \UNIX/). @d DIR_LENGTH 80 @d TITLE_LENGTH 100 @d PATH_SEPARATOR '/' @= int tab_length = 4; int verbatim = FALSE; int user_linefeed = FALSE; int one_side = FALSE; char outdir[DIR_LENGTH + 1]; char include_file[FILE_NAME_LENGTH + 1]; char title[TITLE_LENGTH + 1];@# #ifdef __EMX__ char optchar[] = "-/"; char pathsepchar[] = "\\/"; #else char optchar[] = "-"; char pathsepchar[] = "/"; #endif@# char at_nmb[] = "@@#"; /* the \.{CWEB} command for inserting vertical space */ @ @= {int c; int i; outdir[0] = '\0'; include_file[0] = '\0'; title[0] = '\0';@# #ifdef __EMX__ optswchar = optchar; #endif@# strcpy(title, "c2cweb output"); /* the default title */@# while ((c = getopt(argc, argv, "b:f:lo:t:v1")) != EOF) {switch (c) {case 'b': if (strchr(optchar, optarg[0])) /* check if argument is an option */ usage();@# if (strlen(optarg) >= TITLE_LENGTH) fprintf(stderr,@/ "\nTitle too long. Will use \"c2cweb output\".\n"); else strcpy(title, optarg); break; case 'f': if (strchr(optchar, optarg[0])) usage();@# if (strlen(optarg) >= FILE_NAME_LENGTH) fprintf(stderr,@/ "\nInclude file name too long. Will be ignored.\n"); else strcpy(include_file, optarg); break; case 'l': user_linefeed = TRUE; break; case 'o': if (strchr(optchar, optarg[0])) usage();@# if ((i = strlen(optarg)) >= DIR_LENGTH) fprintf(stderr,@/ "\nOutput directory name too long. Will be ignored.\n"); else {strcpy(outdir, optarg); if (!strchr(pathsepchar, outdir[i - 1])) /* check if last character is a path separator */ {outdir[i] = PATH_SEPARATOR; outdir[i + 1] = '\0'; } } break; case 't': if (strchr(optchar, optarg[0])) usage();@# tab_length = atoi(optarg); if (tab_length == 0 || tab_length > 8) tab_length = 4; /* default value */ break; case 'v': verbatim = TRUE; break; case '1': one_side = TRUE; break; default: usage(); break; } }@# if (optind == argc) usage(); } @ The output file has the same name as the input file but a different extension: it will append a \.{w} or, if the extension is three characters long, substitute the third character with a \.{w}, i.e. \.{.c} becomes \.{.cw}, \.{.h} becomes \.{.hw}, \.{.cpp} will be replaced by \.{.cpw} and so on (notice that for example \.{.cppp} also becomes \.{.cpw}). If the character to be changed is a \.{w}, an \.{x} is used instead of. This will be done by the function |modify_filename()|. No checking for overwriting existing files is done. @d FILE_NAME_LENGTH 80 @= FILE *in, *out; @ @= void open_files(char *); @ @c void open_files(filename) char *filename; {char buffer[DIR_LENGTH + FILE_NAME_LENGTH + 1]; if (strlen(filename) > FILE_NAME_LENGTH - 2) {fprintf(stderr, "\n File name too long.\n"); exit(-1); }@# if ((in = fopen(filename, "rt")) == NULL) {fprintf(stderr, "\n Can't open input file %s\n", filename); exit(-1); }@# strcpy(buffer, outdir); strcat(buffer, filename); modify_filename(buffer);@# if ((out = fopen(buffer, "wt")) == NULL) {fprintf(stderr, "\n Can't open output file %s\n", buffer); exit(-1); } } @*1 The output header. This is the header of the last output file. You must call \.{CWEAVE} with this file as an argument---all other processed files will be included. Additionally this `master' file will include the file \.{compiler.w}, which should contain all system dependent definitions (like \.{va\_decl} or \.{va\_arg}) not contained in the \.{CWEAVE} program. The syntax of \.{compiler.w} is \.{CWEB} syntax; please read the documentation if you have questions. You should set the environment variable |CWEBINPUTS| used by \.{CWEAVE} (and \.{CTANGLE}) to the directory where \.{compiler.w} resides. The function |_getname()| will accept forward and backward slashes as path separators if you compile under emx. However, options can be introduced with |'-'| and |'/'|, therefore paths starting with a forward slash must be enclosed in double quotes (not under \UNIX/). @= fprintf(out,@/ "\\font\\symb=cmsy10\n"@/ "\\font\\math=cmmi10\n"@/ "\\def\\ob"@/ "{\\parskip=0pt\\parindent=0pt%%\n"@/ "\\let\\\\=\\BS\\let\\{=\\LB\\let\\}=\\RB\\let\\~=\\TL%%\n"@/ "\\let\\ =\\SP\\let\\_=\\UL\\let\\&=\\AM\\let\\^=\\CF%%\n"@/ "\\obeyspaces\\frenchspacing\\tt}\n"@/ "\n"@/ "\\def\\e{\\hfill\\break\\hbox{}}\n"@/ "\\def\\{{\\relax\\ifmmode\\lbrace\\else$\\lbrace$\\fi}\n"@/ "\\def\\}{\\relax\\ifmmode\\rbrace\\else$\\rbrace$\\fi}\n"@/ "\\def\\takenone#1{\\hskip-0.1em}\n"@/ "\\let\\ZZZ=\\relax\n"@/ "\n"@/ "%s"@/ "\n"@/ "\\pageno=\\contentspagenumber \\advance\\pageno by 1\n"@/ "\\let\\maybe=\\iftrue\n"@/ "\n"@/ "\\def\\title{%s}\n"@/ "\n"@/ "@@i compiler.w\n"@/ "\n", one_side ? "\\let\\lheader=\\rheader\n" : "", title);@# if (*include_file) fprintf(out,@/ "@@i %s\n"@/ "\n", include_file);@# for (i = optind; i < argc - 1; i++) {strcpy(buffer, argv[i]); modify_filename(buffer);@# fprintf(out, "@@i %s\n", _getname(buffer)); }@# fputc('\n', out);@# @*1 Input Handling. @d BUFFER_LENGTH 500 @= void handle_input(void); @ This is a wild hack. Perhaps in a future version I will improve it. @= char buffer[BUFFER_LENGTH + 1]; @ The main routine. Characters which will be treated in a special way by \.{CWEB} and \TeX\ are handled within a great switch. @d xisspace(c) (isspace(c) && ((unsigned char)c < 0200)) @c void handle_input(void) {char *buf_p; char ch; @; line_number = 0; while (get_line()) {buf_p = buffer;@# do {ch = *buf_p;@# @;@# switch (ch) {case ' ': if (leading_blanks) {blank_count++; goto end; } break;@# case '\t': {int i = tab_length - (column % tab_length); column += i - 1; /* we'll say later |column++| */@# if (leading_blanks) {blank_count += i; goto end; }@# while (i--) fputc(' ', out); goto end; } break;@# case '{': @; break;@# case '}': @; break;@# case '/': @; break;@# case '*': @; break;@# case '\n': @; break;@# case '@@': @; break;@# case '\'': @; break;@# case '\"': @; break;@# case '\\': @; break;@# default: @; break; }@# fputc(ch, out);@# end: buf_p++; column++; } while(ch != '\n'); } } @ @= int any_input = FALSE; /* set after the first non blank character in a new section */ int brace_count = 0; int blank_count = 0;@# int in_comment = FALSE; int in_C = FALSE; int in_string = FALSE; int short_comment = FALSE; int leading_blanks = TRUE; int double_linefeed = FALSE; /* set if last character was a linefeed */ int linefeed_comment = FALSE; /* set if a comment follows a linefeed immediately */ int comment_slash = FALSE; /* set if last character was a slash */ int comment_star = FALSE; /* set if last character was a star */ int escape_state = FALSE; /* needed to check whether in string or at the end of a preprocessor line */ int before_TeX_text = FALSE; /* the state after leaving a function block */ int function_blocks = FALSE; /* set if function block algorithm is on */ @ Here comes a bunch of |if|-statements. @= @;@# if (double_linefeed && ch == '/') linefeed_comment = TRUE;@# if (double_linefeed && (ch == ' ' || ch == '\t')) leading_blanks = TRUE;@# if (ch != '\n') /* multiple linefeed ? */ double_linefeed = FALSE;@# if (!xisspace(ch)) /* whitespaces ? */ {any_input = TRUE;@# if (before_TeX_text && function_blocks) {before_TeX_text = FALSE;@# if (function_blocks == WAIT) function_blocks = TRUE; /* start at the second occurrence */ else {fputs("@@\n"@/ "\\ind=2\n\n", out); /* start a new text section */@# if (leading_blanks) {leading_blanks = FALSE;@# while (blank_count--) fputc(' ', out); blank_count = 0; } } }@# if (in_comment && leading_blanks) {leading_blanks = FALSE;@# while (blank_count--) fputc(' ', out); blank_count = 0; } }@# if (!(ch == '/' || xisspace(ch))) /* whitespace or possible start of comment ? */ {if (!(in_comment || in_C || comment_slash)) /* outside of code ? */ {in_C = TRUE;@# fputs("@@c\n", out); /* start of a new code section */@# if (leading_blanks) {leading_blanks = FALSE;@# while (blank_count--) fputc(' ', out); blank_count = 0; } }@# if (!(in_comment || comment_slash) && leading_blanks) {leading_blanks = FALSE;@# while (blank_count--) fputc(' ', out); blank_count = 0; } }@# if (comment_slash && !(ch == '*' || ch == '/')) /* start of a comment ? */ {comment_slash = FALSE; if (!in_comment) linefeed_comment = FALSE;@# fputc('/', out); }@# if (comment_star && ch != '/') /* end of a comment ? */ {comment_star = FALSE;@# fputc('*', out); }@# if (escape_state && !(ch == '\"' || ch == '\n' || ch == '\\')) /* end of string or end of preprocessor line or backslash itself ? */ escape_state = FALSE;@# @ @= if (buf_p == buffer) {if (!(in_comment || in_string)) {if (!strncmp(buf_p, "/""*@@@@*""/", 6)) {in_C = FALSE; before_TeX_text = TRUE; function_blocks = WAIT; /* switch on the algorithm */ brace_count = 0;@# if (any_input) fputs("\n@@\n"@/ "\\ind=2\n\n", out); /* start a new section */@# any_input = FALSE; *(buf_p--) = '\n'; /* the rest of the line will be ignored */ goto end; } else if (!strncmp(buf_p, "/""*@@*""/", 5)) {in_C = FALSE; before_TeX_text = TRUE; function_blocks = FALSE; /* switch off the algorithm */@# if (any_input) fputs("\n@@\n"@/ "\\ind=2\n\n", out);@# any_input = FALSE; *(buf_p--) = '\n'; goto end; } else if (!strncmp(buf_p, "/""*{*""/", 5)) {brace_count++; /* a dummy opening brace */ fputs("@@{\n", out);@# ch = '\n'; /* an end of line is simulated */ goto end; } else if (!strncmp(buf_p, "/""*}*""/", 5)) {brace_count--; /* a dummy closing brace */ fputs("@@}\n", out);@# if (!brace_count && function_blocks) /* end of function block reached ? */ {in_C = FALSE; before_TeX_text = TRUE;@# break; }@# ch = '\n'; goto end; } else if (!strncmp(buf_p, "/""*{}*""/", 6)) {fputs("@@{@@}\n", out);@# /* a dummy function body */ ch = '\n'; goto end; } } } @ @= if (in_comment) fputc('\\', out); else if (in_string) break; else if (function_blocks) {brace_count++; in_C = TRUE; } @ @= if (in_comment) {fputs("{\\char64}", out); goto end; } else /* \.{CWEB} needs \.{@@@@} to output \.{@@} */ fputc('@@', out); @ @= if (!in_comment) {if (*(buf_p + 1) == '\"' && *(buf_p + 2) == '\'') escape_state = TRUE; /* this catches |'"'| */ } @ @= if (!in_comment) /* start or end of a string ? */ {if (escape_state) escape_state = FALSE; else in_string = TRUE - in_string; } @ @= if (in_comment) {fputs("{\\symb\\char110}", out); goto end; } else escape_state = TRUE - escape_state; /* continuation of preprocessor line or an escape character */ @ @= if (in_comment) fputc('\\', out); else if (in_string) break; else if (function_blocks) {brace_count--; if (!brace_count) /* end of function block reached ? */ {in_C = FALSE;@# before_TeX_text = TRUE; break; } } @ @= if (comment_star) {@; } else if (comment_slash) {@; } else {comment_slash = TRUE;@# goto end; } @ @= comment_star = FALSE; leading_blanks = FALSE;@# if (!short_comment) {in_comment = FALSE; /* end of comment */@# if (!in_C) {linefeed_comment = FALSE;@# if (verbatim) @q { @> fputs("*""/}", out);@# if (*(buf_p + 1) == '\n') /* end of line ? */ fputs("\\e{}%", out);@# goto end; } if (in_C && verbatim) {if (linefeed_comment) {linefeed_comment = FALSE;@# fputs("*""/@@>", out); if (*(buf_p + 1) == '\n' && !user_linefeed) fputs("@@/", out); goto end; } else @q { @> fputc('}', out);@# }@# linefeed_comment = FALSE;@# if (in_C || verbatim) fputc('*', out); else goto end; } else fputc('*', out); @ @= comment_slash = FALSE;@# if (!short_comment) {in_comment = TRUE; short_comment = TRUE; /* start of a short comment */@# if (!in_C && verbatim) {fputs("{\\ob{}", out); @q } @> if (leading_blanks) {leading_blanks = FALSE;@# while (blank_count--) fputc(' ', out); blank_count = 0; } fputs("//", out);@# goto end; }@# if (in_C && verbatim) {if (leading_blanks || linefeed_comment) {linefeed_comment = TRUE;@# if (!user_linefeed) fputs("@@/", out); @q { @> fputs("@@t}\\8{\\ob{}", out); @q } @> /* this cryptic command starts a comment line without indentation */ if (leading_blanks) {leading_blanks = FALSE;@# while (blank_count--) fputc(' ', out); blank_count = 0; } fputs("//", out); } else fputs("//{\\ob{}", out);@# @q } @> goto end; }@# if (in_C || verbatim) fputc('/', out); else goto end; } else fputc('/', out); @ @= if (comment_slash) {comment_slash = FALSE;@# if (in_comment && !short_comment) /* uuh, ooh, for people who `comment out' code with comments instead of using \.{\#if 0} and \.{\#endif} for example---this non-ANSI behaviour would cause bad formatted code and is therefore not supported; the program exits */ {fprintf(stderr, " Error line %d: Nested comments not supported\n", line_number); exit(-1); }@# if (!short_comment) {in_comment = TRUE; /* start of comment */@# if (!in_C && verbatim) {fputs("{\\ob{}", out); @q } @> if (leading_blanks) {leading_blanks = FALSE;@# while (blank_count--) fputc(' ', out); blank_count = 0; } fputs("/""*", out);@# goto end; }@# if (in_C && verbatim) {if (leading_blanks || linefeed_comment) {linefeed_comment = TRUE; if (!user_linefeed) fputs("@@/", out); @q { @> fputs("@@t}\\8{\\ob{}", out); @q } @> if (leading_blanks) {leading_blanks = FALSE;@# while (blank_count--) fputc(' ', out); blank_count = 0; } fputs("/""*", out); } else fputs("/""*{\\ob{}", out);@# @q } @> goto end; }@# if (in_C || verbatim) fputc('/', out); else {fputs(" ", out);@# goto end; } } else fputc('/', out); } else {comment_star = TRUE;@# goto end; } @ @= blank_count = 0;@# if (!in_comment && in_C) {if (double_linefeed == FALSE) {double_linefeed = TRUE; if (escape_state) {escape_state = FALSE; /* continuation of a preprocessor line */ leading_blanks = TRUE;@# if (in_string) fputc('\n', out); else fputs("\n@@/", out); goto end; }@# if (!leading_blanks && user_linefeed) fputs("@@/", out); } else if (double_linefeed == TRUE) {double_linefeed = DONE; /* blank lines in the input will be output as little white space between code lines */@# fputs(at_nmb, out); } }@# leading_blanks = TRUE;@# if (short_comment) {short_comment = FALSE; in_comment = FALSE; double_linefeed = TRUE;@# if (verbatim) {if (linefeed_comment && in_C) fputs("@@>", out); else @q { @> fputc('}', out); }@# if (!in_C) fputs("\\e{}%", out); else if (linefeed_comment && verbatim) fputs("@@/", out);@# linefeed_comment = FALSE; }@# if (in_comment && in_C && verbatim && linefeed_comment) { @q { @> fputs("@@>@@/\n@@t}\\8{\\ob{}", out); @q } @> /* continuation of a comment line without indentation */ goto end; }@# if (in_comment && verbatim) /* Both \.{CWEAVE} and \TeX\ need an input at the beginning of a line to prevent leading blanks be swallowed while in verbatim mode; this will be the macro \.{\\e} which causes a linebreak */ {fputs("\n\\e{}", out); goto end; } @ All other special characters will be substituted with proper sequences \TeX\ can understand. @= if (in_comment) {switch (ch) {case '#': fputs("{\\#}", out); break;@# case '$': fputs("{\\$}", out); break;@# case '%': fputs("{\\%}", out); break;@# case '&': fputs("{\\AM}", out); break;@# case '_': fputs("{\\_}", out); break;@# case '^': fputs("{\\^{}}", out); break;@# case '\\': fputs("{\\symb\\char110}", out); break;@# case '~': fputs("{\\~{}}", out); break;@# case '|': fputs("{\\symb\\char106}", out); break;@# case '<': fputs("{\\math\\char60}", out); break;@# case '>': fputs("{\\math\\char62}", out); break;@# default: fputc(ch, out); break; }@# goto end; } @ Because the index macros defined in \.{cwebmac.tex} don't append a dot, we have to redefine \.{\\ZZZ}. @= fprintf(out,@/ "\n"@/ "@@*Index.\n"@/ "\\let\\ZZZ=\\takenone\n"); @ @= void usage(void); @ @c void usage(void) {fprintf(stderr,@/ "Usage: c2cweb [switches] csourcefile(s) | @@responsefile(s)"@/ "\n"@/ "\n possible switches:"@/ "\n"@/ "\n -b \"title\" set title"@/ "\n -l use input linefeeds"@/ "\n -o dirname set output directory (must already exist)"@/ "\n -f filename use this file as a format file"@/ "\n -t tablength set tabulator length (default 4)"@/ "\n -v verbatim mode"@/ "\n -1 one-sided output"@/ "\n"@/ "\n");@# exit(-1); } @ @= void modify_filename(char *); @ If a response file is expanded, trailing blanks can occur which will be ignored here. The same happens if a filename with trailing blanks is enclosed in quotes. @c void modify_filename(name) char *name; {char *p; if ((p = strrchr(name, '.')) != NULL) {p++; if (*p && *p != ' ') p++; if (*p && *p != ' ') p++; if (*p != 'w') *p = 'w'; else *p = 'x'; p++; *p = '\0'; } else strcat(name, ".w"); } @ @= int line_number = 0; int column; @ @= char *get_line(void); @ |get_line()| gets the next line and removes all trailing blanks or tabs. @c char *get_line(void) {char *p; int i = BUFFER_LENGTH; if ((p = fgets(buffer, BUFFER_LENGTH + 1, in)) != NULL) {while (i--) {if (*(p++) == '\n') break; }@# p--; p--; while ((*p == ' ' || *p == '\t') && p >= buffer) p--; *(p + 1) = '\n'; *(p + 2) = '\0';@# line_number++; column = 0; } return(p); } @ @= char tempbuf[2 * FILE_NAME_LENGTH + 1]; @ @= char *protect_underlines(char *); @ This function is needed for the names of named sections, i.e. file names. @c char *protect_underlines(p) char *p; {char *q; q = tempbuf;@# do {if (*p == '_') *(q++) = '\\'; *(q++) = *p; } while (*(p++));@# return tempbuf; } @ @= #ifndef __EMX__ char *_getname(char *); #endif @ For Linux and other \UNIX/-systems the function |_getname()| must be defined. @c #ifndef __EMX__ char *_getname(char *path) {char *p; p = strrchr(path, '/'); return p == NULL ? path : (p + 1); } #endif @* Hints and Tricks. Some words are reserved by \.{CWEAVE} you would like to use. Here is a small list of them (see the manual for a complete one): \in \.{error}, \.{line}, \.{try}, $\ldots$ To make a word unreserved write a line \in \.{@@s} \\{ident} \\{x} into \.{compiler.w} where \\{ident} is the reserved word. Some words you would expect \.{CWEAVE} knows actually are not in its memory: \in \.{va\_arg}, \.{va\_end}, \.{va\_start}, $\ldots$ To make a word \\{ident1} reserved you should write \in \.{@@s} \\{ident1} \\{ident2} into \.{compiler.w}. Now \\{ident1} will behave as \\{ident2}; for example \in \.{@@s va\_decl va\_dcl} causes \.{CWEAVE} to treat \.{va\_decl} as it treats \.{va\_dcl}. Bear always in mind that \.{CWEAVE} has been developed for use with \UNIX/ and not for DOS or OS/2. Weird non-ANSI constructions like \in \&{void \_FAR} |*| \&{\_FARFUNC \_Cdecl} \\{memcpy} $\ldots$ will cause some, hmmm, troubles if you use the original \.{CWEAVE} program. Until now it's also a bit difficult to program for Windows\tm\ or the Presentation Manager\tm, because you have to include every structure and constant you use in your program manually, which can be a tedious work. Do not use the same name for a structure and an instance of it. This means that you must avoid things like this: \in \&{struct foo} \\{foo;} \.{CWEAVE} would be confused totally. The same applies to all identifiers which will be used as reserved and as unreserved words at the same time (This usually will not affect identical names of variables and functions. But in this case the reader of your program will get confused). Most of the \CEE/ constants are written in upper case, and \.{CWEAVE} writes them in typewriter type. But some constants like \.{\_Windows} or \.{\_\_cplusplus} are mixed or lower case, and \.{CWEAVE} will use italic type instead of. Look at \.{compiler.w} again to see a workaround how to get a correct output (Note: underline characters are converted to `x' in the \TeX\ control string). Nested comments are not supported; the program aborts with an error message. If you have unbalanced braces due to \.{\#ifdef foo} $\ldots$ \.{\#endif} constructions, you can keep \.{c2cweb} and your editor happy if you write a \.{/*\{*/} or \.{/*\}*/} command where needed; otherwise memory of \.{CWEAVE} can overflow. \.{c2cweb} replaces these commands with the equal \.{CWEB} constructions (only defined in this package's modified \.{CWEAVE} version!). Another trick with preprocessor conditionals: \in \.{/*@@*/} \in \&{\#ifdef} \.{foo} \in\in \&{int} \\{func1} $\{\,\ldots\,\}$ \in \&{\#endif} \in \.{/*@@@@*/} Without \.{/*@@*/} the \&{\#endif} instruction would be written into the next section. The \.{/*\{\}*/} command proved to be useful in cases like this: \in \&{\#if} \.{\_\_STDC\_\_} \in\in \&{void} \\{func1}(\&{int} \\{a}) \in \.{/*\{\}*/} \in \&{\#else} \in\in \&{void} \\{func1}(\\{a}) \in\in\in \&{int} \\{a}; \in \&{\#endif} \in $\{\,\ldots\,\}$ It is essentially the same as \in \.{/*\{*/} \in \.{/*\}*/} but will not start a new section (Thus it is not necessary to insert a \.{/*@@*/} before the actual function). @* Index. @q end of c2cweb.w @>