ftp.nice.ch/pub/next/unix/developer/docgen.0.3.2.s.tar.gz#/docgen-0.3.2/parsers.c

This is parsers.c in view mode; [Download] [Up]

/*
  docgen  Objective C Document Generator
  Copyright (C) 1995  Bill Bereza.
  
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  
  Email:
  berezaw@river.it.gvsu.edu

  S-mail:
  Bill Bereza
  9526 Judson Rd.
  Ravenna, MI 49451-9427

  */
/* docgen	Objective C Document Generator
 * Copyright (c) 1994 Bill Bereza
 *
 *	$Log:	parsers.c,v $
 * Revision 1.39  96/01/07  01:30:27  berezaw
 * additions for 0.3.2
 * 
 * Revision 1.38  95/07/26  18:56:08  berezaw
 * *** empty log message ***
 * 
 * Revision 1.37  95/07/26  17:50:28  berezaw
 * got before and after comments parsing to work
 * 
 * added findrealchr() function
 * 
 * Revision 1.36  95/07/26  16:17:32  berezaw
 * 0.3.1
 * 
 * Revision 1.35  95/07/21  21:40:17  berezaw
 * *** empty log message ***
 * 
 * Revision 1.34  95/07/16  19:19:55  berezaw
 * 
 * bug fix found by pburka
 * 
 *
 * Revision 1.33.1  95/04/26  13:34:00  pburka
 * added an input check to make sure there's something
 * in the file.
 *
 *
 * Revision 1.33  95/03/02  18:20:50  berezaw
 * added gathermethod() to get each method as a block
 * create COMMINTERN macro to do the check and printing
 * of comments for a method
 * created GETNEWLINE macro for getting a new line
 * of text from the queue
 * 
 * Revision 1.32  95/03/02  03:44:47  berezaw
 * using GNU regex library
 * rewrote recmp() as docgen_recmp()
 * added GPL stuff to each file
 * increase rev. to 0.1.9
 * 
 * Revision 1.31  95/02/14  12:36:44  berezaw
 * 
 * fixed new line getting in parsemethods
 * 
 * Revision 1.30  95/02/13  19:05:34  berezaw
 * 
 * will now skip to next line on end of comments
 * 
 * Revision 1.29  95/02/02  18:32:14  berezaw
 * 0.1.7, fixed problems with skipping lines
 * in odd classes
 * 
 * Revision 1.28  95/02/02  17:57:38  berezaw
 * didn't skip spaces often enough
 * 
 * Revision 1.27  95/02/02  15:21:59  berezaw
 * 0.1.6
 * 
 * Revision 1.26  95/02/01  18:49:59  berezaw
 * 
 * 0.1.6
 * 
 * Revision 1.25  95/02/01  16:41:36  berezaw
 * better parsing for i-vars in parseivars
 * 
 * Revision 1.24  95/01/31  18:28:40  berezaw
 * 
 * added 0.1.5 stuff
 * 
 * Revision 1.23  95/01/30  14:11:20  berezaw
 * fixed fancy text
 * 
 * Revision 1.22  95/01/30  13:39:47  berezaw
 * *** empty log message ***
 * 
 * Revision 1.21  95/01/30  13:32:23  berezaw
 * added loop to check for nested fancy text
 * 
 * Revision 1.20  95/01/29  02:52:44  berezaw
 * (parsecomments) started support for word decorations
 * 
 * Revision 1.19  95/01/29  01:52:52  berezaw
 * reused code from parsemethods to create general
 * comment printing function to handle italicizing
 * variable names, and to eventually handle fancy
 * text
 * 
 * Revision 1.18  95/01/27  00:59:35  berezaw
 * *** empty log message ***
 * 
 * Revision 1.17  95/01/27  00:31:46  berezaw
 * added '-x' option to hide methods beginning with '_'
 * 
 * Revision 1.16  95/01/26  15:32:56  berezaw
 * added BSD, SYSV and HPUX checks
 * 
 * Revision 1.15  95/01/26  15:27:35  berezaw
 * now checks for comments for method types
 * 
 * Revision 1.14  95/01/26  05:35:22  berezaw
 * major fixing in parseivars and parsemethodtypes
 * added dcmp()
 * changed parsemethods to handle multiple classes,
 * to use a list that can be sorted,
 * and to handle method declarations better
 * 
 * Revision 1.13  94/11/28  12:08:35  berezaw
 * *** empty log message ***
 * 
 *
 *	$Id: parsers.c,v 1.39 96/01/07 01:30:27 berezaw Exp $
 */

#include "parsers.h"

int docgen_recmp(char *expr, char *line)
{
	static struct re_pattern_buffer expbuf;
	static bool set=no;

	int matret;
	
	if(!set) {
		set=yes;
		re_syntax_options=0;
		re_syntax_options=DOCGEN_SYNTAX;

		expbuf.buffer=(char *)malloc(DOCGEN_REBUF);
		expbuf.allocated=DOCGEN_REBUF;
		expbuf.translate=NULL;
	}

	if(re_compile_pattern(expr, strlen(expr), &expbuf)) {
		fprintf(stderr, "docgen_recmp:error compiling expression\n");
		return -1;
	}

	switch((matret=re_match(&expbuf, line, strlen(line), 0, 0)))
	{
	  case -1:
		return 1;
	  default:
		return 0;
	}
}

#define DYNLINESEP "||--||"

