/*9%&LaTeX\input{doccode.tex}\chardef\bslash=`\\\documentclass{report}\begin{document}\begin{titlepage}\vspace*{3in}{\Huge\bf Docstrip}\vspace{4pt}Version 1.0\vspace*{3in}{\small This document and the program it describes are 	copyright \copyright\ 1998 by Jeffrey Lassahn.  I give permission to	distribute them to anyone as long as they are not modified.	Modified versions may also be distributed as long as they bear this notice	and clearly state the name of the person who made the modification, and the	date of the modification.  I make no warranties of any sort regarding the	reliability or usefulness of either this document or this computer program.}	\end{titlepage}\chapter{Docstrip}\section{How to Use}Docstrip is a simple program that extracts specially marked comments from Csource files and outputs the comment text to another file, or the console.This allows detailed documentation to be placed in the source files and extracted to produce documentation files or manual pages.  The docstripsource code is documented this way, using \LaTeX\ as the documentation language.The command line format for Docstrip is:\begin{quote}\textc{docstrip [-v<verboseness>] [-s<string>] [-e<string>] {\linebreak}[-f<file name>] [-t<tabsize>] <files>}\end{quote}These options do the following things.\begin{description}\item[-v] Sets how detailed the extracted documentation should be. Each comment which begins with 	\textc{{/}{*}{0}} through 	\textc{{/}{*}{9}} will be extracted by docstrip if the number on the comment is equal to or greater than the number specified by the -v switch.  The default if -v is not specified is $0$.  If -v is given \emph{without} a number, all comments will be extracted, and the source code will be sent to the output as well.  This is not quite the same as just copying the original source file.\item[-s] specifies the text that should be inserted before a block of literal source code when the -v switch is used.  If -v is not used or is used with a number then -s has no effect.\item[-e] specifies the text that should be inserted after a block of literal source code when the -v switch is used.  If -v is not used or is used with a number then -e has no effect.\item[-f] specifies the filename to write the output of the program to. if more than one file is to be processed, they can be sent to seprate output files with a line such as 	\textc{docstrip -fout1 in1 -fout2 in2 -fout3 in3}.\item[-t] specifies how many spaces a tab character represents.  Docstrip always replaces tab characters with spaces in its output.  The default value is $4$.\end{description}To get the \LaTeX\ source for the help page to Docstrip, type\begin{quote}\textc{docstrip -v9 -fdocstrip.tex docstrip.c}\end{quote}or, to get the help page along with the implementation notes for theprogram, type\begin{quote}\textc{docstrip -v -fdocstrip.tex -s\bslash begincode -e\bslash endcode docstrip.c}\end{quote}The \TeX\ file which results from either of these expects to have access toanother file named \textc{doccode.tex}, which includes some macros usefulfor formatting C code including\textc{\bslash begincode} and \textc{\bslash endcode}.*//*0\section{Source Code Notes}The following is a function by function description of the interesting parts of the source.*/#include <stdio.h>char *startcode=NULL;char *stopcode=NULL;#define NORMSTATE 0#define QUOTESTATE 1#define COMMENTSTATE 2#define DOCSTATE 10FILE *outfile;int verbose=10;int tabnum=4;/*0\subsection{main}The main function processes the command line arguments, and calls dofile()for each file specified on the commend line.*/int main(int argc,char *argv[]){FILE *fp;int i;		outfile=stdout;	for (i=1;i<argc;i++) {		if (argv[i][0]=='-') {			setoptions(&(argv[i][1]));		}		else {			fp=fopen(argv[i],"rb");			if (fp) {				fprintf(stderr,"starting file %s\n",argv[i]);				if (dofile(fp))					fprintf(stderr,"unexpected end of file in %s\n",argv[i]);				fclose(fp);			}			else {				fprintf(stderr,"file \"%s\" couldn't be opened\n",argv[i]);				return 1;			}		}	}	fprintf(stderr,"finished\n");	return 0;}/*0\subsection{setoptions}The setoptions function parses all command line switches.  Switchesand source files can occur in any order.*/int setoptions(char *opt){FILE *f;	switch(opt[0]) {		case 'v':			if ((opt[1]>='0')&&(opt[1]<='9'))				verbose=opt[1]-'0'+10;			else				verbose=0;			break;		case 's':			startcode=&(opt[1]);			break;		case 'e':			stopcode=&(opt[1]);			break;		case 'f':			if (outfile!=stdout)				fclose(outfile);			f=fopen(&(opt[1]),"w");			if (!f) {				fprintf(stderr,"could not open file: %s\n",&(opt[1]));				exit(1);			}			outfile=f;			break;		case 't':			tabnum=atoi(&(opt[1]));			break;		default:			fprintf(stderr,"Unknown option: %s\n",opt);			exit(1);	}	return 0;}/*0\section{outchar}The outchar function generates all actual output, except stderr messages.It uses the \textc{state} variable to decide whether each charater isworthy of being output.  It Also calls outstart and outstop when itsees that it has switched from writing code to writing documenation or\textit{vice versa}.  In order to avoid getting lots of extraneousoutstart and outstop calls it makes sure a real character is going to beoutput before calling either if them.*/static int state=NORMSTATE;static int codestate=1;int outchar(int c){	switch (state) {		case NORMSTATE:		case QUOTESTATE:		case COMMENTSTATE:			if (verbose>0)				return 0;			if (codestate!=0) {				outstart();				codestate=0;			}			myputc(c,outfile);			return 0;/* DOCSTATE and above */		default:			if (verbose>state)				return 0;			if (codestate!=1) {				outstop();				codestate=1;			}			myputc(c,outfile);			return 1;	}		}/*0\section{syncstate}This function is called at the end of each source file to be sure thatthe last block of code in the file gets ended with a call to outstop.*/void syncstate(void){	if (codestate!=1) {		outstop();		codestate=1;	}}/*0\section{myputc}This is just a shell around putc which expands tabs.*/int myputc(int c,FILE *f){int i;	if (c=='\t') {		for (i=0;i<tabnum;i++)			putc(' ',f);	}	else {		putc(c,f);	}	return 1;}		/*0\section{ourstart}This writes the string specified by -s.  It is called whenever we're aboutto begin writing source code instead of documentation.*/int outstart(void){	if (startcode) {		putc('\n',outfile);		fputs(startcode,outfile);	}	return 1;}/*0\section{outstop}This writes the string specified by -e.  It is called whenever we're aboutto stop writing source code.*/int outstop(void){	if (stopcode)		fputs(stopcode,outfile);	return 1;}/*0\section{dofile}This is the function which parses each input file.  It mostly passescharacters through unmodified to outchar, except when it detects abackslash, forward slash, or double quote.  We're really only interestedin finding the comments marked by \textc{{/}{*}}, but we've got tounderstand quote marks because comments can't begin inside a quoted string,and we've got to understand backslashes because a quote mark precededby a backslash idoesn't terminate a string.Since this program already has to keep track of strings, it would be easyto modify outchar to do something special with string constants.*/int dofile(FILE *fp){int c;	while ((c=getc(fp))!=EOF) {		switch (c) {			case '\\':				if (dobackslash(fp)==EOF)					return 1; //unexpected end of file				break;			case '\"':				if (doquote(fp)==EOF)					return 1; //unexpected EOF				break;			case '/':				if (doslash(fp)==EOF)					return 1;				break;			default:				outchar(c);		}	}	syncstate();	return 0;}/*0\section{dobackslash}This function processes backslash escape characters.  It basically just prints out the next character after the backslash, while making sure itisn't interpreted by the other parsing routines.*/int dobackslash(FILE *fp){int c;	outchar('\\');	c=getc(fp);	if (c==EOF)		return EOF;	return outchar(c);}/*0\section{doquote}This function outputs string constants by scanning over them looking for theend quote character.  Note that it also looks for backslashes and callsdobackslash where appropriate.*/int doquote(FILE *fp){int c,oldstate;	oldstate=state;	state=QUOTESTATE;	outchar('\"');	while ((c=getc(fp))!='\"') {		switch (c) {			case '\\':				if (dobackslash(fp)==EOF)					return EOF; //unexpected end of file				break;			case EOF:				return EOF;			default:				outchar(c);		}	}	outchar(c);	state=oldstate;	return 0;}/*0\section{doslash}Slash characters are only interesting if they start comments.  Therefore,we look ahead to the next character.  If it's not an asterisk we just output both characters and keep going.*/int doslash(FILE *fp){int c;	c=getc(fp);	if (c==EOF)		return EOF;	if (c=='*') {		if (EOF==docomment(fp))			return EOF;	}	else {		outchar('/');		outchar(c);	}	return 0;}/*0\section{docomment}If this is a comment marked with a digit for stripping, we call dodocs tohandle it.  Otherwise, we output the characters we have been holding inwait, and scan through until an end of comment sequence is found.*/ int docomment(FILE *fp){int c,oldstate,cend;	cend=0;		oldstate=state;	state=COMMENTSTATE;	c=getc(fp);	if ((c>='0')&&(c<='9')) {		return dodocs(fp,c-'0');	}	else {		outchar('/');		outchar('*');	}		while (1) {		if (c==EOF)			return EOF;		outchar(c);				if ((c=='/')&&(cend==1)) {			break;		}		if (c=='*')			cend=1;				else			cend=0;		c=getc(fp);	}			state=oldstate;	return 0;}/*0\section{dodocs}This function extracts comments marked as documntation.  It outputs characters until it finds an end of comment mark.  It doesn't outputeither the comment start marks or the end of comment marks.  It willalso ignore a newline which immediately follows the end of comment character.*/int dodocs(FILE *fp,int n){int c,oldstate;int cend=0;	oldstate=state;	state=DOCSTATE+n;	while (1) {		c=getc(fp);		if (c==EOF)			return EOF;		if (cend==1) {			if (c=='/') {				/* strip a return char, if there is one */				c=getc(fp);				if ((c!='\n')&&(c!=EOF))					ungetc(c,fp);				break;			}			else {				outchar('*');			}		}		if (c=='*') {			cend=1;				}		else {			cend=0;			outchar(c);				}	}	state=oldstate;	return 0;}/*9\end{document}*/