G:/ScriptBasic/source/reader.c

Go to the documentation of this file.
00001 /*
00002 FILE:   reader.c
00003 HEADER: reader.h
00004 
00005 --GNU LGPL
00006 This library is free software; you can redistribute it and/or
00007 modify it under the terms of the GNU Lesser General Public
00008 License as published by the Free Software Foundation; either
00009 version 2.1 of the License, or (at your option) any later version.
00010 
00011 This library is distributed in the hope that it will be useful,
00012 but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014 Lesser General Public License for more details.
00015 
00016 You should have received a copy of the GNU Lesser General Public
00017 License along with this library; if not, write to the Free Software
00018 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 TO_HEADER:
00021 
00022 typedef struct _SourceLine {
00023   char *line;
00024   long lLineNumber;
00025   long LineLength;
00026   char *szFileName;
00027   struct _SourceLine *next;
00028   } SourceLine, *pSourceLine;
00029 
00030 typedef struct _ImportedFileList {
00031   char *pszFileName;
00032   struct _ImportedFileList *next;
00033   }ImportedFileList,*pImportedFileList;
00034 
00035 typedef struct _ReadObject {
00036   void * (*fpOpenFile)(char *, void *);  // open a file for reading
00037   int (*fpGetCharacter)(void *, void *); // get a character from the opened file
00038   void (*fpCloseFile)(void *, void *);   // close the opened file
00039   void *pFileHandleClass;
00040 
00041   void *(*memory_allocating_function)(size_t, void *);
00042   void (*memory_releasing_function)(void *, void *);
00043   void *pMemorySegment; // This variable is always passed to the memory functions
00044 
00045   ptConfigTree pConfig;
00046 
00047 #define BUFFER_INITIAL_SIZE 1024 //bytes
00048 #define BUFFER_INCREMENT 1024 // number of bytes to increase the buffer size, when it is too small
00049   char *Buffer;  // buffer to read a line
00050   long dwBuffer; // size of Buffer in bytes
00051   long cBuffer;  // the number of character actually in the buffer
00052 
00053   pSourceLine Result; // the lines of the file(s) read
00054 
00055 // iteration variables
00056   pSourceLine CurrentLine; // the current line in the iteration
00057   long NextCharacterPosition; // the position of the next character to be returned during iteration
00058   char fForceFinalNL; // if this is TRUE then an extra new line is 
00059                       // added to the last line if it was terminated by EOF
00060 
00061   pReportFunction report;
00062   void *reportptr; // this pointer is passed to the report function. The caller should set it.
00063   int iErrorCounter;
00064   unsigned long fErrorFlags;
00065 
00066   pImportedFileList pImportList;
00067 
00068   char *FirstUNIXline;
00069   struct _PreprocObject *pPREP;
00070   } ReadObject, *pReadObject;
00071 
00072 */
00073 
00074 #include <stdio.h>
00075 #include <stdlib.h>
00076 #include <string.h>
00077 #include <ctype.h>
00078 
00079 #include "filesys.h"
00080 #include "report.h"
00081 #include "errcodes.h"
00082 #include "conftree.h"
00083 #include "reader.h"
00084 #include "dynlolib.h"
00085 #include "scriba.h"
00086 #include "ipreproc.h"
00087 
00088 /*POD
00089 
00090 This module contains the functions that read a source file.
00091 
00092 Script basic has several passes
00093 until it can start to execute the code. The very first pass is to read the source lines from
00094 the files. The routines in this module perform this task and build up a linked list that contains
00095 the ascii values of the lines.
00096 
00097 The input functions are parametrized, and the caller should support. If you have different
00098 system dependent file reading functions, or if you have the input file in some format in memory or
00099 in any other data holding space you can support these routines with character fetch functions.
00100 
00101 CUT*/
00102 
00103 #if (!defined(_WIN32) && !defined(__MACOS__))
00104 int strnicmp(char *a, char *b, int n){
00105   char ca,cb;
00106 
00107   while( n-- ){
00108     ca = *a++;
00109     cb = *b++;
00110     ca = isupper(ca) ? tolower(ca) : ca;
00111     cb = isupper(cb) ? tolower(cb) : cb;
00112     if( ca == (char)0 && cb == (char)0 )return 0;
00113     if( ca != cb )return ca-cb;
00114     }
00115   return 0;
00116   }
00117 int stricmp(char *a, char *b){
00118   char ca,cb;
00119 
00120   while( 1 ){
00121     ca = *a++;
00122     cb = *b++;
00123     ca = isupper(ca) ? tolower(ca) : ca;
00124     cb = isupper(cb) ? tolower(cb) : cb;
00125     if( ca == (char)0 && cb == (char)0 )return 0;
00126     if( ca != cb )return ca-cb;
00127     }
00128   }
00129 #endif
00130 
00131 #define REPORT(x1,x2,x3,x4) if( pRo->report )pRo->report(pRo->reportptr,x1,x2,x3,REPORT_ERROR,&(pRo->iErrorCounter),x4,&(pRo->fErrorFlags));
00132 
00133 static int reader_AllocateInitialBuffer(pReadObject pRo){
00134   pRo->dwBuffer = BUFFER_INITIAL_SIZE;
00135   pRo->Buffer = (char *)pRo->memory_allocating_function(pRo->dwBuffer,pRo->pMemorySegment);
00136   if( pRo->Buffer )return READER_ERROR_SUCCESS; else return READER_ERROR_MEMORY_LOW;
00137   }
00138 
00139 /*POD
00140 =H reader_IncreaseBuffer()
00141 
00142 When the reader encounters a line which is longer than the currently allocated
00143 input buffer it calls this function to increase the size of the input buffer. The
00144 input buffer is linearly increased by T<BUFFER_INCREMENT> size (defined in the header
00145 section of T<reader.c>
00146 
00147 When a new buffer is allocated the bytes from the old buffer are copied to the new and
00148 the old buffer is released. It is vital that the buffer is always referenced via the
00149 pRo->buffer pointer because resizing buffer does change the location of the buffer.
00150 
00151 If the memory allocation fails the function return T<READER_ERROR_MEMORY_LOW> error.
00152 Otherwise it returns zero.
00153 /*FUNCTION*/
00154 int reader_IncreaseBuffer(pReadObject pRo
00155   ){
00156 /*noverbatim
00157 CUT*/
00158   char *s,*r,*q;
00159 
00160   if( ! pRo->Buffer )return reader_AllocateInitialBuffer(pRo);
00161   r = s = pRo->Buffer;
00162   pRo->dwBuffer += BUFFER_INCREMENT;
00163   pRo->Buffer = (char *)pRo->memory_allocating_function(pRo->dwBuffer,pRo->pMemorySegment);
00164   if( ! pRo->Buffer ){
00165     pRo->Buffer = s;
00166     return READER_ERROR_MEMORY_LOW;
00167     }
00168   for( q = pRo->Buffer ; *q=*s ; s++ , q++ );
00169   pRo->memory_releasing_function(r,pRo->pMemorySegment);
00170   return READER_ERROR_SUCCESS;
00171   }
00172 
00173 /*POD
00174 =H reader_gets()
00175 
00176 This function reads a newline terminated line from the file. The file is
00177 identified by function pRo->fpGetCharacter and the pointer T<fp>.
00178 
00179 When the input buffer is too small it automatically increases the buffer. The
00180 terminating new line is included in the buffer. If the last line of the file is
00181 not terminated by newline an extra newline character is added to this last line.
00182 
00183 The addition of this extra newline character can be switched off setting pRo->fForceFinalNL
00184 to false. Even if this variable is false the normal newline characters which are present
00185 in the file are included in the buffer.
00186 /*FUNCTION*/
00187 int reader_gets(pReadObject pRo,
00188                  void *fp
00189   ){
00190 /*noverbatim
00191 CUT*/
00192   int i,ch;
00193 
00194   ch = pRo->fpGetCharacter(fp,pRo->pFileHandleClass);
00195   if( ch == EOF )return EOF;
00196   for( i=0 ; ch != '\n' && ch != EOF ; i++ ){
00197     if( i >= pRo->dwBuffer )reader_IncreaseBuffer(pRo);
00198     pRo->Buffer[i] = ch;
00199     ch = pRo->fpGetCharacter(fp,pRo->pFileHandleClass);
00200     }
00201   if( i >= pRo->dwBuffer )reader_IncreaseBuffer(pRo);
00202   if( pRo->fForceFinalNL || ch == '\n' )
00203     pRo->Buffer[i++] = '\n'; /* the line terminating \n is included in the buffer */
00204   if( i >= pRo->dwBuffer )reader_IncreaseBuffer(pRo);
00205   pRo->Buffer[i++] = (char)0;/* Mr. Greess pointed out that here we did not count the terminating zero. Corrected by PV 2002MAY13 */
00206   pRo->cBuffer = i;
00207   return !EOF;
00208   }
00209 
00210 /*POD
00211 =H reader_ReadLines()
00212 
00213 This function calls R<reader_ReadLines_r()> to read the lines of the file
00214 given by the file name T<szFileName> into pRo->Result. For further
00215 information see R<reader_ReadLines_r()>.
00216 /*FUNCTION*/
00217 int reader_ReadLines(pReadObject pRo,
00218                      char *szFileName
00219   ){
00220 /*noverbatim
00221 The function returns zero or the error code.
00222 CUT*/
00223   int iResult;
00224   pSourceLine p;
00225 
00226   iResult = 0;
00227   if( pRo->pPREP )iResult = ipreproc_Process(pRo->pPREP,PreprocessorReadStart,pRo);
00228   if( iResult )return iResult;
00229   
00230   pRo->FirstUNIXline = NULL;
00231   iResult = reader_ReadLines_r(pRo,szFileName,&(pRo->Result));
00232   if( iResult )return iResult;
00233   if( pRo->Result == NULL )return READER_ERROR_EMPTY_INPUT;
00234 
00235   if( pRo->pPREP )iResult = ipreproc_Process(pRo->pPREP,PreprocessorReadDone0,pRo);
00236   if( iResult )return iResult;
00237 
00238   /* On UNIX we may start scriptbasic the usual UNIX way having the
00239      first line specifying the interpreter. This first line starts
00240      like #!/usr/bin/scriba and has to be skipped by the interpreter. 
00241 
00242      We do skip it on Win32 too to provide portability.
00243 
00244      On Win32 we may write a scriptbasic program into a .cmd file
00245 
00246      @goto start
00247 
00248      scriptbasic program lines
00249 
00250      rem """
00251      :start
00252      @echo off
00253      scriba %0 %1 %2 %3 %4 %5 %6 %7 %8 %9
00254      rem """
00255 
00256      This first line will be skipped by ScriptBasic on Win32 and for
00257      portability reasons on UNIX-es as well.
00258 
00259      If you want a real portable selfstarting script and do not mind Win32 complaining some
00260      then you can write:
00261 
00262      #! /usr/bin/scriba
00263      goto start
00264      start:
00265      
00266      scriptbasic program lines
00267 
00268      rem """
00269      :start
00270      @echo off
00271      scriba %0 %1 %2 %3 %4 %5 %6 %7 %8 %9
00272      rem """
00273 
00274      It will run on both OS-es.
00275   */
00276   if( strncmp(pRo->Result->line,"#!",2) == 0 ||
00277       strncmp(pRo->Result->line,"@goto",5) == 0 ){
00278     pRo->Result = (p=pRo->Result)->next;
00279     pRo->FirstUNIXline = p->line;
00280     pRo->memory_releasing_function(p,pRo->pMemorySegment);
00281     }else{
00282     pRo->FirstUNIXline = NULL;
00283     }
00284 
00285   if( pRo->pPREP )iResult = ipreproc_Process(pRo->pPREP,PreprocessorReadDone1,pRo);
00286   if( iResult )return iResult;
00287 
00288   /* include the files that has to be included or imported */
00289   reader_ProcessIncludeFiles(pRo,&(pRo->Result));
00290 
00291   iResult = ipreproc_Process(pRo->pPREP,PreprocessorReadDone2,pRo);
00292   if( iResult )return iResult;
00293 
00294   /* load the internal preprocessors and let them process the source code */
00295   reader_LoadPreprocessors(pRo,&(pRo->Result));
00296 
00297   if( pRo->pPREP )iResult = ipreproc_Process(pRo->pPREP,PreprocessorReadDone3,pRo);
00298   return iResult;
00299   }
00300 /*POD
00301 =H reader_ReadLines_r()
00302 
00303 This function reads the lines of a file and creates a linked list
00304 of the read lines.
00305 /*FUNCTION*/
00306 int reader_ReadLines_r(pReadObject pRo,
00307                        char *szFileName,
00308                        pSourceLine *pLine
00309   ){
00310 /*noverbatim
00311 The file is identified by its name given in the string variable T<szFileName>. The
00312 file is opened by the function pointed by pRo->fpOpenFile This function should return
00313 a void pointer and this void pointer is passed to R<reader_gets()> (T<reader_gets>) to get a
00314 single character.
00315 
00316 The argument T<pLine> is a pointer to a T<SourceLine> pointer. The linked list lines read
00317 will be chained into this pointer. The last read line will be followed by the line pointed by
00318 T<*pLine> and T<*pLine> will point to the first line.
00319 
00320 This design makes it easy to use and elegant to perform file inclusions. The caller has to
00321 pass the address of the pointer field T<next> of the source line after which the file is to
00322 be inserted.
00323 
00324 See also T<ReadLines> that calls this function.
00325 CUT*/
00326   void *fp;
00327   pSourceLine pL;
00328   long nLine;
00329 
00330   if( szFileName == NULL ){
00331     pRo->iErrorCounter++;
00332     return READER_ERROR_FILE_OPEN;
00333     }
00334   fp = pRo->fpOpenFile(szFileName,pRo->pFileHandleClass);
00335   if( fp == NULL ){
00336     REPORT(szFileName ,0 ,READER_ERROR_FILE_OPEN,NULL);
00337     return READER_ERROR_FILE_OPEN;
00338     }
00339   nLine = 1; /* the first line comes first */
00340   while( reader_gets(pRo,fp) != EOF ){
00341     pL = (pSourceLine)pRo->memory_allocating_function(sizeof(SourceLine),pRo->pMemorySegment);
00342     if( pL == NULL )return READER_ERROR_MEMORY_LOW;
00343     pL->line = (char *)pRo->memory_allocating_function(pRo->cBuffer,pRo->pMemorySegment);
00344     if( pL->line == NULL ){
00345       pRo->memory_releasing_function(pL,pRo->pMemorySegment);
00346       return READER_ERROR_MEMORY_LOW;
00347       }
00348     pL->szFileName = szFileName;
00349     pL->lLineNumber = nLine++; /* count the lines */
00350     pL->LineLength = pRo->cBuffer;
00351     /* copy the content of the buffer to the line */
00352     strcpy(pL->line,pRo->Buffer);
00353     /* link the line into the list */
00354     pL->next = (*pLine);
00355     *pLine = pL;
00356     pLine = &(pL->next);
00357     }
00358   pRo->fpCloseFile(fp,pRo->pFileHandleClass);
00359   return READER_ERROR_SUCCESS;
00360   }
00361 
00362 extern int GlobalDebugDisplayFlag;
00363 /*POD
00364 =H reader_ProcessIncludeFiles()
00365 
00366 This function is called from R<reader_ReadLines()> after calling R<reader_ReadLines_r()>.
00367 
00368 This function goes through all the lines and checks if there is any line
00369 containing an include directive.
00370 
00371 An include directive is a line starting with a word INCLUDE (case insensitive) and
00372 is followed by the file name. The file name can be enclodes between double quotes.
00373 
00374 Note that the processing of the include directives are done on the characters on the
00375 line, because they are processed before any tokenization of the lexer module. This
00376 can cause some problem only when there is an include like line inside a multiline string.
00377 For example:
00378 
00379 =verbatim
00380 a = """Hey this is a multiline string
00381 include "subfile.txt"
00382 """
00383 =noverbatim
00384 
00385 This B<will> include the file T<subfile.txt> and its content will become part of the string.
00386 This becomes more complicated when the file T<subfile.txt> contains strings.
00387 
00388 The file name may not be enclosed between double quotes. In this case the file is tried to be
00389 found in predefined system directories.
00390 
00391 If the programmer uses the command IMPORT instead of INCLUDE the file will only be included if
00392 it was not included yet into the current program.
00393 /*FUNCTION*/
00394 void reader_ProcessIncludeFiles(pReadObject pRo,
00395                                 pSourceLine *pLine
00396   ){
00397 /*noverbatim
00398 
00399 The file read is inserted into the plce where the include statement was.
00400 
00401 CUT*/
00402 #define FNLEN 1024
00403   pSourceLine p;
00404   char *s,*file_name;
00405   CFT_NODE Node;
00406   char szBuffer[FNLEN];
00407   void *fp;
00408   int isImport; /* true if the statement is import and not include */
00409   pImportedFileList pIFL;
00410   long IncludeCounter;
00411 
00412   IncludeCounter = 1000;
00413   cft_GetEx(pRo->pConfig,"maxinclude",&Node,&s,&IncludeCounter,NULL,NULL); 
00414   p = *pLine;
00415   while( p ){
00416     s = p->line;
00417     while( isspace(*s) )s++;
00418     if( (((!strnicmp(s,"include",7)) && (s+=7) && !(isImport=0) ) ||
00419         ((!strnicmp(s,"import" ,6)) && (s+=6) &&  (isImport=1) )) &&
00420         /* there should be at least one space after the keyword */
00421         isspace(*s) ){
00422       if( --IncludeCounter == 0 ){
00423         REPORT(p->szFileName ,p->lLineNumber ,READER_ERROR_TOOMANY_INCLUDE,NULL);
00424         return;
00425         }
00426       while( isspace(*s) )s++;
00427       if( *s == '"' ){
00428         /* include user files relative to the current source file */
00429         s++;
00430         file_name = s; /* start of the file name */
00431         while( *s && *s != '"' )s++; /* find the end of the file name */
00432         if( *s != '"' ){
00433           REPORT(p->szFileName ,p->lLineNumber ,READER_ERROR_INCLUDE_SYNTAX,NULL);
00434           p = p->next;
00435           continue;
00436           }
00437         *s = (char)0;
00438         s++;
00439         while( isspace(*s) )s++;
00440         if( *s && *s != '\n' ){
00441           REPORT(p->szFileName ,p->lLineNumber ,READER_ERROR_INCLUDE_SYNTAX,NULL);
00442           p = p->next;
00443           continue;
00444           }
00445         file_name = reader_RelateFile(pRo,p->szFileName,file_name);
00446         /* here we have to modify the file name to handle the ../ and other constructs to be
00447            relative to the actual file */
00448         }else{
00449         /* include installed standard file at standard location */
00450         file_name = s;
00451         while( *s && ! isspace(*s) )s++; /* find the end of the file name */
00452         if( *s ){
00453           *s = (char)0;
00454           s++;
00455           }else *s = (char)0;
00456         while( isspace(*s) )s++;
00457         if( *s && *s != '\n' ){
00458           REPORT(p->szFileName ,p->lLineNumber ,READER_ERROR_INCLUDE_SYNTAX,NULL);
00459           p = p->next;
00460           continue;
00461           }
00462 
00463         if( GlobalDebugDisplayFlag ){
00464           fprintf(stderr,"Searching installed module header file '%s' ...\n",file_name);
00465           }
00466         /* here we try to iterate all the configuration defines include directories and
00467            try if there is an openable file in one of the. If there is the first is
00468            used */
00469         fp = NULL;
00470         for( cft_GetEx(pRo->pConfig,"include",&Node,&s,NULL,NULL,NULL);  
00471              ! cft_GetEx(pRo->pConfig,NULL,&Node,&s,NULL,NULL,NULL) ; 
00472              Node = cft_EnumNext(pRo->pConfig,Node) ){
00473           if( ! strcmp(cft_GetKey(pRo->pConfig,Node),"include") ){
00474             if( s && strlen(s) > FNLEN )REPORT(p->szFileName ,p->lLineNumber ,READER_ERROR_INCLUDE_SYNTAX,NULL);
00475             if( s )strcpy(szBuffer,s); else *szBuffer = (char)0;
00476             strcat(szBuffer,file_name);
00477             fp = pRo->fpOpenFile(szBuffer,pRo->pFileHandleClass);  /* open a file for reading (just for test: can we?) */
00478             if( GlobalDebugDisplayFlag ){
00479               fprintf(stderr,"Checking installed module header file location '%s' Result=%s\n",szBuffer, fp ? "OK" : "FAILED" );
00480               }
00481             if( fp != NULL )break;
00482             }
00483           }
00484 
00485         if( fp == NULL ){/* if there was no file openable during the search */
00486           REPORT(p->szFileName ,p->lLineNumber ,READER_ERROR_INCLUDE_FILE,NULL);
00487           goto NotInclude;
00488           }
00489         pRo->fpCloseFile(fp,pRo->pFileHandleClass);/* close the file because it was opened */
00490         file_name = pRo->memory_allocating_function(strlen(szBuffer)+1,pRo->pMemorySegment);
00491         if( file_name == NULL )REPORT(p->szFileName ,p->lLineNumber ,READER_ERROR_MEMORY_LOW,NULL);
00492         strcpy(file_name,szBuffer);
00493         }
00494       /* file_name now points to a file openable by fpOpenFile */
00495       if( isImport ){
00496         /* check that this file was not included yet. */
00497         pIFL = pRo->pImportList;
00498         while( pIFL ){
00499           if( ! strcmp(file_name,pIFL->pszFileName) ){
00500             *pLine = (*pLine)->next; /* unlink the line including the import statement */
00501             p = *pLine;
00502             goto NextP;
00503             }
00504           pIFL = pIFL->next;
00505           }
00506         }
00507       /* if it is not import or if the file was not included yet then put the name of the file
00508          on the included file list. This puts an included file on this list as many times as
00509          it is included, but it does not matter. */
00510       pIFL = pRo->memory_allocating_function(sizeof(ImportedFileList),pRo->pMemorySegment);
00511       if( pIFL == NULL )REPORT(p->szFileName ,p->lLineNumber ,READER_ERROR_MEMORY_LOW,NULL);
00512       pIFL->next = pRo->pImportList;
00513       pIFL->pszFileName = file_name;
00514       pRo->pImportList = pIFL;
00515       *pLine = (*pLine)->next; /* unlink the line containing the 'include' statement */
00516       if( GlobalDebugDisplayFlag ){
00517         fprintf(stderr,"Including file '%s'\n",file_name);
00518         }
00519       reader_ReadLines_r(pRo,file_name,pLine);
00520       /* release the line containing the include statement */
00521       pRo->memory_releasing_function(p->line,pRo->pMemorySegment);
00522       pRo->memory_releasing_function(p,pRo->pMemorySegment);
00523       /* file_name is not released, because that buffer is
00524          referenced several times. That buffer is released when the
00525          whole reader segment is released. */
00526       p = *pLine;
00527       }else
00528 NotInclude:
00529     if( p ){
00530       pLine = &(p->next);
00531       p = *pLine;
00532       }
00533 NextP:;
00534     }
00535   }
00536 
00537 /*POD
00538 =H reader_LoadPreprocessors()
00539 
00540 Preprocessors are not part of ScriptBasic. They can be implemented as external DLLs and
00541 should be configured in the configuration file.
00542 
00543 When a line contains
00544 
00545 =verbatim
00546 USE preprocessorname
00547 =noverbatim
00548 
00549 this reader module loads the preprocessor DLL or SO (dll under unix) file.
00550 
00551 /*FUNCTION*/
00552 void reader_LoadPreprocessors(pReadObject pRo,
00553                                  pSourceLine *pLine
00554   ){
00555 /*noverbatim
00556 
00557 CUT*/
00558 #define FNLEN 1024
00559   pSourceLine p,*prev;
00560   char *s;
00561   int iError;
00562   char szBuffer[FNLEN];
00563 
00564   if( pRo->pPREP == NULL ){
00565     /* if the embedding application does not provide 
00566        the environment then ignore the preprocess lines 
00567        all we do is to unlink the 'preprocess' lines */
00568     p = *pLine;
00569     prev = pLine;
00570     while( p ){
00571       s = p->line;
00572       while( isspace(*s) )s++;
00573       if( !strnicmp(s,"use",3) )
00574         (*prev) = p->next; /* unlink the 'use' line */
00575       prev = &(p->next);
00576       p = p->next;
00577       }
00578     return;
00579     }
00580 
00581   p = *pLine;
00582   prev = pLine;
00583   while( p ){
00584     s = p->line;
00585     while( isspace(*s) )s++;
00586     if( !strnicmp(s,"use",3) ){
00587       s += 3;
00588       if( ! isspace(*s) ){
00589         prev = &(p->next);
00590         p = p->next;
00591         continue;
00592         }
00593       while( isspace(*s) )s++;
00594       if( strlen(s) > FNLEN ){
00595         REPORT(p->szFileName ,p->lLineNumber ,READER_ERROR_PREPROC_LONG ,s);
00596         continue;
00597         }
00598       strcpy(szBuffer,s);
00599       s = szBuffer;
00600       while( *s && ! isspace(*s) )s++;
00601       *s = (char)0;
00602       if( pRo->pPREP && (iError = ipreproc_LoadInternalPreprocessor(pRo->pPREP,szBuffer)) ){
00603         REPORT(p->szFileName ,p->lLineNumber ,iError ,szBuffer);
00604         }
00605       (*prev) = p->next; /* unlink the 'preprocess' line */
00606       }else
00607       prev = &(p->next);
00608     p = p->next;
00609     }
00610   }
00611 
00612 
00613 /*POD
00614 =H reader_StartIteration()
00615 
00616 The package supports functions that help upper layer modules to iterate
00617 through the lines read. This function should be called to start the iteration
00618 and to set the internal iteration pointer to the first line.
00619 /*FUNCTION*/
00620 void reader_StartIteration(pReadObject pRo
00621   ){
00622 /*noverbatim
00623 CUT*/
00624   pRo->CurrentLine = pRo->Result;
00625   pRo->NextCharacterPosition = 0;
00626   }
00627 
00628 /*POD
00629 =H reader_NextLine()
00630 
00631 This function returns a string which is the next line during iteration.
00632 This function does NOT read anything from any file, only returns a pointer to
00633 a string that was already read.
00634 
00635 This function can be used together with R<reader_NextCharacter()>. When a line was partially
00636 passed to an upper layer that uses T<reader_NextCharacter> this function
00637 will only return the rest of the line.
00638 
00639 /*FUNCTION*/
00640 char *reader_NextLine(pReadObject pRo
00641   ){
00642 /*noverbatim
00643 CUT*/
00644   long ThisCharacter;
00645 
00646   if( ! pRo->CurrentLine )return NULL;
00647   if( pRo->CurrentLine->line[pRo->NextCharacterPosition] == (char)0 ){
00648     pRo->CurrentLine = pRo->CurrentLine->next;
00649     pRo->NextCharacterPosition = 0;
00650     }
00651   if( ! pRo->CurrentLine )return NULL;
00652   ThisCharacter = pRo->NextCharacterPosition;
00653   pRo->NextCharacterPosition = pRo->CurrentLine->LineLength; /* we have returned all the characters */
00654   return pRo->CurrentLine->line+ThisCharacter;
00655   }
00656 
00657 /*POD
00658 =H reader_NextCharacter()
00659 
00660 This function gets the next character from the actual line, or gets the
00661 first character of the next line.
00662 
00663 This function does NOT read anything from any file, only returns a character from a string that
00664 was already read.
00665 
00666 When the last character of the last line was passed it return T<EOF>
00667 /*FUNCTION*/
00668 int reader_NextCharacter(void *p
00669   ){
00670 /*noverbatim
00671 CUT*/
00672   pReadObject pRo = (pReadObject)p;
00673   if( ! pRo->CurrentLine )return EOF;
00674   if( pRo->CurrentLine->line[pRo->NextCharacterPosition] == (char)0 ){
00675     pRo->CurrentLine = pRo->CurrentLine->next;
00676     pRo->NextCharacterPosition = 0;
00677     }
00678   if( ! pRo->CurrentLine )return EOF;
00679   return (int)pRo->CurrentLine->line[pRo->NextCharacterPosition++];
00680   }
00681 
00682 
00683 /*POD
00684 =H reader_FileName()
00685 
00686 This function returns the file name of the actual line. This is the string that
00687 was used to name the file when it was opened. This can be different for different
00688 lines when the reader is called several times to resolve the "include" statements.
00689 /*FUNCTION*/
00690 char *reader_FileName(void *p
00691   ){
00692 /*noverbatim
00693 CUT*/
00694   pReadObject pRo = (pReadObject)p;
00695   if( ! pRo || ! pRo->CurrentLine )return "No-File";
00696   return pRo->CurrentLine->szFileName;
00697   }
00698 
00699 /*POD
00700 =H reader_LineNumber()
00701 
00702 This function returns the line number of the current line durig iteration.
00703 This number identifies the line in the file where it was read from.
00704 
00705 /*FUNCTION*/
00706 long reader_LineNumber(void *p
00707   ){
00708 /*noverbatim
00709 CUT*/
00710   pReadObject pRo = (pReadObject)p;
00711   if( !pRo || ! pRo->CurrentLine )return 0;
00712   return pRo->CurrentLine->lLineNumber;
00713   }
00714 
00715 /* two functions to call the posix standard memory allocation functions */
00716 static void *reader_malloc(size_t n, void *pMemorySegment){
00717   return malloc(n);
00718   }
00719 static void reader_free(void *p, void *pMemorySegment){
00720   free(p);
00721   }
00722 
00723 /* three functions to call the posix standard file open, read, close functions */
00724 static void *_MyOpenFile(char *FileName, void *p){
00725   return (void *)file_fopen(FileName,"r");
00726   }
00727 static int _MyGetCharacter(void *fp, void *p){
00728   return file_fgetc( (FILE *)fp);
00729   }
00730 static void _MyCloseFile(void *fp, void *p){
00731   file_fclose((FILE *)fp);
00732   }
00733 
00734 /*POD
00735 =H reader_InitStructure()
00736 
00737 This function should be called to initialize the reader structure. It sets the
00738 file handling routines to the standard T<fopen>, T<fclose> and T<getc> functions,
00739 and also sets the function pointers so that the module uses T<malloc> and T<free>.
00740 
00741 /*FUNCTION*/
00742 void reader_InitStructure(pReadObject pRo
00743   ){
00744 /*noverbatim
00745 CUT*/
00746   /* set the file handling functions to the posix standard functions */
00747   pRo->fpOpenFile = _MyOpenFile;
00748   pRo->fpGetCharacter = _MyGetCharacter;
00749   pRo->fpCloseFile = _MyCloseFile;
00750   pRo->pFileHandleClass = NULL;
00751 
00752   pRo->memory_allocating_function = reader_malloc;
00753   pRo->memory_releasing_function = reader_free;
00754   pRo->pMemorySegment = NULL;
00755 
00756   pRo->pImportList = NULL;
00757 
00758   /* this should be NULL and zero. the buffer will be allocated during the first call to reader_gets */
00759   pRo->Buffer = NULL;
00760   pRo->dwBuffer = 0;
00761 
00762   pRo->Result = NULL;
00763   pRo->fForceFinalNL = 1; /* it is better to have the last line terminated by a new line character even
00764                              if the file itself has a truncated last line without line feed at the end. */
00765   pRo->pPREP = NULL; /* The caller has to set an appropriate SbProgram object to handle the internal
00766                         preprocessors */
00767   }
00768 
00769 /*POD
00770 =H reader_RelateFile()
00771 
00772 This function gets a file name, which is either absolute or relative
00773 to the current working directory and another file name which is absolute or
00774 relative to the first one.
00775 
00776 The return value of the function is a file name which is either absolute or
00777 relative to the current woring directory.
00778 
00779 The return value is dynamically allocated and is to be release by the caller.
00780 The allocation function is taken from the class function and the segment is
00781 T<pMemorySegment>.
00782 /*FUNCTION*/
00783 char *reader_RelateFile(pReadObject pRo,
00784                        char *pszBaseFile,
00785                        char *pszRelativeFile
00786   ){
00787 /*noverbatim
00788 CUT*/
00789   long lLen;
00790   char *pszBuffer,*s,*r,*q;
00791 
00792 #ifdef __MACOS__
00793 #define ischardsep(s) ((s) == ':')
00794 
00795   if( !ischardsep(*pszRelativeFile)
00796 #else
00797 
00798   /* note that \\ is a valid directory separator in scriba include statement 
00799      on UNIX as well, because stupid DOS programmers may use it and their code
00800      should run on UNIX boxes as well. */
00801 #define ischardsep(s) ((s) == '/' || (s) == '\\')
00802 
00803   if( ischardsep(*pszRelativeFile)
00804 #ifdef WIN32
00805     /* if a DOS programmer uses an absolute path including drive character, I can't help
00806        (laughing) */
00807       || pszRelativeFile[1] == ':'
00808 #endif
00809 #endif
00810       ){
00811     /* this is an absolute file name */
00812     lLen = strlen(pszRelativeFile)+1;
00813     pszBuffer = pRo->memory_allocating_function(lLen,pRo->pMemorySegment);
00814     if( pszBuffer == NULL ){
00815       REPORT("" ,0 ,READER_ERROR_MEMORY_LOW,NULL);
00816       return NULL;
00817       }
00818     strcpy(pszBuffer,pszRelativeFile);
00819 #ifndef __MACOS__
00820     /* this code aids brain dead DOS programmers using \ as directory
00821        separator to run their code on UNIX. */
00822     s = pszBuffer;
00823     while( *s ){
00824       if( *s == '\\' )*s = '/';
00825       s++;
00826       }
00827 #endif
00828     return pszBuffer;
00829     }
00830 
00831   lLen = strlen(pszBaseFile) + strlen(pszRelativeFile) +1;
00832   pszBuffer = pRo->memory_allocating_function(lLen,pRo->pMemorySegment);
00833   if( pszBuffer == NULL ){
00834     REPORT("" ,0 ,READER_ERROR_MEMORY_LOW,NULL);
00835     return NULL;
00836     }
00837   strcpy(pszBuffer,pszBaseFile);
00838   r = s = pszBuffer;
00839   /* find the last directory separator */
00840   while( *s ){
00841     if( ischardsep(*s) )r = s;
00842     s++;
00843     }
00844   if( ischardsep(*r) )r++;
00845   /* append the file name after the directory of the base file */
00846   strcpy(r,pszRelativeFile);
00847   s = pszBuffer;
00848   while( *s ){
00849     r = s+1;
00850     while( *r && ! ischardsep(*r) )r++;
00851     if(  (ischardsep(*r) && r[1] == '.' && r[2] == '.' && ischardsep(r[3])) &&
00852        /* this extra condition is to prevent "../../" to be removed */
00853         !( s[0] == '.' && s[1] == '.' && ischardsep(s[2]) ) ){
00854       /* this is a name/../ structure */
00855       q = s;
00856       r += 4;
00857       while( *q++ = *r++ );
00858       }else
00859       s = r+1;
00860     }
00861 #ifndef __MACOS__
00862   /* this code aids brain dead DOS programmers using \ as directory
00863      separator to run their code on UNIX. */
00864   s = pszBuffer;
00865   while( *s ){
00866     if( *s == '\\' )*s = '/';
00867     s++;
00868     }
00869 #endif
00870   return pszBuffer;
00871   }
00872 
00873 /*POD
00874 =H reader_DumpLines()
00875 
00876 This is a debug function that prints the lines into a debug file.
00877 /*FUNCTION*/
00878 void reader_DumpLines(pReadObject pRo,
00879                       FILE *fp
00880   ){
00881 /*noverbatim
00882 
00883 CUT*/
00884   pSourceLine p;
00885   p = pRo->Result;
00886   while( p ){
00887     fprintf(fp,"%s",p->line);
00888     p = p->next;
00889     }
00890   }

Generated on Sun Mar 12 23:56:31 2006 for ScriptBasic by  doxygen 1.4.6-NO