Changeset 3834 for branches/GRACE/src/win32k/ldr/myldrOpen.cpp
- Timestamp:
- Jul 17, 2000, 12:43:41 AM (25 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/GRACE/src/win32k/ldr/myldrOpen.cpp
r2838 r3834 1 /* $Id: myldrOpen.cpp,v 1.10 2000-02-21 09:24:01bird Exp $1 /* $Id: myldrOpen.cpp,v 1.10.4.1 2000-07-16 22:43:36 bird Exp $ 2 2 * 3 3 * myldrOpen - ldrOpen. 4 4 * 5 * Copyright (c) 1998- 1999knut st. osmundsen5 * Copyright (c) 1998-2000 knut st. osmundsen 6 6 * 7 7 * Project Odin Software License can be found in LICENSE.TXT … … 17 17 18 18 #define INCL_OS2KRNL_IO 19 #define INCL_OS2KRNL_TCB 19 20 20 21 /******************************************************************************* … … 30 31 31 32 #include "log.h" 33 #include "avl.h" 34 #include "options.h" 32 35 #include <peexe.h> 33 36 #include <exe386.h> 37 #include "elf.h" 34 38 #include "OS2Krnl.h" 35 39 #include "dev32.h" 40 #include "ldr.h" 41 #include "ldrCalls.h" 36 42 #include "ModuleBase.h" 37 43 #include "pe2lx.h" 38 #include "elf.h"39 #include "avl.h"40 #include "ldr.h"41 #include "ldrCalls.h"42 #include "options.h"43 44 #include "myExecPgm.h" 44 45 /******************************************************************************* 46 * Global Variables * 47 *******************************************************************************/ 48 extern BOOL fQAppType; /* From LDRQAppType */ 45 #include "env.h" 46 #include "vprintf.h" /* Make 100% sure we have va_start. */ 47 48 49 49 50 50 /******************************************************************************* 51 51 * Internal Functions * 52 52 *******************************************************************************/ 53 static unsigned getArgsLength(const char *pachArgs); 53 /* static */ APIRET AddArgsToFront(int cArgs, ...); 54 /* static */ APIRET SetExecName(const char *pszExeName); 55 /* static */ APIRET OpenPATH(PSFN phFile, char *pszFilename, PULONG pfl); 54 56 55 57 … … 59 61 * @param phFile Pointer to file handler. Holds filehandle on output. 60 62 * @param pszFilename Pointer to filename. 61 * @parma p aram3 Probablysome flags.63 * @parma pfl Pointer to some flags. 62 64 */ 63 ULONG LDRCALL myldrOpen(PSFN phFile, char *pszFilename, ULONG param3)65 ULONG LDRCALL myldrOpen(PSFN phFile, PSZ pszFilename, PULONG pfl) 64 66 { 65 ULONG rc; 66 67 /* 67 static int cNesting = 0; /* This is an variable which hold the nesting */ 68 /* level of this function. This is useful */ 69 /* when we call it recurcively. */ 70 /* The maximum nesting level is currently 3. */ 71 /* When the maximum depth has been reached */ 72 /* we'll not intercept loading any longer! */ 73 ULONG rc; /* Return value. */ 74 75 /** @sketch 68 76 * Try open the file (thats why this function is called anyway) 69 77 */ 70 rc = ldrOpen(phFile, pszFilename, param3); 71 72 /* log sucesses */ 78 rc = ldrOpen(phFile, pszFilename, pfl); 73 79 if (rc == NO_ERROR) 74 kprintf(("ldrOpen: phFile=%#.4x, flags=%#.8x, pszFn=%s\n", *phFile, param3, pszFilename)); 75 76 /* 80 kprintf(("myldrOpen-%d: phFile=%#.4x, flags=%#.8x, pszFn=%s\n", cNesting, *phFile, pfl, pszFilename)); 81 82 83 /** @sketch 77 84 * Are we to intercept the loading? 78 * - Only if open were succesful and one of the loaders are enabled. 85 * - If open were successful. 86 * - And Not too deep nesting. 87 * - And that this isn't an unsupported load. 88 * - And one of the loaders are enabled. 79 89 */ 80 if (rc == NO_ERROR && (options.fElf || options.fPE != FLAGS_PE_NOT || options.fScript)) 81 { 82 char *pszBuffer = (char*)rmalloc(640); /* Read buffer. */ 83 PIMAGE_DOS_HEADER pMzHdr = (PIMAGE_DOS_HEADER)pszBuffer; /* Pointer to the buffer as it were a dosheader. */ 84 PIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)pszBuffer; /* Pointer to the buffer as if it were an NT header. */ 85 char *pach = pszBuffer; /* Finally an pointer to the buffer as if it were chars.. (which it is!) */ 86 PEXECPGMBUFFER pBuffer; /* Pointer to a buffer containing the programname and arguments. */ 87 /* For scripts and PE.EXE this has to be changed to have correct */ 88 /* parameters sendt in to the program. */ 89 unsigned cchRead = sizeof(IMAGE_DOS_HEADER); /* Amount of the buffer which contains valid data. */ 90 unsigned cbFile; /* Filesize (0xffffffff if call to SftFileSize failed - should _never_ happen though) */ 91 92 /* 93 * Verify that rmalloc completed successfully. 94 */ 95 if (pszBuffer == NULL) 90 if (rc == NO_ERROR 91 && cNesting < 3 92 && !isLdrStateLoadingUnsupported() 93 && isAnyLoaderEnabled() 94 ) 95 { 96 union _u_ReadBufferPointers /* Read buffer pointer(s). */ 96 97 { 97 kprintf(("ldrOpen: rmalloc(1024) failed\n")); 98 char *pach; /* Pointer to the buffer as char. */ 99 PIMAGE_DOS_HEADER pMzHdr; /* Use the buffer as a dosheader. */ 100 PIMAGE_NT_HEADERS pNtHdrs; /* Use the buffer as a NT header. */ 101 } u1; 102 unsigned cbFile; /* Filesize (0xffffffff if call to SftFileSize failed - should _never_ happen though) */ 103 unsigned cbRead; /* Amount of the buffer which contains valid data. */ 104 char * psz; /* Multipurpose string pointer no.1. */ 105 char * psz2; /* Multipurpose string pointer no.2. */ 106 char * psz3; /* Multipurpose string pointer no.3. */ 107 108 /** @sketch 109 * Allocate read buffer from resident heap. 110 * IF this fails THEN we'll simply return NO_ERROR. 111 */ 112 u1.pach = (char*)rmalloc(640); 113 if (u1.pach == NULL) 114 { 115 kprintf(("myldrOpen-%d: rmalloc(640) failed\n", cNesting)); 98 116 return NO_ERROR; 99 117 } 100 118 101 /* 102 * Try get the filesize 119 120 /** @sketch 121 * Increment nesting level. 122 */ 123 cNesting++; 124 125 126 /** @sketch 127 * Get the filesize. On failure filesize is set to ~0. 103 128 */ 104 129 rc = SftFileSize(*phFile, (PULONG)SSToDS(&cbFile)); 105 130 if (rc != NO_ERROR) 106 131 { 107 kprintf((" ldrOpen: SftFileSize failed with rc=%d\n", rc));132 kprintf(("myldrOpen-%d: SftFileSize failed with rc=%d\n", cNesting, rc)); 108 133 cbFile = (unsigned)~0; 109 134 } 110 135 111 /* 112 * See if this is an recognizable module format. 113 * This costs up to two disk reads! 114 */ 115 rc = ldrRead(*phFile, 0UL, pMzHdr, 0UL, cchRead, NULL); 116 if (rc == NO_ERROR) 136 137 /** @sketch 138 * Read the size of a DOS (ie. MZ) header. 139 * IF successful and more stuff in file THEN 140 * See if this is an recognizable module binary format: 141 */ 142 cbRead = min(sizeof(IMAGE_DOS_HEADER), cbFile); 143 rc = ldrRead(*phFile, 0UL, u1.pMzHdr, 0UL, cbRead, NULL); 144 if (rc == NO_ERROR && cbRead < cbFile) 117 145 { 118 /* 119 * PE header? 120 * - If DOS Magic is found AND a valid e_lfanew (offset of NE/LX/LE/PE header) is found 121 * - OR if PE siganture is found. 146 /** @sketch 147 * If LX header just give up at once. 122 148 */ 123 if ((pMzHdr->e_magic == IMAGE_DOS_SIGNATURE && 124 pMzHdr->e_lfanew > sizeof(IMAGE_DOS_HEADER) && pMzHdr->e_lfanew < 0x04000000UL) /* Larger than 64 bytes and less that 64MB. */ 125 || *(PULONG)pach == IMAGE_NT_SIGNATURE) 126 { /* 127 * MZ or PE header found 149 if (u1.pMzHdr->e_magic == E32MAGIC) 150 goto cleanup; 151 152 /** @sketch 153 * IF PE or MZ header THEN 154 */ 155 if (u1.pMzHdr->e_magic == IMAGE_DOS_SIGNATURE 156 || u1.pNtHdrs->Signature == IMAGE_NT_SIGNATURE) 157 { 158 ULONG offPe; /* Offset to PE header. */ 159 160 /** @sketch 161 * --- 162 * We now known that this is file has a MZ or a PE header. If it's 163 * a MZ header, we might end up with no "New" header or the "New" 164 * header might turn out to be a NE, LE, or LX header. I any of 165 * these non PE headers occur OS/2 will take care of it, we'll do nothing. 166 * --- 167 * IF PE loading is disable or MZ header and e_lfanew is invalid THEN 168 * return (successfully) to the caller. 169 * ENDIF 170 * (Find the offset of the PE header while testing (offPe).) 128 171 */ 129 130 /* if PE loading is diable return to the caller */131 if ( options.fPE == FLAGS_PE_NOT)172 if (isPELoaderDisabled()) 173 goto cleanup; 174 if (u1.pMzHdr->e_magic == IMAGE_DOS_SIGNATURE) 132 175 { 133 rfree(pszBuffer); 134 return NO_ERROR; 176 offPe = u1.pMzHdr->e_lfanew; 177 if (offPe < sizeof(IMAGE_DOS_HEADER) || offPe > 0x04000000UL) 178 goto cleanup; 135 179 } 136 137 /* 138 * Read the PE header if it isn't what we allready have! 180 else 181 offPe = 0; 182 183 184 /** @sketch 185 * Read the PE header. 186 * If the read failes or not PE signature, there isn't anything for us to do. 139 187 */ 140 cchRead = sizeof(IMAGE_NT_HEADERS); 141 if (*(PULONG)pach != IMAGE_NT_SIGNATURE) 142 rc = ldrRead(*phFile, pMzHdr->e_lfanew, pach, 0UL, cchRead, NULL); 143 else 144 rc = ldrRead(*phFile, 0UL, pach, 0UL, cchRead, NULL); 145 146 /* 147 * If successfully read, and a PE signature is present the continue and try load it! 148 * Else don't do anything, simply return NO_ERROR to the caller. (probably NE or LX exec) 188 rc = ldrRead(*phFile, offPe, u1.pach, 0UL, sizeof(IMAGE_NT_HEADERS), NULL); 189 if (rc != NO_ERROR || u1.pNtHdrs->Signature != IMAGE_NT_SIGNATURE) 190 goto cleanup_noerror; 191 192 193 /** @sketch 194 * PE signature found! 149 195 */ 150 if (rc == NO_ERROR && *(PULONG)pach == IMAGE_NT_SIGNATURE) 151 { /* 152 * PE signature found. 153 */ 154 kprintf(("ldrOpen: PE executable...\n")); 155 156 /* 157 * PE2LX? 158 * - When PE2LX flag is set 159 * - OR when the MIXED flag is set and the image is with the first 64MB of memory. 160 */ 161 if (options.fPE == FLAGS_PE_PE2LX 162 || (options.fPE == FLAGS_PE_MIXED 163 && !((pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL == 0UL) 164 && pNtHdrs->OptionalHeader.ImageBase >= 0x04000000UL /* 64MB */ 165 ) 196 kprintf(("myldrOpen-%d: PE executable...\n", cNesting)); 197 198 199 /** @sketch 200 * Use Pe2Lx? 201 * - When Pe2Lx flag is set 202 * - When the MIXED flag is set and the image isn't an executable 203 * above the first 64MB private limit without relocations 204 */ 205 if (isPe2LxLoaderEnabled() 206 || (isMixedPeLoaderEnabled() 207 && ((u1.pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL) 208 || !(u1.pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) 209 || u1.pNtHdrs->OptionalHeader.ImageBase < 0x04000000UL /* 64MB */ 166 210 ) 167 211 ) 168 { /* 169 * Pe2Lx (Ring0 of course) 170 * - Create a Pe2Lx class, 171 * - initiate it 172 * - Add the module to the module tree so we may find it later... 173 * - Set the handle state to 'our'. 174 */ 175 Pe2Lx * pPe2Lx = new Pe2Lx(*phFile); 176 if (pPe2Lx != NULL) 212 ) 213 { /** @sketch 214 * Pe2Lx (Ring0 of course) 215 * - Create a Pe2Lx class, 216 * - initiate it 217 * - Add the module to the module tree so we may find it later... 218 * - Set the (file)handle state to 'our'. 219 * - Set pExeModule to module pointer and loaderstate to our exe. 220 */ 221 Pe2Lx * pPe2Lx = new Pe2Lx(*phFile); 222 if (pPe2Lx != NULL) 223 { 224 rc = pPe2Lx->init(pszFilename); 225 if (rc == NO_ERROR) 177 226 { 178 rc = pPe2Lx->init(pszFilename); 227 kprintf(("myldrOpen-%d: Successfully init of Pe2Lx object.\n", cNesting)); 228 rc = addModule(*phFile, NULL, MOD_TYPE_PE2LX, pPe2Lx); 179 229 if (rc == NO_ERROR) 180 230 { 181 kprintf(("ldrOpen: Successfully init of Pe2Lx object.\n")); 182 rc = addModule(*phFile, NULL, MOD_TYPE_PE2LX, pPe2Lx); 183 if (rc == NO_ERROR) 184 #pragma info(notrd) 185 SetState(*phFile, HSTATE_OUR); 186 #pragma info(restore) 187 else 188 kprintf(("ldrOpen: Failed to add the module. rc=%d\n")); 231 #pragma info(notrd) 232 SetState(*phFile, HSTATE_OUR); 233 #pragma info(restore) 234 if (pPe2Lx->isExe()) 235 { 236 setLdrStateLoadingOurEXE(); 237 pExeModule = getModuleBySFN(*phFile); 238 #ifdef DEBUG 239 if (pExeModule == NULL) 240 kprintf(("myldrOpen-%d: getModuleBySFN failed when setting pExeModule! FATAL!\n", cNesting)); 241 #endif 242 } 189 243 } 190 244 else 191 kprintf(("ldrOpen: Failed to init Pe2Lx object. rc=%d\n")); 192 if (rc != NO_ERROR) 193 delete pPe2Lx; 245 kprintf(("myldrOpen-%d: Failed to add the module. rc=%d\n", cNesting)); 194 246 } 195 247 else 196 kprintf(("ldrOpen: Failed to allocate Pe2Lx object.\n")); 248 kprintf(("myldrOpen-%d: Failed to init Pe2Lx object. rc=%d\n", cNesting)); 249 if (rc != NO_ERROR) 250 delete pPe2Lx; 197 251 } 198 252 else 253 kprintf(("myldrOpen-%d: Failed to allocate Pe2Lx object.\n", cNesting)); 254 255 goto cleanup; 256 } 257 258 259 /** @sketch 260 * Using PE.EXE to start EXE? 261 * - When the file is an EXE file and PE.EXE is enabled. 262 */ 263 if ((u1.pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL == 0UL) 264 && (options.fPE == FLAGS_PE_PE || options.fPE == FLAGS_PE_MIXED) 265 && (isLdrStateExecPgm() || isLdrStateQAppType()) 266 ) 267 { 268 /** @sketch 269 * PE.EXE: 270 * Find pe.exe - look in current directory and thru the PATH. 271 * Note! We use the read buffer (u1.p*) as a storage for the 272 * pe.exe filename and path. 273 */ 274 kprintf(("myldrOpen-%d: pe.exe - opening\n", cNesting)); 275 ldrClose(*phFile); 276 strcpy(u1.pach, "PE.EXE"); 277 rc = ldrOpen(phFile, u1.pach, pfl); /* This isn't recusive! */ 278 if (rc != NO_ERROR) 279 rc = OpenPATH(phFile, u1.pach, pfl); 280 if (rc == NO_ERROR) 199 281 { 200 /* 201 * Starting of PE.EXE enable? 282 /** @sketch 283 * If we're in tkExecPgm state we'll have to shuffle the parameters 284 * and executable filename tkExecPgm were called with. 285 * If not tkExecPgm we can't do anything about parameters (and there is 286 * probably nothing to do either). 202 287 */ 203 if (options.fPE == FLAGS_PE_PE || options.fPE == FLAGS_PE_MIXED) 204 { /* 205 * pe.exe - need the path! 206 */ 207 kprintf(("ldrOpen: pe.exe - opening\n")); 208 ldrClose(*phFile); 209 rc = ldrOpen(phFile, "pe.exe", param3); /* path....! problems! */ 210 kprintf(("ldrOpen: pe.exe - open returned with rc = %d\n", rc)); 211 rfree(pszBuffer); 212 return rc; 288 kprintf(("myldrOpen-%d: pe.exe - %s\n", cNesting, u1.pach)); 289 if (isLdrStateExecPgm() && fTkExecPgm) 290 { 291 rc = AddArgsToFront(1, achTkExecPgmFilename); 292 if (rc == NO_ERROR) 293 { 294 rc = SetExecName(u1.pach); 295 if (rc != NO_ERROR) 296 kprintf(("myldrOpen-%d: pe.exe - failed to set pe.exe as execname. rc=%d\n", cNesting)); 297 } 298 else 299 kprintf(("myldrOpen-%d: pe.exe - failed to add programname as argument. rc=%d\n", cNesting, rc)); 300 goto cleanup_noerror; 213 301 } 214 302 } 303 else 304 kprintf(("myldrOpen-%d: pe.exe - couldn't find/open pe.exe\n", cNesting)); 215 305 } 216 rfree(pszBuffer); 217 return NO_ERROR; 306 goto cleanup; 307 } 308 /** @sketch End of PE Loading. */ 309 310 311 /** @sketch 312 * ELF image? 313 */ 314 if (u1.pach[0] == ELFMAG0 && u1.pach[1] == ELFMAG1 && u1.pach[2] == ELFMAG2 && u1.pach[3] == ELFMAG3) 315 { 316 if (isELFDisabled()) 317 goto cleanup_noerror; 318 319 /* 320 * ELF signature found. 321 */ 322 kprintf(("myldrOpen-%d: ELF image! - not implemented yet!\n", cNesting)); 323 324 /* 325 * Do nothing more yet. NEED AN ELF LOADER!!! 326 */ 327 goto cleanup; 328 } 329 330 331 /** @sketch 332 * Java image? 333 */ 334 if (u1.pach[0] == 0xCA && u1.pach[1] == 0xFE && u1.pach[2] == 0xBA && u1.pach[3] == 0xBE) 335 { 336 if (isJAVADisabled()) 337 goto cleanup_noerror; 338 339 /** @sketch 340 * Java signature found. 341 * Try find the java executor in current dir or PATH: java.exe 342 */ 343 kprintf(("myldrOpen-%d: Jave image!\n", cNesting)); 344 #if 1 345 ldrClose(*phFile); 346 strcpy(u1.pach, "JAVA.EXE"); 347 rc = ldrOpen(phFile, u1.pach, pfl); 348 if (rc != NO_ERROR) 349 rc = OpenPATH(phFile, u1.pach, pfl); 350 if (rc == NO_ERROR) 351 { 352 kprintf(("myldrOpen-%d: java - %s\n", cNesting, u1.pach)); 353 /** @sketch 354 * If we're in tkExecPgm state we'll have to create an extra agrument, the 355 * class name without a .class extention. We'll do this after the filename 356 * in the u1 buffer. This argument is of course added to the front. 357 * Then the Executable name is to be updated. 358 */ 359 if (isLdrStateExecPgm() && fTkExecPgm) 360 { 361 psz = u1.pach + 1 + CCHMAXPATH; /* ASSUMES that buffer is at least CCHMAXPATH*2 + 1 */ 362 strcpy(psz, pszFilename); 363 psz2 = psz + strlen(psz); 364 while (psz2 > psz && *psz2 != '\\' && *psz2 != '/') 365 { 366 if (*psz2 == '.') 367 { 368 *psz2 = '\0'; 369 break; 370 } 371 psz2--; 372 } 373 374 rc = AddArgsToFront(1, psz); 375 if (rc == NO_ERROR) 376 { 377 rc = SetExecName(u1.pach); 378 if (rc != NO_ERROR) 379 kprintf(("myldrOpen-%d: java - failed to set java.exe as execname. rc=%d\n", cNesting, rc)); 380 } 381 else 382 kprintf(("myldrOpen-%d: java - failed to add programname as argument. rc=%d\n", cNesting, rc)); 383 goto cleanup_noerror; 384 } 385 } 386 else 387 kprintf(("myldrOpen-%d: java - couldn't find/open java.exe\n", cNesting)); 388 389 #endif 390 /** @sketch 391 * End of Java loading. (return) 392 */ 393 goto cleanup; 394 } 395 } 396 else 397 { 398 /** @sketch 399 * ELSE - the reading size of a DOS header failed or file is smaller than the dos header. 400 * IF read failed or filesize is less than 4 bytes THEN 401 * return no_error to the caller. 402 * ENDIF 403 */ 404 #ifdef DEBUG 405 if (rc != NO_ERROR) 406 { 407 kprintf(("myldrOpen-%d: ldrRead failed cbRead=%d, cbFile=%d, rc=%d\n", cNesting, cbRead, cbFile, rc)); 408 goto cleanup_noerror; 409 } 410 if (cbRead < 4) 411 { 412 kprintf(("myldrOpen-%d: File too small! cbFile=%d\n", cNesting, cbFile)); 413 goto cleanup_noerror; 414 } 415 #else 416 if (rc != NO_ERROR || cbRead < 4) //just forget files less than 4 bytes! 417 goto cleanup_noerror; 418 #endif 419 } 420 /** @sketch ENDIF (dos header read) */ 421 422 423 424 /* 425 * Only unreconized files passes this point! 426 * 427 * * Fileformats with lower priority should reside here. * 428 * 429 */ 430 431 /** @sketch 432 * UNIX styled script? 433 * - Starts with a hash (#) 434 * - And we're loading an EXE 435 * - And we're either in QAppType or ExecPgm state. 436 * - And that a bang (!) is the first char after the hash (ignoring blanks). 437 */ 438 if (*u1.pach == '#' 439 && isLdrStateLoadingEXE() 440 && (isLdrStateQAppType() || isLdrStateExecPgm()) 441 ) 442 { 443 if (isUNIXScriptDisabled()) 444 goto cleanup_noerror; 445 /* 446 * Look for a bang (!). Tabs and spaces are skipped, anything else result in error. 447 */ 448 psz = u1.pach + 1; 449 while ((*psz == ' ' || *psz == '\t') && psz - u1.pach < cbRead) 450 psz++; 451 if (*psz == '!') 452 { 453 /** @sketch Found UNIX styled script! */ 454 455 /** @sketch 456 * Read more of the script if necessary. (max is 256 chars (- Linux max is 127)) 457 * Terminate the string read from the file to make sure with stop somewhere! 458 */ 459 if (cbRead < cbFile /*&& cbRead != 256*/) 460 { 461 cbRead = min(256, cbFile); 462 rc = ldrRead(*phFile, 0UL, u1.pach, 0UL, cbRead, NULL); 463 } 464 u1.pach[cbRead] = '\0'; 465 466 if (rc == NO_ERROR) 467 { 468 /** @sketch 469 * Parse out filename and optional arguments (if any). 470 * The result of the parsing is that: 471 * psz will point at the executable name. 472 * psz2 will point at the arguments. 473 * Both strings are trimmed. 474 */ 475 psz++; /* psz points to the bang, skip it. */ 476 while (*psz == ' ' || *psz == '\t') /* skip blanks after bang */ 477 psz++; 478 if (*psz == '\r' || *psz == '\n' || *psz == '\0') /* End-of-line? */ 479 { 480 kprintf(("myldrOpen-%d: script no executable name.\n", cNesting)); 481 goto cleanup_noerror; /* other error code? */ 482 } 483 psz2 = psz + 1; /* Not end-of-line, so add 1 before searching for args. */ 484 while (*psz2 != '\0' && *psz2 != '\n' && *psz2 != '\r' /* skip executable name. */ 485 && *psz2 != ' ' && *psz2 != '\t') 486 psz2++; 487 while (*psz2 == ' ' || *psz2 == '\t') /* skip blanks after executable - pad them with '\0'! */ 488 *psz2++ = '\0'; 489 490 psz3 = psz2; 491 while (*psz3 != '\n' && *psz3 != '\r' && *psz3 != '\0') /* find end of parameters and terminate the string. */ 492 psz3++; 493 *psz3 = '\0'; 494 while (psz3 >= psz2 && (*psz3 == '\0' || *psz3 == ' ' || *psz3 == '\t')) /* trim args */ 495 *psz3-- = '\0'; 496 497 498 /** @sketch 499 * IF tkExecPgm THEN 500 * Correct parameters - ie. add exec name (as argv[0]), 501 * arguments (psz2) as argv[1+], old exec name, and finally 502 * the existing parameters (current argv[1+]). 503 * Set the executable name. 504 * ENDIF 505 * Open the new executable file recursively. (psz) 506 */ 507 if (isLdrStateExecPgm()) 508 { 509 if (*psz2) 510 rc = AddArgsToFront(3, psz, psz2, achTkExecPgmFilename); 511 else 512 rc = AddArgsToFront(2, psz, achTkExecPgmFilename); 513 if (rc != NO_ERROR) 514 { 515 kprintf(("myldrOpen-%d: AddArgsToFront failed with rc=%d\n", cNesting)); 516 goto cleanup_noerror; 517 } 518 rc = SetExecName(psz); 519 if (rc != NO_ERROR) 520 kprintf(("myldrOpen-%d: SetExecName failed with rc=%d\n", cNesting)); 521 } 522 rc = myldrOpen(phFile, psz, pfl); 523 } 524 else 525 { 526 kprintf(("myldrOpen-%d: script - failed to read more of the script!, rc=%d cbRead=%d cbFile=%d.\n", 527 cNesting, rc, cbRead, cbFile)); 528 } 529 530 goto cleanup; 218 531 } 219 532 else 220 533 { 221 /* 222 * ELF image? 223 */ 224 if (pach[0] == ELFMAG0 && pach[1] == ELFMAG1 && pach[2] == ELFMAG2 && pach[3] == ELFMAG3) 225 { 226 /* 227 * ELF signature found. 228 */ 229 kprintf(("ldrOpen: ELF executable! - not implemented yet!\n")); 230 231 /* 232 * Do nothing more yet. NEED AN ELF LOADER!!! 233 */ 234 rfree(pszBuffer); 235 return NO_ERROR; 236 } 534 kprintf(("myldrOpen-%d: script - hash found but no bang (!).\n", cNesting)); 535 } 536 } /**@sketch ENDIF - UNIX styled script. */ 537 538 539 540 /** @sketch 541 * REXX script? 542 * - Starts with a REXX start comment ('/','*') 543 * - And we're loading an EXE 544 * - And we're either in QAppType or ExecPgm state. 545 * - Extention is currently ignored.... 546 * .RX and .REX are known to be pure REXX scripts. 547 * While .CMD has to invoked used the commandline OS2_SHELL or COMSPEC variable. 548 */ 549 if (*u1.pach == '/' && u1.pach[1] == '*' 550 && isLdrStateLoadingEXE() 551 && (isLdrStateQAppType() || isLdrStateExecPgm()) 552 ) 553 { 554 if (isREXXScriptDisabled()) 555 goto cleanup_noerror; 556 557 /** @sketch 558 * Found REXX styled script! 559 * Find the REXX interpreter. We'll use kRx.exe to execute the REXX scripts. 560 * (This interpreter could be embedded as a child of ModuleBase as it turned out 561 * to be quite small about 700 bytes.) 562 */ 563 kprintf(("myldrOpen-%d: Found REXX script\n", cNesting)); 564 ldrClose(*phFile); 565 strcpy(u1.pach, "KRX.EXE"); 566 rc = ldrOpen(phFile, u1.pach, pfl); 567 if (rc != NO_ERROR) 568 rc = OpenPATH(phFile, u1.pach, pfl); 569 570 /** @sketch 571 * IF tkExecPgm THEN 572 * Correct parameters - ie. add exec name (as argv[0]), old exec name, 573 * and finally the existing parameters (current argv[1+]). 574 * Set the executable name. 575 * ENDIF 576 */ 577 if (rc == NO_ERROR && isLdrStateExecPgm()) 578 { 579 rc = AddArgsToFront(2, u1.pach, achTkExecPgmFilename); 580 if (rc != NO_ERROR) 581 { 582 kprintf(("myldrOpen-%d: AddArgsToFront failed with rc=%d\n", cNesting)); 583 goto cleanup_noerror; 584 } 585 rc = SetExecName(u1.pach); 586 if (rc != NO_ERROR) 587 kprintf(("myldrOpen-%d: SetExecName failed with rc=%d\n", cNesting)); 588 589 goto cleanup_noerror; 590 } 591 goto cleanup; 592 } /**@sketch ENDIF - REXX styled script. */ 593 594 595 /* 596 * Cleanup with rc set to NO_ERROR. 597 */ 598 cleanup_noerror: 599 rc = NO_ERROR; 600 601 /* 602 * Cleanup without having rc set to NO_ERROR. 603 * Decrement the nesting count. 604 */ 605 cleanup: 606 rfree(u1.pach); 607 cNesting--; 608 } 609 #ifdef DEBUG 610 else if (cNesting >= 3) 611 kprintf(("myldrOpen-%d: cNesting = %d, which is too deep!\n", cNesting, cNesting)); 612 #endif 613 614 /** @sketch 615 * Return rc. 616 */ 617 return rc; 618 } 619 620 621 /** 622 * Adds new arguments to the front of the startup arguments for the program about to be 623 * executed. 624 * 625 * @returns OS/2 return code. 626 * @param cArgs Count of arguments to add. At least 1!!! 627 * @param ... Pointers to the arguments to add. 628 * The first argument have to be the executable name. This have to the 629 * the only argument in the first string. 630 * The other arguements are space separated, so you could add a bunch 631 * of arguments in a single string! 632 * The last argument should be the old first parameter if this is to be 633 * preserved. The old first parameter is overwritten since it's 634 * normally the executable name. 635 * 636 * @status completly implemented. 637 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no) 638 * @remark Implementation note: 639 * The arguments convention is as follows: 640 * First argument, which should be the executable name, is terminated with a '\0'. 641 * It starts at offset 0 into the argument buffer, of course. 642 * All other arguemnts are separated by a space and follows the immediately after the 643 * first argument. 644 * The arguments are terminated by a double nulltermination: '\0\0'. 645 */ 646 APIRET AddArgsToFront(int cArgs, ...) 647 { 648 va_list vaarg; /* Variable length argument list. */ 649 int cchOldArgs; /* Length of the old arguments (including the first argument). */ 650 /* cchOldArgs = 1 means no arguments. It don't include the very last '\0' */ 651 /* (remember argumets are terminated with two '\0's). */ 652 int iSecondArg; /* Index of the second argument. (Used to skip the first argument.) */ 653 /* Used first on the original arguments and them when adding the first */ 654 /* new argument. */ 655 int cchNewArgs; /* Length of the new arguments to be inserted. */ 656 int i; /* Loop variable. Current function argument. */ 657 char * psz; /* General string pointer. */ 658 659 660 /** @sketch 661 * Assert that we're in the right state. 662 * Calc the length of the existing parameters. 663 * Calc the length of the new arguments to determin. 664 * Assert that the new arguments have length > 0. 665 */ 666 #ifdef DEBUG 667 if (!isLdrStateExecPgm()) 668 { 669 kprintf(("AddArgsToFront: not in tkExecPgm state.\n")); 670 return ERROR_INVALID_PARAMETER; 671 } 672 #endif 673 if (!fTkExecPgm) 674 { 675 kprintf(("AddArgsToFront: called when not in tkExecPgm data is invalid!\n")); 676 return ERROR_INVALID_PARAMETER; 677 } 678 679 cchOldArgs = iSecondArg = strlen(&achTkExecPgmArguments[0]) + 1; 680 psz = &achTkExecPgmArguments[cchOldArgs]; 681 while (*psz != '\0') 682 psz = &achTkExecPgmArguments[(cchOldArgs += strlen(psz) + 1)]; 683 684 va_start(vaarg, cArgs); 685 for (cchNewArgs = i = 0; i < cArgs; i++) 686 cchNewArgs += strlen(va_arg(vaarg, char *)) + 1; /* 1 is for space or '\0'. */ 687 va_end(vaarg); 688 #ifdef DEBUG 689 if (cchNewArgs == 0) 690 { 691 kprintf(("AddArgsToFront: the size of the arguments to add is zero!\n")); 692 return ERROR_INVALID_PARAMETER; 693 } 694 #endif 695 696 697 /** @sketch 698 * Check if we have enough room for the new arguments. Fail if not enough. 699 * Move the existing arguments to make room for the new ones. 700 * !IMPORTANT! The first existing arguments (executable name) is skipped !IMPORTANT! 701 * !IMPORTANT! in this move as this have to be re-added in this call! !IMPORTANT! 702 */ 703 if (cchOldArgs + 1 + cchNewArgs > CCHARGUMENTS) 704 { 705 kprintf(("AddArgsToFront: argument buffer is too small to hold the arguments to add, cchOldArgs=%d, cchNewArgs=%d\n", 706 cchOldArgs, cchNewArgs)); 707 return ERROR_BAD_ARGUMENTS; 708 } 709 710 if (cchOldArgs > 1) 711 { 712 memmove(&achTkExecPgmArguments[cchNewArgs], &achTkExecPgmArguments[iSecondArg], 713 cchOldArgs - iSecondArg + 1); 714 } 715 else 716 achTkExecPgmArguments[cchNewArgs + 1] = '\0'; 717 718 719 /** @sketch 720 * Copy new arguments. 721 * Since the first argument is special case we'll do it separately. (Uses '\0' as separator.) 722 * We assume that the entire first argument passed into this function should be the first argument! 723 * (This don't have to be true for the other arguments since these are space separated. You could 724 * pass in more than argument in a single string.) 725 * Loop thru the rest of the new arguments and add them with space as separator. 726 */ 727 va_start(vaarg, cArgs); 728 psz = va_arg(vaarg, char *); 729 iSecondArg = strlen(psz) + 1; 730 memcpy(&achTkExecPgmArguments[0], psz, iSecondArg); 731 732 psz = &achTkExecPgmArguments[iSecondArg]; 733 i = 2; /* one in advance to avoid a +1. */ 734 while (1) 735 { 736 strcpy(psz, va_arg(vaarg, char *)); 737 psz += strlen(psz); 738 /* IF more arguments following (new or old) THEN add a space separator ELSE break loop */ 739 if (cchOldArgs > 1 || i < cArgs) 740 *psz++ = ' '; 741 else 742 break; 743 i++; 744 } 745 va_end(vaarg); 746 747 #ifdef DEBUG /* assertion */ 748 if (psz != &achTkExecPgmArguments[cchNewArgs]) 749 { 750 kprintf(("AddArgsToFront: !Assertion failed! psz didn't end up where it should! (psz -> %d should be %d)\n", 751 psz - &achTkExecPgmArguments[0], cchNewArgs)); 752 if (cchOldArgs <= 1) 753 psz[0] = psz[1] = '\0'; 754 } 755 #endif 756 757 return NO_ERROR; 758 } 759 760 761 /** 762 * Sets the executable name of the module. 763 * This function is normally invoked after a different executable than the one requested was 764 * opened. It does _NOT_ set the new executable name as the first argument, since it is more 765 * convenient to this while calling AddArgsToFront to add other arguments. 766 * 767 * @returns OS/2 return code. 768 * @param pszExecName Pointer to new executable name. 769 * @status completly implemented. 770 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no) 771 * @remark . 772 */ 773 APIRET SetExecName(const char *pszExecName) 774 { 775 #ifdef DEBUG 776 int cch; 777 cch = strlen(pszExecName); 778 if (cch > CCHMAXPATH) 779 { 780 kprintf(("ChangeExecName: filename is too long! cch=%d. name=%s\n", cch, pszExecName)); 781 return ERROR_FILENAME_EXCED_RANGE; 782 } 783 if (!isLdrStateExecPgm()) 784 { 785 kprintf(("ChangeExecName: called when not in tkExecPgm state!!! FATAL ERROR!\n")); 786 return ERROR_INVALID_PARAMETER; 787 } 788 #endif 789 if (!fTkExecPgm) 790 { 791 kprintf(("ChangeExecName: called when not in tkExecPgm data is invalid!!! FATAL ERROR!\n")); 792 return ERROR_INVALID_PARAMETER; 793 } 794 795 strcpy(achTkExecPgmFilename, pszExecName); 796 797 return 0; 798 } 799 800 801 /** 802 * Opens a file using the PATH environment variable of the current process. 803 * @returns OS2 return code. 804 * @param phFile Pointer to filehandle. The filehandle is set to the SFN for the opened 805 * file on successful return. 806 * The filehandle is 0 on failure. 807 * @param pszFilename Pointer to filename buffer. This will hold the filename on input. 808 * On successful return it holds the filepath found. 809 * On failiure it's undefined. 810 * @param pfl Some flags set by ldrOpen. 811 * @sketch stub 812 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no) 813 * @remark 814 */ 815 APIRET OpenPATH(PSFN phFile, char *pszFilename, PULONG pfl) 816 { 817 APIRET rc; 818 USHORT TCBFailErr_save; 819 int cchFilename; /* Filename length + 1. */ 820 const char *pszPath = ScanEnv(GetEnv(FALSE), "PATH"); /* Current Process environment? */ 821 822 /**@sketch 823 * No PATH environment. 824 */ 825 if (pszPath == NULL) 826 return ERROR_FILE_NOT_FOUND; 827 828 cchFilename = strlen(pszFilename) + 1; 829 830 /**@sketch 831 * We'll have to save the TCBFailErr since we don't want to cause 832 * Hard Errors while searching invalid paths, etc. (ldrOpenPath does this!) 833 */ 834 TCBFailErr_save = tcbGetTCBFailErr(tcbGetCur()); 835 836 /**@ sketch 837 * Loop thru the PATH trying to open the specified file in each 838 * directory. 839 */ 840 while (*pszPath != '\0') 841 { 842 const char * pszNext = pszPath; 843 int cchPath; 844 char chEnd, ch; 845 846 /* 847 * Find end of this path. 848 */ 849 while (*pszPath == ' ') pszPath++; //skip leading spaces. 850 chEnd = *pszPath == '"' ? '"' : ';'; 851 while ((ch = *pszNext) != chEnd && ch != '\0') 852 pszNext++; 853 854 if (chEnd == '"') 855 { 856 cchPath = pszNext - (++pszPath); //pszPath points at '"' before incrementing it. 857 if (ch != '\0') 858 pszNext++; 859 } 860 else 861 { 862 /* Trim the string. */ 863 cchPath = pszNext - pszPath; 864 while (cchPath > 0 && pszPath[cchPath-1] == ' ') //?? 865 cchPath--; 866 } 867 868 /* 869 * No length? No Path! Or path'\'filename too long? => Next 870 */ 871 if (cchPath > 0 && cchPath + cchFilename + 1 < CCHMAXPATH) 872 { 873 static char achFilename[CCHMAXPATH]; 874 /* 875 * Build filename 876 */ 877 memcpy(achFilename, pszPath, cchPath); 878 if ((ch = achFilename[cchPath - 1]) == '\\' || ch == '/') 879 cchPath--; 880 else 881 achFilename[cchPath] = '\\'; 882 memcpy(&achFilename[cchPath + 1], pszFilename, cchFilename); /* cchFilename = length + 1; hence we copy the terminator too. */ 883 884 /* 885 * Try open the file. 886 */ 887 rc = myldrOpen(phFile, achFilename, pfl); 888 switch (rc) 889 { 890 case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: case ERROR_ACCESS_DENIED: case ERROR_INVALID_ACCESS: 891 case ERROR_INVALID_DRIVE: case ERROR_NOT_DOS_DISK: case ERROR_REM_NOT_LIST: case ERROR_BAD_NETPATH: 892 case ERROR_NETWORK_BUSY: case ERROR_DEV_NOT_EXIST: case ERROR_TOO_MANY_CMDS: case ERROR_ADAP_HDW_ERR: 893 case ERROR_UNEXP_NET_ERR: case ERROR_BAD_REM_ADAP: case ERROR_NETNAME_DELETED: case ERROR_BAD_DEV_TYPE: 894 case ERROR_NETWORK_ACCESS_DENIED: case ERROR_BAD_NET_NAME: case ERROR_TOO_MANY_SESS: case ERROR_REQ_NOT_ACCEP: 895 case ERROR_INVALID_PASSWORD: case ERROR_OPEN_FAILED: case ERROR_INVALID_NAME: case ERROR_FILENAME_EXCED_RANGE: 896 case ERROR_VC_DISCONNECTED: 897 break; 898 899 case NO_ERROR: 900 default: 901 tcbSetTCBFailErr(tcbGetCur(), TCBFailErr_save); 902 return rc; 237 903 } 238 904 } 905 #ifdef DEBUG 906 else if (cchPath > 0) kprintf(("OpenPATH: Path component is too long\n")); 907 #endif 239 908 240 909 /* 241 * Only unreconized files and readerror passes this point! 242 * 243 * * Fileformats with lower priority should reside here. * 244 * 245 */ 246 247 /* 248 * If the initial readoperation failed try to read a smaller amount, in case it is a small script... 249 * 4 bytes is a small amount isn't it? 250 */ 251 if (rc != NO_ERROR) 252 { 253 kprintf(("ldrOpen: first ldrread failed with rc=%d. tries to read 4 byte.\n", rc)); 254 cchRead = 4; 255 if ((rc = ldrRead(*phFile, 0UL, pach, 0UL, cchRead, NULL)) != NO_ERROR) 256 kprintf(("ldrOpen: second ldrread failed with rc=%d.\n ", rc)); 257 } 258 259 /* 260 * Now we'll try again, UNIX styled script? 261 */ 262 if (rc == NO_ERROR && *pach == '#' && pach[1] == '!') 263 { 264 /* 265 * UNIX styled script? 266 * FIXME! Must be more than 64 bytes long? 267 * No options! 268 * Firstline < 64 bytes! 269 */ 270 kprintf(("ldrOpen: unix script?\n")); 271 cchRead = min(cbFile, 256); 272 rc = ldrRead(*phFile, 0UL, pach, 0UL, cchRead, NULL); 273 if (rc == NO_ERROR) 274 { 275 char *pszStart = pach+2; 276 kprintf(("ldrOpen: script debug 1\n")); 277 278 /* Make sure we don't read to much... */ 279 pszBuffer[cchRead] = '\0'; 280 281 /* 282 * Skip blanks 283 */ 284 pszStart = pszBuffer + 2; /* skips the "#!" stuff. */ 285 while (*pszStart != '\0' && (*pszStart == ' ' || *pszStart == '\t')) 286 pszStart++; 287 kprintf(("ldrOpen: script debug 2\n")); 288 289 /* anything left on the line? */ 290 if (*pszStart != '\0' && *pszStart != '\r' && *pszStart != '\n') 291 { 292 char * pszEnd; /* Pointer to the end of the string(s) when the next step is finished. */ 293 unsigned cchToAdd = 1; /* Chars to add */ 294 BOOL fFirst = TRUE; /* Set if a '\0' has not been set yet. 295 * If this is clear, there are one or more parameters after the interpreter name. */ 296 297 /* 298 * find linesize and make parameters ready for copying 299 */ 300 pszEnd = pszStart; 301 kprintf(("ldrOpen: script debug 3\n")); 302 while (*pszEnd != '\0' && *pszEnd != '\r' && *pszEnd != '\n') 303 { 304 if (fFirst && (*pszEnd == ' ' || *pszEnd == '\t')) 305 { 306 *pszEnd = '\0'; 307 fFirst = FALSE; 308 if (pszEnd[1] == '\0' || pszEnd[1] == '\r' || pszEnd[1] == '\n') 309 fFirst = TRUE; 310 } 311 312 /* next */ 313 pszEnd++; 314 cchToAdd++; 315 } 316 *pszEnd = '\0'; 317 kprintf(("ldrOpen: script debug 4\n")); 318 319 /* 320 * If ldrQueryApp type we don't have any ExecPgm buffer we need to mess with. 321 * We'll simply try open the the interpreter. 322 */ 323 if (fQAppType) 324 { 325 rc = ldrClose(*phFile); 326 rc = ldrOpen(phFile, pszStart, param3); /* FIXME, recusion! check that name not equal! Use flags to prevent race? */ 327 } 328 else 329 { 330 /* 331 * Find the ExecPgm buffer. 332 */ 333 pBuffer = QueryBufferPointerFromFilename(pszFilename); 334 kprintf(("ldrOpen: script debug 5\n")); 335 if (pBuffer != NULL) 336 { 337 unsigned cchArguments = getArgsLength(pBuffer->achArgs); /* minus the first argument. */ 338 unsigned cchScriptnameDelta = strlen(pBuffer->szFilename) - strlen(pBuffer->achArgs); /* scriptname size difference. */ 339 340 kprintf(("ldrOpen: script debug 6\n")); 341 342 /* 343 * Is there enough space in the struct? 344 */ 345 if (cchArguments + cchToAdd + cchScriptnameDelta < sizeof(pBuffer->achArgs)) 346 { 347 kprintf(("ldrOpen: script debug 7\n")); 348 /* 349 * Open the interpreter. 350 */ 351 rc = ldrClose(*phFile); 352 rc = ldrOpen(phFile, pszStart, param3); /* FIXME, recusion! check that name not equal! Use flags to prevent race? */ 353 if (rc == NO_ERROR) 354 { 355 kprintf(("ldrOpen: script debug 8\n")); 356 /* Make space for the addition arguments. */ 357 #ifdef DEBUG 358 char *psz = &pBuffer->achArgs[0]; 359 int i = 0; 360 while (*psz != '\0') 361 { 362 kprintf(("Arg %d: %s\n", i++, psz)); 363 psz += 1 + strlen(psz); 364 } 365 #endif 366 memmove(&pBuffer->achArgs[cchToAdd + cchScriptnameDelta], 367 &pBuffer->achArgs[0], cchArguments); 368 369 /* 370 * Copy the arguments. 371 */ 372 kprintf(("ldrOpen: script debug 8\n")); 373 memcpy(&pBuffer->achArgs[0], pszStart, cchToAdd); /* Interpreter with arguments */ 374 if (!fFirst) 375 pBuffer->achArgs[cchToAdd - 1] = ' '; 376 strcpy(&pBuffer->achArgs[cchToAdd], pszFilename); /* Scriptname */ 377 kprintf(("ldrOpen: script debug a\n")); 378 379 #ifdef DEBUG 380 psz = &pBuffer->achArgs[0]; 381 i = 0; 382 while (*psz != '\0') 383 { 384 kprintf(("Arg %d: %s\n", i++, psz)); 385 psz += 1 + strlen(psz); 386 } 387 #endif 388 } 389 else 390 kprintf(("ldrOpen: failed to open interpreter (%s), rc=%d\n", pszStart, rc)); 391 } 392 else 393 { 394 kprintf(("ldrOpen: Argument buffer too small, %d\n", cchArguments + cchToAdd)); 395 rc = ERROR_BAD_EXE_FORMAT; 396 } 397 } 398 else 399 { 400 kprintf(("ldrOpen: QueryBufferPointerFromFilename failed.\n")); 401 rc = ERROR_BAD_EXE_FORMAT; /*?*/ 402 } 403 } 404 } 405 else 406 { 407 kprintf(("ldrOpen: no interpereter on the first line.\n")); 408 rc = ERROR_BAD_EXE_FORMAT; /*?*/ 409 } 410 } 411 else 412 { 413 kprintf(("ldrOpen: read of min(cbFile, 256) = %d failed, rc = %d\n", cchRead, rc)); 414 } 415 } /* else inn other formats here. */ 416 rfree(pszBuffer); 417 } 418 return rc; 910 * Next 911 */ 912 if (*pszNext == '\0') 913 break; 914 pszPath = pszNext + 1; 915 } 916 917 918 /* 919 * File is not found. 920 */ 921 *phFile = 0; 922 tcbSetTCBFailErr(tcbGetCur(), TCBFailErr_save); 923 return ERROR_FILE_NOT_FOUND; 419 924 } 420 421 422 /**423 * Get the lenght of the arguments.424 * @returns Lenght in char, includes the two '\0's.425 * @param pachArgs Pointer to the ASCIIZs which makes up the arguments.426 * @status completely implemented.427 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)428 */429 static unsigned getArgsLength(const char *pachArgs)430 {431 unsigned cch = 1;432 const char *psz = pachArgs;433 434 while (*psz != '\0')435 {436 register unsigned cch2 = strlen(psz);437 cch += cch2;438 psz += cch2 + 1;439 }440 441 return cch;442 }
Note:
See TracChangeset
for help on using the changeset viewer.