int parseivars(FILE *RTFf, FILE *Hf, char *FQh)
{
	char *hline, *stringptr, *idescstr, *as;
	DSTRING *dynstring;
	
#ifndef NODEBUG
	if(V_USELESS)
	  fprintf(stderr,"parseivars(FILE, FILE, %s)\n", FQh);
#endif

#ifdef OBJC
	[myForm putIHeader];
#else
	fputihead(RTFf);			/* Setup margins, font and header for I-Vars */
#endif
	
	hline=fgetdline(Hf);
	
	/* initialize it now, so that we know we can safely free it later */
	dynstring=initdstr();
	
	/* parse through each ivar line */
	while(docgen_recmp(IVAREND, hline) && !feof(Hf)) {
		if(!strchr(hline, ';')) {
			free(hline);
			hline=fgetdline(Hf);
			continue;
		}
		idescstr=strstr(hline,"//");
		if(!idescstr)
		  idescstr=strstr(hline,"/*");
		
		if(idescstr)
		  idescstr++;
		
		stringptr=strtok(hline, IVTOK);	/* split line up into tokens
										 * seperated by IVTOK
										 */
		
#ifndef NODEBUG
		if(V_USELESS)
		  fprintf(stderr,"%s\n\n",stringptr);
#endif
		
		/* parse through each ivar on a line */
		while((stringptr!=NULL) && docgen_recmp(WHITEXP, stringptr)) {
			char *varp=&stringptr[strlen(stringptr)-1];
			char *stopper=stringptr;
			
			SKIPSPACE(stringptr);
			if(*stringptr == IDESCCHAR)
			  break;

			while(isspace(*varp) && (varp!=stopper))
			  varp--;
			if((*varp!='{') && (*varp!='}')) {
				while(*varp) {
					while(isspace(*varp) && (varp!=stopper))
					  varp--;
					while(!isspace(*varp) && (varp!=stopper))
					  varp--;
					while(isspace(*varp) && (varp!=stopper))
					  varp--;
					if(((*varp==',') || (*varp==':')) && (varp!=stopper))
					  varp--;
					else {
						varp++; *varp='\0';
					}
				}
				varp++;
			}
			else if(*varp!='}') {
				*(varp+1)='\0';
				varp=stringptr;
			}
			if(!strchr(stringptr,'{') && !strchr(stringptr,'}')) {
				while(*stringptr) {
					fputc(*stringptr++, RTFf);
				}
				fputc('\t', RTFf);
			}
			stringptr=varp; varp=&stringptr[strlen(stringptr)-1];
			SKIPSPACE(stringptr);
			if((*varp!='{') && (*varp!='}')) {
#ifdef OBJC
				[myForm makeBold];
				fprintf(RTFf, "%s", stringptr);
				[myForm unBold];
				[myForm putLine:";"];
#else
				fputbold(RTFf, stringptr);
				fputline(RTFf, ";");
#endif
			}
			else
#ifdef OBJC
			  [myForm putLine:" "];
#else
			  fputline(RTFf, " ");
#endif
			if(*stringptr=='*')
			  stringptr++;

			if((*varp!='{') && (*varp!='}')) {
				// daddchar(dynstring, IFEOL);
				// daddchar(dynstring, '\n');
				// dstrarrcat(dynstring, IFSIZE);
				dstrarrcat(dynstring, stringptr);
				daddchar(dynstring, '\t');
				if(idescstr != NULL) {
					++idescstr;
					SKIPSPACE(idescstr);
					as=strstr(idescstr,"*/");
					if(as)
					  *as='\0';
					dstrarrcat(dynstring, idescstr);
				}
				else
				  dstrarrcat(dynstring, "No Description");
				dstrarrcat(dynstring,DYNLINESEP);
				// daddchar(dynstring, IFEOL);
				// daddchar(dynstring, '\n');
				
				while(*stringptr) {
					SKIPSPACE(stringptr);
					daddchar(GLOB_istring, '#');
					while(!isspace(*stringptr) && (*stringptr != ',') && *stringptr) {
						daddchar(GLOB_istring, *stringptr++);
					}
					daddchar(GLOB_istring, '#');
					if(*stringptr)
					  stringptr++;
				}
			}
			stringptr=strtok(NULL, IVTOK);
		}
		free(hline);
		hline=fgetdline(Hf);
	}

	/* print out the ivar description lines */
#ifdef OBJC
	[myForm putIVarDescription];
	[myForm putLine:" "];
	[myForm changeFontSize:28];
#else
	fputivardesc(RTFf);
	fputline(" ");
	fchfontsize(RTFf,28);
#endif
	
	stringptr=arrdstr(dynstring);
  {
	  char *zptr=strtok(stringptr, DYNLINESEP);

	  while(zptr) {
#ifdef OBJC
		  [myForm putLine:" "];
		  [myForm putLine:zptr];
#else
		  fputline(RTFf," ");
		  fputline(RTFf, zptr);
#endif
		  zptr=strtok(NULL,DYNLINESEP);
	  }
  }
	// fprintf(RTFf,"%s",stringptr);
	if(!strlen(stringptr))
#ifdef OBJC
	  [myForm putLine:"No additional instance variables declared."];
#else
	  fputline(RTFf, "No additional instance variables declared.");
#endif
	freedstr(dynstring);
	free(stringptr);
	
	if(feof(Hf)) {
		free(hline);
		if(V_ERR)
		  fprintf(stderr,"Unexpected end of file in %s\n", FQh);
		return -1;
	}
	free(hline);
	
#ifndef NODEBUG
	if(V_USELESS)
	  fprintf(stderr, "parseivars: returning\n");
#endif
	
	return 0;
}

