#ifndef lint
static char *RCSid = "$Id: doc2ag.c%v 3.38.2.70 1993/09/19 23:02:23 woo Exp woo $";
#endif


/*
 * doc2ag.c  -- program to convert Gnuplot .DOC format to AmigaGuideŽ document
 *
 * This is version 1.0 written by Thomas Huchtemeier
 *
 * usage:  doc2ag [file.doc [file.guide]]
 *
 *   where file.doc is a Gnuplot .DOC file, and file.guide will be an
 *   AmigaGuideŽ hyper-text file
 *
 * typical usage for GNUPLOT:
 *
 *   doc2ag gnuplot.doc gnuplot.guide
 */

/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
/*!!!!!!!!!!!!!	DO NOT EDIT THE FOLLOWING LINES  !!!!!!!*/
/*!!!!!!!!!!!!!	FOR MODIFICATIOS SEE doc2ag.user !!!!!!!*/
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/

char   Version[]  = "$VER: Doc2AG V1.1 ";

#define TEMP_FILE       "temp_gnu.guide"
#define TRUE            1
#define FALSE           0

#include "doc2ag.user"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

typedef int boolean;

struct topicList {
  int depth;
  char *string;
  char *orgString;
};

struct linkList {
  char *search;
  char *linkTo;
};

FILE   *temp_guide,
       *infile,
       *outfile;
struct topicList *topic[MAX_TOPIC_NUM];
struct linkList *linkEntry[MAX_LINK_NUM];
int    topicNum = 0,
       linkNum = 0,
       oldDepth = 2;
       result = 0;

void file_header(void);
void make_topics(void);
void make_nodes(void);
void insert_text(void);
void filter_line(char *line);
void make_links(char *line);
void make_entry(int topicDepth, char *topicString, char *orgString);
void parse_line(char *line);
char *upcase(char *string);
void section(char *line);

main(int argc, char *argv[])
{
  infile = stdin;
  outfile = stdout;

  if (argc > 3) {
    fprintf(stderr, "Usage: %s [infile [outfile]]\n", argv[0]);
    exit(1);
  }
  if (argc >= 2)
    if ( (infile = fopen(argv[1], "r")) == (FILE *)NULL) {
      fprintf(stderr, "doc2ag error: Can't open %s for reading\n", argv[1]);
      exit(1);
    }
  if (argc == 3)
    if ( (outfile = fopen(argv[2], "w+")) == (FILE *)NULL) {
      fprintf(stderr, "doc2ag error: Can't open %s for writing\n", argv[2]);
    }
  if ( (temp_guide = fopen(TEMP_FILE,"w+")) == (FILE *)NULL) {
    fprintf(stderr, "doc2ag error: Can't open %s for writing\n", TEMP_FILE);
  }

  file_header();
  make_nodes();
  topicNum -= 1;
  make_topics();
  fclose(temp_guide);
  remove(TEMP_FILE);
  fprintf(stderr, "doc2ag success: Conversion completed. Found %d topics and %d links.\n", topicNum,
	 linkNum);
  exit(0);
}

/* The file header gives the required information for an AmigaGuideŽ database */

void file_header(void)
{
  fputs("@database \"gnuplot.guide\"\n@master \"gnuplot.doc\"\n@wordwrap\n\n",
	temp_guide);
}

/* Find out the beginning and the end of each node */

void make_nodes(void)
{
  static char line[MAX_LINE_LEN];

   while (fgets(line,MAX_LINE_LEN, infile)) {
     filter_line(line);
   }
  fputs("@endnode\n", temp_guide);
  rewind(temp_guide);
}

/* Insert (sub)topics to special nodes */

