🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.sources.reviewed
Article #10

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

#10
From: Adam Bryant
Date: Tue, 03 Nov 1992 22:11
939 lines
28365 bytes
Submitted-by: Adam Bryant <adb@cs.bu.edu>
Posting-number: Volume 2, Issue 41
Archive-name: cextract/part03
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 3 (of 5)."
# Contents:  main.c
# Wrapped by adb@csa on Fri Oct 30 16:20:36 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'main.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'main.c'\"
else
echo shar: Extracting \"'main.c'\" \(24410 characters\)
sed "s/^X//" >'main.c' <<'END_OF_FILE'
X/*
X *
X * The purpose of this program is to extract the function descriptions
X * (prototypes) from C source code.  It also provides for the creation
X * of documentation based on those prototypes.
X *
X * The specific reason for the creation of this program was to
X * provide a method for automatically creating header files to
X * describe all of the functions to be used within a multi-file
X * program.
X *
X * This file makes use of code in the companion files parse.c and io.c.
X *
X * Copyright (c) 1990, 1991, 1992 by Adam Bryant
X *
X * See Copyright notice in the file parse.c or in the manual page.
X *
X */
X/*
X * Version history:
X *
X * For Version 0.30:  file parse functions were separated out into a
X *   separate file so that other programs could make use of them.  The
X *   functions descriptions are now stored in dynamic memory and
X *   functions are provided to allow the manipulation of those
X *   descriptions.
X * Version 0.31:  Only a few minor adjustments were made to cextract...
X *   only as much as was needed to build the cextdoc program using the
X *   cproto.[ch] files.
X * Version 0.40:  Totally rewrote the parsing code so that the use of
X *   temporary files is unnecessary.  Instead, two dynamic storage
X *   elements have been created.  Besides being much faster, this
X *   method is also much more elegant than the prior method.
X * Version 0.41:  Switched the NOSETBUFFER to a SETBUFFER flag.
X *   Added in the ability to handle the vararg system to convert it
X *   to the "..." for ANSI C.
X *
X * ... change notes for later versions in parse.c
X *
X * Note on Version 1.0: merged the cextract and cextdoc into one
X *   program, with differential being made based on the command line
X *   or in the program name.  Version sent to comp.sources.reviewed.
X *
X */
X#include "xtract.h"
X#ifndef VMS
X#include <sys/types.h>
X#include <sys/stat.h>
X#else
X#include <types.h>
X#include <stat.h>
X#endif /* VMS */
X
X/* definition strings to be set when the C preprocessor runs */
X#ifndef VAXC
X#define CPP_GIVEN "-D__CEXTRACT__"
X#define CPP_GIVEN2 "-D__CEXTDOC__"
X#else
X#define CPP_GIVEN "/define=__CEXTRACT__"
X#define CPP_GIVEN2 "/define=__CEXTDOC__"
X#endif /* VAXC */
X
X/* output filter from the parsing routines */
Xvoid
Xout_char(type, outch)
X  int type, outch;
X{
X  /* to keep track of where the output list is */
X  int tab_width = get_option(OPT_TABWIDTH);
X  static P_BUFDATA curbuf[2] = { NULL, NULL };
X  static int curpos[2] = { 0, 0 };
X  static int tab_count = 0;
X
X  /* split output properly */
X  switch (type) {
X  case 0:
X    /* documentation mode should never get this */
X    if (doc_extract != DOC_NONE) {
X      fprintf(stderr, "Serious Internal Error: bad out_char() data\n");
X      exit(1);
X    }
X  case 1:
X    /* give it to the first one */
X    if (tempbuf[type] == NULL) {
X
X      /* start the list */
X      if ((tempbuf[type] = (P_BUFDATA) malloc(sizeof(S_BUFDATA))) == NULL) {
X	fprintf(stderr, "Memory Allocation Error\n");
X	exit(1);
X      }
X      tempbuf[type]->next = NULL;
X      curbuf[type] = tempbuf[type];
X    }
X
X    /* have we got enough in this one? */
X    if (curpos[type] == MID_SIZE) {
X
X      /* finish it off */
X    build_memory:
X      curbuf[type]->data[MID_SIZE] = '\0';
X      curpos[type] = 0;
X
X      /* start the next one */
X      if ((curbuf[type]->next =
X	   (P_BUFDATA) malloc(sizeof(S_BUFDATA))) == NULL) {
X	fprintf(stderr, "Memory Allocation Error\n");
X	exit(1);
X      }
X      curbuf[type] = curbuf[type]->next;
X      curbuf[type]->next = NULL;
X
X    }
X
X    /* special output filter for the documentation mode */
X    if (doc_extract != DOC_NONE) {
X
X      /* tab expansion? */
X      if (tab_width > 0) {
X	if (outch == '\t') {
X	  /* fill out tabs properly */
X	  while (tab_count++ < tab_width) {
X
X	    /* check that it will fit */
X	    if (curpos[type] == MID_SIZE) {
X	      tab_count--;
X	      goto build_memory;
X	    }
X
X	    /* put out the space */
X	    curbuf[type]->data[(curpos[type])++] = ' ';
X
X	  }
X	  tab_count = 0;
X	  break;
X	} else if (outch == '\\') {
X	  /* account for font changes */
X	  tab_count -= 2;
X	} else if (outch == '\n') {
X	  /* now on a new line */
X	  tab_count = 0;
X	} else {
X	  /* just count */
X	  if (++tab_count == tab_width) {
X	    tab_count = 0;
X	  }
X	}
X      }
X
X    }
X
X    /* now store it */
X    curbuf[type]->data[(curpos[type])++] = outch;
X    break;
X  case -1:
X    /* close everything up */
X    if (curbuf[0] != NULL) {
X      curbuf[0]->data[curpos[0]] = '\0';
X    }
X    if (curbuf[1] != NULL) {
X      curbuf[1]->data[curpos[1]] = '\0';
X    }
X    break;
X  default:
X    /* ain't one of mine */
X    fprintf(stderr, "Serious Internal Error: bad data to out_char()\n");
X    break;
X  }
X
X}
X
X/* show the version of the program */
Xvoid
Xshow_version ()
X{
X  fprintf(stderr, "%s: version %d.%d, Copyright 1992 by Adam Bryant\n",
X	  prog_name, VERSION, PATCHLEVEL);
X}
X
X/* quickly show a sting and indicate if it is on or off */
Xstatic void
Xquick_show (str, mode)
X  char *str;
X  int mode;
X{
X  fprintf(stderr, "\t%s is %s.\n", str, mode ? "on":"off");
X}
X
X/* show all of the settings */
Xvoid
Xshow_settings ()
X{
X  P_MACRO macro_temp;
X  P_SUBST sub_tmp;
X
X  /* give the version info and current settings */
X  show_version();
X  fprintf(stderr, "\n  List of current settings:\n");
X  fprintf(stderr, "\tC preprocessor in use: %s\n", cpp_prog);
X  switch (doc_extract) {
X  case DOC_NONE:
X    fprintf(stderr, "\tfunction prototype extraction mode.\n");
X    if (output_file[0][0] != '\0') {
X      fprintf(stderr, "\tthe output file is \"%s\".\n", output_file[0]);
X    }
X    if (header_string[0] != '\0') {
X      fprintf(stderr, "\tthe header string is \"%s\".\n", header_string);
X    }
X    quick_show("merged ANSI and K&R C output", get_option(OPT_COMPACT));
X    if (!get_option(OPT_COMPACT)) {
X      quick_show("showing both ANSI and K&R C", get_option(OPT_BOTHUSE));
X      if (!get_option(OPT_BOTHUSE)) {
X	quick_show("ANSI C output format", get_option(OPT_STDCUSE));
X      }
X    }
X    quick_show("showing prototypes at all times", get_option(OPT_SHOWANYWAY));
X    break;
X  case DOC_NORMAL:
X    fprintf(stderr, "\ttext documentation mode.\n");
X    if (output_file[1][0] != '\0') {
X      fprintf(stderr, "\tthe output file is \"%s\".\n", output_file[1]);
X    }
X    break;
X  case DOC_ROFF:
X    fprintf(stderr, "\ttroff documentation mode.\n");
X    if (output_file[1][0] != '\0') {
X      fprintf(stderr, "\tthe output file is \"%s\".\n", output_file[1]);
X    }
X    break;
X  default:
X    fprintf(stderr, "\tError, unknown mode.\n\n");
X    exit(1);
X    break;
X  }
X  if (get_option(OPT_SORTMODE) == SORT_ALL) {
X    quick_show("sorting for all functions", TRUE);
X  } else if (get_option(OPT_SORTMODE) == SORT_FILE) {
X    quick_show("sorting for each file", TRUE);
X  } else {
X    quick_show("sorting", FALSE);
X  }
X  if (get_option(OPT_WRAPPOINT) > 0) {
X    fprintf(stderr, "\tlong prototype lists wrap at column %d.\n",
X	    get_option(OPT_WRAPPOINT));
X  } else {
X    quick_show("wrapping of long prototypes", FALSE);
X  }
X  quick_show("separating function name and type", get_option(OPT_TYPEWRAP));
X  quick_show("showing first comment in each file",
X	     get_option(OPT_FIRSTCOMMENT));
X  quick_show("sending file name with first comment",
X	     get_option(OPT_PREPEND));
X  quick_show("comment capturing", get_option(OPT_COMMENTS));
X  if (get_option(OPT_STATICMODE) == ONLY_STATICS) {
X    quick_show("only static functions are searched for", TRUE);
X  } else {
X    quick_show("including static functions in search",
X	       get_option(OPT_STATICMODE) == ANY_STATICS);
X  }
X  quick_show("prepending extern to function declarations",
X	     get_option(OPT_EXTERNS));
X  quick_show("testing for multi-line comments",
X	     !get_option(OPT_SINGLECOMMENTS));
X
X  /* now show all of the macro definitions */
X  if (macro_list != NULL) {
X    fprintf(stderr, "\n  List of preprocessor definitions:\n");
X    for (macro_temp = macro_list;
X	 macro_temp != NULL;
X	 macro_temp = macro_temp->next) {
X      /* use? */
X      if ((macro_temp->usewhen != 2) &&
X	  (macro_temp->usewhen != (doc_extract != DOC_NONE))) continue;
X      fprintf(stderr, "\t%s\n", macro_temp->m_str);
X    }
X  }
X
X  /* now check the substitutions */
X  if (subst_list != NULL) {
X    fprintf(stderr, "\n  List of prototype substitutions:\n");
X    for (sub_tmp = subst_list;
X	 sub_tmp != NULL;
X	 sub_tmp = sub_tmp->next) {
X      /* used? */
X      if ((sub_tmp->usewhen != 2) &&
X	  (sub_tmp->usewhen != (doc_extract != DOC_NONE))) continue;
X
X      /* check it */
X      switch (sub_tmp->submode) {
X      case SUBST_FULL:
X	/* the full string needs replacing? */
X	fprintf(stderr, "\treplace all \"%s\" with \"%s\"\n",
X		sub_tmp->from_str, sub_tmp->to_str);
X	break;
X      case SUBST_TYPE:
X	/* the type only needs replacing? */
X	fprintf(stderr, "\treplace type \"%s\" with \"%s\"\n",
X		sub_tmp->from_str, sub_tmp->to_str);
X	break;
X      case SUBST_NAME:
X	/* the variable only needs replacing? WHY!? */
X	fprintf(stderr, "\treplace name \"%s\" with \"%s\"\n",
X		sub_tmp->from_str, sub_tmp->to_str);
X	break;
X      }
X    }
X  }
X
X  /* all done */
X  fprintf(stderr, "\n");
X  exit(0);
X}
X
X/* quick function to copy a "string" */
Xint
Xcopy_str (o_str, in_str)
X  char *o_str, *in_str;
X{
X  int ch, count = 0, ch_read = 0;
X
X  /* find the beginning of the string */
X  while ((*in_str != '"') &&
X	 (*in_str != '\0')) {
X    ch_read++;
X    in_str++;
X  }
X
X  /* now was a string found? */
X  if (*in_str == '\0') {
X    return(-1);
X  }
X  ch_read++;
X  in_str++;
X
X  /* copy all of the string */
X  while (((ch = *(in_str)) != '\0') &&
X	 (ch != '"')) {
X    /* copy it */
X    o_str[count++] = ch;
X    ch_read++;
X    in_str++;
X  }
X  if (ch == '"') ch_read++;
X
X  /* finish up and get out */
X  o_str[count] = '\0';
X  return(ch_read);
X}
X
X/* show the usage for the program */
Xvoid
Xshow_usage()
X{
X#ifndef VMS
X  fprintf(stderr,
X	  "Usage: %s [-Q#] [+AaBbPpNnVvxZ -Hstr -Yprg -o ofile] [[-opts] file ... ]\n",
X	  prog_name);
X  fprintf(stderr, "\t+A        alphabetically sort all functions [incompatable with +C]\n");
X  fprintf(stderr, "\t+a        alphabetically sort functions within each file [default off]\n");
X  fprintf(stderr, "\t-B        build a system configuration file based on settings\n");
X  fprintf(stderr, "\t-b        build a configuration file in the current directory\n");
X  fprintf(stderr, "\t+C        include first comment from each file\n");
X  fprintf(stderr, "\t+c        include comments immediately preceding functions\n");
X  fprintf(stderr, "\t-Dexpr    send a macro definition to the C preprocessor\n");
X  fprintf(stderr, "\t+E        add a preceding 'extern' to each function definition\n");
X  fprintf(stderr, "\t+F        toggle prepending of file name to first comment [need +C]\n");
X  fprintf(stderr, "\t-f#cc     in troff doc-mode set various font styles\n");
X  fprintf(stderr, "\t-Hstring  build header file with read test on string\n");
X  fprintf(stderr, "\t-Idir     add directory to search list for include files\n");
X  fprintf(stderr, "\t-o file   send output to a file instead of standard output\n");
X  fprintf(stderr, "\t           [take first non-flag as filename for output]\n");
X  fprintf(stderr, "\t+m        allow multi-line comments when parsing comments\n");
X  fprintf(stderr, "\t-N        do documentation output in troff ms format\n");
X  fprintf(stderr, "\t-n        do documentation output in normal text\n");
X  fprintf(stderr, "\t+P        create output in both ANSI and non-ANSI prototypes\n");
X  fprintf(stderr, "\t+p        create output in ANSI C prototype format\n");
X  fprintf(stderr, "\t-Q#       do not read in config files, or only those specified\n");
X  fprintf(stderr, "\t-qfile    read in a specified configuration file\n");
X  fprintf(stderr, "\t+r        remove variable names prior to output\n");
X  fprintf(stderr, "\t+S        show parameters for non-ANSI portion of output\n");
X  fprintf(stderr, "\t+s        include static function definitions\n");
X  fprintf(stderr, "\t-s=only   capture only static function definitions\n");
X  fprintf(stderr, "\t-s=none   exclude static function definitions\n");
X  fprintf(stderr, "\t-s=any    include static function definitions\n");
X  fprintf(stderr, "\t-TN       set tab width in doc-mode to N characters [0 = off]\n");
X  fprintf(stderr, "\t-Uname    undefine the given macro name\n");
X  fprintf(stderr, "\t-V        display the program settings then exit\n");
X  fprintf(stderr, "\t-v        display version information then exit\n");
X  fprintf(stderr, "\t+W        place function type on a separate line.\n");
X  fprintf(stderr, "\t+w#       wrap long parameter output [dflt length 72]\n");
X  fprintf(stderr, "\t-x        run program in extraction mode\n");
X  fprintf(stderr, "\t-Yprog    specify program to use as a C preprocessor\n");
X  fprintf(stderr, "\t+Z        create the output as a compact, merged list\n");
X#else /* VMS */
X  fprintf(stderr, "USAGE: %s [/READ-CONFIG=#] [/OUTPUT=outfile] [[/OPTS] FILE ... ]\n",
X	  prog_name);
X#endif /* VMS */
X  exit(1);
X}
X
X/* set the value of a global option */
Xvoid
Xset_option(which, how, value)
X  Optype which;
X  int how, value;
X{
X#ifdef CHECK_INPUTS
X  /* check input values */
X  if ((which < 0) ||
X      (which >= OPT_NUMBER)) {
X    fprintf(stderr, "SERIOUS ERROR: Internal option value out of range %d\n",
X	    which);
X    exit(1);
X  }
X  if ((how < 0) ||
X      (how > 2)) {
X    fprintf(stderr, "SERIOUS ERROR: Internal option method out of range %d\n",
X	    how);
X    exit(1);
X  }
X#endif /* CHECK_INPUTS */
X
X  /* now set properly */
X  if (how == 2) {
X    global_opts[0][which] = value;
X    global_opts[1][which] = value;
X  } else {
X    global_opts[how][which] = value;
X  }
X}
X
X/* retrieve the value of a global option */
Xint
Xget_option(which)
X  Optype which;
X{
X#ifdef CHECK_INPUTS
X  /* check input values */
X  if ((which < 0) ||
X      (which >= OPT_NUMBER)) {
X    fprintf(stderr, "SERIOUS ERROR: Internal option value out of range %d\n",
X	    which);
X    exit(1);
X  }
X#endif /* CHECK_INPUTS */
X
X  /* return whichever setting is needed */
X  if (doc_extract == DOC_NONE) {
X    return(global_opts[0][which]);
X  }
X  return(global_opts[1][which]);
X}
X
X/* set configuration settings based on the initial name */
Xstatic void
Xcext_init()
X{
X  /* enter the proper mode */
X  if (strncmp(CEXTDOC_NAME, prog_name, strlen(prog_name)) == 0) {
X    doc_extract = DOC_NORMAL;
X  } else {
X    doc_extract = DOC_NONE;
X  }
X
X  /* set documentation mode options properly */
X  set_option(OPT_COMMENTS, 1, TRUE);
X  set_option(OPT_STATICMODE, 1, ANY_STATICS);
X  set_option(OPT_EXTERNS, 1, FALSE);
X  set_option(OPT_STDCUSE, 1, TRUE);
X  set_option(OPT_SORTMODE, 1, SORT_FILE);
X
X  /* set extraction mode options now */
X  set_option(OPT_COMMENTS, 0, FALSE);
X  set_option(OPT_STATICMODE, 0, NO_STATICS);
X  set_option(OPT_EXTERNS, 0, TRUE);
X  set_option(OPT_STDCUSE, 0, FALSE);
X  set_option(OPT_SORTMODE, 0, SORT_NONE);
X
X  /* now set common options */
X  set_option(OPT_NONAMES, 2, FALSE);
X  set_option(OPT_COMPACT, 2, FALSE);
X  set_option(OPT_TYPEWRAP, 2, FALSE);
X  set_option(OPT_WRAPPOINT, 2, 0);
X  set_option(OPT_BOTHUSE, 2, TRUE);
X  set_option(OPT_SINGLECOMMENTS, 2, TRUE);
X  set_option(OPT_FIRSTCOMMENT, 2, FALSE);
X  set_option(OPT_SHOWANYWAY, 2, TRUE);
X  set_option(OPT_PREPEND, 2, FALSE);
X  set_option(OPT_TABWIDTH, 2, 8);
X}
X
X/* return FALSE if character is not a normal part of a filename */
Xint
Xfname_char(ch)
X  int ch;
X{
X  switch (ch) {
X#ifdef MS_DOS
X  case '\\':
X#endif /* MS_DOS */
X#ifdef VMS
X  case ']':
X  case ':':
X#endif /* VMS */
X  case '/':
X    /* nope */
X    return(FALSE);
X  }
X  return(TRUE);
X}
X
X/* routine to build the macros onto the prototype line */
Xstatic void
Xappend_macros(str_out)
X  char *str_out;
X{
X  P_MACRO macro_temp;
X
X#ifndef VAXC
X  for (macro_temp = macro_list;
X       macro_temp != NULL;
X       macro_temp = macro_temp->next) {
X
X    /* check for use */
X    if ((macro_temp->usewhen != 2) &&
X	(macro_temp->usewhen != (doc_extract != DOC_NONE))) continue;
X
X    /* use it */
X    strcat(str_out, macro_temp->m_str);
X    strcat(str_out, " ");
X
X  }
X#else
X  char und_str[MID_SIZE], def_str[MID_SIZE], inc_str[MID_SIZE];
X  int und_uses = 0, def_uses = 0, inc_uses = 0;
X  int und_len = strlen(UNDEF_LEADER);
X  int inc_len = strlen(UNDEF_LEADER);
X  int def_len = strlen(DEF_LEADER);
X
X  /* initialize */
X  und_str[0] = '\0';
X  def_str[0] = '\0';
X  inc_str[0] = '\0';
X
X  /* properly build the command line for the C compiler */
X  for (macro_temp = macro_list;
X       macro_temp != NULL;
X       macro_temp = macro_temp->next) {
X
X    /* check for use */
X    if ((macro_temp->usewhen != 2) &&
X	(macro_temp->usewhen != (doc_extract != DOC_NONE))) continue;
X
X    /* check for the definition statement */
X    if (strncmp(DEF_LEADER, macro_temp->m_str, def_len) == 0) {
X      if (def_uses++ > 0) {
X	strcat(def_str, ",");
X      }
X      if (macro_temp->m_str[def_len] == '(') {
X	strcat(def_str, macro_temp->m_str + def_len + 1);
X	def_str[strlen(def_str) - 1] = '\0';
X	def_uses++;
X      } else {
X	strcat(def_str, macro_temp->m_str + def_len);
X      }
X    } else if (strncmp(UNDEF_LEADER, macro_temp->m_str, und_len) == 0) {
X      if (und_uses++ > 0) {
X	strcat(und_str, ",");
X      }
X      if (macro_temp->m_str[und_len] == '(') {
X	strcat(und_str, macro_temp->m_str + und_len + 1);
X	und_str[strlen(und_str) - 1] = '\0';
X	def_uses++;
X      } else {
X	strcat(und_str, macro_temp->m_str + und_len);
X      }
X    } else if (strncmp(INC_LEADER, macro_temp->m_str, inc_len) == 0) {
X      if (inc_uses++ > 0) {
X	strcat(inc_str, ",");
X      }
X      if (macro_temp->m_str[inc_len] == '(') {
X	strcat(inc_str, macro_temp->m_str + inc_len + 1);
X	und_str[strlen(inc_str) - 1] = '\0';
X	def_uses++;
X      } else {
X	strcat(inc_str, macro_temp->m_str + inc_len);
X      }
X    } else {
X      strcat(str_out, macro_temp->m_str);
X    }
X
X  }
X
X  /* now finish everything off */
X  if (def_uses > 0) {
X    strcat(str_out, DEF_LEADER);
X    if (def_uses > 1) strcat(str_out, "(");
X    strcat(str_out, def_str);
X    if (def_uses > 1) strcat(str_out, ")");
X  }
X  if (und_uses > 0) {
X    strcat(str_out, UNDEF_LEADER);
X    if (und_uses > 1) strcat(str_out, "(");
X    strcat(str_out, und_str);
X    if (und_uses > 1) strcat(str_out, ")");
X  }
X  if (inc_uses > 0) {
X    strcat(str_out, INC_LEADER);
X    if (inc_uses > 1) strcat(str_out, "(");
X    strcat(str_out, inc_str);
X    if (inc_uses > 1) strcat(str_out, ")");
X  }
X  strcat(str_out, " ");
X#endif /* VAXC */
X}
X
X/* control program flow and command line options */
Xint
Xmain (argc, argv)
X  int argc;
X  char **argv;
X{
X  char cur_argstr[MID_SIZE], sysl_out[MID_SIZE];
X  extern int getpid();
X  struct stat fst;
X  int count, j, len;
X
X  /* initialize some things */
X  fpout = stdout;
X  len = strlen(argv[0]);
X  for (j = len - 1; j > 0; j--) {
X    if (fname_char(argv[0][j]) == FALSE) {
X      j++;
X      break;
X    }
X  }
X  strcpy(prog_name, &(argv[0][j]));
X  out_filenum = 0;
X  start_comment[0] = '\0';
X  header_string[0] = '\0';
X  cur_cfg_file[0] = '\0';
X  output_file[0][0] = '\0';
X  output_file[1][0] = '\0';
X  cfg_file[0] = '\0';
X  sprintf(cpp_prog, "%s %s", CPP, CPP_COMMENTS);
X  macro_list = last_macro = NULL;
X  cext_init();
X
X  /* check for config file flags */
X  count = 1;
X  if ((count < argc) &&
X      is_switch(argv[count][0])) {
X
X#ifndef VMS
X    /* are we skipping config files? */
X    if (argv[count][1] == 'Q') {
X
X      /* check the type */
X      if (isdigit(argv[count][2])) {
X	cfg_switch = argv[count][2] - '0';
X	if ((cfg_switch < 0) ||
X	    (cfg_switch > 7) ||
X	    (argv[count][3] != '\0')) {
X	  fprintf(stderr, "Invalid value for -Q flag\n");
X	  show_usage();
X	}
X      } else if (argv[count][2] != '\0') {
X	fprintf(stderr, "Invalid data following the -Q flag\n");
X	show_usage();
X      } else {
X	cfg_switch = 0;
X      }
X
X      /* processed this one */
X      count++;
X    } else {
X#endif /* VMS */
X
X      /* now test for full length string */
X      if (minmatch_str(&(argv[count][1]), "read-config", 4)) {
X	len = strlen(argv[count]);
X	for (j = 4; j < len; j++) {
X	  if ((argv[count][j] == ':') ||
X	      (argv[count][j] == '=')) {
X	    j++;
X	    break;
X	  }
X	}
X	if (j == len) {
X	  cfg_switch = 0;
X	} else if (isdigit(argv[count][j])) {
X	  cfg_switch = argv[count][j] - '0';
X	  if ((cfg_switch < 0) ||
X	      (cfg_switch > 7) ||
X	      (argv[count][j + 1] != '\0')) {
X	    fprintf(stderr, "Invalid value for -read-config flag\n");
X	    show_usage();
X	  }
X	} else {
X	  fprintf(stderr, "Invalid data following the -read-config flag\n");
X	  show_usage();
X	}
X	count++;
X      }
X
X#ifndef VMS
X    }
X#endif /* VMS */
X  }
X
X  /* now do the configurations */
X  add_macro(2, CPP_GIVEN);
X  add_macro(1, CPP_GIVEN2);
X  do_config();
X
X  /* go through the entire argument list */
X  while (count 	< argc) {
X
X    /* check for any switches */
X    if (is_switch(argv[count][0])) {
X
X      /* call the routine for command line parsing */
X      if (argv[count][0] == '-') {
X	j = FALSE;
X      } else {
X	j = TRUE;
X      }
X      strcpy(cur_argstr, &(argv[count][1]));
X      parse_cmd(cur_argstr, j, TRUE);
X
X    } else {
X
X      /* treat it as a file name */
X      if (out_filenum == 1) {
X
X	/* store the output file name */
X	strcpy(output_file[0], argv[count]);
X	strcpy(output_file[1], argv[count]);
X	out_filenum = 2;
X
X      } else {
X
X	/* check if both C and A are on */
X	if (get_option(OPT_FIRSTCOMMENT) &&
X	    (get_option(OPT_SORTMODE) == SORT_ALL)) {
X	  fprintf(stderr, "Sorting all functions and displaying file names does not mix\n");
X	  show_usage();
X	}
X
X	/* yes, a file has been parsed */
X	out_filenum = 3;
X
X	/* process the file for input */
X	strcpy(file_name, argv[count]);
X	strcpy(errout_filename, file_name);
X	line_count = 1;
X
X	/* check if the file exits */
X	if (stat( file_name, &fst ) != 0) {
X	  sprintf(cur_argstr, "error: could not access file <%s>",
X		  file_name);
X	  err_msg(cur_argstr);
X	  exit(1);
X	} else if ((fpin = fopen(file_name, "r")) == NULL) {
X	  sprintf(cur_argstr, "error: could not read file <%s>",
X		  file_name);
X	  err_msg(cur_argstr);
X	  exit(1);
X	}
X	fclose(fpin);
X
X	/* build /lib/cpp line */
X	sprintf(sysl_out, "%s ", cpp_prog);
X	append_macros(sysl_out);
X	strcat(sysl_out, file_name);
X 
X	/* open it */
X	if ((fpin = open_input(sysl_out, "r")) == NULL) {
X	  sprintf(tmp_str, "unable to open CPP \"pipe\" to file <%s>",
X		  file_name);
X	  err_msg(tmp_str);
X	  exit(1);
X	}
X
X#ifdef SETBUFFER
X	/* now set the buffer */
X	setbuffer(fpin, inbuffer, BUFFER_SIZE);
X#endif /* SETBUFFER */
X
X	/* now do it */
X	parse_file();
X	files_parsed++;
X	if (get_option(OPT_SORTMODE) != SORT_ALL) {
X	  send_file();
X	}
X	close_input(fpin);
X
X      }
X
X    }
X
X    /* now go to the next argument */
X    count++;
X  }
X
X  /* check if -o flag is unprocessed */
X  if (out_filenum == 1) {
X    fprintf(stderr, "No output file specified\n");
X    show_usage();
X  }
X  if ((out_filenum == 2) || (out_filenum == 0)) {
X    fprintf(stderr, "No files for parsing specified\n");
X    show_usage();
X  }
X
X  /* send out everything, if it hasn't been done */
X  if (get_option(OPT_SORTMODE) == SORT_ALL) {
X    send_file();
X  }
X
X  /* now close things up and combine if needed */
X  cxt_close();
X  exit(0);
X  return(0);
X}
X
X/* transmit the currently stored comment to the output file */
Xvoid
Xsend_first_comment(begin_str)
X  char *begin_str;
X{
X  int mode;
X
X  /* check for last comment */
X  if (start_comment[0] != '\0') {
X
X    /* check the mode */
X    if (doc_extract == DOC_NONE) {
X      mode = 0;
X    } else {
X      mode = 1;
X    }
X
X    /* give the header for troff output */
X    dont_space = FALSE;
X    if (doc_extract == DOC_ROFF) {
X      start_block = TRUE;
X      init_roff(1);
X      if (total_out > 0) {
X	out_str(1, ".bp\n");
X	out_str(1, ".sp 0.5i\n");
X      }
X      out_str(1, ".KS\n");
X      out_str(1, ".nf\n");
X      out_str(1, ".ft 2\n");
X    } else {
X      out_char(mode, '\n');
X    }
X
X    /* duplicate if needed */
X    if (!get_option(OPT_COMPACT) &&
X	(doc_extract == DOC_NONE) &&
X	(get_option(OPT_SHOWANYWAY) &&
X	 get_option(OPT_BOTHUSE))) {
X      out_char(1, '\n');
X      out_str(0, begin_str);
X      out_str(1, begin_str);
X      out_str(0, start_comment);
X      out_str(1, start_comment);
X      out_char(0, '\n');
X      out_char(1, '\n');
X    } else {
X      out_str(mode, begin_str);
X      out_str(mode, start_comment);
X      out_char(mode, '\n');
X    }
X
X  }
X}
X
X#ifdef TEST
X/* dummy function that will never be used but makes a nice test */
Xchar (*(*test_it())[])(same_foo, x, y, fpfoo, z, fpp2)
X  int *x, z;
X  FILE* fpfoo, fpp2;
X  char (*(*same_foo())[])();
X  int *y;
X{
X  /* As scary a bit of code as I could think of.  Is it valid? */
X}
X#endif /*TEST*/
END_OF_FILE
if test 24410 -ne `wc -c <'main.c'`; then
    echo shar: \"'main.c'\" unpacked with wrong size!
fi
# end of 'main.c'
fi
echo shar: End of archive 3 \(of 5\).
cp /dev/null ark3isdone
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.221136.16892@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: v02i041: cextract - (Ver. 1.7) C prototyper/header file generator, Part03/05
Message-ID: <1992Nov3.221136.16892@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:11:36 GMT
Approved: csr@calvin.dgbt.doc.ca
Lines: 939