void parsemethodtype(FILE *Hf, FILE *RTFf, char *hline)
{
	char *stringptr, *descstr, *as;
	
#ifndef NODEBUG
	if(V_USELESS)
	  fprintf(stderr, "parsemethodtype(FILE *, FILE *)\n");
#endif
	
	descstr=NULL;
	as=NULL;
	/* parse method types until we get to a line
	 * matching DONINT expression
	 */
	while(docgen_recmp(DONEINT, hline) && !feof(Hf)) {
		
#ifndef NODEBUG
		if(V_USELESS)
		  fprintf(stderr, "%s\n", hline);
#endif
		if((!strncmp(hline,"/*",2)) || (!strncmp(hline, "//",2))) {
			descstr=hline+2;
			
			as=strstr(descstr,"*/");
			if(as)
			  *as='\0';

#ifdef OBJC
			[myForm putLine:" "];
			[myForm putLine:descstr];
#else
			fputline(RTFf,"\n");
			fputline(RTFf,descstr);
#endif

			fprintf(RTFf,"\t");
		}
		
		if(!docgen_recmp(METHEXP, hline) && (GLOB_undoc ? docgen_recmp(UNDOC, hline) : 1)) { /* If this is a method expression */
			stringptr=hline;
			SKIPSPACE(stringptr);
			if(*stringptr) {
#ifdef OBJC
				[myForm putFont:1 Char:stringptr[0]];
#else
				fputfchar(RTFf, 1, stringptr[0]); /* put the + or - char */
#endif
				stringptr++;
			}
			SKIPSPACE(stringptr);
			if(*stringptr =='(') { /* Skip past the type */
				SKIPPARENS(stringptr);
			}
#ifdef OBJC
			[myForm changeFont:0];
#else
			fchangefont(RTFf, 0);
#endif
			SKIPSPACE(stringptr);
			while(*stringptr != ':' && !isspace(*stringptr) && *stringptr != ';' && *stringptr)
			  fputc(*stringptr++, RTFf);
			if(*stringptr && *stringptr!=';') {
				fputc(*stringptr++, RTFf);
			}
			
			
			while(*stringptr != ';' && *stringptr) {
				SKIPSPACE(stringptr);
				if(*stringptr=='(') { /* And not all argyments have a type */
					/* And we don't need the
					 * type for the list of
					 * arguments
					 */
					SKIPPARENS(stringptr);
				}
				SKIPSPACE(stringptr);
				while(!isspace(*stringptr) && *stringptr && *stringptr!=';' && (*stringptr!=':'))
				  stringptr++;
				SKIPSPACE(stringptr);
				
				while(*stringptr != ':' && *stringptr && *stringptr != ';')
				  fputc(*stringptr++, RTFf);
				if(*stringptr && *stringptr != ';')
				  fputc(*stringptr++, RTFf);
				
/*				fputc(' ', RTFf); */
			}
#ifdef OBJC
			[myForm putLine:" "];
#else
			fputline(RTFf," ");
#endif
			fprintf(RTFf, "\t");

		} /* if */
		free(hline);
		hline=fgetdline(Hf);
	} /* while */
	
	free(hline);
	
#ifndef NODEBUG
	if(V_USELESS)
	  fprintf(stderr, "parsemethodtype: returning\n");
#endif
}

int dcmp(void *a, void *b)
{
	char *ca=(*(char **)a);
	char *cb=(*(char **)b);
	
	SKIPSPACE(ca);
	SKIPSPACE(cb);
	if(*ca!=*cb)
	  return *ca-*cb;
	if(*ca)
	  ca++;
	if(*cb)
	  cb++;
	SKIPSPACE(ca);
	SKIPSPACE(cb);
	if(*ca=='(') {
		SKIPPARENS(ca);
	}
	if(*cb=='(') {
		SKIPPARENS(cb);
	}
	SKIPSPACE(ca);
	SKIPSPACE(cb);
	return strcmp(ca, cb);
}

