G:/ScriptBasic/source/builder.c

Go to the documentation of this file.
00001 /*
00002 FILE:   builder.c
00003 HEADER: builder.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 This needs expression.h
00021 
00022 TO_HEADER:
00023 // pragma pack(1) will help VC++ 6.0 to pack the structure to 16 bytes
00024 #ifdef WIN32
00025 #pragma warning( disable : 4103 ) // otherwise VC++ may complain about the pragma pack
00026 // do not try to use pack(2) or pack(1) The node size can not be smaller than 16 bytes and
00027 // if you use pragma(1) or pragma(2) the compiler creates unusable code (segmentation fault)
00028 #pragma pack(push,4)
00029 #endif
00030 typedef struct _cNODE {
00031   long OpCode; // the code of operation
00032   union {
00033 
00034     // when the node is a command
00035     struct {
00036       unsigned long next;
00037       union {
00038         unsigned long pNode;  // node id of the node
00039         long lLongValue;
00040         double dDoubleValue;
00041         unsigned long szStringValue; // serial value of the string from the string table
00042         }Argument;
00043       }CommandArgument;
00044 
00045     // when the node is an operation
00046     struct {
00047       unsigned long Argument; // node id of the node list head
00048       }Arguments;
00049 
00050     // when the node is a constant
00051     union {
00052       double dValue;        
00053       long   lValue;        
00054       unsigned long sValue; // serial value of the string from the string table       
00055       }Constant;
00056 
00057     // when the node is a variable
00058     struct {
00059       unsigned long Serial; // the serial number of the variable
00060       }Variable;
00061 
00062     // when node is a user functions
00063     struct {
00064       unsigned long NodeId; // the entry point of the function
00065       unsigned long Argument; // node id of the node list head
00066       }UserFunction;
00067 
00068     // when the node is a node list head
00069     struct {
00070       unsigned long actualm; //car
00071       unsigned long rest;    //cdr
00072       }NodeList;
00073 
00074 
00075     }Parameter;
00076   } cNODE,*pcNODE;
00077 
00078 #ifdef WIN32
00079 #pragma pack(pop)
00080 #endif
00081 
00082 // Symbol table type where each ZCHAR symbol is assigned to a long
00083 // the table is stored in a way so that the long value is followed
00084 // by the ZCHAR symbol in the memory and then the next record comes.
00085 typedef struct _SymbolLongTable {
00086   long value;
00087   char symbol[1];
00088   } SymbolLongTable, *pSymbolLongTable;
00089 
00090 typedef struct _BuildObject {
00091   void *(*memory_allocating_function)(size_t);
00092   void (*memory_releasing_function)(void *);
00093   void *pMemorySegment; //this pointer is passed to the memory allocating functions
00094                         //this pointer is initialized in ex_init
00095   char *StringTable; // all the string constants of the program zero terminated each
00096   unsigned long cbStringTable; // all the bytes of StringTable including the zeroes
00097   unsigned long cbCollectedStrings; // the size of the strings collected so far
00098 
00099   int iErrorCounter;
00100 
00101   long cGlobalVariables;
00102 
00103   pcNODE CommandArray;
00104   unsigned long NodeCounter; // used to count the nodes and assign NodeId
00105   unsigned long StartNode;
00106 
00107   unsigned long cbFTable; // bytes of the function symbol table
00108   unsigned long cbVTable; // bytes of the global variables table
00109   pSymbolLongTable FTable; // the functions symbol table
00110   pSymbolLongTable VTable; // the global variable symbol table
00111 
00112   peXobject pEx; // the symbolic structure to build table from
00113   pReportFunction report;
00114   void *reportptr; // this pointer is passed to the report function. The caller should set it.
00115   unsigned long fErrorFlags;
00116   char *FirstUNIXline;
00117   struct _PreprocObject *pPREP;
00118   } BuildObject, *pBuildObject;
00119 
00120 #define MAGIC_CODE   0x1A534142//this is simply BAS and ^Z on DOS to finish typing to screen
00121 #define VERSION_HIGH 0x00000002
00122 #define VERSION_LOW  0x00000000
00123 #define MYVERSION_HIGH 0x00000000
00124 #define MYVERSION_LOW  0x00000000
00125 #define VARIATION "STANDARD"
00126 typedef struct _VersionInfo {
00127   unsigned long MagicCode;
00128   unsigned long VersionHigh, VersionLow;
00129   unsigned long MyVersionHigh,MyVersionLow;
00130   unsigned long Build;
00131   unsigned long Date;
00132   unsigned char Variation[9];
00133   } VersionInfo,*pVersionInfo;
00134 
00135 #define BU_SAVE_FTABLE 0x00000001
00136 #define BU_SAVE_VTABLE 0x00000002
00137 */
00138 
00139 /*POD
00140 
00141 This module can and should be used to create the memory image for the
00142 executor module from the memory structure that was created by the module
00143 T<expression>.
00144 
00145 The memory structure created by T<expression> is segmented, allocated
00146 in many separate memory chunks. When the module T<expression> has been finished
00147 the size of the memory is known. This builder creates a single memory 
00148 chunk containing all the program code.
00149 
00150 Note that the function names all start with the prefix T<build_> in this module.
00151 
00152 The first argument to each function is a pointer to a T<BuildObject> structure
00153 that contains the "global" variables for the module. This technique is used to ensure
00154 multithread usage. There are no global variables which are really global within the
00155 process.
00156 
00157 The functions in this module are:
00158 
00159 =toc
00160 
00161 CUT*/
00162 
00163 #include <stdlib.h>
00164 #include <string.h>
00165 #include <stdio.h>
00166 
00167 #include "filesys.h"
00168 #include "report.h"
00169 #include "lexer.h"
00170 #include "sym.h"
00171 #include "expression.h"
00172 #include "myalloc.h"
00173 #include "builder.h"
00174 #include "errcodes.h"
00175 #include "buildnum.h"
00176 
00177 #if _WIN32
00178 #include <windows.h>
00179 #if BCC32 || CYGWIN
00180 extern char *_pgmptr;
00181 #endif
00182 #endif
00183 
00184 
00185 #define REPORT(x1,x2,x3,x4) if( pBuild->report )pBuild->report(pBuild->reportptr,x1,x2,x3,REPORT_ERROR,&(pBuild->iErrorCounter),x4,(&pBuild->fErrorFlags))
00186 
00187 #define CALL_PREPROCESSOR(X,Y) if( pBuild->pPREP && pBuild->pPREP->n )ipreproc_Process(pBuild->pPREP,X,Y)
00188 
00189 /*POD
00190 =H The structure of the string table
00191 
00192 The string table contains all string contansts that are used in the program.
00193 This includes the single and multi line strings as well as symbols. (note that
00194 even the variable name after the keyword T<next> is ignored but stored in the
00195 string table).
00196 
00197 The strings in the string table are stored one after the other zero character
00198 terminated. Older version of ScriptBasic v1.0b21 and before stored string
00199 constants zero character terminated. Because of this string constants containing
00200 zero character were truncated (note that T<\000> creates a zero character in a
00201 string constant in ScriptBasic).
00202 
00203 The version v1.0b22 changed the way string constants are stored and the way
00204 string table contains the strings. Each string is stored with its length.
00205 The length is stored as a T<long> on T<sizeof(long)> bytes. This is followed by
00206 the string. Whenever the code refers to a string the byte offset of the first
00207 character of the string is stored in the built code. For example the very first
00208 string starts on the 4. byte on 32 bit machines.
00209 
00210 Altough the string length and zero terminating characters are redundant information
00211 both are stored to avoid higher level mistakes causing problem.
00212 CUT*/
00213 
00214 /*POD
00215 =H build_AllocateStringTable()
00216 
00217 This function allocates space for the string table. The size of the
00218 string table is already determined during syntax analysis. The determined
00219 size should be enough. In some cases when there are repeated string constants
00220 the calculated sizte is bigger than the real one. In that case the larger memory
00221 is allocated and used, but only the really used part is written to the cache file.
00222 
00223 If the program does not use any string constants then a dummy string table of length
00224 one byte is allocated.
00225 
00226 /*FUNCTION*/
00227 void build_AllocateStringTable(pBuildObject pBuild,
00228                           int *piFailure
00229   ){
00230 /*noverbatim
00231 
00232 The first argument is the usual pointer to the "class" structure. The second argument
00233 is the result value. It can have two values:
00234 
00235 =itemize
00236 =item T<BU_ERROR_SUCCESS> which is guaranteed zero, means the function was successful.
00237 =item T<BU_ERROR_MEMORY_LOW> means the memory allocation function could not allocate the
00238 neccessary memory
00239 =noitemize
00240 
00241 The string table is allocated using the function T<alloc_Alloc>. The string table
00242 is pointed by the class variable T<StringTable>. The size of the table is stored in
00243 T<cStringTable>
00244 
00245 CUT*/
00246 
00247   /* allocate memory for the strings */
00248   if( pBuild->cbStringTable == 0L )pBuild->cbStringTable=1L; /* lets allocate some space */
00249   pBuild->StringTable = alloc_Alloc(pBuild->cbStringTable ,pBuild->pMemorySegment);
00250   pBuild->cbCollectedStrings = 0L;
00251   if( pBuild->StringTable == NULL ){
00252     REPORT("",0L,BU_ERROR_MEMORY_LOW,NULL);
00253     *piFailure = BU_ERROR_MEMORY_LOW;
00254     return;
00255     }
00256   *piFailure = BU_ERROR_SUCCESS;
00257   return;
00258   }
00259 /*POD
00260 =H build_StringIndex()
00261 
00262 In the built code all the strings are references using the offset of the string
00263 from the string table (See R<build_AllocateStringTable()>). This function calculates this value
00264 for the string.
00265 
00266 This function is used repetitively during the code building. Whenever a string index is
00267 sought that is not in the string table yet the string is put into the table and the
00268 index is returned.
00269 
00270 If there is not enough space in the string table the function calls the system function
00271 T<exit> and stops the process. This is rude especially in a multithread application
00272 but it should not ever happen. If this happens then it is a serious internal error.
00273 /*FUNCTION*/
00274 unsigned long build_StringIndex(pBuildObject pBuild,
00275                                 char *s,
00276                                 long sLen
00277   ){
00278 /*noverbatim
00279 CUT*/
00280   unsigned long ulIndex;
00281   char *r;
00282   long lLen;
00283 
00284   ulIndex = 0;
00285   while( ulIndex < pBuild->cbCollectedStrings ){
00286     /* SUN Solaris generated segmentation fault for accessing this memory with 'lLen=*(long*)ptr' assignment */
00287     memcpy( &lLen, pBuild->StringTable + ulIndex , sizeof(long));
00288     ulIndex += sizeof(long);
00289     if( sLen == lLen && !memcmp(s,pBuild->StringTable + ulIndex,lLen) )return ulIndex;
00290     ulIndex += lLen;
00291     ulIndex++; /* step over the extra zchar at the end of the string */
00292     }
00293   /* the string is not in the table, we have to place it */
00294   r = pBuild->StringTable + pBuild->cbCollectedStrings;
00295   if( sLen+1+pBuild->cbCollectedStrings > pBuild->cbStringTable ){
00296     fprintf(stderr,"String table build up. Internal error!\n");
00297     exit(2000);
00298     }
00299   memcpy(r,&sLen,sizeof(long));
00300   r += sizeof(long);
00301   memcpy(r,s,sLen+1);
00302   ulIndex = pBuild->cbCollectedStrings + sizeof(long);
00303   pBuild->cbCollectedStrings += sLen + sizeof(long) + 1;
00304   return ulIndex;
00305   }
00306 
00307 /*POD
00308 =H build_Build_l()
00309 
00310 This function converts an T<eNODE_l> list to T<cNODE> list in a loop.
00311 This function is called from R<build_Build()> and from R<build_Build_r()>.
00312 
00313 /*FUNCTION*/
00314 int build_Build_l(pBuildObject pBuild,
00315                   peNODE_l Result
00316   ){
00317 /*noverbatim
00318 The function returns the error code, or zero in case of success.
00319 
00320 CUT*/
00321   int iFailure;
00322 
00323   while( Result ){
00324     pBuild->CommandArray[Result->NodeId-1].OpCode = eNTYPE_LST;
00325     pBuild->CommandArray[Result->NodeId-1].Parameter.NodeList.actualm = Result->actualm ? Result->actualm->NodeId : 0;
00326     pBuild->CommandArray[Result->NodeId-1].Parameter.NodeList.rest = Result->rest ? Result->rest->NodeId : 0;
00327     if(  iFailure = build_Build_r(pBuild,Result->actualm) )return iFailure;
00328     Result = Result->rest;
00329     }
00330 
00331   return BU_ERROR_SUCCESS;
00332   }
00333 /*POD
00334 =H build_Build_r()
00335 
00336 This function builds a single node. This actually means copiing the values
00337 from the data structure created by the module T<expression>. The major
00338 difference is that the pointers of the original structure are converted to
00339 T<unsigned long>. Whenever a pointer pointed to a T<eNODE> the T<unsigned long>
00340 will contain the T<NodeId> of the node. This ID is the same for the T<eNODE> and
00341 for the T<cNODE> that is built from the T<eNODE>.
00342 
00343 /*FUNCTION*/
00344 int build_Build_r(pBuildObject pBuild,
00345                   peNODE Result
00346   ){
00347 /*noverbatim
00348 
00349 The node to be converted is passed by the pointer T<Result>. The return value is
00350 the error code. It is zero (T<BU_ERRROR_SUCCESS>) in case of success.
00351 
00352 When the node pointed by T<Result> references other nodes the function recursively
00353 calls itself to convert the referenced nodes.
00354 
00355 CUT*/
00356   pcNODE pThis;
00357   unsigned long *q;
00358   pLineSyntax pCommand;
00359   int iFailure;
00360   int j;
00361 
00362   if( Result == NULL )return BU_ERROR_SUCCESS;
00363   pThis = pBuild->CommandArray+Result->NodeId-1;
00364   pThis->OpCode = Result->OpCode;
00365 
00366   /* convert an array access node */
00367   if( Result->OpCode == eNTYPE_ARR || Result->OpCode == eNTYPE_SAR ){
00368     pThis->Parameter.Arguments.Argument = Result->Parameter.Arguments.Argument->NodeId;
00369     return build_Build_l(pBuild,Result->Parameter.Arguments.Argument);
00370     }
00371 
00372   /* Convert a user function node */
00373   if( Result->OpCode == eNTYPE_FUN ){
00374     if( Result->Parameter.UserFunction.pFunction->node == 0 ){
00375       REPORT("",0L,EX_ERROR_FUNCTION_NOT_DEFINED,Result->Parameter.UserFunction.pFunction->FunctionName);
00376       }
00377     pThis->Parameter.UserFunction.NodeId = Result->Parameter.UserFunction.pFunction->node;
00378     pThis->Parameter.UserFunction.Argument = Result->Parameter.UserFunction.Argument ?
00379                                         Result->Parameter.UserFunction.Argument->NodeId : 0;
00380     return build_Build_l(pBuild,Result->Parameter.UserFunction.Argument);
00381     }
00382 
00383   /* Convert a local/global variable node */
00384   if( Result->OpCode == eNTYPE_LVR || Result->OpCode == eNTYPE_GVR ){
00385     pThis->Parameter.Variable.Serial = Result->Parameter.Variable.Serial;
00386     return BU_ERROR_SUCCESS;
00387     }
00388 
00389   if( Result->OpCode == eNTYPE_DBL ){
00390     pThis->Parameter.Constant.dValue = Result->Parameter.Constant.Value.dValue;
00391     return BU_ERROR_SUCCESS;
00392     }
00393 
00394   if( Result->OpCode == eNTYPE_LNG ){
00395     pThis->Parameter.Constant.lValue = Result->Parameter.Constant.Value.lValue;
00396     return BU_ERROR_SUCCESS;
00397     }
00398 
00399   if( Result->OpCode == eNTYPE_STR ){
00400     pThis->Parameter.Constant.sValue = build_StringIndex(pBuild,Result->Parameter.Constant.Value.sValue,Result->Parameter.Constant.sLen);
00401     return BU_ERROR_SUCCESS;
00402     }
00403 
00404   q = pBuild->pEx->Binaries;
00405 
00406   while( *q && *q != (unsigned)pThis->OpCode )q+=2;
00407   if( *q ){
00408     pThis->Parameter.Arguments.Argument = Result->Parameter.Arguments.Argument->NodeId;
00409     return build_Build_l(pBuild,Result->Parameter.Arguments.Argument);
00410     }
00411 
00412   q = pBuild->pEx->Unaries;
00413   while( *q && *q != (unsigned)pThis->OpCode )q++;
00414   if( *q ){
00415     pThis->Parameter.Arguments.Argument = Result->Parameter.Arguments.Argument->NodeId;
00416     return build_Build_l(pBuild,Result->Parameter.Arguments.Argument);
00417     }
00418 
00419   pCommand = pBuild->pEx->Command;
00420   while( pCommand && pCommand->CommandOpCode != 0 && pCommand->CommandOpCode != pThis->OpCode )pCommand++;
00421 
00422 #define NEXT_ARGUMENT if( Result->Parameter.CommandArgument.next ){\
00423             if( pThis->Parameter.CommandArgument.next = Result->Parameter.CommandArgument.next->NodeId ){\
00424               pThis = pBuild->CommandArray+Result->Parameter.CommandArgument.next->NodeId-1;\
00425               pThis->OpCode = eNTYPE_CRG;\
00426               Result = Result->Parameter.CommandArgument.next;\
00427               }\
00428             break;\
00429             }\
00430           else{\
00431             pThis->Parameter.CommandArgument.next = 0;\
00432             return BU_ERROR_SUCCESS;\
00433             }
00434 
00435   if( pCommand && pCommand->CommandOpCode ){
00436     for( j=0 ; j < MAX_LEXES_PER_LINE && pCommand->lexes[j].type ; j++ ){
00437       switch( pCommand->lexes[j].type ){
00438         /****************************************************************/
00439         /* lex types that do not generate any parameter for the command */
00440         /****************************************************************/
00441         case  EX_LEX_NSYMBOL:
00442         case  EX_LEX_SET_NAME_SPACE:
00443         case  EX_LEX_CHARACTER:
00444         case  EX_LEX_LOCAL:
00445         case  EX_LEX_LOCALL:
00446         case  EX_LEX_FUNCTION:
00447         case  EX_LEX_THIS_FUNCTION:
00448         case  EX_LEX_LABEL_DEFINITION:
00449         case  EX_LEX_STAR:
00450         case  EX_LEX_NOEXEC:
00451         case  EX_LEX_COME_FORWARD:
00452         case  EX_LEX_COME_BACK:
00453         case  EX_LEX_LOCAL_END:
00454           break;
00455 
00456         /****************************************************************/
00457         /*      lex types that generate parameters for the command      */
00458         /****************************************************************/
00459 
00460         case  EX_LEX_LABEL:
00461         case  EX_LEX_GO_FORWARD:
00462         case  EX_LEX_GO_BACK:
00463           pThis->Parameter.CommandArgument.Argument.pNode =
00464             Result->Parameter.CommandArgument.Argument.pLabel ?
00465             Result->Parameter.CommandArgument.Argument.pLabel->node : 0;
00466           NEXT_ARGUMENT;
00467 
00468         case EX_LEX_LVAL:
00469         case EX_LEX_EXP:
00470           pThis->Parameter.CommandArgument.Argument.pNode = 
00471             Result->Parameter.CommandArgument.Argument.pNode->NodeId;
00472           iFailure = build_Build_r(pBuild,Result->Parameter.CommandArgument.Argument.pNode);
00473           if( iFailure )return iFailure;
00474           NEXT_ARGUMENT;
00475 
00476         case EX_LEX_LVALL:
00477         case EX_LEX_EXPL:
00478           pThis->Parameter.CommandArgument.Argument.pNode = 
00479             Result->Parameter.CommandArgument.Argument.pNodeList->NodeId;
00480           iFailure = build_Build_l(pBuild,Result->Parameter.CommandArgument.Argument.pNodeList);
00481           if( iFailure )return iFailure;
00482           NEXT_ARGUMENT;
00483 
00484         case EX_LEX_STRING:
00485         case EX_LEX_SYMBOL:
00486         case EX_LEX_ASYMBOL:
00487           pThis->Parameter.CommandArgument.Argument.szStringValue = 
00488              build_StringIndex(pBuild,Result->Parameter.CommandArgument.Argument.szStringValue,Result->Parameter.CommandArgument.sLen);
00489           NEXT_ARGUMENT;
00490 
00491         case EX_LEX_ARG_NUM:
00492         case EX_LEX_LOCAL_START:
00493         case EX_LEX_LONG:
00494           pThis->Parameter.CommandArgument.Argument.lLongValue =
00495              Result->Parameter.CommandArgument.Argument.lLongValue;
00496           NEXT_ARGUMENT;
00497 
00498         case EX_LEX_DOUBLE:
00499           pThis->Parameter.CommandArgument.Argument.dDoubleValue =
00500              Result->Parameter.CommandArgument.Argument.dDoubleValue;
00501           NEXT_ARGUMENT;
00502 
00503         default:
00504           fprintf(stderr,"This is a serious internal error. STOP\n");
00505           exit(1000);
00506         }       
00507       }
00508     return BU_ERROR_SUCCESS;
00509     }
00510   /* this is some built in function */
00511   pThis->OpCode = Result->OpCode;
00512   if( Result->Parameter.Arguments.Argument )
00513     pThis->Parameter.Arguments.Argument = Result->Parameter.Arguments.Argument->NodeId;
00514   else
00515     pThis->Parameter.Arguments.Argument = 0; /* the function was called without argument */
00516   return build_Build_l(pBuild,Result->Parameter.Arguments.Argument);
00517   }
00518 
00519 /*POD
00520 =H build_Build()
00521 
00522 This is the main entry function for this module. This function initializes the
00523 class variable pointed by T<pBuild> and calls R<build_Build_l()> to build up the 
00524 command list.
00525 /*FUNCTION*/
00526 int build_Build(pBuildObject pBuild
00527   ){
00528 /*noverbatim
00529 CUT*/
00530   int iFailure;
00531 
00532   pBuild->cbFTable = 0;
00533   pBuild->cbVTable = 0;
00534   pBuild->FTable = NULL;
00535   pBuild->VTable = NULL;
00536 
00537   pBuild->NodeCounter = pBuild->pEx->NodeCounter;
00538   pBuild->cGlobalVariables = pBuild->pEx->cGlobalVariables;
00539   pBuild->report = pBuild->pEx->report;
00540   pBuild->reportptr = pBuild->pEx->reportptr;
00541   if( pBuild->pEx->NodeCounter ==0 ){
00542     /* Kevin Landman [KevinL@pptvision.com] recognized that here pMemorySegment
00543        remains uninitialized and in a multi-thread embedding application
00544        calling scriba_destroy will miserably crash. */
00545     pBuild->pMemorySegment = NULL;
00546     REPORT("",0L,BU_ERROR_NO_CODE,NULL);
00547     return BU_ERROR_NO_CODE;
00548     }
00549   pBuild->pMemorySegment = alloc_InitSegment(pBuild->memory_allocating_function,
00550                                              pBuild->memory_releasing_function);
00551   if( pBuild->pMemorySegment == NULL ){
00552     REPORT("",0L,BU_ERROR_MEMORY_LOW,NULL);
00553     return BU_ERROR_MEMORY_LOW;
00554     }
00555   pBuild->CommandArray = alloc_Alloc(sizeof(cNODE) * pBuild->NodeCounter , pBuild->pMemorySegment );
00556   if( pBuild->CommandArray == NULL ){
00557     REPORT("",0L,BU_ERROR_MEMORY_LOW,NULL);
00558     return BU_ERROR_MEMORY_LOW;
00559     }
00560   pBuild->cbStringTable = pBuild->pEx->cbStringTable;
00561   build_AllocateStringTable(pBuild,&iFailure);
00562   if( iFailure )return iFailure;
00563 
00564   if( (iFailure = build_CreateVTable(pBuild)) != BU_ERROR_SUCCESS )return iFailure;
00565   if( (iFailure = build_CreateFTable(pBuild)) != BU_ERROR_SUCCESS )return iFailure;
00566 
00567 
00568   pBuild->StartNode = pBuild->pEx->pCommandList->NodeId;
00569   return build_Build_l(pBuild, pBuild->pEx->pCommandList);
00570   }
00571 
00572 static VersionInfo sVersionInfo;
00573 /*POD
00574 =H build_MagicCode()
00575 
00576 This is a simple and magical calculation that converts any ascii date to
00577 a single unsigned long. This is used as a magic value in the binary format
00578 of the compiled basic code to help distinguish incompatible versions.
00579 
00580 This function also fills in the sVersion static struct that contains the version
00581 info.
00582 /*FUNCTION*/
00583 unsigned long build_MagicCode(pVersionInfo p
00584   ){
00585 /*noverbatim
00586 CUT*/
00587   unsigned long magic;
00588   unsigned char *s;
00589 
00590   s = (unsigned char *)__DATE__;
00591 
00592   magic = *s++;
00593   magic += *s++;
00594   magic += *s++;
00595   magic -= 199;
00596   if( magic > 9 )magic -= 10;
00597   if( magic > 15 )magic -=4;
00598   if( magic == 16 )magic =15;
00599   s++;
00600   magic <<= 8;
00601   magic |= ((unsigned long)*s++) << 4;
00602   magic |= ((unsigned long)*s++) << 0;
00603   s++;
00604   magic |= ((unsigned long)*s++) << 24;
00605   magic |= ((unsigned long)*s++) << 20;
00606   magic |= ((unsigned long)*s++) << 16;
00607   magic |= ((unsigned long)*s++) << 12;
00608 
00609   sVersionInfo.Build = SCRIPTBASIC_BUILD;
00610   sVersionInfo.Date = magic;
00611   sVersionInfo.MagicCode = MAGIC_CODE;
00612   sVersionInfo.VersionHigh = VERSION_HIGH;
00613   sVersionInfo.VersionLow = VERSION_LOW;
00614   sVersionInfo.MyVersionHigh = MYVERSION_HIGH;
00615   sVersionInfo.MyVersionLow = MYVERSION_LOW;
00616   memcpy(sVersionInfo.Variation, VARIATION, 9);
00617   if( p ){
00618     p->Build = SCRIPTBASIC_BUILD;
00619     p->Date = magic;
00620     p->MagicCode = MAGIC_CODE;
00621     p->VersionHigh = VERSION_HIGH;
00622     p->VersionLow = VERSION_LOW;
00623     p->MyVersionHigh = MYVERSION_HIGH;
00624     p->MyVersionLow = MYVERSION_LOW;
00625     memcpy(p->Variation, VARIATION, 9);
00626     }
00627   return magic;
00628   }
00629 
00630 /*POD
00631 =H build_SaveCCode()
00632 
00633 This function saves the binary code of the program into the file
00634 given by the name T<szFileName> in C programming language format.
00635 
00636 The saved file can be compiled using a C compiler on the platform it was
00637 saved. The generated C file is not portable.
00638 
00639 /*FUNCTION*/
00640 void build_SaveCCode(pBuildObject pBuild,
00641                     char *szFileName
00642   ){
00643 /*noverbatim
00644 CUT*/
00645   FILE *fp;
00646   unsigned long i,j;
00647   unsigned char *s;
00648 
00649   fp = file_fopen(szFileName,"w");
00650   if( fp == NULL )return;
00651 
00652   fprintf(fp,"/* FILE: %s\n",szFileName);
00653   fprintf(fp,"   This file contains the binary code of a ScriptBasic program\n");
00654   fprintf(fp,"   To run this file you have to compile it to object file and\n");
00655   fprintf(fp,"   link it with scribast.lib or whatever the library code is\n");
00656   fprintf(fp,"   called on your platform.\n");
00657   fprintf(fp,"*/\n");
00658 
00659   fprintf(fp,"unsigned long ulGlobalVariables=%ld;\n",pBuild->cGlobalVariables);
00660   fprintf(fp,"unsigned long ulNodeCounter=%ld;\n",pBuild->NodeCounter);
00661   fprintf(fp,"unsigned long ulStartNode=%ld;\n",pBuild->StartNode);
00662   fprintf(fp,"unsigned long ulStringTableSize=%ld;\n",pBuild->cbStringTable);
00663 
00664   fprintf(fp,"unsigned char szCommandArray[] ={\n");
00665   for( i=0 ; i < pBuild->NodeCounter ; i++ ){
00666      s = (unsigned char *)(pBuild->CommandArray+i);
00667      for( j=0 ; j < sizeof(cNODE) ; j++ )
00668        fprintf(fp,"0x%02X, ",s[j]);
00669      fprintf(fp,"\n");
00670      }
00671   fprintf(fp,"0x00 };\n");
00672 
00673   fprintf(fp,"char szStringTable[]={\n");
00674   s = (unsigned char *)pBuild->StringTable;
00675   for( i=0 ; i < pBuild->cbStringTable ; i++ ){
00676     fprintf(fp,"0x%02X, ",s[i]);
00677     if( i%16 == 15 )fprintf(fp,"\n");
00678     }
00679   fprintf(fp,"\n0x00 };\n");
00680   fprintf(fp,"#ifdef WIN32\n");
00681   fprintf(fp,"main(int argc, char *argv[]){stndlone(argc,argv);}\n");
00682   fprintf(fp,"#else\n");
00683   fprintf(fp,"char **_environ;\n");
00684   fprintf(fp,"main(int argc, char *argv[], char *env[]){stndlone(argc,argv,env);}\n");
00685   fprintf(fp,"#endif\n");
00686 
00687   fprintf(fp,"/*End of file %s */",szFileName);
00688   file_fclose(fp);
00689   }
00690 
00691 /*POD
00692 =H build_SaveCorePart()
00693 
00694 This function saves the binary content of the compiled file into an
00695 already opened file. This is called from both T<build_SaveCode> and from
00696 T<build_SaveECode>.
00697 
00698 Arguments:
00699 =itemize
00700 =item T<pBuild> is the build object
00701 =item T<fp> is the T<FILE *> file pointer to an already binary write opened (T<"wb">) file.
00702 =noitemize
00703 
00704 The file T<fp> is not closed even if error occures while writing the file.
00705 
00706 /*FUNCTION*/
00707 int build_SaveCorePart(pBuildObject pBuild,
00708                        FILE *fp,
00709                        unsigned long fFlag
00710   ){
00711 /*noverbatim
00712 The function returns T<BU_ERROR_SUCCESS> (zero) if there was no error or T<BU_ERROR_FAIL> if the function fails
00713 writing the file.
00714 CUT*/
00715   unsigned char longsize;
00716 /* perform a file write and return error if there is some error writing the file */
00717 #define MYFWRITE(buffer,pieces,size,fp) if( fwrite(buffer,size,pieces,fp) != pieces ){\
00718                                         return BU_ERROR_FAIL;\
00719                                         }
00720   longsize = sizeof(long)+0x30;
00721   MYFWRITE((void *)&longsize,1,1,fp)
00722   build_MagicCode(NULL);
00723 
00724   MYFWRITE((void *)&sVersionInfo,1,sizeof(sVersionInfo),fp);
00725 
00726   MYFWRITE((void *)&(pBuild->cGlobalVariables),1,sizeof(unsigned long),fp);
00727   MYFWRITE((void *)&(pBuild->NodeCounter),1,sizeof(unsigned long),fp);
00728   MYFWRITE((void *)&(pBuild->StartNode),1,sizeof(unsigned long),fp);
00729   MYFWRITE((void *)&(pBuild->cbCollectedStrings),1,sizeof(unsigned long),fp);
00730   MYFWRITE((void *)pBuild->CommandArray,pBuild->NodeCounter,sizeof(cNODE),fp);
00731   MYFWRITE((void *)pBuild->StringTable,pBuild->cbCollectedStrings,sizeof(char),fp);
00732 
00733   /* We put these tables here together with the counters. This lets us to strip off
00734      the symbolic information in case it is not needed. */
00735   if( fFlag & BU_SAVE_FTABLE ){
00736     MYFWRITE((void *)&(pBuild->cbFTable),1,sizeof(unsigned long),fp);
00737     MYFWRITE((void *)pBuild->FTable,pBuild->cbFTable,sizeof(char),fp);
00738     }
00739   if( fFlag & BU_SAVE_VTABLE ){
00740     MYFWRITE((void *)&(pBuild->cbVTable),1,sizeof(unsigned long),fp);
00741     MYFWRITE((void *)pBuild->VTable,pBuild->cbVTable,sizeof(char),fp);
00742     }
00743 #undef MYFWRITE
00744   return BU_ERROR_SUCCESS;
00745   }
00746 
00747 /*POD
00748 =H build_SaveCore()
00749 
00750 This function saves the binary content of the compiled file into an
00751 already opened file. This is called from both T<build_SaveCode> and from
00752 T<build_SaveECode>.
00753 
00754 Arguments:
00755 =itemize
00756 =item T<pBuild> is the build object
00757 =item T<fp> is the T<FILE *> file pointer to an already binary write opened (T<"wb">) file.
00758 =noitemize
00759 
00760 The file T<fp> is not closed even if error occures while writing the file.
00761 
00762 /*FUNCTION*/
00763 int build_SaveCore(pBuildObject pBuild,
00764                    FILE *fp
00765   ){
00766 /*noverbatim
00767 The function returns T<BU_ERROR_SUCCESS> (zero) if there was no error or T<BU_ERROR_FAIL> if the function fails
00768 writing the file.
00769 CUT*/
00770   return build_SaveCorePart(pBuild,fp,BU_SAVE_FTABLE|BU_SAVE_VTABLE);
00771   }
00772 
00773 /*POD
00774 =H build_SaveCode()
00775 
00776 This function saves the binary code of the program into the file
00777 given by the name T<szFileName>.
00778 
00779 This version is hard wired saving the code into an operating system
00780 file because it uses T<fopen>, T<fclose> and T<fwrite>. Later versions
00781 may use other compatible functions passed as argument and thus allowing
00782 output redirection to other storage media (a database for example).
00783 
00784 However I think that this code is quite simple and therefore it is easier
00785 to rewrite the whole function along with R<build_LoadCode()> for other storage
00786 media than writing an interface function.
00787 
00788 The saved binary code is NOT portable. It saves the internal values
00789 as memory image to the disk. It means that the size of the code depends
00790 on the actual size of long, char, int and other types. The byte ordering
00791 is also system dependant.
00792 
00793 The saved binary code can only be loaded with the same version, and build of
00794 the program, therefore it is vital to distinguish each compilation of
00795 the program. To help the recognition of the different versions, the code starts
00796 with a version structure.
00797 
00798 The very first byte of the code contains the size of the long on the target machine.
00799 If this is not correct then the code was created on a different processor and the code
00800 is incompatible.
00801 
00802 The version info structure has the following fileds:
00803 =itemize
00804 =item T<MagicCode> is a magic constant. This contains the characters BAS and a character 1A that
00805 stops output to screen on DOS operating systems.
00806 =item T<VersionHigh> The high part of the version of the STANDARD version.
00807 =item T<VersionLow> The low part of the version of the STANDARD version.
00808 =item T<MyVersionHigh> The high part of the version of the variation.
00809 This is always zero for the STANDARD version.
00810 =item T<MyVersionLow>  The low part of the version of the variation.
00811 This is always zero for the STANDARD version.
00812 =item T<Build> A build code which is automatically calculated from the compilation date.
00813 =item T<Variation> 8 characters (NOT ZERO TERMINATED!) naming the version "STANDARD" for the
00814 STANDARD version (obvious?)
00815 =noitemize
00816 
00817 /*FUNCTION*/
00818 int build_SaveCode(pBuildObject pBuild,
00819                    char *szFileName
00820   ){
00821 /*noverbatim
00822 The function returns zero on success (T<BU_ERROR_SUCCESS>) and T<BU_ERROR_FAIL>
00823 if the code could not be saved.
00824 CUT*/
00825   FILE *fp;
00826 
00827   /* we just do not like a zero length string table */
00828   if( ! pBuild->cbCollectedStrings )pBuild->cbCollectedStrings = 1;
00829 
00830   fp = file_fopen(szFileName,"wb");
00831   if( fp == NULL )return BU_ERROR_FAIL;
00832 
00833   if( pBuild->FirstUNIXline )fprintf(fp,pBuild->FirstUNIXline);
00834 
00835   build_SaveCore(pBuild,fp);
00836   file_fclose(fp);
00837   return BU_ERROR_SUCCESS;
00838   }
00839 
00840 /*POD
00841 =H build_SaveECode()
00842 
00843 This function saves the binary code of the program into the file
00844 given by the name T<szFileName> in exe format.
00845 
00846 This is actually nothing but the copy of the original interpreter file and
00847 the binary code of the BASIC program appended to it and some extra information
00848 at the end of the file to help the reader to find the start of the binary 
00849 BASIC program when it tries to read the exe file.
00850 
00851 /*FUNCTION*/
00852 void build_SaveECode(pBuildObject pBuild,
00853                      char *pszInterpreter,
00854                      char *szFileName
00855   ){
00856 /*noverbatim
00857 CUT*/
00858   FILE *fp,*fi;
00859   int ch;
00860   long lCodeStart;
00861   /* SCRIPTBASIC */
00862   char magics[11+sizeof(long)];
00863 
00864 #if _WIN32
00865   /* under WIN32 we know it better, igore the argument */
00866   pszInterpreter = _pgmptr;
00867 #endif
00868 
00869   /* copy the original exe file to the output */
00870   fi = file_fopen(pszInterpreter,"rb");
00871 
00872   if( fi == NULL ){
00873     REPORT("",0L,BU_ERROR_ECODE_INPUT,NULL);
00874     return;
00875     }
00876 
00877   /* if the interpreter can be opened then open the output file */
00878   fp = file_fopen(szFileName,"wb");
00879   if( fp == NULL ){
00880     file_fclose(fi);
00881     REPORT("",0L,BU_ERROR_FAIL,NULL);
00882     return;
00883     }
00884 
00885   while( EOF != (ch=getc(fi)) ){
00886     putc(ch,fp);
00887     }
00888   file_fclose(fi);
00889 
00890   lCodeStart = ftell(fp);
00891 
00892   build_SaveCore(pBuild,fp);
00893 
00894   /* print this string there to be sure that this is a scriptbasic code */
00895   strcpy(magics,"SCRIPTBASIC");
00896   /* this is a long telling where the binary code starts */
00897   memcpy(magics+11,&lCodeStart,sizeof(long));
00898 
00899   file_fwrite(magics,1,11+sizeof(long),fp);
00900 
00901   file_fclose(fp);
00902   }
00903 
00904 /*POD
00905 =H build_GetExeCodeOffset()
00906 
00907 This function checks that the actually running exe contains the binary BASIC program
00908 attached to its end. It returns zero if not, otherwise it returns 1.
00909 
00910 =itemize
00911 =item The argument T<pszInterpreter> should be T<argv[0]> thus the code can open the executable
00912 file and check if it really contains the BASIC code
00913 =item T<plOffset> should point to a long variable ready to recieve the file offset where the BASIC
00914 code starts
00915 =item T<plEOFfset> should point to a long variable ready to receive the file offset where the
00916 BASIC code finishes. This is the position of the last byte belonging to the BASIC code, thus if
00917 T<ftell(fp) >>T< *plEOFfset> means the file pointer is after the code and should treat it as EOF
00918 condition when reading the BASIC program code.
00919 =noitemize
00920 
00921 It is guaranteed that both T<*plOffset> and T<*plEOFfset> will be set to T<0> (zero) if the file
00922 proves to be a standard BASIC interpreter without appended BASIC code.
00923 
00924 /*FUNCTION*/
00925 int build_GetExeCodeOffset(char *pszInterpreter,
00926                             long *plOffset,
00927                             long *plEOFfset
00928   ){
00929 /*noverbatim
00930 CUT*/
00931   FILE *fp;
00932   char magics[11+sizeof(long)];
00933   long lOf;
00934 
00935 #if _WIN32
00936   /* under WIN32 we know it better, igore the argument */
00937   pszInterpreter = _pgmptr;
00938 #endif
00939   /* they are guaranteed to be zero in case the exe is a simple interpreter */
00940   *plOffset = *plEOFfset = 0L;
00941   /* open the executable file */
00942   fp = file_fopen(pszInterpreter,"rb");
00943   /* if it can not be read then this can not be, well... this is some weird error */
00944   if( fp == NULL )return 0;
00945   /* The executable file created by the option -Eo finishes with the 
00946      characters 'SCRIPTBASIC' (11 characters)  and a long number containing
00947      the offset where the code start */
00948   lOf = 11 + sizeof(long);
00949   /* seek it to where the magic characters are expected to start */
00950   fseek(fp,-lOf,SEEK_END);
00951   *plEOFfset = ftell(fp) - 1;
00952   /* read the 11 characters 'SCRIPTBASIC' and the long containing the offset */
00953   file_fread(magics,1,11+sizeof(long),fp);
00954   file_fclose(fp);
00955   if( memcmp(magics,"SCRIPTBASIC",11) ){
00956     *plEOFfset = 0L;
00957     return 0L;
00958     }
00959   memcpy(plOffset,magics+11,sizeof(long));
00960   return 1;
00961   }
00962 
00963 /*POD
00964 =H build_LoadCore()
00965 
00966 This function loads the binary code from an opened file.
00967 
00968 Arguments:
00969 
00970 =itemize
00971 =item T<pBuild> is the build object
00972 =item T<szFileName> is the name of the file that is opened. Needed for reporting purposes.
00973 =item T<fp> opened T<FILE *> file pointer opened for binary reading (aka T<"rb">), and positioned where the
00974 BASIC code starts.
00975 =item T<lEOFfset> should be the position of the last byte that belongs to the BASIC code so that T<ftell(fp)>>T<lEOFfset>
00976 is treated as EOF condition. If this value is zero that means that the BASIC code is contained in the file until the
00977 physical end of file.
00978 =noitemize
00979 
00980 /*FUNCTION*/
00981 void build_LoadCore(pBuildObject pBuild,
00982                     char *szFileName,
00983                     FILE *fp,
00984                     long lEOFfset
00985   ){
00986 /*noverbatim
00987 Note that the program does not return error code, but calls the reporting function to report error. The file T<fp> is not closed in the
00988 function even if error has happened during reading.
00989 CUT*/
00990   unsigned long mc;
00991   unsigned long longsize;
00992   int ch;
00993 
00994 #define CORRUPTFILE {REPORT(szFileName,0L,BU_ERROR_FILE_CORRUPT,NULL);return;}
00995 #define CHECKEOF() feof(fp) || (lEOFfset && lEOFfset < ftell(fp))
00996 #define ASSERTEOF if( CHECKEOF() )CORRUPTFILE
00997 
00998   ASSERTEOF
00999   longsize = ch = fgetc(fp);
01000   if( CHECKEOF() )CORRUPTFILE
01001   /* If the first character of the file is # then it should start as
01002      text file and it should be something like #!/usr/bin/scriba
01003      This lasts until \n on UNIX */
01004   if( longsize == '#' ){
01005     ch = fgetc(fp);
01006     if( ch != '!' )CORRUPTFILE
01007     while( ch != EOF && ch != '\n' )ch = fgetc(fp);
01008     if( ch == '\n' )ch = fgetc(fp);
01009     ASSERTEOF
01010     longsize = ch;
01011     }
01012   if( longsize != sizeof(long)+0x30 )CORRUPTFILE
01013 
01014   mc = build_MagicCode(NULL);
01015 
01016   fread((void *)&sVersionInfo,1,sizeof(sVersionInfo),fp);
01017   if( 
01018   sVersionInfo.Build != SCRIPTBASIC_BUILD ||
01019   sVersionInfo.MagicCode != MAGIC_CODE ||
01020   sVersionInfo.VersionHigh != VERSION_HIGH ||
01021   sVersionInfo.VersionLow != VERSION_LOW ||
01022   sVersionInfo.MyVersionHigh != MYVERSION_HIGH ||
01023   sVersionInfo.MyVersionLow != MYVERSION_LOW ||
01024   strncmp(sVersionInfo.Variation, VARIATION, 8) 
01025   )CORRUPTFILE
01026 
01027   fread((void *)&(pBuild->cGlobalVariables),sizeof(unsigned long),1,fp);
01028   ASSERTEOF
01029   fread((void *)&(pBuild->NodeCounter),sizeof(unsigned long),1,fp);
01030   ASSERTEOF
01031   fread((void *)&(pBuild->StartNode),sizeof(unsigned long),1,fp);
01032   ASSERTEOF
01033 
01034   pBuild->CommandArray = alloc_Alloc(sizeof(cNODE) * pBuild->NodeCounter , pBuild->pMemorySegment );
01035   if( pBuild->CommandArray == NULL ){
01036     REPORT(szFileName,0L,BU_ERROR_MEMORY_LOW,NULL);
01037     return;
01038     }
01039 
01040   fread((void *)&(pBuild->cbStringTable),1,sizeof(unsigned long),fp);
01041   ASSERTEOF
01042 
01043   pBuild->StringTable = alloc_Alloc(pBuild->cbStringTable ? pBuild->cbStringTable : 1 ,pBuild->pMemorySegment);
01044   if( pBuild->StringTable == NULL ){
01045     REPORT(szFileName,0L,BU_ERROR_MEMORY_LOW,NULL);
01046     return;
01047     }
01048   fread((void *)pBuild->CommandArray,pBuild->NodeCounter,sizeof(cNODE),fp);
01049   ASSERTEOF
01050   if( pBuild->cbStringTable )
01051     fread((void *)pBuild->StringTable,pBuild->cbStringTable,sizeof(char),fp);
01052 
01053   /* the file may close here in case the user defined function and global variable tables are not present */
01054   if( feof(fp) )return;
01055 
01056   fread((void *)&(pBuild->cbFTable),1,sizeof(unsigned long),fp);
01057   if( feof(fp) ){
01058     pBuild->cbFTable = 0;
01059     return;
01060     }
01061   if( pBuild->cbFTable ){
01062     pBuild->FTable = alloc_Alloc(pBuild->cbFTable,pBuild->pMemorySegment);
01063     if( pBuild->FTable == NULL ){
01064       REPORT(szFileName,0L,BU_ERROR_MEMORY_LOW,NULL);
01065       return;
01066       }
01067     longsize=fread((void *)pBuild->FTable,sizeof(char),pBuild->cbFTable,fp);
01068     if( longsize != pBuild->cbFTable )CORRUPTFILE
01069     if( feof(fp) )return;
01070     }else pBuild->FTable = NULL;
01071 
01072   fread((void *)&(pBuild->cbVTable),1,sizeof(unsigned long),fp);
01073   if( pBuild->cbVTable ){
01074     if( feof(fp) )return;
01075     pBuild->VTable = alloc_Alloc(pBuild->cbVTable,pBuild->pMemorySegment);
01076     if( pBuild->VTable == NULL ){
01077       REPORT(szFileName,0L,BU_ERROR_MEMORY_LOW,NULL);
01078       return;
01079       }
01080     }else pBuild->VTable = NULL;
01081   if( fread((void *)pBuild->VTable,sizeof(char),pBuild->cbVTable,fp) != pBuild->cbVTable)CORRUPTFILE
01082 
01083   }
01084 
01085 /*POD
01086 =H build_LoadCodeWithOffset()
01087 
01088 For detailed definition of the binary format see the code and the documentation of
01089 R<build_SaveCode()>
01090 
01091 In case the file is corrupt the function reports error.
01092 
01093 /*FUNCTION*/
01094 void build_LoadCodeWithOffset(pBuildObject pBuild,
01095                               char *szFileName,
01096                               long lOffset,
01097                               long lEOFfset
01098   ){
01099 /*noverbatim
01100 CUT*/
01101   FILE *fp;
01102 
01103   pBuild->pMemorySegment = alloc_InitSegment(pBuild->memory_allocating_function,
01104                                              pBuild->memory_releasing_function);
01105   if( pBuild->pMemorySegment == NULL ){
01106     REPORT(szFileName,0L,BU_ERROR_MEMORY_LOW,NULL);
01107     return;
01108     }
01109 
01110   fp = file_fopen(szFileName,"rb");
01111 
01112   if( NULL == fp ){
01113     /* borrow an error code from the reader */
01114     REPORT(szFileName,0L,READER_ERROR_FILE_OPEN,NULL);
01115     return;
01116     }
01117   fseek(fp,lOffset,SEEK_SET);
01118   if( fp == NULL )CORRUPTFILE
01119   build_LoadCore(pBuild,szFileName,fp,lEOFfset);
01120   file_fclose(fp);
01121   return;
01122   }
01123 
01124 /*POD
01125 =H build_LoadCode()
01126 
01127 For detailed definition of the binary format see the code and the documentation of
01128 R<build_SaveCode()>
01129 
01130 In case the file is corrupt the function reports error.
01131 
01132 /*FUNCTION*/
01133 void build_LoadCode(pBuildObject pBuild,
01134                     char *szFileName
01135   ){
01136 /*noverbatim
01137 CUT*/
01138   build_LoadCodeWithOffset(pBuild,szFileName,0L,0L);
01139   }
01140 
01141 /*POD
01142 =H build_IsFileBinaryFormat()
01143 
01144 This function test a file reading its first few characters and decides
01145 if the file is binary format of a basic program or not.
01146 
01147 /*FUNCTION*/
01148 int build_IsFileBinaryFormat(char *szFileName
01149   ){
01150 /*noverbatim
01151 CUT*/
01152   FILE *fp;
01153   unsigned long mc;
01154   int ret,ch;
01155   unsigned char longsize;
01156 
01157   if( szFileName == NULL )return 0;/*no file is not binary*/
01158   ret = 1;
01159   fp = file_fopen(szFileName,"rb");
01160   if( fp == NULL )return 0;
01161 
01162   longsize = fgetc(fp);
01163   if( longsize == '#' ){
01164     ch = fgetc(fp);
01165     if( ch != '!' )ret = 0; else {
01166       while( ch != EOF && ch != '\n' )ch = fgetc(fp);
01167       if( ch == '\n' )ch = fgetc(fp);
01168       if( ch == EOF )ret = 0;
01169       longsize = ch;
01170       }
01171     }
01172   if( longsize != sizeof(long)+0x30 )ret=0;
01173 
01174   mc = build_MagicCode(NULL);
01175 
01176   fread((void *)&sVersionInfo,1,sizeof(sVersionInfo),fp);
01177   if( 
01178   sVersionInfo.Build != SCRIPTBASIC_BUILD ||
01179   sVersionInfo.MagicCode != MAGIC_CODE ||
01180   sVersionInfo.VersionHigh != VERSION_HIGH ||
01181   sVersionInfo.VersionLow != VERSION_LOW ||
01182   sVersionInfo.MyVersionHigh != MYVERSION_HIGH ||
01183   sVersionInfo.MyVersionLow != MYVERSION_LOW ||
01184   strncmp(sVersionInfo.Variation, VARIATION, 8) 
01185   )ret = 0;
01186 
01187   file_fclose(fp);
01188   return ret;
01189   }
01190 /*POD
01191 =H build_pprint()
01192 
01193 This is a debug function that prints the build code into a file.
01194 
01195 This function is not finished and the major part of it is commented out using T<#if 0> construct.
01196 /*FUNCTION*/
01197 void build_pprint(pBuildObject pBuild,
01198                   FILE *f
01199   ){
01200 /*noverbatim
01201 CUT*/
01202   unsigned long lIndex;
01203   pcNODE pThis;
01204   int i;
01205   extern LexNASymbol CSYMBOLS[];
01206 
01207   fprintf(f,"Start node is %d\n",pBuild->StartNode);
01208 
01209   for( lIndex = 0 ; lIndex < pBuild->NodeCounter ; lIndex ++ ){
01210     pThis = pBuild->CommandArray+lIndex;
01211     fprintf(f,"%d ",lIndex+1 );
01212     /* convert an array access node */
01213     if( pThis->OpCode == eNTYPE_ARR ){
01214       fprintf(f,"Array access\n");
01215       continue;
01216       }
01217     if( pThis->OpCode == eNTYPE_SAR ){
01218       fprintf(f,"Associative array access\n");
01219       continue;
01220       }
01221 
01222   /* this is a list node */
01223   if( pThis->OpCode == eNTYPE_LST ){
01224     fprintf(f,"List node\n");
01225     fprintf(f," car=%ld\n",pThis->Parameter.NodeList.actualm);
01226     fprintf(f," cdr=%ld\n",pThis->Parameter.NodeList.rest);
01227     continue;
01228     }
01229 
01230   /* Convert a user function node */
01231   if( pThis->OpCode == eNTYPE_FUN ){
01232     fprintf(f,"User function\n");
01233     fprintf(f," Starts at node %ld\n",pThis->Parameter.UserFunction.NodeId);
01234     fprintf(f," Actual argument list root node %ld\n",pThis->Parameter.UserFunction.Argument);
01235     continue;
01236     }
01237 
01238   /* Convert a local/global variable node */
01239   if( pThis->OpCode == eNTYPE_LVR || pThis->OpCode == eNTYPE_GVR ){
01240     fprintf(f,"%s variable serial=%d\n", (pThis->OpCode == eNTYPE_LVR ? "local" : "global"),pThis->Parameter.Variable.Serial);
01241     continue;
01242     }
01243 
01244   for( i = 0 ; CSYMBOLS[i].Symbol ; i++ )
01245     if( CSYMBOLS[i].Code == pThis->OpCode )break;
01246   if( CSYMBOLS[i].Code == pThis->OpCode ){
01247     fprintf(f,"  %s\n",CSYMBOLS[i].Symbol);
01248     continue;
01249     }
01250   if( pThis->OpCode == eNTYPE_DBL ){
01251     fprintf(f," Double value %lf\n",pThis->Parameter.Constant.dValue);
01252     continue;
01253     }
01254 
01255   if( pThis->OpCode == eNTYPE_LNG ){
01256     fprintf(f," Long value %ld\n",pThis->Parameter.Constant.lValue);
01257     continue;
01258     }
01259 
01260   if( pThis->OpCode == eNTYPE_STR ){
01261     fprintf(f," Constant string node id=%d\n", pThis->Parameter.Constant.sValue);
01262     continue;
01263     }
01264 
01265   switch( pThis->OpCode ){
01266     case  eNTYPE_ARR: fprintf(f,", ARRAY ACCESS\n"); break;
01267     case  eNTYPE_SAR: fprintf(f,", SARAY ACCESS\n"); break;
01268     case  eNTYPE_FUN: fprintf(f,", FUNCTION CALL\n"); break;
01269     case  eNTYPE_LVR: fprintf(f,", LOCAL VAR\n"); break;
01270     case  eNTYPE_GVR: fprintf(f,", GLOBAL VAR\n"); break;
01271     case  eNTYPE_DBL: fprintf(f,", DOUBLE\n"); break;
01272     case  eNTYPE_LNG: fprintf(f,", LONG\n"); break;
01273     case  eNTYPE_STR: fprintf(f,", STRING\n"); break;
01274     case  eNTYPE_LST: fprintf(f,", LIST\n"); break;
01275     case  eNTYPE_CRG: fprintf(f,", COMMAND ARG %ld -> %ld\n",  pThis->Parameter.CommandArgument.Argument.pNode,pThis->Parameter.CommandArgument.next);
01276                       break;
01277 
01278       default:
01279       fprintf(f,", %d\n",pThis->OpCode);
01280       }
01281     }
01282   }
01283 
01284 /*Mitchell Greess [m.greess@solutions-atlantic.com]:
01285 This is a utility function which correctly determines the size of a
01286 table item accounting for alignment issues on Solaris.
01287 */
01288 static long build_TableItemBytes(char *SymbolName){
01289   long len;
01290 
01291   /* Watch for alignment on Solaris */
01292   len = strlen(SymbolName) + 1 + sizeof(long);
01293   if (len % sizeof(long)) len += sizeof(long) - (len % sizeof(long));
01294   return len;
01295   }
01296 
01297 /*
01298 This is a callback function used to traverse over the symbol table of user functions
01299 and of global variables. The void pointer points to a long variable that is used to calculate
01300 the final size of the table used to store the compacted symbol table saved to file.
01301 
01302 For each symbol the function counts the length of the string plus the terminating zero plus the
01303 sizeof(long).
01304 */
01305 static void build_CountSymbolBytes(char *SymbolName, void *SymbolValue, void *f){
01306   long *pL;
01307   pL = (long *)f;
01308   *pL += build_TableItemBytes(SymbolName);
01309   }
01310 
01311 /*
01312 This is a callback function that is used to put the strings and the values of the
01313 user function symbol table entries into the memory allocated to store the functions
01314 and their entry point value in a single memory space.
01315 */
01316 static void build_PutFTableItem(char *SymbolName, void *SymbolValue, void *f){
01317   pSymbolUF pF;
01318   pSymbolLongTable pT;
01319   char **pC;
01320 
01321   pC = (char **)f;
01322   pF = (pSymbolUF)SymbolValue;
01323   pT = (pSymbolLongTable)*pC;
01324   pT->value = pF->node;
01325   strcpy(pT->symbol,SymbolName);
01326   *pC += build_TableItemBytes(SymbolName);
01327   }
01328 
01329 /*
01330 This is a callback function that is used to put the strings and the values of the
01331 global variables symbol table entries into the memory allocated to store the variables
01332 and their serial number in a single memory space.
01333 */
01334 static void build_PutVTableItem(char *SymbolName, void *SymbolValue, void *f){
01335   pSymbolVAR pV;
01336   pSymbolLongTable pT;
01337   char **pC;
01338 
01339   pC = (char **)f;
01340   pV = (pSymbolVAR)SymbolValue;
01341   pT = (pSymbolLongTable)*pC;
01342   pT->value = pV->Serial;
01343   strcpy(pT->symbol,SymbolName);
01344   *pC += build_TableItemBytes(SymbolName);
01345   }
01346 
01347 /*POD
01348 =H build_CreateFTable()
01349 
01350 When the binary code of the BASIC program is saved to disk the symbol table of the user
01351 defined functions and the symbol table of global variables is also saved. This may be needed
01352 by some applications that embed ScriptBasic and want to call specific function or alter global variables
01353 of a given name from the embedding C code. To do this they need the serial number of the global variable
01354 or the entry point of the function. Therefore ScriptBasic v1.0b20 and later can save these two tables into
01355 the binary code.
01356 
01357 The format of the tables is simple optimized for space and for simplicity of generation. They are stored
01358 first in a memory chunk and then written to disk just as a series of bytes.
01359 
01360 The format is
01361 
01362 =verbatim
01363 long      serial number of variable or entry point of the function
01364 zchar     zero character terminated symbol
01365 =noverbatim
01366 
01367 This is easy to save and to load. Searching for it is a bit slow. Embedding applications usually
01368 have to search for the values only once, store the serial number/entry point value
01369 in their local variable and use the value.
01370 
01371 The function T<CreateFTable> converts the symbol table of user defined function
01372 collected by symbolic analysis into a single memory chunk.
01373 
01374 The same way R<build_CreateVTable()> converts the symbol table of global variables
01375 collected by symbolic analysis into a single memory chunk.
01376 
01377 /*FUNCTION*/
01378 int build_CreateFTable(pBuildObject pBuild
01379   ){
01380 /*noverbatim
01381 CUT*/
01382   char *p;
01383 
01384   pBuild->cbFTable = 0;
01385   sym_TraverseSymbolTable(pBuild->pEx->UserFunctions,
01386                           build_CountSymbolBytes,
01387                           &(pBuild->cbFTable));
01388   if( pBuild->cbFTable == 0 ){
01389     pBuild->FTable = NULL;
01390     return BU_ERROR_SUCCESS;
01391     }
01392   pBuild->FTable = alloc_Alloc(pBuild->cbFTable,pBuild->pMemorySegment);
01393   if( pBuild->FTable == NULL ){
01394     pBuild->cbFTable = 0;/* just to be safe */
01395     return BU_ERROR_MEMORY_LOW;
01396     }
01397 
01398   p = (char *)pBuild->FTable;
01399   sym_TraverseSymbolTable(pBuild->pEx->UserFunctions,
01400                           build_PutFTableItem,
01401                           &p);
01402 
01403   return BU_ERROR_SUCCESS;
01404   }
01405 
01406 /*POD
01407 =H build_CreateVTable()
01408 
01409 When the binary code of the BASIC program is saved to disk the symbol table of the user
01410 defined functions and the symbol table of global variables is also saved. This may be needed
01411 by some applications that embed ScriptBasic and want to call specific function or alter global variables
01412 of a given name from the embedding C code. To do this they need the serial number of the global variable
01413 or the entry point of the function. Therefore ScriptBasic v1.0b20 and later can save these two tables into
01414 the binary code.
01415 
01416 The format of the tables is simple optimized for space and for simplicity of generation. They are stored
01417 first in a memory chunk and then written to disk just as a series of bytes.
01418 
01419 The format is
01420 
01421 =verbatim
01422 long      serial number of variable or entry point of the function
01423 zchar     zero character terminated symbol
01424 =noverbatim
01425 
01426 This is easy to save and to load. Searching for it is a bit slow. Embedding applications usually
01427 have to search for the values only once, store it in their local variable and use the value.
01428 
01429 The function R<build_CreateFTable()> converts the symbol table of user defined function
01430 collected by symbolic analysis into a single memory chunk.
01431 
01432 The same way T<CreateVTable> converts the symbol table of global variables
01433 collected by symbolic analysis into a single memory chunk.
01434 
01435 /*FUNCTION*/
01436 int build_CreateVTable(pBuildObject pBuild
01437   ){
01438 /*noverbatim
01439 CUT*/
01440   char *p;
01441 
01442   pBuild->cbVTable = 0;
01443   sym_TraverseSymbolTable(pBuild->pEx->GlobalVariables,
01444                           build_CountSymbolBytes,
01445                           &(pBuild->cbVTable));
01446   if( pBuild->cbVTable == 0 ){
01447     pBuild->VTable = NULL;
01448     return BU_ERROR_SUCCESS;
01449     }
01450   pBuild->VTable = alloc_Alloc(pBuild->cbVTable,pBuild->pMemorySegment);
01451   if( pBuild->VTable == NULL ){
01452     pBuild->cbVTable = 0;/* just to be safe */
01453     return BU_ERROR_MEMORY_LOW;
01454     }
01455 
01456   p = (char *)pBuild->VTable;
01457   sym_TraverseSymbolTable(pBuild->pEx->GlobalVariables,
01458                           build_PutVTableItem,
01459                           &p);
01460 
01461   return BU_ERROR_SUCCESS;
01462   }
01463 
01464 /* 
01465 This function is used to search the long value for a function or for a global variable.
01466 
01467 Mitchell Greess [m.greess@solutions-atlantic.com] modified the code April 20, 2002.
01468 to take the alignment issues into account on operating systems like Solaris.
01469 */
01470 static long build_LookupFunctionOrVariable(pSymbolLongTable Table,
01471                                            unsigned long cbTable,
01472                                            char *s){
01473   char *p;
01474   char *SymbolName;
01475   long TableItemLen;
01476 
01477   p = (char *)Table;
01478   while( cbTable ){
01479     SymbolName = p + sizeof(long);
01480     if( ! strcmp(s,SymbolName) ) return Table->value;
01481     TableItemLen = build_TableItemBytes(SymbolName);
01482     p += TableItemLen;
01483     cbTable -= TableItemLen;
01484     Table = (pSymbolLongTable)p;
01485     }
01486   return 0;
01487   }
01488 
01489 /*POD
01490 =H build_LookupFunctionByName()
01491 /*FUNCTION*/
01492 long build_LookupFunctionByName(pBuildObject pBuild,
01493                           char *s
01494   ){
01495 /*noverbatim
01496 CUT*/
01497   return build_LookupFunctionOrVariable(pBuild->FTable,pBuild->cbFTable,s);
01498   }
01499 
01500 /*POD
01501 =H build_LookupVariableByName()
01502 /*FUNCTION*/
01503 long build_LookupVariableByName(pBuildObject pBuild,
01504                           char *s
01505   ){
01506 /*noverbatim
01507 CUT*/
01508   return build_LookupFunctionOrVariable(pBuild->VTable,pBuild->cbVTable,s);
01509   }

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