🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.sources.reviewed
Article #12

v02i043: cextract - (Ver. 1.7) C prototyper/header file generator, Part05/05

#12
From: Adam Bryant
Date: Tue, 03 Nov 1992 22:12
2076 lines
60616 bytes
Submitted-by: Adam Bryant <adb@cs.bu.edu>
Posting-number: Volume 2, Issue 43
Archive-name: cextract/part05
Supersedes: cextract: Volume 1, Issue 4-8

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 5 (of 5)."
# Contents:  parse.c
# Wrapped by adb@csa on Fri Oct 30 16:20:37 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'parse.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'parse.c'\"
else
echo shar: Extracting \"'parse.c'\" \(54378 characters\)
sed "s/^X//" >'parse.c' <<'END_OF_FILE'
X/*
X *
X * main parsing routines for the cextract/cextdoc programs.
X * Also includes a number of generic routines applicable to both.
X *
X * These routines provide the methods used to extract comments,
X * prototypes and function names from C source files.
X *
X * Copyright (c) 1990, 1991, 1992 by Adam Bryant
X *
X * The only restrictions on the use of these routines is that they not
X * be used for monetary gain, and that this copyright notice be left
X * intact.
X *
X * Note:  These programs may be freely used to extract code and/or
X *        documentation at proprietary/corporate sites, as long as the
X *        actual source code is not used within any proprietary code.
X *
X *        The generated output (header) files are distributable in any
X *        manner desired as they are based on the scanned source code,
X *        not my program.
X */
X/*
X * Version history:
X *
X * Changes for Version 0.40 of both cextract and cextdoc:
X *    - implemented new out_char() interface.
X *    - first comments will not be shown if no functions exist.
X *    - implemented concatenation of comments.
X *    - implemented troff output for cextdoc portion.
X *
X * Changes for Version 0.41 of both cextract and cextdoc:
X *    - switched the NO_SETBUFFER to SETBUFFER, making non-use standard.
X *    - added in the DO_VARARGS code to automatically account for the
X *      the variable argument setup in parsing.
X *
X * Changes for Version 0.42 of both:
X *    - made the DO_VARARGS code the default.  (removed #ifdefs)
X *
X * Changes for Version 0.50 (1/18/91) of both:
X *    - added the configuration file code.
X *    - removed much of the DEBUG print statements from stable code.
X *    - made the bad syntax encounters exit the program.
X *
X * Changes for Version 0.51 (1/21/91) of both:
X *    - implemented code to allow compilation on VAX VMS systems.
X *
X * Changes for Version 0.60 (4/15/91) of both:
X *    - more VMS stuff, and addition of files: patchlevel.h,
X *      README, and INSTALL.
X *    - removed all other DEBUG statements.
X *    - left the SETBUFFER code in place, but removed mention
X *      of it from the Makefile, since there was no noticable
X *      speed enhancement through its use.
X *    - upgraded to version 6.0 for release to comp.sources.reviewed.
X *
X * Changes for Version 1.0 (4/17/91-5/3/91) of both:
X *    - removed most system definitions from cproto.h and replaced
X *      with more includes of system header files.  But, there are
X *      still a number of system functions which are not in the header
X *      files on at least Sun systems.
X *    - now use the "is_switch()" function to parse the command line
X *      arguments.  This allowed me to use both '-' and '/' on VMS
X *      systems to avoid confusion in the manual pages.
X *    - simplified the Makefile, especially for the "make install".
X *    - fixed various mispellings/misphrasings in error messages.
X *    - checked the argument in calls to free() within pop_proto().
X *    - configuration files will be checked for in the following order:
X *        system file, home directory, current directory.
X *      any encountered will be read in.  This will allow overriding
X *      of customization options in a logical order.
X *    - implemented switch to control how the config files are read in.
X *    - redid the manual pages and split out the customization section
X *      into its own manual page.
X *    - adjusted the comment capturing code to assure that it doesn't
X *      misassign comments to the wrong functions.
X *    - fixed one minor memory leak.
X *    - adjust system configuration file locations.
X *    - redid VMS popen() and pclose() to assure unique temporary file.
X *    - added in an "err_msg()" routine to call perror() if it exits.
X *    - made sure to check return value of all calls to fputs().
X *    - merged "cextdoc" into "cextract".
X *    - added verbose command line options with same format as the
X *      configuration commands.
X *    - now use the "+" and "-" switches to indicate on and off.
X *    - added a number of new options to support the new merged
X *      programs and to allow displaying of settings.
X *    - added a -Hstring to indicate replacement of the sequence:
X *
X *          #ifndef __CEXTRACT__
X *            ... output ...
X *          #endif
X *
X *      with:
X *
X *          #ifndef string
X *          #define string
X *            ... output ...
X *          #endif
X *
X *    - wrote up a VMS help file for cextract.
X *
X * Version 1.1: Okay, so there were still more changes needed. :-)
X *    - fixed more occurances of proceed instead of precede.
X *    - fixed typos of "it's" instead of "its".
X *    - removed empty targets from Makefile
X *    - add sed facility to Makefile to properly configure man pages.
X *    - wrote a ".so" implementation for cextdoc manual instead of
X *      prior hard link method.
X *    - added a MANDIR variable to the Makefile.
X *    - tweaked the documentation in a few places.
X *    - allow the "!" or "no-" (Non-VMS) or "no" (VMS) prefix to
X *      command line options.
X *    - use "sys$login:" as HOME directory on VMS systems, if HOME
X *      environment variable is not defined.
X *    - changed the "sys$system:" to "sys$library".
X *    - fixed typo of "fclose(*stream)" in the VMS pclose.
X *    - adjusted the VMS qualifier building to build /define=(a,b,c), etc.
X *    - added in a "remove-names" option to eliminate variable names
X *      from the parameter list output.
X *    - fixed strncmp() bug in the check for the "replace" option.
X *    - fixed a number of calls to cfg_err() to pass cmd_line parameter.
X *    - implemented better check for empty or "void" parameter lists.
X *    - merged the void check and style determination into one routine
X *      and called it diverge_style().
X *    - changed a number of routines to use char* math instead of
X *      array stepping using counters.
X *    - added a configuration file section to the VMS help file.
X *    - tweaked the cextract.1 and cextrc.5 manual pages a bit more.
X *    - edited the README, INSTALL, and INSTALL.VMS files.
X *    - implemented checks to be sure that the files to be parsed are
X *      accessible before calling the C preprocessor.
X *    - renamed the files as:
X *         cproto.c     =>   parse.c
X *         cproto.h     =>   xtract.h
X *         cextract.c   =>   main.c
X *      this was done to avoid some of the conflict with files
X *      beginning with the same string of characters.
X *    - made sure that "make test" tested the version in the current
X *      directory.
X *
X * Version 1.2:  yet another pre-submission fix section.
X *    - fixed a typo in the comments.
X *    - separated the INSTALL definition in the Makefile into INSTBIN
X *      and INSTMAN command to allow those systems without "install"
X *      to perform installations properly.
X *    - added "/exe=cextract" to the build.com link operation.
X *    - fixed the character count in the copy_str() function.
X *      [This fixes the misread on the "replace" operation].
X *    - fixed up the "method" description in the README.
X *    - added ${CEXTRACT} to the "test" (ala "make test") dependency.
X *    - removed the "make links" and appended it to "make install"
X *    - changed the "skip-config" command to "read-config".
X *    - had one more go at the cextrc.5 manual page.
X *    - tweaked the cextract.1 manual page as well.
X *    - added check for declaration of array of function pointers.
X *    - added code to parse structure declarations within function
X *      parameter lists.  [why anyone would program like this is
X *      beyond me, but hey, I like to support valid C.]
X *    - made sure to only test for functions where a brace was
X *      preceded by a semi-colon or a paranthesis.
X *    - make sure to clear out the "code_info" array more often.
X *    - increased the array sizes to allow for larger buffer space.
X *      [temporary until implement dynamic memory routines.]
X *    - created a TODO file and removed those items from the bottom
X *      of the README file.
X *    - shortened the BUGS section of the cextract.1 manual page.
X *    - changed the program name check to strncmp() so VMS sites work.
X *    - created a newbuild.com file and mentioned it in INSTALL.VMS.
X *    - fixed errors in the newbuild.com file.
X *    - added a mentioning of the '/' character for VMS options.
X *    - cleaned up the README, INSTALL and other docs.
X *
X * Version 1.3:  first patch after comp.sources.reviewed
X *    - fixed bug where comments within parameter list could cause a
X *	function to be skipped.
X *    - added both the CPP and CPP_COMMENTS flags to the Makefile.
X *    - added patch from Martin Fouts (fouts@bozeman.clipper.ingr.com)
X *	to provide simple changes to allow port to the CLIX OS.
X *    - fixed stupid bug which caused the "-U" flag to not get parsed.
X *    - changed the "#ifdef __STDC__" to the proper "#if __STDC__".
X *    - wrote a new command and flag "build-config" to generate the system
X *	configuration file automatically.  [-B or -b for build]
X *    - made sure the replace macros do not contain duplicates.
X *    - prevented the creation of duplicate '-D','-I', or '-U' entries.
X *    - fixed the "macro_match()" function for all systems. [VMS?]
X *    - reversed the definitions of CFLAGS and COPTS in the Makefile.
X *    - made sure to test if substitution already took place in vargs_find().
X *    - fixed the 'Q' flag so that it properly reads in any numeric.
X *    - added a dependency for the system configuration file to the
X *      "install" target, so that it can be automatically generated.
X *    - fixed the INSTALL, INSTALL.VMS and documentation files to
X *      properly cover the new configuration file building mechanism.
X *    - implemented a better method for the NO_OPEN option.  This should
X *      now work for all systems, not just VMS.
X *    - fixed bug that the type of the function was not checked for
X *      replacement.
X *    - made sure that the subst macros where ordered as they came in.
X *    - fixed bug with parameters for function pointers getting int
X *      tagged on unnecessarily.
X *
X * Version 1.4:  [2/25/92-4/20/92]  Another iteration of fixes.
X *    - allow any sort of text inside of brackets and parenthesis
X *      in the variable declarations.
X *    - fixed the fprintf formats in the font error messages.
X *    - made sure to remove the use of register in prototype types.
X *    - added the +Z option to allow for the compression of the output
X *      using a macro.  [Also under the "merge-output" option.]
X *    - separated functions into a third file [io.c] to reduce the
X *      size of the other files.
X *
X * Version 1.5: [4/29/92-5/18/92] Second submission fixes.
X *    - added ability to exclude all but static functions from search
X *	to allow for the creation of header files for single files.
X *    - fixed the documentation to reflex the new "static" feature.
X *    - fixed some errors in the manual pages.
X *    - adjusted the Makefile:  make echo, sed, nroff and install
X *	configurable; had the "make test" command perform the diff
X *	directly; enhanced the mkdir lines (is it portable?).
X *    - fixed a bug in the out_str() function where it might have
X *	accessed invalid data if the parameter str was NULL.
X *    - for the SGI systems, added a "#define _POSIX_SOURCE" line.
X *    - allow both "no-" and "no" for turning off options.
X *    - implemented a "+W" ("+break-after-types") flag to separate
X *	function type from the function name.
X *    - implemented a "+w#" ("+wrap-parameters") flag to cause the
X *	output of the parameter list to wrap after a given number of
X *	characters.
X *    - added the two new options to the documentation files.
X *    - made use of minmatch_str's return value in io.c and cleaned up
X *	some minor typos.
X *    - fixed serious bug with the default settings for statics.
X *	[Thanks to Jon Whellams for catching this.  *sigh*]
X *    - major rewrite of the options system:
X *	  +  merged most of the options into a two dimensional array.
X *	  +  used this two-dimensional array to distinguish between
X *	     options used in extract mode and doc mode.
X *	  +  this makes it possible for the generated config files to
X *	     contain the proper "extract-only " or "doc-only "
X *	     prefixes.
X *    - merged the two static flags into one flag with multiple
X *	options, removing the confusing "-only-statics" flag.
X *    - fixed the build_rc() routine to properly prefix "doc-only " or
X *	"extract-only " to the output when appropriate.
X *    - the mode ("extract", "doc", etc.) will no longer be written
X *	when a configuration file is generated so "cextdoc" works.
X *
X * Version 1.6: [8/28/92] Third submission fixes.
X *    - added line to set the permissions on the config file during install.
X *    - regenerated the proto.h file to grab the main.c "parce.c" typo fix.
X *    - fixed the documentation for the "-w#" flag. ["-w=#" --> "-w#"]
X *    - for the INSTALL file, I now explicitly mention that installation
X *	of the configuration file might need root permission.
X *    - fixed outdated usages of pclose [pclose -> close_input].
X *    - fixed the wrong usage of "command" in the "open_input" function.
X *    - allow for alternative return values from system().
X *    - make sure that multiple replacements can take place.
X *    - fixed error with "replace" not recognizing "name" selection.
X *    - made sure an error is printed if a bad "replace" format is seen.
X *    - added support for // comments to begin future C++ support.
X *    - provided a "strstr()" function for Xenix sites.
X *    - expanded the "output-file" argument to support separate output
X *	files for extraction and documentation modes.
X *    - added the ability to select the C preprocessor at runtime.
X *    - updated all of the documentation to reflect the above changes.
X *    - preserve the doc/extract selection for macros and replace commands.
X *
X * Version 1.7: [10/30/92] Hopefully last enhancements before acceptance
X *    - keep track of line numbers within the file being parsed.
X *    - any syntax errors will be reported without aborting the file
X *	parsing.
X *    - syntax errors will be accurately reported by file and line number.
X *    - added a check to test for too many left parenthesis.
X *
X */
X#include "xtract.h"
X
X/* storage elements */
XP_PROTO proto_list = NULL, proto_next = NULL;
XP_MACRO macro_list = NULL, last_macro = NULL;
XP_SUBST subst_list = NULL, last_subst = NULL;
XFILE *fpin = NULL;
Xchar code_info[MAX_SIZE+1], cfg_file[MID_SIZE];
Xchar dummy_str[MID_SIZE], file_name[MID_SIZE];
Xint arg_ch_count = 0;
Xint out_file_flag = 0, comment_len = 0, dont_space = TRUE;
Xchar start_comment[MAX_SIZE];
Xchar prog_name[FNAME_SIZE];
Xchar tmp_str[FNAME_SIZE];
Xint dont_stop = FALSE;
Xstatic char last_comment[MAX_SIZE];
X
X/* options for the different parsing methods */
Xint global_opts[2][OPT_NUMBER];
Xint doc_extract = DOC_NONE, cfg_switch = 7;
X
X/* variables for keeping track of things */
Xchar errout_filename[MID_SIZE];
Xint start_block = FALSE, files_parsed = 0, total_out = 0;
Xint line_count = 0;
X
X/* default font values */
Xchar ft_title[3] = "C";
Xchar ft_comment[3] = "CO";
Xchar ft_name[3] = "B";
Xchar ft_plist[3] = "R";
X
X/* add a new substitution macro to the substitution list */
Xvoid
Xadd_subst(type, select, f_str, t_str)
X  int type, select;
X  char *f_str, *t_str;
X{
X  P_SUBST subst_tmp;
X
X  /* check for duplicates */
X  for (subst_tmp = subst_list;
X       subst_tmp != NULL;
X       subst_tmp = subst_tmp->next) {
X    if ((subst_tmp->submode == type) &&
X	(subst_tmp->usewhen == select) &&
X	(strcmp(subst_tmp->from_str, f_str) == 0)) {
X
X      /* can exit routine for complete matches */
X      if (strcmp(subst_tmp->to_str, t_str) == 0) {
X	return;
X      }
X
X      /* otherwise just adjust the replacement */
X      free(subst_tmp->to_str);
X      if ((subst_tmp->to_str =
X	   (char *) malloc(sizeof(char) * (1 + strlen(t_str)))) == NULL) {
X	fprintf(stderr, "Serious Error: Memory Allocation Failure\n");
X	exit(1);
X      }
X      strcpy(subst_tmp->to_str, t_str);      
X      return;
X
X    }
X  }
X
X  /* allocate the memory space */
X  if ((subst_tmp = (P_SUBST) malloc(sizeof(S_SUBST))) == NULL) {
X    fprintf(stderr, "Serious Error: Memory Allocation Failure\n");
X    exit(1);
X  }
X  if (subst_list == NULL) {
X    subst_list = subst_tmp;
X  }
X  if ((subst_tmp->from_str =
X       (char *) malloc(sizeof(char) * (1 + strlen(f_str)))) == NULL) {
X    fprintf(stderr, "Serious Error: Memory Allocation Failure\n");
X    exit(1);
X  }
X  if ((subst_tmp->to_str =
X       (char *) malloc(sizeof(char) * (1 + strlen(t_str)))) == NULL) {
X    fprintf(stderr, "Serious Error: Memory Allocation Failure\n");
X    exit(1);
X  }
X
X  /* now record the information */
X  subst_tmp->submode = type;
X  subst_tmp->usewhen = select;
X  strcpy(subst_tmp->from_str, f_str);
X  strcpy(subst_tmp->to_str, t_str);
X
X  /* place the new link at the end of the chain */
X  subst_tmp->next = NULL;
X  if (last_subst != NULL) {
X    last_subst->next = subst_tmp;
X  }
X  last_subst = subst_tmp;
X}
X
X/* add a new macro to the macro list */
Xvoid
Xadd_macro(select, str)
X  int select;
X  char *str;
X{
X  P_MACRO macro_tmp;
X
X  /* check for duplicate entries */
X  for (macro_tmp = macro_list;
X       macro_tmp != NULL;
X       macro_tmp = macro_tmp->next) {
X    /* no need to add something already there */
X    if (strcmp(macro_tmp->m_str, str) == 0) {
X      /* check for selection change */
X      if (macro_tmp->usewhen != select) {
X	macro_tmp->usewhen = 2;
X      }
X      return;
X    }
X  }
X
X  /* allocate the space */
X  if ((macro_tmp = (P_MACRO) malloc(sizeof(S_MACRO))) == NULL) {
X    fprintf(stderr, "Serious Error: Memory Allocation Failure\n");
X    exit(1);
X  }
X  if (macro_list == NULL) {
X    macro_list = macro_tmp;
X  }
X  if ((macro_tmp->m_str =
X       (char *) malloc(sizeof(char) * (strlen(str) + 1))) == NULL) {
X    fprintf(stderr, "Serious Error: Memory Allocation Failure\n");
X    exit(1);
X  }
X  strcpy(macro_tmp->m_str, str);
X  macro_tmp->usewhen = select;
X  macro_tmp->next = NULL;
X  if (last_macro != NULL) {
X    last_macro->next = macro_tmp;
X  }
X  last_macro = macro_tmp;
X}
X
X/* return TRUE if the macro definition is in the string */
Xstatic int
Xmacro_match(macro, str)
X  char *macro, *str;
X{
X  static int deflen = -1;
X  int i, len, hold = FALSE;
X
X  /* initialize */
X  if (deflen == -1) {
X    deflen = strlen(DEF_LEADER);
X  }
X
X  /* check it */
X  if (strncmp(macro, DEF_LEADER, deflen) == 0) {
X    len = strlen(str);
X    for (i = 0; i < len; i++) {
X      if (macro[i + deflen] != str[i]) break;
X    }
X    if ((i == len) &&
X	((macro[i + deflen] == '=') ||
X	 (macro[i + deflen] == '\0'))) {
X      hold = TRUE;
X    }
X  }
X
X  return(hold);
X}
X
X/* if the matching definition is in the list, remove it */
Xint
Xremoved_macro(select, str)
X  int select;
X  char *str;
X{
X  P_MACRO macro_ptr, macro_tmp;
X  int hold = FALSE;
X
X  /* go through the list */
X  if ((macro_ptr = macro_list) != NULL) {
X
X    /* check the beginning element */
X    if (macro_match(macro_ptr->m_str, str)) {
X      if (macro_ptr->usewhen != select) {
X	/* just return */
X	return(FALSE);
X      } else {
X	macro_list = macro_list->next;
X	macro_ptr->next = NULL;
X	free(macro_ptr->m_str);
X	free(macro_ptr);
X	hold = TRUE;
X      }
X    }
X
X    /* check the rest */
X    macro_tmp = macro_ptr->next;
X    while (hold == FALSE && macro_tmp != NULL) {
X      if (macro_match(macro_tmp->m_str, str)) {
X	if (macro_ptr->usewhen != select) {
X	  /* just return */
X	  return(FALSE);
X	} else {
X	  macro_ptr->next = macro_tmp->next;
X	  macro_tmp->next = NULL;
X	  free(macro_tmp->m_str);
X	  free(macro_tmp);
X	  hold = TRUE;
X	}
X      }
X
X      /* onto the next */
X      macro_ptr = macro_tmp;
X      macro_tmp = macro_tmp->next;
X    }
X  }
X
X  /* now assign the last_macro value and get back to work */
X  if (hold == TRUE) {
X    for (last_macro = macro_list;
X	 (last_macro != NULL) &&
X	 (last_macro->next != NULL);
X	 last_macro = last_macro->next) ;
X  }
X  return(hold);
X}
X
X/* compare two strings considering cases the same */
Xint
Xstr_test (s1, s2)
X  char *s1, *s2;
X{
X  int ch1, ch2;
X
X  for (; *s1 != '\0'; s1++, s2++) {
X    ch1 = (islower(*s1) ? toupper(*s1) : *s1);
X    ch2 = (islower(*s2) ? toupper(*s2) : *s2);
X    if (ch1 != ch2) return(ch1 - ch2);
X  }
X  return(*s1 - *s2);
X}
X
X/* sort the list of functions and their prototypes */
Xvoid
Xsort_proto()
X{
X  P_PROTO p2_ptr;
X  int done = FALSE;
X
X  /* all done? */
X  if ((proto_list == NULL) || (proto_list->next == NULL)) return;
X
X  /* go until done sorting */
X  while (done == FALSE) {
X
X    /* initialize */
X    proto_next = proto_list;
X    done = TRUE;
X
X    /* test for initial swap */
X    if ( str_test( proto_next->name, (proto_next->next)->name ) > 0) {
X      proto_list = proto_next->next;
X      proto_next->next = proto_list->next;
X      proto_list->next = proto_next;
X      proto_next = proto_list;
X    }
X    
X    /* sort remaining linked list */
X    for (; (proto_next->next)->next != NULL; proto_next = proto_next->next) {
X      p2_ptr = (proto_next->next)->next;
X      if ( str_test( (proto_next->next)->name, p2_ptr->name ) > 0) {
X
X	/* swap locations in list */
X	(proto_next->next)->next = p2_ptr->next;
X	p2_ptr->next = proto_next->next;
X	proto_next->next = p2_ptr;
X
X	/* continue sort */
X	done = FALSE;
X      }
X    }
X
X  }
X}
X
X/* remove the top item on the stack */
Xvoid
Xpop_proto()
X{
X  P_PROTO tmp_proto = proto_list;
X
X  /* done? */
X  if (proto_list == NULL) {
X    proto_next = NULL;
X    return;
X  }
X  if (proto_list == proto_next) {
X    proto_next = NULL;
X  }
X
X  /* now do it */
X  proto_list = proto_list->next;
X  tmp_proto->next = NULL;
X  if (tmp_proto->name != NULL) free(tmp_proto->name);
X  if (tmp_proto->ftype != NULL) free(tmp_proto->ftype);
X  if (tmp_proto->fname != NULL) free(tmp_proto->fname);
X  if (tmp_proto->plist != NULL) free(tmp_proto->plist);
X  if (tmp_proto->comment != NULL) free(tmp_proto->comment);
X  free(tmp_proto);
X}
X
X/* initialize the page for troff output, if needed */
Xvoid
Xinit_roff(omode)
X  int omode;
X{
X  static int init_done = FALSE;
X
X  /* perform the initialization */
X  if (init_done == FALSE) {
X    out_str(omode, ".sp 0.5i\n");
X    out_str(omode, ".ps 10\n");
X    out_str(omode, ".vs 12\n");
X    out_str(omode, ".fp 1 ");
X    out_str(omode, ft_title);
X    out_str(omode, "\n.fp 2 ");
X    out_str(omode, ft_comment);
X    out_str(omode, "\n.fp 3 ");
X    out_str(omode, ft_name);
X    out_str(omode, "\n.fp 4 ");
X    out_str(omode, ft_plist);
X    out_char(omode, '\n');
X    init_done = TRUE;
X  }
X}
X
X/* check preprocessor statement for line numbering changes */
Xint
Xpreprocessor_check()
X{
X  int curr_char = '#';
X  int i, l, line_value;
X  char temp_str[MID_SIZE], t2_str[MID_SIZE];
X
X  /* get the whole line */
X  if (fgets(temp_str, MID_SIZE, fpin) != NULL) {
X    /* first handle the exit */
X    l = strlen(temp_str);
X    curr_char = temp_str[l - 1];
X
X    /* now parse things */
X    for (i = 0; i < l; i++) {
X      if (isdigit(temp_str[i])) {
X	if (sscanf(&(temp_str[i]), "%d %s", &line_value,
X		   t2_str) == 2) {
X	  line_count = line_value;
X	  l = strlen(t2_str);
X	  if (t2_str[l - 1] == '"') {
X	    t2_str[l - 1] = '\0';
X	  }
X	  if (t2_str[0] == '"') {
X	    i = 1;
X	  } else {
X	    i = 0;
X	  }
X	  strcpy(errout_filename, &(t2_str[i]));
X	}
X	break;
X      } else if (!isspace(temp_str[i])) {
X	if (strncmp(&(temp_str[i]), "line", 4) != 0) break;
X	else {
X	  i += 3;
X	}
X      }
X    }
X  }
X  return(curr_char);
X}
X
X/* read and store all characters prior to end comment indicator */
Xstatic void
Xget_comment(tagon, head_str, cmt_str)
X  int tagon;
X  char head_str[], cmt_str[];
X{
X  int curr_ch;
X  int prev_ch = ' ';
X
X  /* keep going until scan is complete */
X  if (tagon == TRUE) {
X    if (head_str[0] == '\0') {
X      /* catenate side by side comments */
X      comment_len -= 2;
X    } else {
X      /* insert intermediary white space and comment */
X      for (curr_ch = 0;
X	   head_str[curr_ch] != '\0';
X	   curr_ch++) {
X	cmt_str[comment_len++] = head_str[curr_ch];
X      }
X      cmt_str[comment_len++] = '/';
X      cmt_str[comment_len++] = '*';
X    }
X  } else {
X    comment_len = 0;
X  }
X  while ((curr_ch = getc(fpin)) != EOF) {
X
X    /* test for end of comment */
X    if ((curr_ch == '/') && (prev_ch == '*')) {
X      cmt_str[comment_len++] = '/';
X      break;
X    }
X
X    /* store it */
X    cmt_str[comment_len++] = prev_ch = curr_ch;
X    if (curr_ch == '\n') line_count++;
X  }
X
X  /* close storage */
X  cmt_str[comment_len] = '\0';
X}
X
X/* structure to hold definitions */
Xtypedef struct s_param {
X  char *name;		/* the name of the variable */
X  char *desc;		/* description string */
X  struct s_param *next;	/* pointer to the next element */
X} S_PARAM, *P_PARAM;
XP_PARAM param_list;
X
X/* take all of the variables between the parenthesis and break them down */
Xstatic int
Xfill_param(str)
X  char *str;
X{
X  int paren_loc, count, i, in_word = 1, len = strlen(str);
X  P_PARAM last_param = NULL, param_tmp;
X  char tempc[MID_SIZE];
X
X  /* initialize the list */
X  param_list = NULL;
X  paren_loc = 0;
X  count = 0;
X  tempc[0] = '\0';
X  for (i = 1; i < len; i++) {
X
X    /* check for the end */
X    if (str[i] == ')') {
X      paren_loc = i;
X      break;
X    }
X
X    /* find a variable name; check for separators */
X    if (str[i] == ',') {
X
X      /* check current information */
X      if (in_word == 1) {
X	syntax_err("empty variable list encountered");
X	return(-1);
X      }
X      in_word = 1;
X
X      /* store the name */
X      tempc[count] = '\0';
X      count = 0;
X      if ((param_tmp = (P_PARAM) malloc(sizeof(S_PARAM))) == NULL) {
X	fprintf(stderr, "Serious Error: Memory Allocation Failure\n");
X	exit(1);
X      }
X      if ((param_tmp->name =
X	   (char *) malloc(sizeof(char) * (strlen(tempc) + 1))) == NULL) {
X	fprintf(stderr, "Serious Error: Out of memory\n");
X	exit(1);
X      }
X      strcpy(param_tmp->name, tempc);
X      param_tmp->next = NULL;
X      param_tmp->desc = NULL;
X      if (param_list == NULL) {
X	param_list = param_tmp;
X	last_param = param_list;
X      } else {
X	last_param->next = param_tmp;
X	last_param = param_tmp;
X      }
X
X    } else if (str[i] == ' ') {
X      if (in_word == 2) {
X	in_word = 0;
X      }
X    } else if (id_char(str[i])) {
X      if (in_word == 0) {
X	syntax_err("invalid parameter list encountered");
X	return(-1);
X      }
X      in_word = 2;
X      tempc[count++] = str[i];
X    } else {
X      syntax_err("unable to parse the complex C code");
X      return(-1);
X    }
X
X  }
X
X  /* now store the last name if any was found */
X  if (count != 0) {
X
X    /* store the name */
X    tempc[count] = '\0';
X    if ((param_tmp = (P_PARAM) malloc(sizeof(S_PARAM))) == NULL) {
X      fprintf(stderr, "Serious Error: Memory Allocation Failure\n");
X      exit(1);
X    }
X    if ((param_tmp->name =
X	 (char *) malloc(sizeof(char) * (strlen(tempc) + 1))) == NULL) {
X      fprintf(stderr, "Serious Error: Out of memory\n");
X      exit(1);
X    }
X    strcpy(param_tmp->name, tempc);
X    param_tmp->desc = NULL;
X    param_tmp->next = NULL;
X    if (param_list == NULL) {
X      param_list = param_tmp;
X      last_param = param_list;
X    } else {
X      last_param->next = param_tmp;
X      last_param = param_tmp;
X    }
X  }
X  return(paren_loc);
X}
X
X/* remove any preceding and trailing spaces */
Xvoid
Xtrim_str(str)
X  char *str;
X{
X  register int i = 0, j = 0;
X  int last_space = -1;
X
X  /* trim leading spaces */
X  while (isspace(str[i])) i++;
X
X  /* shift string */
X  while (1) {
X    if ((str[j] = str[i++]) == '\0') {
X      break;
X    } else if (isspace(str[j])) {
X      last_space = j;
X    } else {
X      last_space = -1;
X    }
X    j++;
X  }
X
X  /* now remove any trailing spaces */
X  if (last_space != -1) {
X    str[last_space] = '\0';
X  }
X}
X
X/* just go through and send out the entire parameter list */
Xstatic int
Xoldc_output()
X{
X  int count = 0, lcount = 0;
X  P_PARAM param_tmp;
X
X  /* first test for the simple case */
X  if (param_list == NULL) {
X    strcpy(dummy_str, "void");
X    return(4);
X  }
X  dummy_str[0] = '\0';
X
X  /* go through the list */
X  while (param_list != NULL) {
X
X    /* add in separators */
X    if (count++ > 0) {
X
X      strcat(dummy_str, ", ");
X      lcount += 2;
X
X    }
X
X    /* check the type */
X    if (param_list->desc != NULL) {
X      trim_str(param_list->desc);
X      strcat(dummy_str, param_list->desc);
X      lcount += strlen(param_list->desc);
X      free(param_list->desc);
X    } else {
X      if (get_option(OPT_NONAMES)) {
X	strcat(dummy_str, "int");
X	lcount += 3;
X      } else if (strcmp("int", param_list->name)) {
X	strcat(dummy_str, "int ");
X	strcat(dummy_str, param_list->name);
X	lcount += 4 + strlen(param_list->name);
X      } else {
X	strcat(dummy_str, param_list->name);
X	lcount += strlen(param_list->name);
X      }
X    }
X    free(param_list->name);
X
X    /* clean up storage */
X    param_tmp = param_list;
X    param_list = param_list->next;
X    param_tmp->next = NULL;
X    free(param_tmp);
X
X  }
X
X  /* finish it up */
X  return(lcount);
X}
X
X/* return the proper element */
Xstatic P_PARAM
Xvar_match(str)
X  char *str;
X{
X  int i, len, len2;
X  P_PARAM p_hold;
X
X  /* go through all of the items and find a match */
X  len2 = strlen(str);
X  p_hold = param_list;
X  while (p_hold != NULL) {
X
X    /* check for a match */
X    len = strlen(p_hold->name);
X    for (i = 0; i < len2 - len + 1; i++) {
X      /* check for the match */
X      if (strncmp(str + i, p_hold->name, len) == 0) {
X	/* found a match */
X	if (!id_char(str[i + len])) {
X	  return(p_hold);
X	}
X      } else if (id_char(str[i])) {
X	/* definite non-match */
X	i = len2 - len + 1;
X      }
X    }
X
X    /* go to the next element */
X    p_hold = p_hold->next;
X  }
X
X  return(p_hold);
X}
X
X/* remove the variable name from the given type and variable combo */
Xstatic void
Xkill_variable(str)
X  char *str;
X{
X  register int i;
X
X  /* no need to breakdown this baby */
X  if (strcmp(str, "...") == 0) return;
X
X  /* search for the variable name */
X  trim_str(str);
X  for (i = strlen(str) - 1; i >= 0; i--) {
X
X    /* found it */
X    if (id_char(str[i])) {
X
X      /* clean it up */
X      while ((i >= 0) &&
X	     id_char(str[i])) {
X	str[i--] = ' ';
X      }
X      i++;
X      trim_str(str + i);
X      if (i == 0) {
X	strcpy(str, "int");
X      } else {
X	trim_str(str);
X      }
X      break;
X
X    }
X
X  }
X}
X
X/* separate and rebuild the ANSI format list */
Xstatic int
Xnewc_parse (i_str)
X  char *i_str;
X{
X  char hold_str[MID_SIZE], hold2_str[MID_SIZE];
X  P_SUBST sub_tmp;
X  int depth, cnt = 0, len, i;
X
X  /* start the process */
X  dummy_str[0] = '\0';
X  do {
X
X    /* move along the input string */
X    switch (*i_str) {
X    case ',':
X    case '\0':
X      /* finish off this item */
X      if (cnt == 0) break;
X      hold_str[cnt] = '\0';
X
X      /* search for matches among the replacement code */
X      for (sub_tmp = subst_list;
X	   sub_tmp != NULL;
X	   sub_tmp = sub_tmp->next) {
X
X	/* check for usage */
X	if ((sub_tmp->usewhen != 2) &&
X	    (sub_tmp->usewhen != (doc_extract != DOC_NONE))) continue;
X
X	/* check it */
X	len = strlen(sub_tmp->from_str);
X	switch (sub_tmp->submode) {
X	case SUBST_FULL:
X	  /* the full string needs replacing? */
X	  if (strcmp(hold_str, sub_tmp->from_str) == 0) {
X	    strcpy(hold_str, sub_tmp->to_str);
X	  }
X	  break;
X	case SUBST_TYPE:
X	  /* the type only needs replacing? */
X	  if (strncmp(hold_str, sub_tmp->from_str, len) == 0) {
X	    strcpy(hold2_str, hold_str + len);
X	    strcpy(hold_str, sub_tmp->to_str);
X	    strcat(hold_str, hold2_str);
X	  }
X	  break;
X	case SUBST_NAME:
X	  /* the variable only needs replacing? WHY!? */
X	  if ((cnt > len) &&
X	      (strcmp(hold_str + cnt - len, sub_tmp->from_str) == 0)) {
X	    hold_str[cnt - len] = '\0';
X	    strcat(hold_str, sub_tmp->to_str);
X	  }
X	  break;
X	}
X      }
X
X      /* check for removal of register type */
X      if (strncmp(hold_str, "register ", 9) == 0) {
X	for (i = 0; i < 9; i++) {
X	  hold_str[i] = ' ';
X	}
X      }
X
X      /* do we clean it? */
X      if (get_option(OPT_NONAMES)) {
X	kill_variable(hold_str);
X      } else {
X	trim_str(hold_str);
X      }
X
X      /* append to output */
X      if (dummy_str[0] != '\0') {
X	strcat(dummy_str, ", ");
X      }
X      strcat(dummy_str, hold_str);
X      cnt = 0;
X      break;
X    case '{':
X      /* collect up all of the type declaration */
X      depth = -1;
X      do {
X	hold_str[cnt++] = *i_str;
X	switch (*i_str) {
X	case '\0':
X	  syntax_err("unexpected end of parameters");
X	  return(-1);
X	case '{':
X	  depth++;
X	  break;
X	case '}':
X	  depth--;
X	  break;
X	default:
X	  /* ignore me */
X	  break;
X	}
X	i_str++;
X      } while ((depth > 0) ||
X	       (*i_str != '}'));
X      hold_str[cnt++] = *i_str;
X      break;
X    case ' ':
X      if (cnt == 0) break;
X    default:
X      /* just copy it */
X      hold_str[cnt++] = *i_str;
X    }
X
X  } while (*(i_str++) != '\0');
X
X  /* give back the length */
X  return(strlen(dummy_str));
X}
X
X/* extract all of the parameters using old style format */
Xstatic int
Xoldc_parse(str)
X  char *str;
X{
X  int depth = 0, last_char = ')';
X  int start, count2, in_var;
X  P_PARAM p_tmp;
X  P_SUBST sub_tmp;
X
X  char type_name[MID_SIZE], var_name[MID_SIZE];
X  char oldvar_name[MID_SIZE], tempc[MID_SIZE];
X
X  /* build the parameter list */
X  if ((start = fill_param(str)) == -1) {
X    /* clean up and exit on error */
X    while (param_list != NULL) {
X      p_tmp = param_list;
X      param_list = param_list->next;
X      p_tmp->next = NULL;
X      free(p_tmp);
X    }
X    return(-1);
X  }
X  if (str[start] != ')') {
X    strcpy(dummy_str, "void");
X    return(4);
X  }
X
X  /* begin with non-space character */
X  for (str += start + 1; *str == ' '; str++) ;
X  count2 = 0;
X  in_var = FALSE;
X  type_name[0] = '\0';
X
X  /* now go through the entire structure */
X  for (; *str != '\0'; str++) {
X
X    switch (*str) {
X    case ' ':
X      /* add on to variable or append to type definition */
X      if (in_var == TRUE) {
X	var_name[count2++] = ' ';
X	break;
X      }
X      break;
X    case '{':
X      /* must be a struct declaration */
X      if ((depth != 0) ||
X	  (in_var == TRUE)) {
X	syntax_err("unexpected left brace encountered");
X	return(-1);
X      }
X      depth = -1;
X      if (var_name[count2] != ' ') {
X	var_name[count2++] = ' ';
X      }
X      do {
X	var_name[count2++] = *str;
X	switch (*str) {
X	case '\0':
X	  syntax_err("unable to parse the complex C structure");
X	  return(-1);
X	case '{':
X	  depth++;
X	  break;
X	case '}':
X	  depth--;
X	  break;
X	default:
X	  /* ignore me */
X	  break;
X	}
X	str++;
X      } while ((depth > 0) ||
X	       (*str != '}'));
X      goto saw_space;
X      break;
X    case ',':
X      /* just add on if between parenthesis */
X      if (depth != 0) {
X	var_name[count2++] = ',';
X	break;
X      }
X    case ';':
X      /* found end of declaration? */
X      var_name[count2] = '\0';
X      if ((depth != 0) || (type_name[0] == '\0')) {
X	syntax_err("unable to parse the complex C structure");
X	return(-1);
X      }
X      if (var_name[0] == '\0') {
X	syntax_err("unable to parse the complex C structure");
X	return(-1);
X      }
X
X      /* build the proper string */
X      trim_str(type_name);
X      strcpy(tempc, type_name);
X      strcat(tempc, " ");
X      trim_str(var_name);
X      strcpy(oldvar_name, var_name);
X      strcat(tempc, var_name);
X
X      /* search for matches among the replacement code */
X      for (sub_tmp = subst_list;
X	   sub_tmp != NULL;
X	   sub_tmp = sub_tmp->next) {
X	/* check it */
X	switch (sub_tmp->submode) {
X	case SUBST_FULL:
X	  /* the full string needs replacing? */
X	  if (strcmp(tempc, sub_tmp->from_str) == 0) {
X	    strcpy(tempc, sub_tmp->to_str);
X	  }
X	  break;
X	case SUBST_TYPE:
X	  /* the type only needs replacing? */
X	  if (strcmp(type_name, sub_tmp->from_str) == 0) {
X	    strcpy(tempc, sub_tmp->to_str);
X	    strcpy(type_name, sub_tmp->to_str);
X	    strcat(tempc, " ");
X	    strcat(tempc, var_name);
X	  }
X	  break;
X	case SUBST_NAME:
X	  /* the variable only needs replacing? WHY!? */
X	  if (strcmp(var_name, sub_tmp->from_str) == 0) {
X	    strcpy(tempc, type_name);
X	    strcat(tempc, " ");
X	    strcat(tempc, sub_tmp->to_str);
X	    strcpy(var_name, sub_tmp->to_str);
X	  }
X	  break;
X	}
X      }
X
X      /* now find the proper variable name */
X      if ((p_tmp = var_match(oldvar_name)) == NULL) {
X	char temp_str[MID_SIZE];
X	sprintf(temp_str, "could not place variable %s properly",
X		oldvar_name);
X	syntax_err(temp_str);
X	return(-1);
X      }
X
X      /* now check for variable name removal */
X      if (get_option(OPT_NONAMES)) {
X	kill_variable(tempc);
X      }
X
X      /* set aside space */
X      if ((p_tmp->desc =
X	   (char *) malloc(sizeof(char) * (strlen(tempc) + 1))) == NULL) {
X	fprintf(stderr, "Serious Memory Allocation Error\n");
X	exit(1);
X      }
X
X      /* now stow it */
X      strcpy(p_tmp->desc, tempc);
X
X      /* reset properly */
X      count2 = 0;
X      var_name[0] = '\0';
X      if (*str == ';') {
X	in_var = FALSE;
X	type_name[0] = '\0';
X      }
X      break;
X    case '*':
X      /* just append on, must now be in variable */
X      if ((last_char == ' ') && (in_var == FALSE)) {
X	in_var = TRUE;
X	goto saw_space;
X      } else if (in_var == FALSE) {
X	/* must push out type properly */
X	var_name[count2] = '\0';
X	count2 = 0;
X	if ((type_name[0] != '\0') &&
X	    (type_name[strlen(type_name) - 1] != ' '))  {
X	  strcat(type_name, " ");
X	}
X	if ((type_name[0] != '\0') ||
X	    (strcmp(var_name, "register") != 0)) {
X	  strcat(type_name, var_name);
X	}
X	var_name[0] = '\0';
X      }
X      in_var = TRUE;
X      var_name[count2++] = '*';
X      break;
X    case '(':
X    case '[':
X      /* count depth, assume balanced for both */
X      depth++;
X      if ((last_char == ' ') &&
X	  (in_var == FALSE)) {
X	in_var = TRUE;
X	goto saw_space;
X      }
X      in_var = TRUE;
X      var_name[count2++] = *str;
X      break;
X    case ')':
X    case ']':
X      /* check depth */
X      depth--;
X      if (depth < 0) {
X	syntax_err("misbalanced parenthesis encountered");
X	return(-1);
X      }
X      if (last_char == ' ') goto saw_space;
X      var_name[count2++] = *str;
X      break;
X    default:
X      if (!id_char(*str) && (depth == 0)) {
X	syntax_err("unknown variable structure encountered");
X	return(-1);
X      }
X      /* check for just seeing space */
X      if (last_char == ' ' && in_var == FALSE) {
X
X      saw_space:
X	var_name[count2] = '\0';
X	count2 = 0;
X	if ((type_name[0] != '\0') &&
X	    (type_name[strlen(type_name) - 1] != ' '))  {
X	  strcat(type_name, " ");
X	}
X	if ((type_name[0] != '\0') ||
X	    (strcmp(var_name, "register") != 0)) {
X	  strcat(type_name, var_name);
X	}
X	var_name[0] = '\0';
X
X      }
X      var_name[count2++] = *str;
X      break;
X    }
X
X    /* save the last character */
X    last_char = *str;
X  }
X
X  /* now send everything out */
X  return(oldc_output());
X}
X
X/* function to determine if the parameter list is K&R, ANSI or empty */
Xstatic int
Xdiverge_style(str, len)
X  char *str;
X  int len;
X{
X  int void_fnd = FALSE, nspc_fnd = FALSE, in_word = 1;
X
X  /* perform the first simple test */
X  if (str[len - 1] == ';') return(2);
X
X  /* should have a right parenthesis now  */
X  if (str[len - 1] != ')') {
X    fprintf(stderr, "Expecting right paren in file %s\n", file_name);
X    return(1);
X  }
X
X  /* now check to see if it only lists all variables */
X  for (str++; *str != '\0'; str++) {
X
X    /* check for separators */
X    switch (*str) {
X    case ',':
X      /* variable separator */
X      if (in_word == 1) {
X	fprintf(stderr, "Empty variable list in file %s", file_name);
X	return(1);
X      }
X      nspc_fnd = TRUE;
X      in_word = 1;
X      break;
X    case ' ':
X      /* word separator */
X      if (in_word == 2) {
X	in_word = 0;
X      }
X      break;
X    case ')':
X      if (*(str + 1) != '\0') {
X	return(1);
X      }
X      break;
X    default:
X      /* check for variable name */
X      if ((void_fnd == FALSE) &&
X	  (strncmp(str, "void", 4) == 0)) {
X	void_fnd = TRUE;
X	str += 3;
X	in_word = 2;
X      } else if (id_char(*str)) {
X	if (in_word == 0) {
X	  return(1);
X	}
X	in_word = 2;
X	nspc_fnd = TRUE;
X      } else {
X	return(1);
X      }
X      break;
X    }
X  }
X  return((nspc_fnd == TRUE)? 2 : 0);
X}
X
X/* return length of function name... and store it in space provide */
Xstatic int
Xfind_name(out_name, in_desc, desc_len)
X  char *out_name, *in_desc;
X  int desc_len;
X{
X  int pos, count = 0;
X
X  /* find the end of the name */
X  for (pos = desc_len; pos > 0; pos--) {
X    if (id_char(in_desc[pos])) break;
X  }
X
X  /* find the length of the name */
X  while ((pos > 0) &&
X	 (id_char(in_desc[pos]))) {
X    count++;
X    pos--;
X  }
X
X  /* copy it */
X  if (!id_char(in_desc[pos])) {
X    pos++;
X  } else {
X    count++;
X  }
X  strncpy(out_name, in_desc + pos, count);
X  out_name[count] = '\0';
X  return(count);
X}
X
X/* function to extract the function prototype from preceding characters */
Xstatic void
Xparse_func()
X{
X  P_SUBST sub_tmp;
X  P_PROTO tmp_proto;
X  int count, valid = TRUE, depth = 0, done = FALSE;
X  int sep_point = 0, typelen, cmpstt, len, dummy_len = 0;
X  char *func_declare, *func_list, name_space[MID_SIZE];
X
X  /* clean up the input string */
X  trim_str(code_info);
X  len = strlen(code_info);
X
X  /* now go backwards and find the first occurance */
X  /* of a right parenthesis without a '[', ',' or ';' after */
X  for (count = len - 1; done == FALSE && count > 0; count--) {
X
X    /* check for a select group of characters */
X    switch (code_info[count]) {
X    case ' ':
X      /* don't change validity for spaces */
X      break;
X    case ')':
X      /* check if it is a good match */
X      depth++;
X      if (depth == 1 && valid == TRUE) {
X	done = TRUE;
X      }
X      break;
X    case '(':
X      /* keep proper track of depth */
X      depth--;
X      if (depth < 0) {
X	syntax_err("too many left parenthesis encountered");
X	break;
X      }
X
X      /* watch for function pointers */
X      valid = FALSE;
X      break;
X    case '[':
X    case ';':
X    case ',':
X      /* any paranthesis before this is invalid */
X      valid = FALSE;
X      break;
X    case '=':
X      /* there should be no equal signs anywhere around this */
X      valid = FALSE;
X      count = 0;
X      break;
X    default:
X      /* it can now be a valid parenthesis */
X      valid = TRUE;
X      break;
X    }
X  }
X
X  /* get out if no function type was found */
X  if (done != TRUE) return;
X
X  /* now find the separation point for the function */
X  done = FALSE;
X  for (; done == FALSE && count > 0; count--) {
X    valid = code_info[count];
X    switch (valid) {
X    case ' ':
X      break;
X    case ')':
X      /* go deeper */
X      depth++;
X      break;
X    case '(':
X      /* rise higher */
X      depth--;
X      if (depth == 0) {
X	sep_point = count;
X	done = TRUE;
X      }
X      break;
X    default:
X      break;
X    }
X  }
X
X  /* confirm separation point */
X  if (done == FALSE) {
X    syntax_err("too many right parenthesis encountered");
X    return;
X  }
X
X  /* now find the start of the function declaration */
X  for (; count > 0; count--) {
X    if ((code_info[count] == ';') ||
X	(code_info[count] == '}')) break;
X    if (code_info[count] == '(') {
X      syntax_err("unexpected left parenthesis found while parsing parameters");
X      return;
X    }
X  }
X  if ((code_info[count] == ';') ||
X      (code_info[count] == '}')) count++;
X
X  /* gain space */
X  if ((func_list = (char *) malloc(sizeof(char) * (len - sep_point + 1)))
X      == NULL) {
X    fprintf(stderr, "Serious Error: Malloc failed\n");
X    exit(1);
X  }
X  if ((func_declare =
X       (char *) malloc(sizeof(char) * (sep_point - count + 1))) == NULL) {
X    fprintf(stderr, "Serious Error: Malloc failed\n");
X    exit(1);
X  }
X
X  /* now assign the locations */
X  strcpy(func_list, &(code_info[sep_point]));
X  strncpy(func_declare, &(code_info[count]), sep_point - count);
X  func_declare[sep_point - count] = '\0';
X  trim_str(func_list);
X  trim_str(func_declare);
X
X  /* find just the function name */
X  len = strlen(func_declare);
X  count = find_name(name_space, func_declare, len);
X
X  /* just leave if there is no function */
X  if (count == 0) {
X    free(func_list);
X    free(func_declare);
X    return;
X  }
X
X  /* check for statics which shouldn't be shown */
X  if (get_option(OPT_STATICMODE) != ANY_STATICS) {
X    if (!strncmp(func_declare, "static ", 7) ==
X	(get_option(OPT_STATICMODE) == NO_STATICS)) {
X      last_comment[0] = '\0';
X      comment_len = 0;
X      free(func_list);
X      free(func_declare);
X      return;
X    }
X  }
X
X  /* declare storage space for the function */
X  if ((tmp_proto = (P_PROTO) malloc(sizeof(S_PROTO))) == NULL) {
X    fprintf(stderr, "Memory allocation failure\n");
X    exit(1);
X  }
X  tmp_proto->next = NULL;
X  tmp_proto->name = NULL;
X  tmp_proto->ftype = NULL;
X  tmp_proto->fname = NULL;
X  tmp_proto->plist = NULL;
X  tmp_proto->comment = NULL;
X
X  /* place it in the list */
X  if ((proto_next == NULL) || (proto_list == NULL)) {
X    proto_next = proto_list = tmp_proto;
X  } else {
X    proto_next->next = tmp_proto;
X    proto_next = tmp_proto;
X  }
X  if ((proto_next->fname =
X       (char *) malloc(sizeof(char) * (strlen(file_name) + 2))) == NULL) {
X    fprintf(stderr, "Memory allocation error\n");
X    exit(1);
X  }
X  strcpy(proto_next->fname, file_name);
X
X  /* now copy any preceding comments if desired */
X  if (get_option(OPT_COMMENTS) &&
X      (last_comment[0] != '\0')) {
X    if ((proto_next->comment =
X	 (char *) malloc(sizeof(char) * (comment_len + 5))) == NULL) {
X      fprintf(stderr, "Memory allocation error\n");
X      exit(1);
X    }
X    strcpy(proto_next->comment, "/*");
X    strcat(proto_next->comment, last_comment);
X    last_comment[0] = '\0';
X    comment_len = 0;
X  } else {
X    proto_next->comment = NULL;
X  }
X
X  /* check for the conversion of the type declaration */
X  if (count != len) {
X    if (strncmp("extern ", func_declare, 7) == 0) {
X      cmpstt = 7;
X    } else {
X      cmpstt = 0;
X    }
X    for (sub_tmp = subst_list;
X	 sub_tmp != NULL;
X	 sub_tmp = sub_tmp->next) {
X      /* check it */
X      if (sub_tmp->submode == SUBST_TYPE) {
X
X	/* compare */
X	typelen = strlen(sub_tmp->from_str);
X	if (strncmp(func_declare + cmpstt, sub_tmp->from_str, typelen) == 0) {
X	  strcpy(tmp_str, sub_tmp->to_str);
X	  strcat(tmp_str, func_declare + cmpstt + typelen);
X	  strcpy(func_declare, tmp_str);
X	  len += (strlen(sub_tmp->to_str) - typelen - cmpstt);
X	}
X
X      }
X    }
X  }
X
X  /* output extern if desired */
X  if (get_option(OPT_EXTERNS) &&
X      strncmp("extern ", func_declare, 7) != 0) {
X    strcpy(dummy_str, "extern ");
X    dummy_len = 7;
X  } else {
X    dummy_str[0] = '\0';
X    dummy_len = 0;
X  }
X
X  /* now check if the function type is an integer */
X  if (count == len) {
X    strcat(dummy_str, "int ");
X    strcat(dummy_str, func_declare);
X    dummy_len += 4 + len;
X  } else {
X    strcat(dummy_str, func_declare);
X    dummy_len += len;
X  }
X
X  /* store the function header */
X  if ((proto_next->ftype =
X       (char *) malloc(sizeof(char) * (dummy_len + 2))) == NULL) {
X    fprintf(stderr, "Memory allocation error\n");
X    exit(1);
X  }
X  strcpy(proto_next->ftype, dummy_str);
X
X  /* the function string */
X  if ((proto_next->name =
X       (char *) malloc(sizeof(char) * (count + 2))) == NULL) {
X    fprintf(stderr, "Memory allocation error\n");
X    exit(1);
X  }
X  strcpy(proto_next->name, name_space);
X
X  /* now output empty list amount */
X  len = strlen(func_list);
X  switch (diverge_style(func_list, len)) {
X  case 0:
X    /* empty or "void" parameter list */
X    strcpy(dummy_str, "void");
X    dummy_len = 4;
X    break;
X  case 1:
X    /* ANSI C format! -- remove trailing parenthesis */
X    func_list[--len] = '\0';
X    if ((dummy_len = newc_parse(func_list + 1)) == -1) {
X      free(func_list);
X      free(func_declare);
X      pop_proto();
X      return;
X    }
X    break;
X  default:
X    /* K&R C format */
X    if ((dummy_len = oldc_parse(func_list)) == -1) {
X      free(func_list);
X      free(func_declare);
X      pop_proto();
X      return;
X    }
X    break;
X  }
X
X  /* store it */
X  if ((proto_next->plist =
X       (char *) malloc(sizeof(char) * (dummy_len + 2))) == NULL) {
X    fprintf(stderr, "Memory allocation error\n");
X    exit(1);
X  }
X  strcpy(proto_next->plist, dummy_str);
X
X  /* replace space */
X  free(func_list);
X  free(func_declare);
X}
X
X/* go through the file extracting functions */
Xvoid
Xparse_file()
X{
X  int curr_char, count = 0, was_comment = FALSE, may_flush = FALSE;
X  int depth = 0, num_comment = 0, temp_count = 0, temp_lines = 0;
X  char temp_list[MID_SIZE];
X  int prev_char = '\n', old_prev_real = ' ', prev_real = ' ';
X
X  /* go to it */
X  start_comment[0] = '\0';
X  last_comment[0] = '\0';
X  temp_list[0] = '\0';
X  while ((curr_char = getc(fpin)) != EOF) {
X
X    /* check depth first */
X    if (curr_char == '\n') line_count++;
X    if (depth == 0) {
X
X      /* process the characters */
X      if (curr_char == '#' && prev_char == '\n') {
X
X	/* nuke any preprocessor statements */
X	was_comment = FALSE;
X	prev_char = '#';
X	curr_char = preprocessor_check();
X
X      } else if (curr_char == '"') {
X
X	/* nuke string quotes -- must be on same line */
X	prev_char = curr_char;
X	count = 0;
X	while (!feof(fpin)) {
X	  if ((curr_char = getc(fpin)) == '"') {
X	    break;
X	  } else if (curr_char == '\n') {
X	    syntax_err("unexpected newline found in string");
X	    line_count++;
X	  } else if (curr_char == '\\') {
X	    prev_char = curr_char;
X	    curr_char = getc(fpin);
X	  }
X	  prev_char = curr_char;
X	}
X
X      } else if (curr_char == '\'') {
X
X	/* nuke character quotes -- must be on same line */
X	count = 0;
X	prev_char = curr_char;
X	while (!feof(fpin)) {
X	  if ((curr_char = getc(fpin)) == '\'') {
X	    break;
X	  } else if (curr_char == '\n') {
X	    syntax_err("unexpected newline in character constant");
X	    line_count++;
X	  } else if (curr_char == '\\') {
X	    prev_char = curr_char;
X	    curr_char = getc(fpin);
X	  }
X	  prev_char = curr_char;
X	}
X
X      } else if (curr_char == '*' && prev_char == '/') {
X
X	/* clear out commments; treat as spaces */
X	if (count > 0) count--;
X	if (was_comment == FALSE) {
X	  num_comment++;
X	  temp_count = 0;
X	} else {
X	  temp_count--;
X	  if ((get_option(OPT_SINGLECOMMENTS) == TRUE) &&
X	      (temp_count > 0)) {
X	    num_comment++;
X	    was_comment = FALSE;
X	    temp_count = 0;
X	  }
X	}
X	temp_list[temp_count] = '\0';
X	curr_char = ' ';
X	if (get_option(OPT_COMMENTS) ||
X	    ((num_comment == 1) &&
X	     get_option(OPT_FIRSTCOMMENT))) {
X
X	  /* process it */
X	  if (num_comment == 1) {
X	    get_comment(was_comment, temp_list, start_comment);
X	  } else {
X	    get_comment(was_comment, temp_list, last_comment);
X	  }
X	  was_comment = TRUE;
X	  temp_lines = 0;
X	  temp_count = (-1);
X
X	} else {
X
X	  while ((curr_char = getc(fpin)) != EOF) {
X
X	    /* test for end of comment */
X	    if (curr_char == '\n') line_count++;
X	    if ((curr_char == '/') && (prev_char == '*')) {
X	      break;
X	    }
X	    prev_char = curr_char;
X	  }
X	  curr_char = ' ';
X
X	}
X	prev_real = old_prev_real;
X
X      } else if ((curr_char == '/') && (prev_char == '/')) {
X
X	/* just yank out any C++ comments */
X	if (count > 0) count--;
X	while ((curr_char = getc(fpin)) != EOF) {
X
X	  /* test for end of comment */
X	  if (curr_char == '\n') {
X	    line_count++;
X	    break;
X	  }
X	  prev_char = curr_char;
X
X	}
X	curr_char = ' ';
X	prev_real = old_prev_real;
X
X      } else if (curr_char == '{') {
X
X	/* reset recording process */
X	was_comment = FALSE;
X	may_flush = FALSE;
X	depth++;
X
X	/* now check if it is a function */
X	if ((prev_real == ';') ||
X	    (prev_real == ')')) {
X
X	  /* found end of function, struct, or union definition */
X	  code_info[count] = '\0';
X	  parse_func();
X	  code_info[count = 0] = '\0';
X
X	} else {
X	  code_info[count++] = '{';
X	}
X
X      } else if (curr_char == '}') {
X
X	syntax_err("extra right brace encountered");
X	break;
X
X      }
X
X      /* tag onto the list */
X      if (depth == 0) {
X
X	/* store the characters for later use */
X	if (isspace(curr_char) &&
X	    (count != 0 && code_info[count - 1] != ' ')) {
X
X	  /* combine any white space into a single space character */
X	  code_info[count++] = ' ';
X
X	} else if (!isspace(curr_char)) {
X
X	  /* store any other type directly */
X	  if (curr_char != '/') {
X	    was_comment = FALSE;
X	    temp_count = 0;
X	    temp_lines = 0;
X	  }
X	  code_info[count++] = curr_char;
X	  old_prev_real = prev_real;
X	  prev_real = curr_char;
X
X	}
X
X	/* count the lead in */
X	if (was_comment == TRUE) {
X
X	  /* no multiple newlines */
X	  if (curr_char == '\n') {
X	    if (temp_lines == 0) {
X	      temp_lines = 1;
X	    } else {
X	      temp_lines = 0;
X	      temp_count = -1;
X	      was_comment = FALSE;
X	    }
X	  }
X
X	  /* check beginning */
X	  if (temp_count >= 0) {
X	    temp_list[temp_count] = curr_char;
X	  }
X	  temp_count++;
X
X	}
X
X	/* now note what was last encountered */ 
X	prev_char = curr_char;
X
X      }
X
X    } else {
X
X      /* keep track of depth and got through code otherwise */
X      if (curr_char == '#' && prev_char == '\n') {
X
X	/* nuke any preprocessor statements */
X	prev_char = '#';
X	curr_char = preprocessor_check();
X
X      } else if (curr_char == '"') {
X
X	/* nuke string quotes -- must be on same line */
X	may_flush = TRUE;
X	while (!feof(fpin)) {
X	  prev_char = curr_char;
X	  if ((curr_char = getc(fpin)) == '\n') {
X	    syntax_err("unexpected newline found in string");
X	    line_count++;
X	  } else if (curr_char == '\\') {
X	    prev_char = curr_char;
X	    curr_char = getc(fpin);
X	    continue;
X	  } else if (curr_char == '"') {
X	    break;
X	  }
X	};
X
X      } else if (curr_char == '\'') {
X
X	/* nuke character quotes -- must be on same line */
X	may_flush = TRUE;
X	while (!feof(fpin)) {
X	  prev_char = curr_char;
X	  if ((curr_char = getc(fpin)) == '\n') {
X	    syntax_err("unexpected newline in character constant");
X	    line_count++;
X	  } else if (curr_char == '\\') {
X	    prev_char = curr_char;
X	    curr_char = getc(fpin);
X	  } else if (curr_char == '\'') {
X	    break;
X	  } 
X	}
X
X      } else if (curr_char == '*' && prev_char == '/') {
X
X	/* nuke comments */
X	num_comment++;
X	while ((curr_char = getc(fpin)) != EOF) {
X
X	  /* test for end of comment */
X	  if (curr_char == '\n') line_count++;
X	  if ((curr_char == '/') && (prev_char == '*')) {
X	    break;
X	  }
X	  prev_char = curr_char;
X
X	}
X	curr_char = ' ';
X
X      } else if (curr_char == '/' && prev_char == '/') {
X
X	/* nuke C++ comments (don't count it) */
X	while ((curr_char = getc(fpin)) != EOF) {
X
X	  /* test for end of comment */
X	  if (curr_char == '\n') {
X	    line_count++;
X	    break;
X	  }
X	  prev_char = curr_char;
X
X	}
X	curr_char = ' ';
X
X      } else if (curr_char == '{') {
X
X	depth++;
X	code_info[count++] = curr_char;
X
X      } else if (curr_char == '}') {
X
X	--depth;
X	if (count > 0) code_info[count++] = curr_char;
X	if (may_flush) {
X	  if (depth == 0) may_flush = FALSE;
X	  code_info[count = 0] = '\0';
X	}
X
X      } else {
X	/* just copy on the off chance */
X	code_info[count++] = curr_char;
X      }
X
X      /* now note what was last encountered */ 
X      prev_char = curr_char;
X
X    }
X
X  }
X}
X
END_OF_FILE
if test 54378 -ne `wc -c <'parse.c'`; then
    echo shar: \"'parse.c'\" unpacked with wrong size!