void parsecomments(FILE *RTFf, char *mline, DSTRING *dynstr)
{
	char blank[]="#";
	char punchar[3], tline[255];
	char *hline, *stringptr, *opstrptr=NULL;
	
	stringptr=arrdstr(GLOB_istring);
	
	if(dynstr)
	  opstrptr=arrdstr(dynstr);
	else
	  opstrptr=blank;

#ifndef NODEBUG
	if(V_USELESS)
	  fprintf(stderr,"\n\n%s\n\n", stringptr);
#endif

  {
	  char *z=strstr(mline, "*/");
	  if(z) {
		  z[0]=' ';
		  z[1]=' ';
	  }

	  if(!docgen_recmp("^ *\\* *", mline)) {
		  z=strstr(mline, "*");
		  if(z) {
			  z[0]=' ';
		  }
	  }  
  }
	hline=strtok(mline, DESCRIPTOK);
	while(hline!=NULL) {
		if(strrchr(PUNCS,hline[strlen(hline)-1]) != NULL) {
			punchar[0]=hline[strlen(hline)-1];
			punchar[1]=' ';
			punchar[2]='\0';
			hline[strlen(hline)-1]='\0';
			
#ifndef NODEBUG
			if(V_USELESS)
			  fprintf(stderr,"%s\n", hline);
#endif
			
		}
		else {
			punchar[0]=' ';
			punchar[1]='\0';
		}
		strcpy(tline, "#");
		strcat(tline, hline);
		strcat(tline, "#");
		if(!strstr(stringptr, tline) && !strstr(opstrptr, tline)) {
			bool bolded=no;
			bool italicized=no;
			bool underlined=no;
			bool fancyd=no;

			if(GLOB_fancy) {
				while(!fancyd) {
#ifndef NODEBUG
					if(V_USELESS)
					  printf("%s\n", hline);
#endif
					switch(*hline) {
					  case '*':
						if((hline[strlen(hline)-1]=='*') && (strlen(hline)>1)) {
#ifdef OBJC
							[myForm makeBold];
#else
							fmakebold(RTFf);
#endif
							hline++;
							hline[strlen(hline)-1]='\0';
							bolded=yes;
						}
						else
						  fancyd=yes;
						break;
					  case '%':
						if((hline[strlen(hline)-1]=='%') && (strlen(hline)>1)) {
#ifdef OBJC
							[myForm makeItalic];
#else
							fmakeitalic(RTFf);
#endif
							hline++;
							hline[strlen(hline)-1]='\0';
							italicized=yes;
						}
						else
						  fancyd=yes;
						break;
					  case '_':
						if((hline[strlen(hline)-1]=='_') && (strlen(hline)>1)) {
#ifdef OBJC
							[myForm makeUnderline];
#else
							fmakeul(RTFf);
#endif
							hline++;
							hline[strlen(hline)-1]='\0';
							underlined=yes;
						}
						else
						  fancyd=yes;
						break;
					  default:
						fancyd=yes;
						break;
					}
				}
			}
			fprintf(RTFf, "%s%s", hline, punchar);
			if(bolded)
#ifdef OBJC
			  [myForm unBold];
#else
			  funbold(RTFf);
#endif
			if(italicized)
#ifdef OBJC
			  [myForm unItalic];
#else
			  funitalic(RTFf);
#endif
			if(underlined)
#ifdef OBJC
			  [myForm unUnderline];
#else
			  funul(RTFf);
#endif
		}
		else {
#ifdef OBJC
			[myForm makeItalic];
#else
			fmakeitalic(RTFf);
#endif
			fprintf(RTFf, "%s%s", hline, punchar);
#ifdef OBJC
			[myForm unItalic];
#else
			funitalic(RTFf);
#endif
		}
		hline=strtok(NULL, DESCRIPTOK);
	}
#ifdef OBJC
	[myForm putLine:" "];
#else
	fputline(RTFf, " ");
#endif
	
	free(stringptr);
	if(dynstr)
	  free(opstrptr);
}

static char *findrealchr(char *line, int find)
{
	char *ret=line;
	bool inComm=no;

	while(*ret) {
		if(!strncmp(ret,"//", 2)) /* The rest is all comments */
		  return NULL;

		if(!strncmp(ret,"/*",2))
		  inComm=yes;

		if(!strncmp(ret,"*/",2))
		  inComm=no;

		if(*ret==find && !inComm)
		  return ret;

		ret++;
	}

	return NULL;
}

static char *parseoneliner(DSTRING *methstring, char *ivarstr, char *mline)
{
	char *stringptr;
	
	/* All this just to handle one-line methods */
	stringptr=findrealchr(mline,'{');
	if(!docgen_recmp(METHEXP, mline) && (GLOB_autodoc || stringptr)) {
		if(stringptr) {
			*stringptr='\0';
			stringptr++;
		}
		else
		  stringptr=strchr(mline,'\n');

		if(stringptr) {
			*stringptr='\0';
			stringptr++;
		}
		else
		  stringptr=&mline[strlen(mline)];

		dstrarrcat(methstring, mline);
		daddchar(methstring, '\n');

		dstrarrcat(methstring, "/*\n");

		if(!docgen_recmp(SETEXP, mline)) {
			char autostr[strlen(mline)];
			char *endcol, *endto;
			char *setstr=autostr;
			
			strcpy(setstr, mline);
			setstr=strstr(mline,"set");
			setstr+=3;
			endcol=strchr(setstr,':');
			*endcol='\0';
			endcol++;
			SKIPSPACE(endcol);
			if(*endcol=='(') {
				SKIPPARENS(endcol);
			}
			SKIPSPACE(endcol);
			endto=endcol;
			while(!isspace(*endto) && *endto)
			  endto++;
			*endto='\0';
/*			if(strstr(ivarstr, setstr)) { */
			dstrarrcat(methstring, "Sets ");
			if(isupper(*setstr))
			  *setstr=tolower(*setstr);
			dstrarrcat(methstring, setstr);
			dstrarrcat(methstring, " to ");
			dstrarrcat(methstring, endcol);
		/*	} */
		}
		else {
			char autostr[strlen(mline)];
			char *endto;
			char *getstr=autostr;

			strcpy(getstr, mline);
			SKIPSPACE(getstr);
			if(*getstr)
			  getstr++;
			SKIPSPACE(getstr);
			if(*getstr=='(') {
				SKIPPARENS(getstr);
			}
			endto=getstr;
			while(!isspace(*endto) && *endto)
			  endto++;

			*endto='\0';

			if(strstr(ivarstr, getstr)) {
				dstrarrcat(methstring, "Returns ");
				dstrarrcat(methstring, getstr);
			}
		}
		dstrarrcat(methstring, "\n*/\n");

		mline=stringptr;
	}
	return mline;
}	

#define NULLPOS ((fpos_t)0)

