Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1.  
  2. /*=============================================================================
  3.    GNU UnRTF, a command-line program to convert RTF documents to other formats.
  4.    Copyright (C) 2000,2001 Zachary Thayer Smith
  5.  
  6.    This program is free software; you can redistribute it and/or modify
  7.    it under the terms of the GNU General Public License as published by
  8.    the Free Software Foundation; either version 2 of the License, or
  9.    (at your option) any later version.
  10.  
  11.    This program is distributed in the hope that it will be useful,
  12.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.    GNU General Public License for more details.
  15.  
  16.    You should have received a copy of the GNU General Public License
  17.    along with this program; if not, write to the Free Software
  18.    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  
  20.    The author is reachable by electronic mail at tuorfa@yahoo.com.
  21. =============================================================================*/
  22.  
  23.  
  24. /*----------------------------------------------------------------------
  25.  * Module name:    parse
  26.  * Author name:    Zach Smith
  27.  * Create date:    01 Sep 00
  28.  * Purpose:        Parsing of the RTF file into a structure of Word objects.
  29.  *----------------------------------------------------------------------
  30.  * Changes:
  31.  * 15 Oct 00, tuorfa@yahoo.com: parse.c created with functions taken from word.c
  32.  * 15 Oct 00, tuorfa@yahoo.com: backslash before newline is now \par
  33.  * 08 Apr 01, tuorfa@yahoo.com: removed limit on word length
  34.  * 03 Aug 01, tuorfa@yahoo.com: added input buffering
  35.  * 19 Sep 01, tuorfa@yahoo.com: cleaned up read_word()
  36.  * 22 Sep 01, tuorfa@yahoo.com: moved word_dump() to word.c
  37.  * 22 Sep 01, tuorfa@yahoo.com: added function-level comment blocks
  38.  *--------------------------------------------------------------------*/
  39.  
  40. #include <stdio.h>
  41. #include <stdlib.h>
  42. #include <ctype.h>
  43. #include <string.h>
  44.  
  45. #include "defs.h"
  46. #include "parse.h"
  47. #include "malloc.h"
  48. #include "main.h"
  49. #include "error.h"
  50. #include "word.h"
  51. #include "hash.h"
  52.  
  53.  
  54.  
  55. /* local to getchar stuff */
  56. static int ungot_char=-1;
  57. static int ungot_char2=-1;
  58. static int ungot_char3=-1;
  59.  
  60.  
  61.  
  62. /*========================================================================
  63.  * Name:        my_unget_char
  64.  * Purpose:     My own unget routine, handling up to 3 ungot characters.
  65.  * Args:        Character.
  66.  * Returns:     None.
  67.  *=======================================================================*/
  68.  
  69. static void my_unget_char (int ch)
  70. {
  71.         if (ungot_char>=0 && ungot_char2>=0 && ungot_char3>=0)
  72.                 error_handler ("more than 3 ungot chars");
  73.  
  74.         ungot_char3 = ungot_char2;
  75.         ungot_char2 = ungot_char;
  76.         ungot_char = ch;
  77. }
  78.  
  79.  
  80. static int last_returned_ch=0;
  81.  
  82.  
  83. #define READ_BUF_LEN 2048
  84. static int buffer_size = 0;
  85. static char *read_buf = NULL;
  86. static int read_buf_end = 0;
  87. static int read_buf_index = 0;
  88.  
  89.  
  90.  
  91.  
  92.  
  93. /*========================================================================
  94.  * Name:        my_getchar
  95.  * Purpose:     Gets a character: either an ungot one, or a buffered one.
  96.  * Args:        Input file.
  97.  * Returns:     Character, or EOF.
  98.  *=======================================================================*/
  99.  
  100. static int my_getchar (FILE* f)
  101. {
  102.         int ch;
  103.  
  104.         CHECK_PARAM_NOT_NULL(f);
  105.  
  106.         if (ungot_char>=0) {
  107.                 ch = ungot_char;
  108.                 ungot_char=ungot_char2;
  109.                 ungot_char2=ungot_char3;
  110.                 ungot_char3=-1;
  111.                 last_returned_ch = ch;
  112.                 return ch;
  113.         }
  114.         do {
  115.                 if (read_buf_index >= read_buf_end) {
  116.                         if (!read_buf) {
  117.                                 buffer_size = READ_BUF_LEN;
  118.                                 read_buf = my_malloc (buffer_size);
  119.                                 if (!read_buf) {
  120.                                         buffer_size /= 4;
  121.                                         read_buf = my_malloc (buffer_size);
  122.                                         if (!read_buf)
  123.                                                 error_handler ("cannot allocate read buffer");
  124.                                 }
  125.                         }
  126.                         read_buf_end = fread (read_buf, 1, buffer_size, f);
  127.                         read_buf_index = 0;
  128.                         if (!read_buf_end)
  129.                                 return EOF;
  130.                 }
  131.                 ch = read_buf [read_buf_index++];
  132.  
  133.                 if (ch=='\n') {
  134.                         lineno++;
  135.                         /* Convert \(newline) into \par here */
  136.                         if (last_returned_ch=='\\') {
  137.                                 my_unget_char (' ');
  138.                                 my_unget_char ('r');
  139.                                 my_unget_char ('a');
  140.                                 ch = 'p';
  141.                                 break;
  142.                         }
  143.                 }
  144.         }
  145.         while (ch=='\r' /* || ch=='\n' */ );
  146.  
  147.         if (ch=='\t') ch = ' ';
  148.  
  149.         last_returned_ch = ch;
  150.         return ch;
  151. }
  152.  
  153.  
  154. /* local to read_word */
  155. static char *input_str = NULL;
  156. static unsigned long current_max_length = 1;
  157.  
  158.  
  159.  
  160. /*========================================================================
  161.  * Name:        expand_word_buffer
  162.  * Purpose:     Expands the buffer used to store an incoming word.
  163.  *              This allows us to remove the limit on word length.
  164.  * Args:        None.
  165.  * Returns:     None.
  166.  *=======================================================================*/
  167.  
  168. static int
  169. expand_word_buffer ()
  170. {
  171.         char *new_ptr;
  172.         unsigned long old_length;
  173.         if (!input_str)
  174.                 error_handler ("no input buffer allocated");
  175.         old_length = current_max_length;
  176.         current_max_length *= 2;
  177.         new_ptr = my_malloc (current_max_length);
  178.         if (!new_ptr)
  179.                 error_handler ("out of memory while resizing buffer");
  180.        
  181.         memcpy (new_ptr, input_str, old_length);
  182.         my_free (input_str);
  183.         input_str = new_ptr;
  184.         return TRUE;
  185. }
  186.  
  187.  
  188.  
  189.  
  190. /*========================================================================
  191.  * Name:        read_word
  192.  * Purpose:     The core of the parser, this reads a word.
  193.  * Args:        Input file.
  194.  * Returns:     Number of characters in the word, or zero.
  195.  * Note:        The word buffer is static and local to this file.
  196.  *=======================================================================*/
  197.  
  198. static int
  199. read_word (FILE *f)
  200. {
  201.         int ch, ch2, ix=0;
  202.         int have_whitespace=FALSE;
  203.         int is_control_word=FALSE;
  204.         int has_numeric_param=FALSE; /* if is_control_word==TRUE */
  205.         int need_unget=FALSE;
  206.  
  207.         CHECK_PARAM_NOT_NULL(f);
  208.  
  209.         current_max_length = 10; /* XX */
  210.  
  211.         /* Get some storage for a word.
  212.          */
  213.         input_str = my_malloc (current_max_length);
  214.         if (!input_str)
  215.                 error_handler("cannot allocate word storage");
  216.  
  217.         do {
  218.                 ch = my_getchar(f);
  219.         }
  220.         while (ch=='\n');
  221.  
  222.         if (ch==' ')
  223.         {
  224.                 /* Compress multiple space chars down to one.
  225.                  */
  226.                 while (ch == ' ') {
  227.                         ch = my_getchar(f);
  228.                         have_whitespace=TRUE;
  229.                 }
  230.                 if (have_whitespace) {
  231.                         my_unget_char (ch);
  232.                         input_str[0]=' ';
  233.                         input_str[1]=0;
  234.                         return 1;
  235.                 }
  236.         }
  237.  
  238.         switch(ch)
  239.         {
  240.         case EOF:
  241.                 return 0;
  242.  
  243.         case '\\':
  244.                 ch2 = my_getchar(f);
  245.  
  246.                 /* Look for two-character command words.
  247.                  */
  248.                 switch (ch2)
  249.                 {
  250.                 case '\n':
  251.                         strcpy (input_str, "\\par");
  252.                         return 4;
  253.                 case '~':
  254.                 case '{':
  255.                 case '}':
  256.                 case '\\':
  257.                 case '_':
  258.                 case '-':
  259.                         input_str[0] = '\\';
  260.                         input_str[1] = ch2;
  261.                         input_str[2] = 0;
  262.                         return 2;
  263.                 case '\'':
  264.                         /* Preserve \'## expressions (hex char exprs) for later.
  265.                          */
  266.                         input_str[0]='\\';
  267.                         input_str[1]='\'';
  268.                         ix=2;
  269.                         if(ix==current_max_length) {
  270.                                 if (!expand_word_buffer ())
  271.                                         error_handler("word too long");
  272.                         }
  273.                         ch = my_getchar(f);
  274.                         input_str[ix++]=ch;
  275.                         if(ix==current_max_length) {
  276.                                 if (!expand_word_buffer ())
  277.                                         error_handler("word too long");
  278.                         }
  279.                         ch = my_getchar(f);
  280.                         input_str[ix++]=ch;
  281.                         if(ix==current_max_length) {
  282.                                 if (!expand_word_buffer ())
  283.                                         error_handler("word too long");
  284.                         }
  285.                         input_str[ix]=0;
  286.                         return ix;
  287.                 }
  288.  
  289.                 is_control_word=TRUE;
  290.                 ix=1;
  291.                 input_str[0]=ch;
  292.                 ch=ch2;
  293.                 break;
  294.  
  295.         case '\t':
  296.                 /* In RTF, a tab char is the same as \tab.
  297.                  */
  298.                 strcpy (input_str, "\\tab");
  299.                 return 4;
  300.  
  301.         case '{':
  302.         case '}':
  303.         case ';':
  304.                 input_str[0]=ch;
  305.                 input_str[1]=0;
  306.                 return 1;
  307.  
  308.         }
  309.  
  310.         while (ch!=EOF)
  311.         {
  312.                 /* Several chars always ends a word, and we need to save them.
  313.                  */
  314.                 if (ch=='\t' || ch=='{' || ch=='}' || ch=='\\') {
  315.                         need_unget=TRUE;
  316.                         break;
  317.                 }
  318.  
  319.                 /* A newline always ends a command word; we don't save it.
  320.                  * A newline is ignored if this is not a command word.
  321.                  */
  322.                 if (ch=='\n') {
  323.                         if (is_control_word)
  324.                                 break;
  325.                         ch = my_getchar(f);
  326.                         continue;
  327.                 }
  328.  
  329.                 /* A semicolon always ends a command word; we do save it.
  330.                  * A semicolon never ends a regular word.
  331.                  */
  332.                 if (ch==';') {
  333.                         if (is_control_word) {
  334.                                 need_unget=TRUE;
  335.                                 break;
  336.                         }
  337.                 }
  338.  
  339.                 /* In this parser, a space character terminates
  340.                  * any word, and if it does not follow a command,
  341.                  * then it is a word in itself.
  342.                  */
  343.                 if (ch==' ') {
  344.                         if (!is_control_word)
  345.                                 need_unget=TRUE;
  346.                         break;
  347.                 }
  348.  
  349.                 /* Identify a control word's numeric parameter.
  350.                  */
  351.                 if (is_control_word) {
  352.                         if (!has_numeric_param && (isdigit(ch) || ch=='-'))
  353.                                 has_numeric_param = TRUE;
  354.                         else
  355.                         if (has_numeric_param && !isdigit(ch)) {
  356.                                 if (ch!=' ')
  357.                                         need_unget=TRUE;
  358.                                 break;
  359.                         }
  360.                 }
  361.                
  362.                 input_str[ix++] = ch;
  363.                 if (ix==current_max_length) {
  364.                         if (!expand_word_buffer ())
  365.                                 error_handler("word too long");
  366.                 }
  367.                 ch = my_getchar (f);
  368.         }
  369.  
  370.         if (need_unget)
  371.                 my_unget_char(ch);
  372.  
  373.         input_str[ix]=0;
  374.         return ix;
  375. }
  376.  
  377.  
  378.  
  379. /*========================================================================
  380.  * Name:        word_read
  381.  * Purpose:     This is the recursive metareader which pieces together the
  382.  *              structure of Word objects.
  383.  * Args:        Input file.
  384.  * Returns:     Tree of Word objects.
  385.  *=======================================================================*/
  386.  
  387. Word *
  388. word_read (FILE* f) {
  389.         Word * prev_word = NULL;
  390.         Word * first_word = NULL;
  391.         Word * new_word = NULL; /* temp */
  392.  
  393.         CHECK_PARAM_NOT_NULL(f);
  394.  
  395.         do {
  396.                 if (!read_word(f)) {
  397.                         return first_word;
  398.                 }
  399.  
  400.  
  401.                 if (input_str[0] == '{') {
  402.                         /* Process subwords */
  403.  
  404. #if 0
  405. printf ("processing subword...\n");
  406. #endif
  407.  
  408.                         /* Create a dummy word to point to a sublist */
  409.                         new_word = word_new (NULL);
  410.                         if (!new_word)
  411.                                 error_handler ("cannot allocate word");
  412.  
  413.                         /* Get the sublist */
  414.                         new_word->child = word_read (f);
  415.                         if (!new_word->hash_index && !new_word->child)
  416.                         {
  417.                                 /* printf ("unable to read children!\n"); */
  418.                         }
  419.  
  420.                 } else if (input_str[0] == '}') {
  421. #if 0
  422. printf ("returning from word_read.\n");
  423. #endif
  424.                         return first_word;
  425.                 } else {
  426.                         new_word = word_new (input_str);
  427.                 }
  428.  
  429.                 if (prev_word) prev_word->next = new_word;
  430.  
  431.                 if (!first_word) first_word = new_word;
  432.  
  433.                 prev_word = new_word;
  434.  
  435.                 /* Free up the memory allocated by read_word.
  436.                  */
  437.                 my_free (input_str);
  438.                 input_str = NULL;
  439.         }
  440.         while(1);
  441.  
  442. }
  443.  
  444.  
  445.