00001 /* 00002 FILE: epreproc.c 00003 HEADER: epreproc.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 */ 00023 #include <stdio.h> 00024 #include <stdlib.h> 00025 #include <string.h> 00026 #include <ctype.h> 00027 00028 #ifdef WIN32 00029 #include <process.h> 00030 #else 00031 #ifndef __MACOS__ /* No such thing as command line parameters on classic Mac */ 00032 #include <sys/types.h> 00033 #include <sys/wait.h> 00034 #endif 00035 #endif 00036 00037 #include "errcodes.h" 00038 #include "conftree.h" 00039 #include "myalloc.h" 00040 #include "uniqfnam.h" 00041 #include "epreproc.h" 00042 00043 /*POD 00044 =H External preprocessor handling 00045 00046 This module starts the external preprocessors. 00047 00048 =toc 00049 00050 CUT*/ 00051 00052 /*POD 00053 =section epreproc 00054 =H Execute external preprocessors 00055 00056 This function executes the external preprocessors that are 00057 needed to be executed either by the command line options or 00058 driven by the extensions. 00059 00060 The command line option preprocessors are executed as listed 00061 in the character array T<ppszArgPreprocessor>. These preprocessors 00062 are checked to be run first. 00063 00064 If there is no preprocessors defined on the command line then the 00065 preprocessors defined in the config file for the extensions are 00066 executed. The input file name is also modified by the code. 00067 The input file name is modified so that it will contain the source 00068 code file name after the preprocessing. 00069 00070 The return value of the function is the error code. This is T<PREPROC_ERROR_SUCCESS> 00071 if the preprocessing was successful. This value is zero. If the return 00072 value is positive this is one of the error codes defined in the file T<errcodes.def> 00073 prefixed by T<PREPROC_>. 00074 00075 /*FUNCTION*/ 00076 int epreproc(ptConfigTree pCONF, 00077 char *pszInputFileName, 00078 char **pszOutputFileName, 00079 char **ppszArgPreprocessor, 00080 void *(*thismalloc)(unsigned int), 00081 void (*thisfree)(void *) 00082 ){ 00083 /*noverbatim 00084 00085 The first argument T<pCONF> is the configuration data pointer which is passed to the 00086 configuration handling routines. 00087 00088 The second argument T<pszInputFileName> is the pointer to the pointer to the input file name. 00089 00090 The third argument is an output variable. This will point to the output file name upon success 00091 or to NULL. If this variable is NULL then an error has occured or the file needed no preprocessing. 00092 The two cases can be separated based on the return value of the function. If the file needed 00093 preprocessing and the preprocessing was successfully executed then this variable will point 00094 to a ZCHAR string allocated via the function T<thismalloc>. This is the responsibility of the 00095 caller to deallocate this memory space after use calling the function pointed by T<thisfree>. 00096 00097 The fourth argument T<ppszArgPreprocessor> is an array of preprocessors to be used 00098 on the input file. This array contains pointers that point to ZCHAR strings. 00099 The ZCHAR strings contain the symbolic names of the external preprocessors that 00100 are defined in the configuration file. The configuration file defines 00101 the actual executable for the preprocessor and the temporary directory where the 00102 preprocessed file is stored. The final element of this pointer array should be T<NULL>. 00103 If the pointer T<ppszArgPreprocessor> is T<NULL> or the pointer array pointed by this 00104 contains only the terminating T<NULL> pointer then the extensions of the file name are 00105 used to determine what preprocessors are to be applied. Preprocessors are applied from 00106 left to right order of the file extensions. 00107 00108 The arguments T<thismalloc> and T<thisfree> should point to T<malloc> and T<free> or to 00109 a similar functioning function pair. These functions will be used via the T<myalloc.c> module 00110 and also to allocate the new T<pszOutputFileName> string in case of success. This means that the 00111 caller should use the function pointed by T<thisfree> to release the string pointed by 00112 T<pszOutputFileName> after the function has returned. 00113 00114 CUT*/ 00115 char *pszEPreprocExe, 00116 *pszEPreprocDir, 00117 #define MAXPPARGS 40 00118 *ppa[MAXPPARGS]; 00119 #define PCKL 40 00120 char szPreprocConfigKey[PCKL]; /* this is epreproc$ext where ext is the extension */ 00121 #define FULL_PATH_BUFFER_LENGTH 256 00122 char *PreprocessedFileName; 00123 char EPreProcCmdLin[FULL_PATH_BUFFER_LENGTH]; 00124 int slen; 00125 void *pMemorySegment; 00126 char *s,*q; 00127 char **ppszPreprocessor = ppszArgPreprocessor; 00128 int iPreprIndex,i; 00129 char *pszLastFileName; 00130 #if (!defined(WIN32) && !defined(__MACOS__)) 00131 pid_t pid; 00132 int exit_code; 00133 #endif 00134 00135 *pszOutputFileName = NULL; /* This is the default return value in case of error or in case of 00136 null operation (aka: no preprocessing needed). */ 00137 if( pszInputFileName == NULL )return COMMAND_ERROR_SUCCESS; 00138 pMemorySegment = alloc_InitSegment(thismalloc,thisfree); 00139 if( pMemorySegment == NULL )return COMMAND_ERROR_MEMORY_LOW; 00140 00141 if( ppszPreprocessor == NULL || *ppszPreprocessor == NULL ){ 00142 /* if no preprocessor was specified on the command line then 00143 try to figure out what preprocessors we have to use based 00144 on the extension of the source file. Build the ppszPreprocessor 00145 array and the later is going to use it as if it were given 00146 as argument. */ 00147 00148 /* Calculate the number of extensions. */ 00149 s = pszInputFileName; 00150 slen = 0; /* count the extensions in this variable */ 00151 while( *s ){ 00152 if( *s == '.' )slen++; 00153 s++; 00154 } 00155 /* if there is no preprocessor defined on the command line and 00156 there is no extension of the file name */ 00157 if( slen == 0 ){ 00158 alloc_FinishSegment(pMemorySegment); 00159 return PREPROC_ERROR_SUCCESS; 00160 } 00161 00162 slen ++; /* count the final NULL, which is the terminating element of the array */ 00163 00164 /* Allocate space for the preprocessor array. */ 00165 ppszPreprocessor = alloc_Alloc(slen*sizeof(char *),pMemorySegment); 00166 if( ppszPreprocessor == NULL ){ 00167 alloc_FinishSegment(pMemorySegment); 00168 return PREPROC_ERROR_MEMORY_LOW; 00169 } 00170 for( i = 0 ; i < slen ; i++ )ppszPreprocessor[i] = NULL; 00171 00172 s = pszInputFileName; 00173 iPreprIndex = 0; 00174 while( *s ){ 00175 /* find the first/next extension */ 00176 while( *s && *s != '.' )s++; 00177 if( ! *s )break; /* there is no more extensions */ 00178 s++; /* step over the dot before the extension */ 00179 if( ! *s )break; /* there is no more extensions */ 00180 00181 strcpy(szPreprocConfigKey,"preproc.extensions."); 00182 q = szPreprocConfigKey + 19; /* 19 = strlen("preproc.extensions.") */ 00183 slen = 19;/* we have copied 19 characters so far */ 00184 /* copy the extension after the "epreproc$" string */ 00185 while( *s && *s != '.' ){ 00186 /* if the extension is such long (30 chars or more) then there 00187 is surely no preprocessor configured for it */ 00188 if( slen >= PCKL ){ 00189 /* reset the extension copiing */ 00190 q = szPreprocConfigKey + 19; /* 19 = strlen("preproc.extensions.") */ 00191 break; 00192 } 00193 /* copy the character */ 00194 *q++ = *s++; 00195 /* count the character copied */ 00196 slen++; 00197 } 00198 *q = (char)0; 00199 00200 /* get the symbolic name of the preprocessor assigned to the extension */ 00201 ppszPreprocessor[iPreprIndex] = cft_GetString(pCONF,szPreprocConfigKey); 00202 /* this is OK if there is no preprocessor configured for an extension, but if there is 00203 then have the pointer to the configuration constant string and step with the index. 00204 we do not need to check the index value. It can not step over the allocated space, 00205 because we have allocated space for as many preprocessort symbolic name string pointer 00206 as many dots there are in the file name and there can only be less or equal number of 00207 preprocessors. Over indexing would be something serious internal error. */ 00208 if( ppszPreprocessor[iPreprIndex] )iPreprIndex++; 00209 } 00210 } 00211 00212 /* ---------------------------------------------------------------------------------- 00213 At this point of exection we have all the preprocessor symbolic names in the 00214 array ppszPreprocessor. This was given either by argument or built up based on the 00215 extensions. We only have to apply it. 00216 ---------------------------------------------------------------------------------- */ 00217 00218 /* This pointer points to the actual input file of the preprocessor. At the first 00219 preprocessing this is the input file name. On the next runs this is the output 00220 of the previous preprocessors. */ 00221 pszLastFileName = pszInputFileName; 00222 00223 for( iPreprIndex = 0 ; ppszPreprocessor[iPreprIndex] ; iPreprIndex++ ){ 00224 pszEPreprocExe = pszEPreprocDir = NULL; 00225 if( strlen(ppszPreprocessor[iPreprIndex]) < PCKL - 10 ){/* 10 = strlen("eprep$xxx$") */ 00226 /* figure out the exe of the external preprocessor */ 00227 strcpy(szPreprocConfigKey,"preproc.external."); 00228 strcat(szPreprocConfigKey,ppszPreprocessor[iPreprIndex]); 00229 strcat(szPreprocConfigKey,".executable"); 00230 pszEPreprocExe = cft_GetString(pCONF,szPreprocConfigKey); 00231 /* figure out the directory for the preprocessed file */ 00232 strcpy(szPreprocConfigKey,"preproc.external."); 00233 strcat(szPreprocConfigKey,ppszPreprocessor[iPreprIndex]); 00234 strcat(szPreprocConfigKey,".directory"); 00235 pszEPreprocDir = cft_GetString(pCONF,szPreprocConfigKey); 00236 } 00237 /* if there is no configured executable or temporary file directory for the preprocessor 00238 then this is a preprocessor error (or maybe the specified preprocessor symbolic name 00239 is too long. */ 00240 if( pszEPreprocExe == NULL ){ 00241 alloc_FinishSegment(pMemorySegment); 00242 return PREPROC_ERROR_CONFIG_EXE; 00243 } 00244 if( pszEPreprocDir == NULL ){ 00245 alloc_FinishSegment(pMemorySegment); 00246 return PREPROC_ERROR_CONFIG_DIR; 00247 } 00248 00249 /* Allocate space to hold the full path of the preprocessor output file name. */ 00250 PreprocessedFileName = alloc_Alloc(strlen(pszEPreprocDir)+UNIQ_FILE_NAME_LENGTH,pMemorySegment); 00251 if( PreprocessedFileName == NULL ){ 00252 alloc_FinishSegment(pMemorySegment); 00253 return PREPROC_ERROR_MEMORY_LOW; 00254 } 00255 strcpy(PreprocessedFileName,pszEPreprocDir); 00256 s = PreprocessedFileName + strlen(PreprocessedFileName); /* point to the end of the directory */ 00257 /* create a unique file name from the full path of the source and append it after the directory. */ 00258 uniqfnam(pszLastFileName,s); 00259 00260 /* create the ppa argumentum list for the preprocessor */ 00261 #define CHKARGN if( i >= MAXPPARGS-1 ){\ 00262 alloc_FinishSegment(pMemorySegment);\ 00263 return PREPROC_ERROR_CONFIG_EXE;\ 00264 } 00265 00266 strcpy(EPreProcCmdLin,pszEPreprocExe);/* copy it not to destroy the config string */ 00267 q = EPreProcCmdLin; 00268 i = 1; 00269 ppa[0] = q; 00270 while( *q ){ 00271 if( isspace(*q) ){ 00272 CHKARGN 00273 *q = (char)0; 00274 ppa[i] = q+1; 00275 if( *(ppa[i]) )i++; 00276 } 00277 q++; 00278 } 00279 CHKARGN 00280 ppa[i++] = pszLastFileName; 00281 pszLastFileName = PreprocessedFileName; 00282 CHKARGN 00283 ppa[i++] = PreprocessedFileName; 00284 CHKARGN 00285 ppa[i++] = NULL; 00286 #ifdef WIN32 00287 if( spawnvp(_P_WAIT,EPreProcCmdLin,ppa) ){ 00288 alloc_FinishSegment(pMemorySegment); 00289 return PREPROC_ERROR_FAIL; 00290 } 00291 #elif defined(__MACOS__) 00292 return PREPROC_ERROR_FAIL; 00293 #else 00294 if( ! (pid = fork()) ){ 00295 /* we are the child process */ 00296 execvp(EPreProcCmdLin,ppa); 00297 /* because execvp does not return executing this exit(1) is surely an error */ 00298 exit(1); 00299 } 00300 waitpid(pid,&exit_code,0); 00301 if( exit_code ){ 00302 alloc_FinishSegment(pMemorySegment); 00303 return PREPROC_ERROR_FAIL; 00304 } 00305 #endif 00306 } 00307 *pszOutputFileName = thismalloc(strlen(pszLastFileName)+1); 00308 if( *pszOutputFileName == NULL ){ 00309 alloc_FinishSegment(pMemorySegment); 00310 return PREPROC_ERROR_MEMORY_LOW; 00311 } 00312 strcpy(*pszOutputFileName,pszLastFileName); 00313 alloc_FinishSegment(pMemorySegment); 00314 return PREPROC_ERROR_SUCCESS; 00315 }