static char *gathermethod(char *mline, FILE *Mf, fpos_t lastComment)
{
	bool first=yes;
	DSTRING *methstring;
	char *sline;
	char *ivarstr=arrdstr(GLOB_istring);
	fpos_t currPos;

	sline=mline;
	methstring=initdstr();

	while(!feof(Mf)) {
		while(*sline!='/' && *sline!='{' && *sline) {
			daddchar(methstring, *sline);
			sline++;
		}

		if(GLOB_before && first && lastComment) {
			char *tline;
			char *zline=sline;

			fgetpos(Mf, &currPos);
			if(fsetpos(Mf, &lastComment)<0) {
				perror("docgen:gathermethod():fsetpos->lastComment");
				exit(2);
			}

			tline=fgetdline(Mf);
			sline=tline;
			while(*sline!='/' && *sline) {
				sline++;
			}
			
			if(*sline == '/') {
				if(!strncmp(sline, "//", 2)) {
					dstrarrcat(methstring, "\n/*\n");
					dstrarrcat(methstring, sline+2);
					dstrarrcat(methstring, "\n*/\n");
				}
				else if(!strncmp(sline, "/*", 2)) {
					daddchar(methstring,'\n');
					while(!feof(Mf) && docgen_recmp(STOPEXP, sline)) {
						dstrarrcat(methstring, sline);
						daddchar(methstring,'\n');
						free(tline);
						tline=fgetdline(Mf);
						sline=tline;
					}
					if(feof(Mf)) {
						fprintf(stderr, "Unexpected eof\n");
						exit(3);
					}
					sline=strstr(tline, "*/");
					dstrarrcat(methstring, tline);
					// daddchar(methstring,'\n');
					sline+=2;
				}
			}
			free(tline);
			sline=zline;
			if(fsetpos(Mf, &currPos)<0) {
				perror("docgen:gathermethod():fsetpos->currPos");
				exit(2);
			}
		}

		if((*sline == '/')) {
			if(!strncmp(sline, "//", 2) && GLOB_after) {
				dstrarrcat(methstring, "\n/*\n");
				dstrarrcat(methstring, sline+2);
				dstrarrcat(methstring, "\n*/\n");
			}
			else if(!strncmp(sline, "/*", 2)) {
				if(GLOB_after)
				  daddchar(methstring,'\n');
				while(!feof(Mf) && docgen_recmp(STOPEXP, sline)) {
					if(GLOB_after) {
						dstrarrcat(methstring, sline);
						daddchar(methstring,'\n');
					}
					free(mline);
					mline=fgetdline(Mf);
					sline=mline;
				}
				if(feof(Mf)) {
					fprintf(stderr, "Unexpected eof\n");
					exit(3);
				}
				sline=strstr(mline, "*/");
				if(GLOB_after) {
					dstrarrcat(methstring, mline);
					// daddchar(methstring,'\n');
				}
				sline+=2;
				continue;
			}
		}
		else if(*sline=='{') {
			if(first)
			  daddchar(methstring, '{');
			dstrarrcat(methstring, "\n}\n");
			sline=arrdstr(methstring);
			freedstr(methstring);
			methstring=initdstr();

			mline=parseoneliner(methstring, ivarstr, sline);
			dstrarrcat(methstring, mline);
			dstrarrcat(methstring, "\n\n");
			sline=arrdstr(methstring);
			freedstr(methstring);
			free(ivarstr);
			return sline;
		}
		first=no;
		daddchar(methstring,' ');
		free(mline);
		mline=fgetdline(Mf);
		sline=mline;
	}

	return NULL;
}

#define GETNEWLINE \
	{	mline=nextline;\
		if(!mline) {\
			free(qline);\
			qline=dqueuesize(methlist) ? *(char **)dsdequeue(methlist) : NULL;\
			\
			if(!qline)\
			  return;\
			\
			mline=qline;\
		}\
\
		nextline=strchr(mline,'\n');\
		if(nextline) {\
			*nextline='\0';\
			nextline++;\
		} }\


/* Print description
 * text as long as this
 * isn't a line with a
 * comment ender.
 *
 * But some comments
 * might have text on
 * the same line as the
 * comment ender.
 *
 * Should be no problem
 * since we only want
 * useful comments and
 * they shouldn't be on
 * the same line as a
 * comment ender.
 *
 * But this could be
 * changed to a
 * do-while loop to
 * handle that case.
 */
#ifdef OBJC
#define PUTMETHDESC [myForm putMethodDescription]
#define PUTLINE [myForm putLine:mline]
#else
#define PUTMETHDESC fputmethdesc(RTFf)
#define PUTLINE fputline(RTFf, mline)
#endif

#define COMMINTERN \
{			mline+=2;\
\
			if(GLOB_firstcomm)\
			  parsecomments(RTFf, mline, dynstring);\
\
			GETNEWLINE;\
			\
			while(mline && docgen_recmp(STOPEXP, mline)) {\
				if(docgen_recmp(SEEALSO, mline)) {\
					/* Any line that matches the See Also\
					 * expression is handled differently\
					 * So do this only if it isn't\
					 * a See Also line.\
					 */\
					/* void parsecomments(FILE *RTFf, char *mline, DSTRING *dynstr) */\
					parsecomments(RTFf, mline, dynstring);\
				}\
				else {			/* This IS a See Also Line. */\
								  PUTMETHDESC; \
										/* If this isn't the last line\
										 * of comments, then the\
										 * font style is gonna be\
										 * goofed up\
										 */\
								  PUTLINE; \
				}\
				\
				GETNEWLINE;\
				\
			} /* while(docgen_recmp(STOPEXP, mline) && !feof(Mf)); */ \
			  if(GLOB_firstcomm && mline) \
			  parsecomments(RTFf, mline, dynstring); \
		}

