home *** CD-ROM | disk | FTP | other *** search
/ The Developer Connection…ice Driver Kit for OS/2 3 / DEV3-D1.ISO / source / util2src / bmtbl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-07-24  |  39.1 KB  |  977 lines

  1. /*============================================================================*
  2.  * main() module: bmtbl.c - Build a BookMaster table.
  3.  *
  4.  * (C)Copyright IBM Corporation, 1991.                    Brian E. Yoder
  5.  *
  6.  * The 'bmtbl' command converts a simple table in an ASCII text file into a
  7.  * BookMaster table.  This makes it easy to translate text information that
  8.  * is already in table form into a correct BookMaster table description.
  9.  * Once the BookMaster table description has been built automatically (the
  10.  * tedious part), you can modify and update it as needed.
  11.  *
  12.  * 09/10/91 - Created.
  13.  * 09/13/91 - 1.0 - Initial version completed.
  14.  * 09/26/91 - 1.0 - Updated module header's comments.  No code change.
  15.  * 07/24/92 - Changed "rt" to "r" for fopen(). C Set/2 doesn't allow "rt".
  16.  *============================================================================*/
  17.  
  18. static char ver[]  = "bmtbl: version 1.0";
  19. static char copr[] = "(c)Copyright IBM Corporation, 1991.  All rights reserved.";
  20.  
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <memory.h>
  24. #include <string.h>
  25. #include <malloc.h>
  26. #include <sys/types.h>
  27.  
  28. #include "util.h"
  29. #include "bmtbl.h"
  30.  
  31. /*============================================================================*
  32.  * The list of COL structures that defines a row in the table.
  33.  *============================================================================*/
  34.  
  35. static TROW tblrow = { NULL, NULL, 0 };
  36.  
  37. static int  getrowDone = FALSE;     /* Used only by getrow() */
  38.  
  39. /*============================================================================*
  40.  * Full prototypes for private (static) functions in this module
  41.  *============================================================================*/
  42.  
  43. static void       syntax       ( void );
  44.  
  45. static int        defrow       ( FILE *in, TROW *trow );
  46. static COL       *addcol       ( TROW *trow, uint start, uint width );
  47. static CELL      *addcell      ( COL *col, char *text, uint textlen );
  48. static void       clrcells     ( COL *col );
  49. static void       clrcols      ( TROW *trow );
  50.  
  51. static void       tabletag     ( FILE *out, TROW *trow );
  52. static void       tableattr    ( FILE *out, char *attr, char *val, uint cnt );
  53. static void       tableend     ( FILE *out );
  54. static void       headingtag   ( FILE *out, TROW *trow );
  55. static void       rowtag       ( FILE *out, TROW *trow );
  56.  
  57. static void       bldrows      ( FILE *in, FILE *out, TROW *trow, int hdr );
  58. static int        getrow       ( FILE *in, TROW *trow );
  59. static char      *strip        ( char *str, uint len );
  60. static void       tablerow     ( FILE *out, TROW *trow );
  61.  
  62. /*============================================================================*
  63.  * Main Program Entry Point
  64.  *============================================================================*/
  65.  
  66. main(argc, argv)
  67.  
  68. int argc;                           /* arg count */
  69. char *argv[];                       /* arg pointers */
  70.  
  71. {
  72.   int     rc;                       /* Return code storage */
  73.   long    lrc;                      /* Long return code */
  74.   char   *flagstr;                  /* Pointer to string of flags */
  75.  
  76.   char   *inname;                   /* Pointer to name of input file */
  77.   char   *outname;                  /* Pointer to name of output file */
  78.  
  79.   int     h_flag;                   /* Flags */
  80.   int     d_flag;
  81.   int     r_flag;
  82.  
  83.   FILE   *infile;                   /* Pointers to input/output files */
  84.   FILE   *outfile;
  85.  
  86. /*----------------------------------------------------------------------------*
  87.  * Set initial values of flags:
  88.  *----------------------------------------------------------------------------*/
  89.  
  90.   h_flag    = FALSE;                /* Don't put header in table */
  91.   d_flag    = FALSE;                /* Don't build a tdef: Build the entire table */
  92.   r_flag    = FALSE;                /* Don't reference a tdef: Put all parms in table */
  93.  
  94. /*----------------------------------------------------------------------------*
  95.  * Be sure we have at least one argument:
  96.  *----------------------------------------------------------------------------*/
  97.  
  98.   argc--;                           /* Ignore 1st argument (program name) */
  99.   argv++;
  100.  
  101.   if (argc <= 0) syntax();          /* If no arguments: Display syntax */
  102.  
  103.  /*---------------------------------------------------------------------------*
  104.   * Process flags, if any
  105.   *---------------------------------------------------------------------------*/
  106.  
  107.   flagstr = *argv;                  /* Point 'flagstr' to argument */
  108.   if (*flagstr == '-')              /* If it begins with '-': It's a list of flags */
  109.   {
  110.      flagstr++;                          /* Point past the dash */
  111.  
  112.      while (*flagstr != '\0')            /* For each character in flag string: */
  113.      {
  114.         switch (*flagstr)
  115.         {
  116.            case 'h':
  117.            case 'H':
  118.               h_flag = TRUE;
  119.               break;
  120.  
  121.            default:
  122.               fprintf(stderr, "Invalid flag '%c'.  For help, enter command with no arguments.\n",
  123.                  *flagstr);
  124.               exit(2);
  125.               break;
  126.         }
  127.         flagstr++;                            /* Check next character */
  128.      }
  129.  
  130.      argc--;                             /* Done with flags: Discard them */
  131.      argv++;
  132.   }
  133.  
  134.  /*---------------------------------------------------------------------------*
  135.   * Store pointers to the names of input, output files
  136.   *---------------------------------------------------------------------------*/
  137.  
  138.   if (argc != 2) syntax();          /* If wrong no. of args: Display syntax */
  139.  
  140.   inname = argv[0];                 /* Point to name of input file */
  141.   outname = argv[1];                /* Point to name of output file */
  142.  
  143.  /*---------------------------------------------------------------------------*
  144.   * Open the input file
  145.   *---------------------------------------------------------------------------*/
  146.  
  147.   infile = fopen(inname, "r");
  148.   if (infile == NULL)
  149.   {
  150.      fprintf(stderr, "Cannot access input file: '%s'.\n", inname);
  151.      return(2);
  152.   }
  153.  
  154.  /*---------------------------------------------------------------------------*
  155.   * Build the TROW structure that defines the table
  156.   *---------------------------------------------------------------------------*/
  157.  
  158.   rc = defrow(infile, &tblrow);
  159.   if (rc != 0) return(2);
  160.  
  161.  /*---------------------------------------------------------------------------*
  162.   * Create the output file
  163.   *---------------------------------------------------------------------------*/
  164.  
  165.   outfile = fopen(outname, "w");
  166.   if (outfile == NULL)
  167.   {
  168.      fprintf(stderr, "Cannot create output file: '%s'.\n", outname);
  169.      return(2);
  170.   }
  171.  
  172.  /*---------------------------------------------------------------------------*
  173.   * Build the table definition tag and attributes
  174.   *---------------------------------------------------------------------------*/
  175.  
  176.   tabletag(outfile, &tblrow);
  177.  
  178.  /*---------------------------------------------------------------------------*
  179.   * Build each of the table's rows (and heading row, if specified)
  180.   *---------------------------------------------------------------------------*/
  181.  
  182.   bldrows(infile, outfile, &tblrow, h_flag);
  183.  
  184.  /*---------------------------------------------------------------------------*
  185.   * End the table definition
  186.   *---------------------------------------------------------------------------*/
  187.  
  188.   tableend(outfile);
  189.  
  190.  /*---------------------------------------------------------------------------*
  191.   * Done
  192.   *---------------------------------------------------------------------------*/
  193.  
  194.   return(0);                        /* Return */
  195.  
  196. } /* end of main() */
  197.  
  198. /*============================================================================*
  199.  * syntax() - Display command syntax and exit to operating system!
  200.  *============================================================================*/
  201. static void syntax()
  202. {
  203.   fprintf(stderr, "%s\n", ver);
  204.   fprintf(stderr, "Usage: bmtbl [-flags] infile outfile\n");
  205.   fprintf(stderr, "\n");
  206.   fprintf(stderr, "The bmtbl program processes the input text file, converts it to\n");
  207.   fprintf(stderr, "a BookMaster table definition, and writes the table definition\n");
  208.   fprintf(stderr, "to the output file.  The following flags are supported:\n");
  209.   fprintf(stderr, "\n");
  210.   fprintf(stderr, "  h  The first row of text contains the table's heading.\n");
  211.   fprintf(stderr, "\n");
  212.   exit(2);
  213. }
  214.  
  215. /*============================================================================*
  216.  * defrow
  217.  *
  218.  * REMARKS:
  219.  *   This subroutine reads the first line of the input file.  The first
  220.  *   character in the line is ignored (since it is part of the extra column
  221.  *   that was added to the table to separate the rows!).  The rest of the
  222.  *   line contains a non-blank character just past the end of each column
  223.  *   (and just before the beginning of the next column).
  224.  *
  225.  *   This subroutine initializes the TROW structure with the information that
  226.  *   will define each row of the table: how many columns, the width of each
  227.  *   column, the total width of the table, etc.
  228.  *
  229.  * RETURNS:
  230.  *   0         : Successful.
  231.  *   Other:    : Error.  An error message is written to stderr.
  232.  *
  233.  * NOTES:
  234.  *   Since we assume that a text editor was used to add an extra line and
  235.  *   extra column to the original text, we will ignore the first character
  236.  *   (1st column) of the input line.
  237.  *
  238.  *   The cstart variable is used to store the index (offset) of the start
  239.  *   of a column, relative to the second character of each line of the input
  240.  *   file.  Remember that the first character position of each line is used
  241.  *   to determine whether the line contains text or a row separator.
  242.  *
  243.  *   The cstart of a column is equal to the cstart of the previous column +
  244.  *   the width of the previous column + one (to skip past the column that
  245.  *   contains the non-blank character!).
  246.  *============================================================================*/
  247.  
  248. static int defrow(
  249.  
  250. FILE    *in,                        /* Input file */
  251. TROW    *trow )                     /* Pointer to TROW structure for table */
  252.  
  253. {
  254.   int    rc;                        /* Return code storage */
  255.   char  *line;                      /* Pointer to line information */
  256.   char  *end;                       /* Pointer to end of line */
  257.   uint   len;                       /* Length of line */
  258.   uint   cnt;                       /* Counter */
  259.   char   ch;                        /* Character from line */
  260.   uint   cstart;                    /* Current column: Starting col. no. */
  261.   uint   cwidth;                    /*                 Width */
  262.   COL   *col;                       /* Pointer to structure */
  263.   char   lbuff[MAX_LINE+1];         /* Buffer to hold input line from file */
  264.  
  265.  /*---------------------------------------------------------------------------*
  266.   * Read the line.
  267.   *---------------------------------------------------------------------------*/
  268.  
  269.   line = fgets(lbuff,               /* Read the line into lbuff[] */
  270.                MAX_LINE, in);
  271.   if (line == NULL)
  272.   {
  273.      fprintf(stderr, "Unexpected end-of-file encountered.\n");
  274.      return(2);
  275.   }
  276.  
  277.   if (*line != '\0') line++;        /* Ignore 1st character in line */
  278.  
  279.   len = strlen(line);               /* Find length of line */
  280.   if (len <= 1)                     /* If newline or nothing: */
  281.   {
  282.      fprintf(stderr, "First line is blank.  Table cannot be built.\n");
  283.      return(2);
  284.   }
  285.  
  286.   end = line + len - 1;             /* Point to the end of the line */
  287.   if (*end == '\n')                 /* If last char is a newline: */
  288.   {                                 /* (it's supposed to be with fgets) */
  289.      *end = '\0';                        /* Turn it into a null byte */
  290.      len--;                              /* And update length */
  291.   }
  292.  
  293.  /*---------------------------------------------------------------------------*
  294.   * Loop to look for all non-blank characters in line to define columns.
  295.   *---------------------------------------------------------------------------*/
  296.  
  297.   cstart = 0;                       /* First column starts at offset 0 */
  298.   cwidth = 0;                       /* Initialize column width */
  299.  
  300.   for (;;)                          /* Loop to look for all columns: */
  301.   {
  302.      ch = *line;                         /* Get next character */
  303.      if (ch == '\0')                     /* Done if we reached the end */
  304.         break;
  305.  
  306.      if (ch != ' ')                      /* If we found a non-blank character: */
  307.      {                                        /* It's past the end of the column */
  308.         if (cwidth == 0)                      /* Be sure the column has at least 1 char */
  309.         {
  310.            fprintf(stderr, "A column length of zero was found.  Table cannot be built.\n");
  311.            return(2);
  312.         }
  313.  
  314. //      printf("cstart=%d   cwidth=%d\n",     /* For test purposes */
  315. //              cstart,     cwidth);
  316.  
  317.         col = addcol(trow, cstart, cwidth);   /* Add column to table row def */
  318.         if (col == NULL) return(2);
  319.  
  320.         cstart = cstart + cwidth + 1;         /* Calculate offset of start of next column */
  321.         cwidth = 0;                           /* Initialize length of next column */
  322.      }
  323.      else                                /* Else: character is blank: */
  324.         cwidth++;                             /* Update length of current column */
  325.  
  326.      line++;                             /* Point to next char and continue */
  327.   } /* end of loop to look for all columns */
  328.  
  329.  /*---------------------------------------------------------------------------*
  330.   * Be sure we have at least one column in the table
  331.   *---------------------------------------------------------------------------*/
  332.  
  333.   if (trow->cols == 0)
  334.   {
  335.      fprintf(stderr, "Table has no columns and won't be built.\n");
  336.      return(2);
  337.   }
  338.  
  339.   return(0);                        /* Done */
  340. }
  341.  
  342. /*============================================================================*
  343.  * addcol
  344.  *
  345.  * REMARKS:
  346.  *   This subroutine adds a new COL structure to the linked list that is
  347.  *   contained in the TROW structure.
  348.  *
  349.  *   The column's starting offset is the offset from the second character
  350.  *   in each line of the input text file.  Again, the first character in
  351.  *   each line is set to non-blank to delimit rows in the input text file.
  352.  *
  353.  * RETURNS:
  354.  *   Pointer to new COL structure, or NULL if there is not enough memory.
  355.  *============================================================================*/
  356.  
  357. static COL *addcol(
  358.  
  359. TROW    *trow,                      /* Table row definition */
  360. uint     start,                     /* Column's starting offset */
  361. uint     width )                    /* Column's width */
  362.  
  363. {
  364.   int    rc;                        /* Return code storage */
  365.   COL   *col;                       /* Pointer to new data structure */
  366.  
  367.  /*---------------------------------------------------------------------------*
  368.   * Allocate the structure and fill it in
  369.   *---------------------------------------------------------------------------*/
  370.  
  371.   col = malloc(sizeof(COL));        /* Allocate the memory */
  372.   if (col == NULL)                  /* Be sure it's available */
  373.   {
  374.      fprintf(stderr, "*** Out of memory.\n");
  375.      return(NULL);
  376.   }
  377.  
  378.   memset(col, 0, sizeof(COL));      /* Fill structure with nulls */
  379.  
  380.   col->start = start;               /* Save column's starting offset */
  381.   col->width = width;               /* and its width */
  382.  
  383.  /*---------------------------------------------------------------------------*
  384.   * Add it to the linked list
  385.   *---------------------------------------------------------------------------*/
  386.  
  387.   if (trow->first == NULL)          /* If no items in list: */
  388.   {
  389.      trow->first = col;                  /* Point first/last pointers to new item */
  390.      trow->last  = col;
  391.   }
  392.   else                              /* Else: there's an item in the list */
  393.   {
  394.      trow->last->next = col;             /* Link current last item to this one */
  395.      trow->last = col;                   /* And make this one the last one */
  396.   }
  397.  
  398.  /*---------------------------------------------------------------------------*
  399.   * Update table's row information: total columns, etc.
  400.   *---------------------------------------------------------------------------*/
  401.  
  402.   trow->cols++;                     /* Update no. of columns */
  403.   trow->twidth += width;            /* Update total width of all columns */
  404.  
  405.   return(col);                      /* Done */
  406. }
  407.  
  408. /*============================================================================*
  409.  * addcell
  410.  *
  411.  * REMARKS:
  412.  *   This subroutine adds a new CELL structure to the linked list that is
  413.  *   contained in the COL structure for the current column.  The CELL
  414.  *   structure contains a single line of text that belong's in this
  415.  *   column position for the current row in the table.
  416.  *
  417.  *   The text length does not include any null terminating byte.
  418.  *
  419.  *   The data structure, the text, and the text's terminating null byte
  420.  *   are contained within a single malloc'd block.  This reduces memory
  421.  *   fragmentation and simplifies cleanup.
  422.  *
  423.  * RETURNS:
  424.  *   Pointer to new CELL structure, or NULL if there is not enough memory.
  425.  *============================================================================*/
  426.  
  427. static CELL *addcell(
  428.  
  429. COL     *col,                       /* Pointer to column's COL structure */
  430. char    *text,                      /* Pointer to text */
  431. uint     textlen )                  /* Length of the text */
  432.  
  433. {
  434.   int       rc;                     /* Return code storage */
  435.   uint      mlen;                   /* Length of allocated memory block */
  436.   CELL     *cell;                   /* Pointer to new data structure */
  437.   char     *newmem;                 /* Pointer to newly malloc'd memory */
  438.   char     *newtext;                /* Pointer to new text (in new memory) */
  439.  
  440.  /*---------------------------------------------------------------------------*
  441.   * Allocate the memory and fill it in
  442.   *---------------------------------------------------------------------------*/
  443.  
  444.   mlen = sizeof(CELL) +             /* Calulate size of memory block needed */
  445.          textlen + 1;
  446.  
  447.   newmem = malloc(mlen);            /* Allocate the memory */
  448.   if (newmem == NULL)               /* Be sure it's available */
  449.   {
  450.      fprintf(stderr, "*** Out of memory.\n");
  451.      return(NULL);
  452.   }
  453.   memset(newmem, 0, mlen);          /* Fill new memory with nulls */
  454.  
  455.   cell = (CELL *)newmem;            /* Structure is in beginning of block */
  456.  
  457.   newtext = newmem + sizeof(CELL);  /* The text string will occupy the rest */
  458.   memcpy(newtext, text, textlen);   /* Copy the text just past the CELL */
  459.   newtext[textlen] = '\0';          /* Terminate the text with null byte */
  460.  
  461.   cell->text = newtext;             /* Store pointer to new text string */
  462.   cell->textlen = strlen(text);     /* And store its length */
  463.  
  464.  /*---------------------------------------------------------------------------*
  465.   * Add it to the linked list
  466.   *---------------------------------------------------------------------------*/
  467.  
  468.   if (col->first == NULL)           /* If no items in list: */
  469.   {
  470.      col->first = cell;                  /* Point first/last pointers to new item */
  471.      col->last  = cell;
  472.   }
  473.   else                              /* Else: there's an item in the list */
  474.   {
  475.      col->last->next = cell;             /* Link current last item to this one */
  476.      col->last = cell;                   /* And make this one the last one */
  477.   }
  478.  
  479.   return(cell);                     /* Done */
  480. }
  481.  
  482. /*============================================================================*
  483.  * clrcells
  484.  *
  485.  * REMARKS:
  486.  *   This subroutine frees all of the CELL structures for a particular
  487.  *   COL structure.
  488.  *
  489.  * RETURNS:
  490.  *   None.
  491.  *============================================================================*/
  492.  
  493. static void clrcells(
  494.  
  495. COL     *col )                      /* Pointer to COL structure */
  496.  
  497. {
  498.   CELL  *s;                         /* Pointers to structures to free */
  499.   CELL  *snext;
  500.  
  501.   s = col->first;                   /* Point to first structure in list */
  502.   while (s != NULL)                 /* For each structure in list */
  503.   {
  504.      snext = s->next;                    /* Point to next before s is gone */
  505.      free(s);                            /* Free the structure */
  506.      s = snext;                          /* Reset s to point to next */
  507.   }
  508.  
  509.   col->first = NULL;                /* Reset linked list pointers to 'empty' */
  510.   col->last = NULL;
  511.  
  512.   return;                           /* Done */
  513. }
  514.  
  515. /*============================================================================*
  516.  * clrcols
  517.  *
  518.  * REMARKS:
  519.  *   This subroutine frees all of the CELL structures for all COL structures
  520.  *   that are linked to the TROW structure.  It does NOT free the COL
  521.  *   structures: they define the column structure of the table and remain
  522.  *   constant throughout the life of this program.
  523.  *
  524.  * RETURNS:
  525.  *   Nothing.
  526.  *============================================================================*/
  527.  
  528. static void clrcols(
  529.  
  530. TROW    *trow )                     /* Pointer to TROW structure */
  531.  
  532. {
  533.   COL   *s;                         /* Pointer to COL structure */
  534.  
  535.   s = trow->first;                  /* Point to first structure in list */
  536.   while (s != NULL)                 /* For each structure in list */
  537.   {
  538.      clrcells(s);                        /* Clear its CELLs */
  539.      s = s->next;                        /* Point to next COL and continue */
  540.   }
  541.  
  542.   return;                           /* Done */
  543. }
  544.  
  545. /*============================================================================*
  546.  * tabletag
  547.  *
  548.  * REMARKS:
  549.  *   This subroutine builds the :table tag and all of its attributes.
  550.  *   It writes this information to the output file.
  551.  *
  552.  *   Lots of attributes (and their BookMaster default values, according
  553.  *   to version 3.0) are listed so that the user can change them as
  554.  *   desired without having to look them up.
  555.  *
  556.  * RETURNS:
  557.  *   Nothing.
  558.  *============================================================================*/
  559.  
  560. static void tabletag(
  561.  
  562. FILE    *f,                         /* Output file */
  563. TROW    *trow )                     /* Pointer to TROW structure for table */
  564.  
  565. {
  566.   int    rc;                        /* Return code storage */
  567.  
  568.  /*---------------------------------------------------------------------------*
  569.   * Begin the table tag
  570.   *---------------------------------------------------------------------------*/
  571.  
  572.   fprintf(f, ".*\n");
  573.   fprintf(f, ":table\n");
  574.  
  575.  /*---------------------------------------------------------------------------*
  576.   * Add table attributes that are dependent upon the number of columns
  577.   *---------------------------------------------------------------------------*/
  578.  
  579.   tableattr(f, "cols"     ,  "*"      ,  trow->cols);
  580.   tableattr(f, "hp"       ,  "0"      ,  trow->cols);
  581.   tableattr(f, "concat"   ,  "yes"    ,  trow->cols);
  582.   tableattr(f, "align"    ,  "left"   ,  trow->cols);
  583.   tableattr(f, "valign"   ,  "top"    ,  trow->cols);
  584.  
  585.  /*---------------------------------------------------------------------------*
  586.   * Finish off the table tag with a subset of allowable attributes
  587.   *---------------------------------------------------------------------------*/
  588.  
  589.   fprintf(f, "    rules=both\n");
  590.   fprintf(f, "    frame=box\n");
  591.   fprintf(f, "    hdframe=rules\n");
  592.  
  593.   fprintf(f, "    width=column\n");
  594.  
  595.   fprintf(f, "    split=no\n");
  596.   fprintf(f, "    talign=left\n");
  597.   fprintf(f, "    scale='1'.\n");
  598.   fprintf(f, ".*\n");
  599.  
  600.   return;                           /* Done */
  601. }
  602.  
  603. /*============================================================================*
  604.  * tableattr
  605.  *
  606.  * REMARKS:
  607.  *   This subroutine writes a table attribute and its initial values
  608.  *   to the output file.  It writes one initial value for each column
  609.  *   in the table.  This makes it easier for someone to change the
  610.  *   initial values for each column long after the table is built and
  611.  *   embedded within a BookMaster document.
  612.  *
  613.  * RETURNS:
  614.  *   Nothing.
  615.  *
  616.  * EXAMPLE:
  617.  *   The statement:  tableattr(f, "cols", "*", 4);
  618.  *   Produces:       cols='* * * *'
  619.  *============================================================================*/
  620.  
  621. static void tableattr(
  622.  
  623. FILE    *f,                         /* Output file */
  624. char    *attr,                      /* Pointer to name of attribute */
  625. char    *val,                       /* Pointer to initial value */
  626. uint     cnt )                      /* No. of columns in the table */
  627.  
  628. {
  629.   fprintf(f, "    %s = '", attr);   /* Print the attribute name and quote */
  630.  
  631.   while (cnt > 0)                   /* For each column: */
  632.   {
  633.      fprintf(f, "%s", val);              /* Print the value */
  634.      cnt--;
  635.      if (cnt != 0) fprintf(f, " ");      /* If not last column: add a space */
  636.   }
  637.  
  638.   fprintf(f, "'\n");                /* Add ending quote and newline */
  639.   return;                           /* Done */
  640. }
  641.  
  642. /*============================================================================*
  643.  * tableend
  644.  *
  645.  * REMARKS:
  646.  *   This subroutine writes the end-of-table tag to the output file.
  647.  *
  648.  * RETURNS:
  649.  *   Nothing.
  650.  *============================================================================*/
  651.  
  652. static void tableend(
  653.  
  654. FILE    *f )                        /* Output file */
  655.  
  656. {
  657.   fprintf(f, ".*\n");
  658.   fprintf(f, ":etable.\n");
  659.   return;                           /* Done */
  660. }
  661.  
  662. /*============================================================================*
  663.  * bldrows
  664.  *
  665.  * REMARKS:
  666.  *   This subroutine assumes that the TROW structure has been set up and
  667.  *   defines the columns in the table.  It reads the input file and writes
  668.  *   the correct BookMaster tags and text to define all of the rows in the
  669.  *   table.
  670.  *
  671.  *   If the hdr variable is TRUE, then the first row of the table is defined
  672.  *   as a table heading instead of as an ordinary row.
  673.  *
  674.  * RETURNS:
  675.  *   0         : Successful.
  676.  *   Other:    : Error.  An error message is written to stderr.
  677.  *============================================================================*/
  678.  
  679. static void bldrows(
  680.  
  681. FILE    *in,                        /* Input file */
  682. FILE    *out,                       /* Output file */
  683. TROW    *trow,                      /* Pointer to TROW structure for table */
  684. int      hdr )                      /* First row is table heading? (TRUE/FALSE) */
  685.  
  686. {
  687.   int    rc;                        /* Return code storage */
  688.  
  689.  /*---------------------------------------------------------------------------*
  690.   * Loop to build all rows of the table (including heading, if specified)
  691.   *---------------------------------------------------------------------------*/
  692.  
  693.   for (;;)                          /* Loop for each row: */
  694.   {
  695.     /*------------------------------------------------------------*
  696.      * Get the next row of information for the table
  697.      *------------------------------------------------------------*/
  698.  
  699.      rc = getrow(in, trow);              /* Get the text for the row */
  700.      if (rc != 0)                        /* If we're done: break out */
  701.         break;
  702.  
  703.     /*------------------------------------------------------------*
  704.      * Write the appropriate beginning tag for this row
  705.      *------------------------------------------------------------*/
  706.  
  707.      if (hdr == TRUE)                    /* If this is the first row and */
  708.      {                                   /* we need a heading: */
  709.         fprintf(out, ":thd.\n");               /* Do this */
  710.      }
  711.      else                                /* Otherwise: */
  712.      {
  713.         fprintf(out, ".*-------\n");           /* Add a small separator line */
  714.         fprintf(out, ":row.\n");               /* And begin the row */
  715.      }
  716.  
  717.     /*------------------------------------------------------------*
  718.      * Write the text in all columns for this row to output file
  719.      *------------------------------------------------------------*/
  720.  
  721.      tablerow(out, trow);
  722.  
  723.     /*------------------------------------------------------------*
  724.      * If we wrote a heading, end it properly and then set hdr to
  725.      * FALSE:  We only write one heading at the most.
  726.      *------------------------------------------------------------*/
  727.  
  728.      if (hdr == TRUE)                    /* If this is the first row and */
  729.      {                                   /* we have a heading: */
  730.         fprintf(out, ":ethd.\n");             /* End the heading */
  731.         hdr = FALSE;                          /* Only need one heading! */
  732.      }
  733.   } /* end of loop for each row */
  734.  
  735.   return;                           /* Done */
  736. }
  737.  
  738. /*============================================================================*
  739.  * getrow
  740.  *
  741.  * REMARKS:
  742.  *   This subroutine gets the text for the next row in the table and stores
  743.  *   it in the TROW structure.  It stores the text for each column (cell)
  744.  *   in the column's COL structure within TROW.  The text is contains as
  745.  *   zero or more CELL structures linked to a COL structure.
  746.  *
  747.  *   This subroutine sets the static getrowDone variable to TRUE when it's
  748.  *   done.  If we have a row and then get to the end of the file, we want
  749.  *   to save the fact that we reached the end of the file.  That way, we
  750.  *   can return the row successfully to the caller this time, and tell the
  751.  *   caller that we're done the next time.
  752.  *
  753.  * RETURNS:
  754.  *   0         : Successful.
  755.  *   Other:    : End of input file reached: there is no more text to read.
  756.  *
  757.  * NOTES:
  758.  *   Remember that if the first character (column) in a line is non-blank,
  759.  *   then the current row is complete.  Otherwise, we ignore this character
  760.  *   and process the rest of the line.
  761.  *
  762.  *   If the first character in a line is a newline, then the line is blank
  763.  *   and completely ignored.
  764.  *============================================================================*/
  765.  
  766. static int getrow(
  767.  
  768. FILE    *in,                        /* Input file */
  769. TROW    *trow )                     /* Pointer to TROW structure for table */
  770.  
  771. {
  772.   int    rc;                        /* Return code storage */
  773.  
  774.   COL   *col;                       /* Pointer to a column structure */
  775.   char  *coltxt;                    /* Pointer to start of column's text */
  776.   CELL  *cell;                      /* Pointer to a text cell structure */
  777.  
  778.   char   ch;                        /* Character */
  779.   char  *line;                      /* Pointer to line buffer */
  780.   uint   len;                       /* Line length */
  781.   char   lbuff[MAX_LINE+1];         /* Buffer to hold input line from file */
  782.  
  783.   if (getrowDone == TRUE)           /* If we're done: Don't do anything more */
  784.      return(1);
  785.  
  786.  /*---------------------------------------------------------------------------*
  787.   * Initialization
  788.   *---------------------------------------------------------------------------*/
  789.  
  790.   clrcols(trow);                    /* Clear all text from previous column */
  791.  
  792.  /*---------------------------------------------------------------------------*
  793.   * Loop to read and store the text from all lines in the row
  794.   *---------------------------------------------------------------------------*/
  795.  
  796.   for (;;)                          /* Loop for each input text line: */
  797.   {
  798.     /*------------------------------------------------------------*
  799.      * Get next line from input file and store it.  Convert line to
  800.      * a space-padded length of MAX_LINE so we can easily pull
  801.      * columns from it.
  802.      *------------------------------------------------------------*/
  803.  
  804.      memset(lbuff, ' ', MAX_LINE);       /* Fill line buffer with spaces, and */
  805.      lbuff[MAX_LINE] = '\0';             /* Terminate it with a null byte */
  806.  
  807.      line = fgets(lbuff, MAX_LINE, in);  /* Get the next line from the file */
  808.      if (line == NULL)                   /* If we've reached the end of file: */
  809.      {
  810.         getrowDone = TRUE;                    /* Remember that we have */
  811.         break;                                /* And break out of loop */
  812.      }
  813.  
  814.      ch = *line;                         /* Check first character in line: */
  815.      if (ch == '\n') continue;           /* If newline: line is blank: Ignore it */
  816.      if (ch != ' ') break;               /* If not blank: we're done with row */
  817.  
  818.      len = strlen(line);                 /* Get length of line we just read */
  819.      lbuff[len] = ' ';                   /* Turn null byte back into space */
  820.  
  821.      if ((len != 0) &&                   /* If last char in line is newline: */
  822.          (lbuff[len-1] == '\n'))         /* Then turn it into a space, too */
  823.              lbuff[len-1] = ' ';
  824.  
  825.      line++;                             /* Bump past initial column of line */
  826.  
  827.     /*------------------------------------------------------------*
  828.      * Store the information from each column in the column's COL
  829.      * structure.  Strip off leading and trailing spaces before
  830.      * storing.  Don't store zero-length strings.
  831.      *------------------------------------------------------------*/
  832.  
  833.      col = trow->first;                  /* Start with first column */
  834.      while (col != NULL)                 /* For each column in table: */
  835.      {
  836.         coltxt = line + col->start;           /* Point to start of text for column */
  837.         coltxt = strip(coltxt, col->width);   /* Strip off excess blanks */
  838.         if (*coltxt != '\0')                  /* If text string is not null: */
  839.         {
  840.            cell = addcell(col, coltxt,             /* Add text to column */
  841.                           strlen(coltxt));
  842.            if (cell == NULL) return(1);            /* If out of memory: done */
  843.         }
  844.  
  845.         col = col->next;                      /* Go on to next column */
  846.      } /* end of loop to process each column in table */
  847.  
  848.   } /* end of loop for each input text line */
  849.  
  850.  /*---------------------------------------------------------------------------*
  851.   * Done with the row
  852.   *---------------------------------------------------------------------------*/
  853.  
  854.   return(0);                        /* Done */
  855. }
  856.  
  857. /*============================================================================*
  858.  * strip
  859.  *
  860.  * REMARKS:
  861.  *   This subroutine stores a null byte past the end of the last non-blank
  862.  *   character in the string.  It then returns a pointer to the first
  863.  *   non-blank character in the string.
  864.  *
  865.  *   If all characters in the string are blank, then str returns with
  866.  *   a pointer to a null byte.  The null byte will actually be put into
  867.  *   *str.
  868.  *
  869.  *   Be sure that there are len+1 bytes available (starting at str) that
  870.  *   this subroutine can modify!
  871.  *============================================================================*/
  872.  
  873. static char *strip(
  874.  
  875. char    *str,                       /* Pointer to string */
  876. uint     len )                      /* Length of string */
  877.  
  878. {
  879.   char   ch;                        /* Character */
  880.   char  *end;                       /* Pointer to end of string */
  881.  
  882.   if (len == 0) return(str);        /* Don't need to strip null strings! */
  883.  
  884.  /*---------------------------------------------------------------------------*
  885.   * Strip trailing blanks by putting a null byte past last non-blank character
  886.   *---------------------------------------------------------------------------*/
  887.  
  888.   end = str + len - 1;              /* Point to last char in string */
  889.   while (len > 0)                   /* For each character in the string: */
  890.   {
  891.      if (*end != ' ')                    /* If we're pointing to non-blank char: */
  892.      {
  893.         end[1] = '\0';                        /* Store a null byte just past it */
  894.         break;                                /* And break out of loop */
  895.      }
  896.  
  897.      len--;                              /* Char is blank: decrement len, and */
  898.      end--;                              /* Go to previous character in string */
  899.   }
  900.  
  901.   if (len == 0)                     /* If we checked and all chars are blank: */
  902.      *str = '\0';                        /* Make the string zero-length */
  903.  
  904.  /*---------------------------------------------------------------------------*
  905.   * Strip leading blanks by advancing 'str' until it points to first non-blank
  906.   * character or to the null terminating byte, whichever comes first
  907.   *---------------------------------------------------------------------------*/
  908.  
  909.   for (;;)                          /* Loop to find first non-blank char: */
  910.   {
  911.      ch = *str;                          /* Get current character */
  912.      if (ch == '\0')                     /* If at end of string: */
  913.         break;                                /* We're done */
  914.      if (ch != ' ')                      /* If character is not blank: */
  915.         break;                                /* We're done */
  916.      str++;                              /* Char is blank: skip past it */
  917.   }                                      /* and continue looping */
  918.  
  919.   return(str);                      /* Done */
  920. }
  921.  
  922. /*============================================================================*
  923.  * tablerow
  924.  *
  925.  * REMARKS:
  926.  *   This subroutine writes the tags and text for a row in the table.
  927.  *   The TROW structure contains a list of COL structures, one for
  928.  *   each column in the table.  The text for each column is stored
  929.  *   in a list of CELL structures that are linked to that column's COL
  930.  *   structure.
  931.  *
  932.  * RETURNS:
  933.  *   Nothing.
  934.  *============================================================================*/
  935.  
  936. static void tablerow(
  937.  
  938. FILE    *f,                         /* Output file */
  939. TROW    *trow )                     /* Pointer to TROW structure for table */
  940.  
  941. {
  942.   int    rc;                        /* Return code storage */
  943.   COL   *col;                       /* Pointer to column structure */
  944.   CELL  *cell;                      /* Pointer to cell text structure */
  945.  
  946.  /*---------------------------------------------------------------------------*
  947.   * Write the text for all columns in the row
  948.   *---------------------------------------------------------------------------*/
  949.  
  950.   col = trow->first;                /* Start with first column */
  951.   while (col != NULL)               /* For each column in the table row: */
  952.   {
  953.     /*-------------------------------------------------------*
  954.      * Write the text for the current column
  955.      *-------------------------------------------------------*/
  956.  
  957.      fprintf(f, ":c.\n");                /* Write column tag */
  958.  
  959.      cell = col->first;                  /* Start with first line of text: */
  960.      while (cell != NULL)                /* For each line of text in column: */
  961.      {
  962.        /*-----------------------------------------------*
  963.         * Loop to write all lines of text for column
  964.         *-----------------------------------------------*/
  965.  
  966.         if (cell->textlen != 0)               /* If cell's text is not null: */
  967.            fprintf(f, "%s\n", cell->text);         /* Write it to output file */
  968.         cell = cell->next;                    /* Go to next line of text */
  969.  
  970.      } /* end of loop for each line of text in column */
  971.  
  972.      col = col->next;                    /* Go to next column */
  973.   } /* end of loop for each column in the table row */
  974.  
  975.   return;                           /* Done */
  976. }
  977.