fi
# end of 'parse.c'
fi
echo shar: End of archive 5 \(of 5\).
cp /dev/null ark5isdone
MISSING=""
for I in 1 2 3 4 5 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 5 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

exit 0 # Just in case...
-- 
        Andrew Patrick acting as Comp.Sources.Reviewed Moderator
              Department of Communications, Ottawa, CANADA
                         csr@calvin.dgbt.doc.CA
Technical Details
Message-ID: <1992Nov3.221258.17006@rick.dgbt.doc.ca>
Path: arc1-ca.pugleaf.net!sparky!uunet!utcsri!torn!cunews!dgbt!rick.dgbt.doc.ca!calvin.dgbt.doc.ca!csr

Show Full Headers
Complete Article Headers
Newsgroups: comp.sources.reviewed
Path: arc1-ca.pugleaf.net!sparky!uunet!utcsri!torn!cunews!dgbt!rick.dgbt.doc.ca!calvin.dgbt.doc.ca!csr
From: Adam Bryant <adb@cs.bu.edu>
Subject: v02i043: cextract - (Ver. 1.7) C prototyper/header file generator, Part05/05
Message-ID: <1992Nov3.221258.17006@rick.dgbt.doc.ca>
Originator: csr@calvin.dgbt.doc.ca
Sender: news@rick.dgbt.doc.ca
Nntp-Posting-Host: calvin.dgbt.doc.ca
Organization: Communications Research Centre, Ottawa
Date: Tue, 3 Nov 1992 22:12:58 GMT
Approved: csr@calvin.dgbt.doc.ca
Lines: 2076