/* do until
 * this matches
 * a comment ender
 */

#define COMMIES 5

void parsemethods(FILE *Mf, FILE *RTFf, char *mline, fpos_t currPos)
{
	char *hline, *stringptr;
	DSTRING *dynstring;
	DLSTQUEUE *methlist;
	DSTRING *methstring;
	int descrip=0;
	int aclass=0;
	char *qline, *nextline;
	int parencount;
	char *ivarstr;
	int commieloop;
	bool inComments=no;

	fpos_t prePos=NULLPOS, lastComment=NULLPOS;
	
#ifndef NODEBUG
	if(V_USELESS)
	  fprintf(stderr, "parsemethods(FILE *, FILE *, char *\"%s\")\n", mline);
#endif

	methlist=initdqueue(sizeof(char *));

	prePos=currPos;

	/* go to the first _uncommented_ method */
	while((docgen_recmp(METHEXP, mline) || inComments) && !feof(Mf)) {
		if(!docgen_recmp(STARTEXP, mline)) {
			inComments=yes;
			lastComment=prePos;
		}
		if(!inComments && strstr(mline,"//"))
		  lastComment=prePos;
		if(!docgen_recmp(STOPEXP, mline))
		  inComments=no;

		free(mline);
		
		fgetpos(Mf, &prePos);
		mline=fgetdline(Mf);
	}
	if(feof(Mf))
	  return;
	
	ivarstr=arrdstr(GLOB_istring);
	
	methstring=initdstr();

	qline=gathermethod(mline, Mf, lastComment);

	if(qline)
	  dsenqueue(methlist, &qline);

#ifndef NODEBUG
	if(V_USELESS)
	  fprintf(stderr,mline);
#endif

	lastComment=NULLPOS;
	fgetpos(Mf, &prePos);

	mline=fgetdline(Mf);
	inComments=no;
	while(!feof(Mf) && docgen_recmp(DONEINT ,mline)) {
		while((docgen_recmp(METHEXP, mline) || inComments) && !feof(Mf)) {
			if(!docgen_recmp(STARTEXP, mline)) {
				inComments=yes;
				lastComment=prePos;
			}
			if(!inComments && strstr(mline,"//"))
			  lastComment=prePos;
			if(!docgen_recmp(STOPEXP, mline))
			  inComments=no;
			
			free(mline);
			fgetpos(Mf, &prePos);

			mline=fgetdline(Mf);
		}

		if(feof(Mf) && docgen_recmp(DONEINT, mline))
		  break;

		qline=gathermethod(mline, Mf, lastComment);
		
#ifndef NODEBUG
		if(V_USELESS)
		  fprintf(stderr,qline);
#endif
		if(qline)
		  dsenqueue(methlist, &qline);

		lastComment=NULLPOS;
		fgetpos(Mf, &prePos);
		mline=fgetdline(Mf);
	}
	
	if(mline)
	  free(mline);

	dynstring=initdstr();
	dstrarrcat(dynstring, DEFITAL);
	
	if(GLOB_sort)
	  dlstsort(methlist, dcmp);

	qline=*(char **)dsdequeue(methlist);

	if(!qline)
	  return;

	nextline=strchr(qline, '\n');
	if(!nextline)
	  return;
	*nextline='\0';
	nextline++;
	mline=qline;
	
	if(!mline)
	  return;
	
	if(!docgen_recmp(CLASSMETH, mline)) {
#ifdef OBJC
		[myForm putMethodHeader:"Class"];
#else
		fputmethodh(RTFf, "Class");	/* Set up for Class Methods */
#endif
		aclass=1;
	}
	else
#ifdef OBJC
	  [myForm putMethodHeader:"Instance"];
#else
	  fputmethodh(RTFf, "Instance");
#endif
	
	/* keep parsing until we hit a line matching DONEINT */
	while(mline && docgen_recmp(DONEINT, mline)) {

		while(mline && docgen_recmp(DONEINT, mline) && docgen_recmp(METHEXP, mline)) {
			GETNEWLINE;
		}

		if(GLOB_undoc ? !docgen_recmp(UNDOC, mline) : 0) {
			free(qline);
			qline=dqueuesize(methlist) ? *(char **)dsdequeue(methlist) : NULL;
			
			if(!qline)
			  break;
			
			nextline=strchr(qline, '\n');
			if(nextline) {
				*nextline='\0';
				nextline++;
			}
			mline=qline;
			continue;
		}

		if(!docgen_recmp(METHEXP, mline)) { /* this line, or any line found in the .m file
											 * matches the METHEXP pattern.
											 */
			if(aclass && docgen_recmp(CLASSMETH, mline)) {
#ifdef OBJC
				[myForm putMethodHeader:"Instance"];
#else
				fputmethodh(RTFf, "Instance");
#endif
				aclass=0;
			}
			
			descrip=0; freedchars(dynstring); /* These variables are used with
											   * the comments, but should be reset
											   * for every new method.
											   */

			/* Everything between here and the comment beginning
			 * with DONE is just for printing the name of the method
			 * and find the arguments to the method
			 */
			hline=mline;		/* We need to free mline
								 * so we must have another pointer
								 * that will be stepped through the string
								 *
								 * stringptr and hline are just pointers
								 * used for stepping
								 *
								 * It may seem pointless to use hline here
								 * because we just assign it to stringptr,
								 * but we want to reset stringptr back to
								 * hline's position later
								 */

			SKIPSPACE(hline);
			
			stringptr=hline;
			
			while(isspace(*stringptr) || *stringptr == '-' || *stringptr == '+')
			  stringptr++;
			if(*stringptr == '(') {	/* Right now we just want the name
									 * so skip the type
									 */
				SKIPPARENS(stringptr);
			}
			SKIPSPACE(stringptr);
#ifdef OBJC
			[myForm putLine:" "];
			[myForm putMethodL1];
#else
			fputline(RTFf," ");	/* Start a new line */
			fputmethodl1(RTFf);	/* Set up for method name */
#endif
			while(*stringptr != ':' && *stringptr)
			  fputc(*stringptr++, RTFf);
			if(*stringptr)
			  fputc(*stringptr++, RTFf);
			
			while(*stringptr) {	/* This while statement goes through
								 * the rest of the line and puts
								 * the name of each argument in
								 * the dynamic string, dynstring
								 */
				
				SKIPSPACE(stringptr);
				if(*stringptr=='(') { /* And not all argyments have a type */
					/* And we don't need the
					 * type for the list of
					 * arguments
					 */
					SKIPPARENS(stringptr);
				}
				SKIPSPACE(stringptr);
				daddchar(dynstring, '#');
				while(!isspace(*stringptr) && (*stringptr!=':') && *stringptr)
				  daddchar(dynstring, *stringptr++);
				daddchar(dynstring, '#'); /* The '#' seperates each argument in the big
										   * list of names
										   */
				SKIPSPACE(stringptr);
				
				while(*stringptr != ':' && *stringptr)
				  fputc(*stringptr++, RTFf);
				if(*stringptr)
				  fputc(*stringptr++, RTFf);
				
			} /* while(*stringptr) */
#ifdef OBJC
			[myForm putLine:" "];
#else
			fputline(RTFf, " ");
#endif
			/* DONE parsing for arguments to method */
			  
			stringptr=hline;	/* re-set stringptr back to hline
								 * which is the first non-space character
								 * in the line
								 */
#ifdef OBJC
			[myForm putMethodL2];
#else
			fputmethodl2(RTFf);	/* Set up for full method line */
#endif
			fprintf(RTFf,"%c",stringptr[0]); /* That first character
												* should be a + or -
												* so print it specially
												*/
#ifdef OBJC
			[myForm changeFont:0];
#else
			fchangefont(RTFf, 0);
#endif
			stringptr++;
			SKIPSPACE(stringptr);
			if(*stringptr == '(') {	/* Not all methods have a type */
				parencount=1;
				while(parencount && *stringptr) {
					fputc(*stringptr++, RTFf);
					if(*stringptr=='(')
					  parencount++;
					else if(*stringptr==')')
					  parencount--;
				}
				if(*stringptr)
				  fputc(*stringptr++, RTFf);
			}
#ifdef OBJC
			[myForm makeBold];
#else
			fmakebold(RTFf);	/* The method name will be bold */
#endif
			SKIPSPACE(stringptr);
			while(*stringptr != ':' && *stringptr)
			  fputc(*stringptr++, RTFf);
			
			if(*stringptr)
			  fputc(*stringptr++, RTFf); /* Put the ':' as bold also */
#ifdef OBJC
			[myForm unBold];
#else
			funbold(RTFf);
#endif			
			while(*stringptr) {	/* Eveything else after the first ':'
								 * should be an argument.
								 */
				SKIPSPACE(stringptr);

				if(*stringptr=='(') { /* If there is a type, print it unspecially */
				    /* Until you hit a
					 * closing ')'
					 */
					 
					parencount=1;
					while(parencount && *stringptr) {
						fputc(*stringptr++, RTFf);
						if(*stringptr=='(')
						  parencount++;
						else if(*stringptr==')')
						  parencount--;
					}

					if(*stringptr)
					  fputc(*stringptr++, RTFf);
				}
				SKIPSPACE(stringptr);
#ifdef OBJC
				[myForm makeItalic];
#else
				fmakeitalic(RTFf); /* The argument itself is italicized */
#endif		
				while(!isspace(*stringptr) && (*stringptr!=':') && *stringptr)
				  fputc(*stringptr++, RTFf);
#ifdef OBJC
				[myForm unItalic];
#else
				funitalic(RTFf); /* Note: This is not a fun italic */
#endif
				SKIPSPACE(stringptr);
				fputc(' ', RTFf); 
#ifdef OBJC
				[myForm makeBold];
#else
				fmakebold(RTFf);
#endif
				while(*stringptr != ':' && *stringptr)
				  fputc(*stringptr++, RTFf);
				
				if(*stringptr)
				  fputc(*stringptr++, RTFf);
#ifdef OBJC
				[myForm unBold];
#else
				funbold(RTFf);
#endif
			} /* while(*stringptr) */
#ifdef OBJC
			[myForm putLine:" "];
#else
			fputline(RTFf, " ");
#endif
		} /* if(!docgen_recmp(METHEXP, mline)) */

		/* Handle up to COMMIES comments in a method */
		for(commieloop=0;commieloop<COMMIES;commieloop++) {

			GETNEWLINE;

			if(!docgen_recmp(STARTEXP, mline)) { /* else this or any line that matches
												  * a comment beginning line will
												  * have it's comments printed.
												  * That's why this program will
												  * print out any comments in a
												  * method, even after it's already
												  * printed a No Description line
												  */
				COMMINTERN;
				descrip=1;
			}
			else
			  break;			/* get away from these commies! */
		}
	} /* while(docgen_recmp(DONEINT, mline) && !feof(Mf)) */
	
	freedstr(dynstring);

	free(ivarstr);
	free(qline);
	freedlist(methlist);
	
#ifndef NODEBUG
	if(V_USELESS)
	  fprintf(stderr, "parsemethods: returning\n");
#endif
}