void make_topics(void)
{
  static char line[MAX_LINE_LEN];
  int         actStart,
              actEnd,
              actDepth,
              maxDepth,
              counter = 1,
              oldDepth,
              oldCounter,
              depthBuffer,
              sectionPos[MAX_TOPIC_NUM],
              sectionCounter,
              tempCounter;
  boolean     cont, firstTopic = TRUE;

  while (fgets(line, MAX_LINE_LEN, temp_guide)) {
    if (line[0] == ' ') {
      parse_line(line);
    }
    else {
      fputs(line, outfile);
    }
    maxDepth = 3;
    sectionCounter = 0;
    cont = TRUE;
    if ((strcmp(line, SUBTOPIC_STRING) == 0) && (firstTopic)) {
      firstTopic = FALSE;
      fputc('\n', outfile);
      while (counter <= topicNum) {
        if (topic[counter]->depth == 2) {
	  fputs(topic[counter]->string, outfile);
	  fputc('\n', outfile);
        }
      counter++;
      }
    counter = 1;
    }
    else if ((strcmp(line, SUBTOPIC_STRING) == 0) && (!firstTopic)) {
      while ((topic[counter]->depth == 2) && (counter <= topicNum)) {
	counter++;
      }
      actStart = counter;
      while ((topic[counter]->depth > 2) && (counter <= topicNum)) {
        if (topic[counter]->depth > maxDepth) {
          maxDepth = topic[counter]->depth;
        }
	counter++;
      }
      actEnd = counter - 1;
      actDepth = topic[actStart]->depth;
      counter = actStart;
      fputc('\n', outfile);
      while (counter <= actEnd) {
        if (topic[counter]->depth == actDepth) {
          fputs(topic[counter]->string, outfile);
	  fputc('\n', outfile);
        }
        counter++;
      }
      insert_text();
      actDepth++;
      counter = actStart;
      while ((topic[counter]->depth < actDepth) && (counter <= actEnd)) {
        counter++;
      }
      oldDepth = topic[counter]->depth;
      fputc('\n', outfile);
      while (counter <= actEnd) {
        if (topic[counter]->depth == actDepth) {
          fputs(topic[counter]->string, outfile);
	  fputc('\n', outfile);
          cont = TRUE;
        }
        else {
          if ((topic[counter]->depth < oldDepth)) {
            depthBuffer = topic[counter]->depth;
            oldCounter = counter;
            while ((topic[counter]->depth == depthBuffer) && (counter <= actEnd)) {
              counter++;
            }
            counter--;
            if (counter != actEnd) {
              insert_text();
            }
            counter = oldCounter;
          }
          if ((topic[counter]->depth > actDepth) && (cont)) {
            if (cont) {
              sectionCounter++;
              sectionPos[sectionCounter] = counter;
              cont = FALSE;
            }
          }
        }

        oldDepth = topic[counter]->depth;
        counter++;
      }
      counter = actEnd + 1;
    }
    if ((sectionCounter > 0) && (topic[counter]->depth < oldDepth)) {
      insert_text();
      tempCounter = 1;
      while (tempCounter <= sectionCounter) {
        fputc('\n', outfile);
        while (topic[sectionPos[tempCounter]]->depth == 5) {
          fputs(topic[sectionPos[tempCounter]]->string, outfile);
	  fputc('\n', outfile);
          sectionPos[tempCounter]++;
        }
        if (tempCounter != sectionCounter) {
          insert_text();
        }
        tempCounter++;
      }
    }
  }
}

/* Read the temp_guide.gnu file until another (sub)topic occurs */

void insert_text(void)
{
  static char line[MAX_LINE_LEN];

  while ((fgets(line, MAX_LINE_LEN, temp_guide)) && (strcmp(line, SUBTOPIC_STRING) != 0)) {
    if (line[0] == ' ') {
      parse_line(line);
    }
    else {
      fputs(line, outfile);
    }
  }
  if (strcmp(line, SUBTOPIC_STRING) == 0) {
    fputs(line, outfile);
  }
}

/* Seperate relevant from non-relevant lines */

void filter_line(char *line)
{
  switch(line[0]) {		        	/* control character */
    case '?' :  {				/* interactive help entry */
      make_links(line);
      break;
    }
    case '@' :  break;				/* start/end table */
    case '#' :  break;				/* latex table entry */
    case '%' :  break;				/* troff table entry */
    case '\n': 					/* empty text line */
    case ' ' :  {				/* normal text line */
      fputs(line, temp_guide);
      break;
    }
    default  : {
      if (isdigit(line[0])) {  			/* start of a (sub)topic */
        section(line);
	make_links(line+1);
      }
      else {
        fprintf(stderr, "doc2ag error: Unknown control code '%c' in column 1\n", line[0]);
      }
      break;
    }
  }
}

/* Upcase strings */

char *upcase(char *string)
{
  static char upString[MAX_LINE_LEN];
  int counter = 0;

  while(string[counter]) {
    upString[counter] = toupper(string[counter]);
    counter++;
  }
  upString[counter] = '\0';
  return upString;
}

/* Mark keywords found in each line */

