home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libnet / htmparse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  16.6 KB  |  511 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18. /*** htmparse.c ***************************************************/
  19. /*   description:    html parser                                   */
  20.  
  21.  
  22.  /********************************************************************
  23.  
  24.   $Revision: 3.1 $
  25.   $Date: 1998/03/28 03:31:31 $
  26.  
  27.  *********************************************************************/
  28.  
  29. #include "xp.h"
  30. #include "xp_str.h"
  31. #include "htmparse.h"
  32. #include "prtypes.h"
  33. #include "pa_tags.h"
  34. #include "pa_parse.h" /* for pa_tokenize_tag */
  35. #include "prmem.h"
  36. #if 0
  37. #include "prio.h"  /* for test only */
  38. #include <stdio.h> /* for test only */
  39. #endif
  40.  
  41. typedef char ParseState;
  42.  
  43. /* states of the parser */
  44. #define PS_START 0 /* starting state */
  45. #define PS_BETWEEN_TAGS 1 /* characters not enclosed by < > */
  46. #define PS_TAG_NAME 2
  47. #define PS_EMPTY_TAG 3
  48. #define PS_CLOSE_BRACKET 4
  49. #define PS_ATTRIBUTE 5
  50. #define PS_EQUALS 6
  51. #define PS_VALUE 7
  52. #define PS_START_COMMENT 8
  53. #define PS_END_COMMENT 9
  54.  
  55. typedef struct _CRAWL_TagStruc {
  56.     char *name;
  57.     intn token;
  58.     char **attributeNames;
  59.     char **attributeValues; /* max length of html attribute is 1024 chars */
  60.     uint16 sizeNames;
  61.     uint16 numNames;
  62.     uint16 sizeValues;
  63.     uint16 numValues;
  64.     PRBool emptyTagp;
  65.     PRBool endTagp;
  66. } CRAWL_TagStruc;
  67.  
  68. /* maintains state of parser */
  69. typedef struct _CRAWL_ParseObjStruc {
  70.     ParseState state;
  71.     CRAWL_Tag tag;
  72.     char *data;
  73.     uint16 dataLen;
  74.     uint16 dataSize;
  75.     char *str;
  76.     uint16 strLen;
  77.     uint16 strSize;
  78.     char prev1;
  79.     char prev2;
  80.     char inQuote; /* current quote character. when not in quote, value is '\0' */
  81.     PRBool inComment; /* we don't support comment nesting anymore */
  82.     PRBool inScript; /* inside <SCRIPT> and </SCRIPT> */
  83.     PRBool skipWhitespace;
  84.     PRBool isRDF;
  85. } CRAWL_ParseObjStruc;
  86.  
  87. /* prototypes */
  88. static CRAWL_Tag crawl_makeTag();
  89. static void crawl_recycleTag(CRAWL_Tag tag);
  90. static void crawl_destroyTag(CRAWL_Tag tag);
  91. static void crawl_recycleParseObj(CRAWL_ParseObj obj);
  92.  
  93. int crawl_appendString(char **str, uint16 *len, uint16 *size, char c);
  94. int crawl_appendStringList(char ***list_p, uint16 *len, uint16 *size, char *str);
  95.  
  96. /* accessors */
  97. PR_IMPLEMENT(CRAWL_Tag) CRAWL_GetTagParsed(CRAWL_ParseObj obj) {
  98.     if (obj->data != NULL) return NULL;
  99.     else return obj->tag;
  100. }
  101.  
  102. PR_IMPLEMENT(char*) CRAWL_GetDataParsed(CRAWL_ParseObj obj) {
  103.     if (obj->data != NULL) return obj->data;
  104.     else return NULL;
  105. }
  106.  
  107. PR_IMPLEMENT(char*) CRAWL_GetTagName(CRAWL_Tag tag) {
  108.     return tag->name;
  109. }
  110.  
  111. PR_IMPLEMENT(intn) CRAWL_GetTagToken(CRAWL_Tag tag) {
  112.     return tag->token;
  113. }
  114.  
  115. PR_IMPLEMENT(PRBool) CRAWL_IsEmptyTag(CRAWL_Tag tag) {
  116.     return tag->emptyTagp;
  117. }
  118.  
  119. PR_IMPLEMENT(PRBool) CRAWL_IsEndTag(CRAWL_Tag tag) {
  120.     return tag->endTagp;
  121. }
  122.  
  123. PR_IMPLEMENT(uint16) CRAWL_GetNumberOfAttributes(CRAWL_Tag tag) {
  124.     return tag->numNames;
  125. }
  126.  
  127. PR_IMPLEMENT(char*) CRAWL_GetNthAttributeName(CRAWL_Tag tag, uint16 n) {
  128.     return *(tag->attributeNames + n);
  129. }
  130.  
  131. PR_IMPLEMENT(char*) CRAWL_GetNthAttributeValue(CRAWL_Tag tag, uint16 n) {
  132.     return *(tag->attributeValues + n);
  133. }
  134.  
  135. PR_IMPLEMENT(char*) CRAWL_GetAttributeValue(CRAWL_Tag tag, char *attributeName) {
  136.     int count = 0;
  137.     while (count < tag->numNames) {
  138.         if (XP_STRCASECMP(attributeName, *(tag->attributeNames + count)) == 0)
  139.             return *(tag->attributeValues + count);
  140.         count++;
  141.     }
  142.     return NULL;
  143. }
  144.  
  145. static CRAWL_Tag crawl_makeTag() {
  146.     CRAWL_Tag tag = PR_NEWZAP(CRAWL_TagStruc);
  147.     if (tag == NULL) return NULL;
  148.     tag->sizeNames = tag->sizeValues = 4;
  149.     tag->attributeNames = (char**)PR_MALLOC(sizeof(char*) * tag->sizeNames);
  150.     if (tag->attributeNames == NULL) return NULL;
  151.     tag->attributeValues = (char**)PR_MALLOC(sizeof(char*) * tag->sizeValues);
  152.     if (tag->attributeValues == NULL) return NULL;
  153.     return tag;
  154. }
  155.  
  156. static void crawl_recycleTag(CRAWL_Tag tag) {
  157.     int count;
  158.     if (tag->name != NULL) PR_Free(tag->name);
  159.     tag->name = NULL;
  160.     for (count = 0; count < tag->numNames; count++) {
  161.         PR_Free(*(tag->attributeNames + count));
  162.     }
  163.     tag->numNames = 0;
  164.     for (count = 0; count < tag->numValues; count++) {
  165.         PR_Free(*(tag->attributeValues + count));
  166.     }
  167.     tag->numValues = 0;
  168.     tag->emptyTagp = PR_FALSE;
  169.     tag->endTagp = PR_FALSE;
  170. }
  171.  
  172. static void crawl_destroyTag(CRAWL_Tag tag) {
  173.     crawl_recycleTag(tag);
  174.     if (tag->attributeNames != NULL) PR_Free(tag->attributeNames);
  175.     if (tag->attributeValues != NULL) PR_Free(tag->attributeValues);
  176.     PR_Free(tag);
  177. }
  178.  
  179. static void crawl_recycleParseObj(CRAWL_ParseObj obj) {
  180.     crawl_recycleTag(obj->tag);
  181.     if (obj->data != NULL) PR_Free(obj->data);
  182.     obj->data = NULL;
  183.     obj->dataLen = obj->dataSize = 0;
  184. }
  185.  
  186. PR_IMPLEMENT(CRAWL_ParseObj) CRAWL_MakeParseObj() {
  187.     CRAWL_ParseObj obj = PR_NEWZAP(CRAWL_ParseObjStruc);
  188.     if (obj == NULL) return NULL;
  189.     obj->tag = crawl_makeTag();
  190.     if (obj->tag == NULL) {
  191.         PR_Free(obj);
  192.         return NULL;
  193.     }
  194.     return obj;
  195. }
  196.  
  197. PR_IMPLEMENT(void) CRAWL_DestroyParseObj(CRAWL_ParseObj obj) {
  198.     crawl_destroyTag(obj->tag);
  199.     if (obj->data != NULL) PR_Free(obj->data);
  200.     obj->data = NULL;
  201.     obj->dataLen = obj->dataSize = 0;
  202.     if (obj->str != NULL) PR_Free(obj->str);
  203.     obj->str = NULL;
  204.     obj->strLen = obj->strSize = 0;
  205.     PR_Free(obj);
  206. }
  207.  
  208. #define STRING_EXPANSION_INCREMENT 16
  209. /* returns 0 if no error, -1 if no memory */
  210. int crawl_appendString(char **str, uint16 *len, uint16 *size, char c) {
  211.     if (*len == *size) {
  212.         char *newName = (char*)PR_MALLOC(*size + STRING_EXPANSION_INCREMENT);
  213.         char *old = *str;
  214.         if (newName == NULL) return -1;
  215.         XP_MEMCPY(newName, *str, *size);
  216.         *str = newName;
  217.         if (old != NULL) PR_Free(old);
  218.         *size += STRING_EXPANSION_INCREMENT;
  219.     }
  220.     *(*str + *len) = c;
  221.     ++(*len);
  222.     return 0;
  223. }
  224.  
  225. #define STRINGLIST_EXPANSION_INCREMENT 8
  226.  
  227. /* returns 0 if no error, -1 if no memory */
  228. int crawl_appendStringList(char ***list_p, uint16 *len, uint16 *size, char *str) {
  229.     char **list = *list_p;
  230.     if (*len == *size) {
  231.         char **newList = (char**)PR_MALLOC(sizeof(char*) * (*size + STRINGLIST_EXPANSION_INCREMENT));
  232.         char **old = list;
  233.         if (newList == NULL) return -1;
  234.         XP_MEMCPY(newList, list, (sizeof(char*) * (*size)));
  235.         list = newList;
  236.         if (old != NULL) PR_Free(old);
  237.         *size += STRINGLIST_EXPANSION_INCREMENT;
  238.     }
  239.     *(list + *len) = str;
  240.     ++(*len);
  241.     *list_p = list;
  242.     return 0;
  243. }
  244.  
  245. /* returns index to last character of buffer parsed */
  246. PR_IMPLEMENT(int) CRAWL_ParserPut(CRAWL_ParseObj obj, char *str, uint32 len, CRAWL_ParseFunc func, void *data) {
  247.     uint32 n = 0; /* where we are in the buffer */
  248.     uint32 lastn = 0; /* position the last time in the loop */
  249.     char c;
  250.  
  251.     while (n < len) {
  252.         if (lastn < n) { /* we advanced a character */
  253.             obj->prev1 = obj->prev2;
  254.             obj->prev2 = c;
  255.         }
  256.         lastn = n;
  257.         c = *(str + n);
  258.         if (obj->inComment) {
  259.             /* if we're in a comment, ignore everything until we detect end of comment */
  260.             if ((obj->prev1 == '-') && (obj->prev2 == '-') && (c == '>')) obj->inComment = PR_FALSE;
  261.             n++;
  262.         } else if (obj->skipWhitespace) {
  263.             if ((c == ' ') || (c == '\n') || (c == '\r')) {
  264.                 n++;
  265.             } else obj->skipWhitespace = PR_FALSE;
  266.         } else {
  267.             PRBool endOfString = PR_FALSE;
  268.             switch (obj->state) {
  269.             case PS_START:
  270.             /* PS_START - expecting open bracket or character data */
  271.                 if (c == '<') {
  272.                     obj->state = PS_TAG_NAME;
  273.                     n++;
  274.                 } else {
  275.                     obj->state = PS_BETWEEN_TAGS;
  276.                 }
  277.                 break;
  278.             case PS_BETWEEN_TAGS:
  279.             /* PS_BETWEEN_TAGS - expecting open bracket (terminating character data) or more character data */
  280.                 if (obj->inQuote == c) {
  281.                     obj->inQuote = '\0'; /* close quote */
  282.                 } else if ((c == '"') || (obj->inScript && (c == '\''))) { /* start a quote, only double quotes significant in between tags */
  283.                     obj->inQuote = c;
  284.                 }
  285.                 /* open bracket not in quoted section indicates end of data */
  286.                 if ((obj->inQuote == '\0') && (c == '<')) {
  287.                     obj->state = PS_START;
  288.                     if (crawl_appendString(&obj->data, &obj->dataLen, &obj->dataSize, '\0') != 0) /* null terminate string */
  289.                         return CRAWL_PARSE_OUT_OF_MEMORY;
  290.                     if (func(obj, PR_FALSE, data) == PARSE_STOP) return CRAWL_PARSE_TERMINATE;
  291.                     crawl_recycleParseObj(obj);
  292.                 } else {
  293.                     if (crawl_appendString(&obj->data, &obj->dataLen, &obj->dataSize, c) != 0)
  294.                         return CRAWL_PARSE_OUT_OF_MEMORY;
  295.                     n++;
  296.                 }
  297.                 break;
  298.             case PS_TAG_NAME:
  299.             /* PS_TAG_NAME - terminated by space, \r, \n, >, / */
  300.                 if ((c == '"') || (c == '\'')) return CRAWL_PARSE_ERROR; /* error - these are not allowed in tagname */
  301.                 else if (c == ' ') {
  302.                     /* Note: Both mozilla and XML don't allow any spaces between < and tagname.
  303.                        Need to check for zero-length tagname.
  304.                     */
  305.                     if (obj->str == NULL) return CRAWL_PARSE_ERROR; /* obj->str is the buffer we're working on */
  306.                     endOfString = PR_TRUE;
  307.                     obj->state = PS_ATTRIBUTE;
  308.                     obj->skipWhitespace = PR_TRUE;
  309.                     n++;
  310.                 } else if (c == '/') {
  311.                     if (obj->tag->name == NULL) obj->tag->endTagp = PR_TRUE; /* indicates end tag if no tag name read yet */
  312.                     else if (obj->isRDF) { /* otherwise its an empty tag (RDF only) */
  313.                         endOfString = PR_TRUE;
  314.                         obj->tag->emptyTagp = PR_TRUE;
  315.                         obj->state = PS_CLOSE_BRACKET;
  316.                     } else return CRAWL_PARSE_ERROR;
  317.                     n++;
  318.                 } else if (c == '>') {
  319.                     endOfString = PR_TRUE;
  320.                     obj->state = PS_CLOSE_BRACKET;
  321.                 } else if ((c != '\r') && (c != '\n')) {
  322.                     if (crawl_appendString(&obj->str, &obj->strLen, &obj->strSize, c) != 0)
  323.                         return CRAWL_PARSE_OUT_OF_MEMORY;
  324.                     n++;
  325.                 } else {
  326.                     endOfString = PR_TRUE;
  327.                     obj->state = PS_ATTRIBUTE; /* note - mozilla allows newline after tag name */
  328.                     obj->skipWhitespace = PR_TRUE;
  329.                     n++;
  330.                 }
  331.                 if (endOfString) {
  332.                     if (crawl_appendString(&obj->str, &obj->strLen, &obj->strSize, '\0') != 0) /* null terminate string */
  333.                         return CRAWL_PARSE_OUT_OF_MEMORY;
  334.                     if (strcmp(obj->str, "!--") == 0) {  /* html comment */
  335.                         obj->inComment = PR_TRUE;
  336.                         obj->state = PS_START;
  337.                     } else {
  338.                         obj->tag->name = obj->str;
  339.                         obj->tag->token = pa_tokenize_tag(obj->str);
  340.                     }
  341.                     obj->str = NULL;
  342.                     obj->strLen = obj->strSize = 0;
  343.                     endOfString = PR_FALSE;
  344.                 }
  345.                 break;
  346.             case PS_CLOSE_BRACKET:
  347.             /* PS_CLOSE_BRACKET - expecting a close bracket, anything else is an error */
  348.                 if (c == '>') {
  349.                     if (!obj->isRDF && (obj->tag->token == P_SCRIPT)) {
  350.                         /* we're inside a script tag (not RDF) */
  351.                         if (obj->tag->endTagp) obj->inScript = PR_FALSE;
  352.                         else obj->inScript = PR_TRUE;
  353.                     }
  354.                     if (func(obj, PR_TRUE, data) == PARSE_STOP) return CRAWL_PARSE_TERMINATE;
  355.                     crawl_recycleParseObj(obj);
  356.                     obj->state = PS_START;
  357.                     n++;
  358.                 } else return CRAWL_PARSE_ERROR; /* error */
  359.                 break;
  360.             case PS_ATTRIBUTE:
  361.             /* PS_ATTRIBUTE - expecting an attribute name, or / (RDF only) or > indicating no more attributes */
  362.                 /* accept attributes without values, such as <tag attr1 attr2=val2>
  363.                    or <tag attr2=val2 attr1>
  364.                 */
  365.                 if (obj->inQuote == c) {
  366.                     obj->inQuote = '\0'; /* close quote */
  367.                 } else if (((c == '"') || (c == '\'')) && (obj->inQuote == '\0')) {
  368.                     /* start a quote if none is already in effect */
  369.                     obj->inQuote = c;
  370.                 }
  371.                 if (obj->inQuote == '\0') {
  372.                     if ((((c == '/') && obj->isRDF) || (c == '>')) && (obj->str == NULL)) {
  373.                         obj->state = PS_CLOSE_BRACKET;
  374.                     } else if ((c == ' ') || (c == '=') || (c == '\n') || (c == '\r') || ((c == '/') && obj->isRDF) || (c == '>')) {
  375.                         if (crawl_appendString(&obj->str, &obj->strLen, &obj->strSize, '\0') != 0) /* null terminate string */
  376.                             return CRAWL_PARSE_OUT_OF_MEMORY;
  377.                         if (crawl_appendStringList(&obj->tag->attributeNames, &obj->tag->numNames, &obj->tag->sizeNames, obj->str) != 0)
  378.                             return CRAWL_PARSE_OUT_OF_MEMORY;
  379.                         obj->str = NULL;
  380.                         obj->strLen = obj->strSize = 0;
  381.                         obj->state = PS_EQUALS; /* if non-null attribute name */
  382.                     } else {
  383.                         if (crawl_appendString(&obj->str, &obj->strLen, &obj->strSize, c) != 0)
  384.                             return CRAWL_PARSE_OUT_OF_MEMORY;
  385.                         n++;
  386.                     }
  387.                 } else {
  388.                     if (crawl_appendString(&obj->str, &obj->strLen, &obj->strSize, c) != 0)
  389.                         return CRAWL_PARSE_OUT_OF_MEMORY;
  390.                     n++;
  391.                 }
  392.                 break;
  393.             case PS_EQUALS:
  394.                 if ((c == ' ') || (c == '\n') || (c == '\r')) {
  395.                     obj->skipWhitespace = PR_TRUE;
  396.                     n++;
  397.                 } else if (c == '=') {
  398.                     obj->skipWhitespace = PR_TRUE;
  399.                     obj->state = PS_VALUE;
  400.                     n++;
  401.                 } else { /* no value for the attribute - error in RDF? */
  402.                     if (crawl_appendString(&obj->str, &obj->strLen, &obj->strSize, '\0') != 0) /* null terminate string */
  403.                         return CRAWL_PARSE_OUT_OF_MEMORY;
  404.                     if (crawl_appendStringList(&obj->tag->attributeValues, &obj->tag->numValues, &obj->tag->sizeValues, obj->str) != 0)
  405.                         return CRAWL_PARSE_OUT_OF_MEMORY;
  406.                     obj->str = NULL;
  407.                     obj->strLen = obj->strSize = 0;
  408.                     obj->state = PS_ATTRIBUTE;
  409.                 }
  410.                 break;
  411.             case PS_VALUE:
  412.             /* expecting a value, or space, / (RDF only), or > indicating end of value. */
  413.                 {
  414.                     PRBool include = PR_TRUE; /* whether the current character should be included in value */
  415.                     if (obj->inQuote == c) {
  416.                         obj->inQuote = '\0'; /* close quote */
  417.                         include = PR_FALSE;
  418.                     } else if (((c == '"') || (c == '\'')) && (obj->inQuote == '\0')) {
  419.                         /* start a quote if none is already in effect */
  420.                         obj->inQuote = c;
  421.                         include = PR_FALSE;
  422.                     }
  423.                     if (obj->inQuote == '\0') {
  424.                         if ((c == '/') && obj->isRDF) {
  425.                             endOfString = PR_TRUE;
  426.                             obj->state = PS_CLOSE_BRACKET;
  427.                             n++;
  428.                         } else if (c == '>') {
  429.                             endOfString = PR_TRUE;
  430.                             obj->state = PS_CLOSE_BRACKET;
  431.                         } else if ((c == ' ') || (c == '\r') || (c == '\n')) {
  432.                             endOfString = PR_TRUE;
  433.                             obj->skipWhitespace = PR_TRUE;
  434.                             obj->state = PS_ATTRIBUTE; /* if non-null value name */
  435.                             n++;
  436.                         } else if (include) {
  437.                             if (crawl_appendString(&obj->str, &obj->strLen, &obj->strSize, c) != 0)
  438.                                 return CRAWL_PARSE_OUT_OF_MEMORY;
  439.                             n++;
  440.                         } else n++;
  441.                     } else if (include) {
  442.                         if (crawl_appendString(&obj->str, &obj->strLen, &obj->strSize, c) != 0)
  443.                             return CRAWL_PARSE_OUT_OF_MEMORY;
  444.                         n++;
  445.                     } else n++;
  446.                     if (endOfString) {
  447.                         if (crawl_appendString(&obj->str, &obj->strLen, &obj->strSize, '\0') != 0) /* null terminate string */
  448.                             return CRAWL_PARSE_OUT_OF_MEMORY;
  449.                         if (crawl_appendStringList(&obj->tag->attributeValues, &obj->tag->numValues, &obj->tag->sizeValues, obj->str) != 0)
  450.                             return CRAWL_PARSE_OUT_OF_MEMORY;
  451.                         obj->str = NULL;
  452.                         obj->strLen = obj->strSize = 0;
  453.                         endOfString = PR_FALSE;
  454.                     }
  455.                     break;
  456.                 }
  457.             default:
  458.                 break;
  459.             }
  460.         }
  461.     }
  462.     return CRAWL_PARSE_NO_ERROR;
  463. }
  464.  
  465. #if 0
  466. void printParseObj(CRAWL_ParseObj obj, PRBool isTag, void *data) {
  467.     if (isTag) {
  468.         CRAWL_Tag tag = CRAWL_GetTagParsed(obj);
  469.         if (CRAWL_IsEndTag(tag)) {
  470.             printf("</%s>\n", CRAWL_GetTagName(tag));
  471.         } else {
  472.             uint16 i;
  473.             printf("<%s", CRAWL_GetTagName(tag));
  474.             for (i = 0; i < CRAWL_GetNumberOfAttributes(tag); i++) {
  475.                 printf(" %s=\"%s\"", CRAWL_GetNthAttributeName(tag, i), CRAWL_GetNthAttributeValue(tag, i));
  476.             }
  477.             if (CRAWL_IsEmptyTag(tag)) printf("/>\n");
  478.             else printf(">\n");
  479.         }
  480.     } else printf(">>>>>%s<<<<<\n", CRAWL_GetDataParsed(obj));
  481. }
  482.  
  483. void parseLocalFile (char *url) {
  484.         PRFileDesc *fp;
  485.         int32 len;
  486.         char *path;
  487.         static char buf[512]; /* xxx alloc */
  488.         CRAWL_ParseObj parse;
  489.  
  490.         /* XXX need to unescape URL */
  491.         path=&url[8];
  492.         fp = PR_Open(path,  PR_RDONLY, 0644);  /* WR_ONLY|PR_TRUNCATE */
  493.         if(fp == NULL)
  494.         {
  495.             /* abortRDFParse(file); */
  496.             return;
  497.         }
  498.         parse = CRAWL_MakeParseObj();
  499.         while((len=PR_Read(fp, buf, 512))>0) {
  500.             int result;
  501.             result = CRAWL_ParserPut(parse, buf, len, printParseObj, NULL);
  502.             if (result == len) printf("************NO ERRORS************\n");
  503.             else printf("************PARSING ERROR************\n");
  504.         }
  505.         PR_Close(fp);
  506.         CRAWL_DestroyParseObj(parse);
  507.         /* finishRDFParse(file); */
  508.         return;
  509. }
  510. #endif
  511.