void traceinheritance(FILE *RTFf, char *hdir, char *hname)
{
	char pathsep[]=":";
	FILE *Hf;
	char *hline, *stringptr, *FQh;
	ofiletype fty;
	DSTRING *dhname;
	char *hpath, *honepath, *fhname;
	static unsigned long of_counter=MAX_INHERIT_SEARCHES;
	
#ifndef NODEBUG	
	if(V_USELESS)
	  fprintf(stderr, "traceinheritance(FILE *, %s, %s)\n", hdir, hname);
#endif

	dstrarrcat(GLOB_istring, hname);
	daddchar(GLOB_istring, '#');
	
	dhname=dstrarr(hdir);
	hpath=arrdstr(dhname);
	free(dhname);
	
	dhname=dstrarr(hname);
	dstrarrcat(dhname, ".h");
	fhname=arrdstr(dhname);
	free(dhname);
	
	honepath=strtok(hpath, pathsep);
	
	Hf=NULL;
	FQh=NULL;
	
	if(GLOB_recurs) {			/* We want to recursively search for
								 * the file in every directory in directories
								 * of the search path.
								 */
		
		while(honepath!=NULL && FQh==NULL) { /* Keep looping until either
											  * we find a file or run out
											  * of directories.
											  */
			FQh=findfile(fhname, honepath);
			honepath=strtok(NULL, pathsep);
		}
		if(FQh!=NULL) {
			Hf=fopen(FQh,"r");
			free(FQh);
		}
	}
	else
	  while(honepath!=NULL && Hf==NULL) {
		  FQh=dirfile(honepath, fhname);
		  if(FQh) {
			  Hf=fopen(FQh, "r");
			  free(FQh);
		  }
		  honepath=strtok(NULL, pathsep);
	  }
	
	free(fhname);
	
	free(hpath);
	
	if(Hf == NULL) {
		if(V_STAT)
		  fprintf(stderr, "traceinheritance: %.200s not found in %.800s\n", hname, hdir);
		return;
	}
	
	if(!--of_counter) {			/* If this is true, the we must be tracing too many */
		if(V_BADERR)
		  fprintf(stderr,"traceinheritance: We seem to have hit an infinite loop in\n%s\n",hname);
		return;
	}
	
#ifndef NODEBUG
	if(V_USELESS)
	  fprintf(stderr, "of_counter = %ld\n", of_counter);
#endif
	
	/* read to the @interface line in the .h file */
	hline=fgetdline(Hf);
	while(!feof(Hf) && docgen_recmp(INTERFACE, hline)) {
		free(hline);
		hline=fgetdline(Hf);
	}
	
	if(feof(Hf)) {
		/* since all these eof error blocks are so
		 * similar I was thinking of having another
		 * function, but I don't like closing
		 * files outside of the function they were
		 * opened in. And a goto or macro wouldn't
		 * handle all the different cases
		 */
		free(hline);
		if(V_BADERR)
		  fprintf(stderr,"Unexpected end of file %s\n", hname);
		if(fclose(Hf) == EOF) 
		  if(V_BADERR)
			perror("I couldn't close the header file. Pray.");
		of_counter++;
		return;
	}
	
	/* check for wether we're dealing with a Class or
	 * Category
	 */
	if(strstr(hline, CLASSCHK) != NULL)
	  fty=class;
	else if(strstr(hline, CATCHK) != NULL)
	  fty=category;
	else {
		free(hline);
		if(fclose(Hf) == EOF) 
		  if(V_BADERR)
			perror("I couldn't close the header file. Pray.");
		if(V_STAT)
		  fprintf(stderr,"traceinheritance: %s is neither a Class or a Category\n", hname);
		of_counter++;
		return;
	}
	
	/* If strtok fails on this first pass
	 * then exit with an error.
	 */
	if((stringptr=strtok(hline, INTTOK)) == NULL) {
		free(hline);
		if(V_ERR)
		  fprintf(stderr, "Improper @interface description in\n%s\n", hname);
		if(fclose(Hf) == EOF) 
		  if(V_BADERR)
			perror("I couldn't close the header file. Pray.");
		of_counter++;
		return;
	}
	else
	  stringptr=strtok(NULL, INTTOK);
	
	if(fty==class) {
		stringptr=strtok(NULL, INTTOK);
		if(stringptr != NULL) {
			fprintf(RTFf, " %s %s",GLOB_sep, stringptr);
			traceinheritance(RTFf, hdir, stringptr);
		}
	}
	else {
		stringptr=strtok(NULL, INTTOK);
		if(stringptr!=NULL)
		  stringptr[strlen(stringptr)-1]='\0';
		if(stringptr != NULL) {
			fprintf(RTFf, " %s %s",GLOB_sep, stringptr);
			traceinheritance(RTFf, hdir, stringptr);
		}
	}
	
	of_counter++;
	
#ifndef NODEBUG
	if(V_USELESS)
	  fprintf(stderr, "of_counter++ = %ld\n", of_counter);
#endif
	
	if(fclose(Hf) == EOF) 
	  if(V_BADERR)
		perror("I couldn't close the header file. Pray.");
	return;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.