void parse_line(char *line)
{
  register char actChar;
  static boolean inquote = FALSE;
  char upStringBuffer[MAX_LINE_LEN],
       upLinkSearch[MAX_LINE_LEN],
       stringBuffer[MAX_LINE_LEN];
  int counter = 0, linkCounter = 1;
  static boolean foundLink = FALSE;

  while( (actChar = *line++) != '\0') {
    switch(actChar) {
      case '`':
        if (inquote) {
          inquote = FALSE;
	  stringBuffer[counter] = '\0';
          while ((!foundLink) && (linkCounter <= linkNum)) {
	    strcpy(upStringBuffer ,upcase(stringBuffer));
	    strcpy(upLinkSearch ,upcase(linkEntry[linkCounter]->search));
 	    if (strcmp(upStringBuffer, upLinkSearch) == 0) {
	      foundLink = TRUE;
	      fprintf(outfile, "@{\" %s \" link \"%s\"}", stringBuffer,
		linkEntry[linkCounter]->linkTo);
	    }
	    else {
	      linkCounter++;
	    }
	  }
	  if ((!foundLink) && (linkCounter >= linkNum)) {
	    fprintf(outfile, "%s%s%s", MARK_TEXT, stringBuffer, UNMARK_TEXT);
	  }
        }
        else {
          inquote = TRUE;
          foundLink = FALSE;
          linkCounter = 1;
	  counter = 0;
        }
        break;
      default:
	if (inquote) {
	  stringBuffer[counter++] = actChar;
        }
	else {
          fputc(actChar, outfile);
 	}
        break;
    }
  }
}


/* Make an entry to the (sub)topic-list */

void make_entry(int topicDepth, char *topicString, char *orgString)
{
  if (topic[topicNum] = calloc(1, sizeof(struct topicList))) {
    if (topic[topicNum]->string=calloc(strlen(topicString)+1,1)) {
      if (topic[topicNum]->orgString=calloc(strlen(orgString)+1,1)) {
        topic[topicNum]->depth = topicDepth;
        strcpy(topic[topicNum]->string, topicString);
        strcpy(topic[topicNum]->orgString, orgString);
      }
    }
  }
  else {
    fprintf(stderr, "doc2ag error: Conversion terminated. Sorry, not enough memory to do my work.\n");
    exit(0);
  }
}

/* Link interactive help-entries to the recommended (sub)topic */

void make_links(char *line)
{
  static char string[MAX_LINE_LEN];
  boolean noMemory = FALSE;

  sscanf(line+1,"%[^\n]s", string);
  if (string[0] != '\0') {
    linkNum++;
    if (linkNum <= MAX_LINK_NUM) {
      if (linkEntry[linkNum] = calloc(1, sizeof(struct linkList))) {
        if (linkEntry[linkNum]->search=calloc(strlen(string)+1,1)) {
          strcpy(linkEntry[linkNum]->search, string);
          if (isspace(line[0])) {
            if (linkEntry[linkNum]->linkTo=calloc(strlen(string)+1,1)) {
              strcpy(linkEntry[linkNum]->linkTo, string);
	    }
	    else {
              noMemory = TRUE;
	    }
          }
          else {
            if (linkEntry[linkNum]->linkTo=calloc(strlen(topic[topicNum-1]->orgString)+1,1)) {
              strcpy(linkEntry[linkNum]->linkTo, topic[topicNum-1]->orgString);
            }
	    else {
              noMemory = TRUE;
	    }
          }
        }
      }
      else {
        noMemory = TRUE;
      }
      if (noMemory) {
        fprintf(stderr, "doc2ag error: Conversion terminated. Sorry, not enough memory to do my work\n");
        exit(0);
      }
    }
    else {
      fprintf(stderr, "doc2ag error: Conversion terminated. Sorry, too many links. Please revise your #DEFINEs.\n");
      exit(0);
    }
  }
}

/* Insert (sub)topic entries (nodes) to the temp_guide.gnu file and compound
   link-entry for the (sub)topic-list */

void section(char *line)
{
  static char buttonString[MAX_LINE_LEN],
              stringBuffer[MAX_LINE_LEN],
              string[MAX_LINE_LEN],
              titleString[MAX_LINE_LEN];
  int         actDepth;

  sscanf(line,"%d %[^\n]s", &actDepth, string);
  sprintf(buttonString, "%-20s", string);
  switch(actDepth) {
    case 1:
      fputs("@node main \"", temp_guide);
      break;
    default:
      if (oldDepth < actDepth){
        fputs(SUBTOPIC_STRING, temp_guide);
	fputs("\n@endnode\n", temp_guide);
      }
      else {
        fputs("@endnode\n", temp_guide);
      }
      sprintf(stringBuffer, "   @{\" %s \" link \"%s\"}", buttonString, string);
      make_entry(actDepth, stringBuffer, string);
      fputs("\n@node \"", temp_guide);
      break;
  }
  if (islower(string[0])) {
    string[0] = toupper(string[0]);
  }
  strcpy(titleString, TITLE);
  strcat(titleString, string);
  fprintf(temp_guide, "%s\"\n@title \"%s\"\n", string, titleString);
  oldDepth = actDepth;
  topicNum++;
  if (topicNum > MAX_TOPIC_NUM) {
    fprintf(stderr, "doc2ag error: Conversion terminated. Sorry, too many topics. Please revise your #DEFINEs.\n");
    exit(0);
  }
}
