source: trunk/src/win32k/ldr/myldrOpen.cpp@ 5086

Last change on this file since 5086 was 5086, checked in by bird, 25 years ago

Moved ldrCalls.h into the OS2Krnl.h tree as OS2KLDR.h.
Also moved the Ldr definitions from OS2Krnl.h and into OS2KLDR.h.

File size: 42.6 KB
Line 
1/* $Id: myldrOpen.cpp,v 1.14 2001-02-10 11:11:46 bird Exp $
2 *
3 * myldrOpen - ldrOpen.
4 *
5 * Copyright (c) 1998-2000 knut st. osmundsen
6 *
7 * Project Odin Software License can be found in LICENSE.TXT
8 *
9 */
10
11
12/*******************************************************************************
13* Defined Constants And Macros *
14*******************************************************************************/
15#define INCL_DOSERRORS
16#define INCL_NOPMAPI
17
18#define INCL_OS2KRNL_IO
19#define INCL_OS2KRNL_TCB
20#define INCL_OS2KRNL_SEM
21#define INCL_OS2KRNL_SEC
22#define INCL_OS2KRNL_LDR
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#include <os2.h>
28
29#include "devSegDf.h" /* Win32k segment definitions. */
30#include "rmalloc.h"
31#include "malloc.h"
32#include <memory.h>
33#include <stdlib.h>
34#include <string.h>
35#include <stdarg.h>
36
37#include "log.h"
38#include "avl.h"
39#include "options.h"
40#include <peexe.h>
41#include <exe386.h>
42#include "elf.h"
43#include "OS2Krnl.h"
44#include "dev32.h"
45#include "ldr.h"
46#include "ModuleBase.h"
47#include "pe2lx.h"
48#include "myExecPgm.h"
49#include "env.h"
50#include "vprintf.h" /* Make 100% sure we have va_start. */
51
52
53
54/*******************************************************************************
55* Internal Functions *
56*******************************************************************************/
57/* static */ APIRET AddArgsToFront(int cArgs, ...);
58/* static */ APIRET SetExecName(const char *pszExeName);
59/* static */ APIRET OpenPATH(PSFN phFile, char *pszFilename, PULONG pfl);
60
61
62/**
63 * ldrOpen override.
64 * @returns Return code.
65 * @param phFile Pointer to file handler. Holds filehandle on output.
66 * @param pszFilename Pointer to filename.
67 * @parma pfl Pointer to some flags.
68 */
69ULONG LDRCALL myldrOpen(PSFN phFile, PSZ pszFilename, PULONG pfl)
70{
71 static int cNesting = 0; /* This is an variable which hold the nesting */
72 /* level of this function. This is useful */
73 /* when we call it recurcively. */
74 /* The maximum nesting level is currently 3. */
75 /* When the maximum depth has been reached */
76 /* we'll not intercept loading any longer! */
77 ULONG rc; /* Return value. */
78
79 /** @sketch
80 * Try open the file (that's why this function is called)
81 * Apply Extention fix if this is requested.
82 */
83 if (fldrOpenExtentionFix)
84 {
85 int cchFilename = strlen(pszFilename);
86 pszFilename[cchFilename - 4] = '\0';
87 rc = ldrOpen(phFile, pszFilename, pfl);
88 if (rc != NO_ERROR)
89 {
90 pszFilename[cchFilename - 4] = '.';
91 rc = ldrOpen(phFile, pszFilename, pfl);
92 }
93 }
94 else
95 rc = ldrOpen(phFile, pszFilename, pfl);
96
97 if (rc == NO_ERROR)
98 kprintf(("myldrOpen-%d: phFile=%#.4x, flags=%#.8x, pszFn=%s\n", cNesting, *phFile, pfl, pszFilename));
99
100
101 /** @sketch
102 * Are we to intercept the loading?
103 * - If open were successful.
104 * - And Not too deep nesting.
105 * - And that this isn't an unsupported load.
106 * - And one of the loaders are enabled.
107 */
108 if (rc == NO_ERROR
109 && cNesting < 3
110 && !isLdrStateLoadingUnsupported()
111 && isAnyLoaderEnabled()
112 )
113 {
114 union _u_ReadBufferPointers /* Read buffer pointer(s). */
115 {
116 char *pach; /* Pointer to the buffer as char. */
117 unsigned long *pul; /* Pointer to the buffer as unsigned long. */
118 PIMAGE_DOS_HEADER pMzHdr; /* Use the buffer as a dosheader. */
119 PIMAGE_NT_HEADERS pNtHdrs; /* Use the buffer as a NT header. */
120 } u1;
121 unsigned cbFile; /* Filesize (0xffffffff if call to SftFileSize failed - should _never_ happen though) */
122 unsigned cbRead; /* Amount of the buffer which contains valid data. */
123 char * psz; /* Multipurpose string pointer no.1. */
124 char * psz2; /* Multipurpose string pointer no.2. */
125 char * psz3; /* Multipurpose string pointer no.3. */
126
127 /** @sketch
128 * Allocate read buffer from resident heap.
129 * IF this fails THEN we'll simply return NO_ERROR.
130 */
131 u1.pach = (char*)rmalloc(640);
132 if (u1.pach == NULL)
133 {
134 kprintf(("myldrOpen-%d: rmalloc(640) failed\n", cNesting));
135 goto ret;
136 }
137
138
139 /** @sketch
140 * Increment nesting level.
141 */
142 cNesting++;
143
144
145 /** @sketch
146 * Get the filesize. On failure filesize is set to ~0.
147 */
148 rc = SftFileSize(*phFile, (PULONG)SSToDS(&cbFile));
149 if (rc != NO_ERROR)
150 {
151 kprintf(("myldrOpen-%d: SftFileSize failed with rc=%d\n", cNesting, rc));
152 cbFile = (unsigned)~0;
153 }
154
155
156 /** @sketch
157 * Read the size of a DOS (ie. MZ) header.
158 * IF successful and more stuff in file THEN
159 * See if this is an recognizable module binary format:
160 */
161 cbRead = min(sizeof(IMAGE_DOS_HEADER), cbFile);
162 rc = ldrRead(*phFile, 0UL, u1.pMzHdr, 0UL, cbRead, NULL);
163 if (rc == NO_ERROR && cbRead < cbFile)
164 {
165 /** @sketch
166 * If LX header just give up at once.
167 */
168 if (u1.pMzHdr->e_magic == E32MAGIC)
169 goto cleanup;
170
171 /** @sketch
172 * IF PE or MZ header THEN
173 */
174 if (u1.pMzHdr->e_magic == IMAGE_DOS_SIGNATURE
175 || u1.pNtHdrs->Signature == IMAGE_NT_SIGNATURE)
176 {
177 ULONG offPe; /* Offset to PE header. */
178
179 /** @sketch
180 * ---
181 * We now known that this is file has a MZ or a PE header. If it's
182 * a MZ header, we might end up with no "New" header or the "New"
183 * header might turn out to be a NE, LE, or LX header. I any of
184 * these non PE headers occur OS/2 will take care of it, we'll do nothing.
185 * ---
186 * IF PE loading is disable or MZ header and e_lfanew is invalid THEN
187 * return (successfully) to the caller.
188 * ENDIF
189 * (Find the offset of the PE header while testing (offPe).)
190 */
191 if (isPELoaderDisabled())
192 goto cleanup;
193 if (u1.pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
194 {
195 offPe = u1.pMzHdr->e_lfanew;
196 if (offPe < sizeof(IMAGE_DOS_HEADER) || offPe > 0x04000000UL)
197 goto cleanup;
198 }
199 else
200 offPe = 0;
201
202
203 /** @sketch
204 * Read the PE header.
205 * If the read failes or not PE signature, there isn't anything for us to do.
206 */
207 rc = ldrRead(*phFile, offPe, u1.pach, 0UL, sizeof(IMAGE_NT_HEADERS), NULL);
208 if (rc != NO_ERROR || u1.pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
209 goto cleanup_noerror;
210
211
212 /** @sketch
213 * PE signature found!
214 */
215 kprintf(("myldrOpen-%d: PE executable...\n", cNesting));
216
217
218 /** @sketch
219 * Use Pe2Lx?
220 * - When Pe2Lx flag is set
221 * - When the MIXED flag is set and the image isn't an executable
222 * above the first 64MB private limit without relocations
223 */
224 if (isPe2LxLoaderEnabled()
225 || (isMixedPeLoaderEnabled()
226 && ((u1.pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL)
227 || !(u1.pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
228 || u1.pNtHdrs->OptionalHeader.ImageBase < 0x04000000UL /* 64MB */
229 )
230 )
231 )
232 { /** @sketch
233 * Pe2Lx (Ring0 of course)
234 * - Create a Pe2Lx class,
235 * - initiate it
236 * - Add the module to the module tree so we may find it later...
237 * - Set the (file)handle state to 'our'.
238 * - Set pExeModule to module pointer and loaderstate to our exe.
239 */
240 Pe2Lx * pPe2Lx = new Pe2Lx(*phFile);
241 if (pPe2Lx != NULL)
242 {
243 rc = pPe2Lx->init(pszFilename);
244 if (rc == NO_ERROR)
245 {
246 kprintf(("myldrOpen-%d: Successfully init of Pe2Lx object.\n", cNesting));
247 rc = addModule(*phFile, NULL, MOD_TYPE_PE2LX, pPe2Lx);
248 if (rc == NO_ERROR)
249 {
250 #pragma info(notrd)
251 SetState(*phFile, HSTATE_OUR);
252 #pragma info(restore)
253 if (pPe2Lx->isExe())
254 {
255 setLdrStateLoadingOurEXE();
256 pExeModule = getModuleBySFN(*phFile);
257 #ifdef DEBUG
258 if (pExeModule == NULL)
259 kprintf(("myldrOpen-%d: getModuleBySFN failed when setting pExeModule! FATAL!\n", cNesting));
260 #endif
261 }
262 }
263 else
264 kprintf(("myldrOpen-%d: Failed to add the module. rc=%d\n", cNesting));
265 }
266 else
267 kprintf(("myldrOpen-%d: Failed to init Pe2Lx object. rc=%d\n", cNesting));
268 if (rc != NO_ERROR)
269 delete pPe2Lx;
270 }
271 else
272 {
273 kprintf(("myldrOpen-%d: Failed to allocate Pe2Lx object.\n", cNesting));
274 rc = ERROR_NOT_ENOUGH_MEMORY;
275 }
276
277 goto cleanup;
278 }
279
280
281 /** @sketch
282 * Using PE.EXE to start EXE?
283 * - When the file is an EXE file and PE.EXE is enabled.
284 */
285 if ((u1.pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL) == 0UL
286 && (options.fPE == FLAGS_PE_PE || options.fPE == FLAGS_PE_MIXED)
287 && (isLdrStateExecPgm() || isLdrStateQAppType())
288 )
289 {
290 /** @sketch
291 * PE.EXE:
292 * Find pe.exe - look in current directory and thru the PATH.
293 * Note! We use the read buffer (u1.p*) as a storage for the
294 * pe.exe filename and path.
295 */
296 kprintf(("myldrOpen-%d: pe.exe - opening\n", cNesting));
297 ldrClose(*phFile);
298 strcpy(u1.pach, "PE.EXE");
299 rc = ldrOpen(phFile, u1.pach, pfl); /* This isn't recusive! */
300 if (rc != NO_ERROR)
301 rc = OpenPATH(phFile, u1.pach, pfl);
302 if (rc == NO_ERROR)
303 {
304 /** @sketch
305 * If we're in tkExecPgm state we'll have to shuffle the parameters
306 * and executable filename tkExecPgm were called with.
307 * If not tkExecPgm we can't do anything about parameters (and there is
308 * probably nothing to do either).
309 * We'll always enclose the PE executable name in quotes.
310 */
311 kprintf(("myldrOpen-%d: pe.exe - %s\n", cNesting, u1.pach));
312 if (isLdrStateExecPgm() && fTkExecPgm)
313 {
314 u1.pach[0] = '"';
315 strcpy(&u1.pach[1], achTkExecPgmFilename);
316 u1.pach[strlen(u1.pach)] = '\0';
317 rc = AddArgsToFront(2, ldrpFileNameBuf, u1.pach);
318 if (rc == NO_ERROR)
319 {
320 rc = SetExecName(ldrpFileNameBuf);
321 if (rc != NO_ERROR)
322 kprintf(("myldrOpen-%d: pe.exe - failed to set pe.exe as execname. rc=%d\n", cNesting));
323 }
324 else
325 kprintf(("myldrOpen-%d: pe.exe - failed to add programname as argument. rc=%d\n", cNesting, rc));
326 goto cleanup_noerror;
327 }
328 }
329 else
330 kprintf(("myldrOpen-%d: pe.exe - couldn't find/open pe.exe\n", cNesting));
331 }
332 goto cleanup;
333 }
334 /** @sketch End of PE Loading. */
335
336
337 /** @sketch
338 * ELF image?
339 */
340 if (*u1.pul == ELFMAGICLSB)
341 {
342 if (isELFDisabled())
343 goto cleanup_noerror;
344
345 /*
346 * ELF signature found.
347 */
348 kprintf(("myldrOpen-%d: ELF image! - not implemented yet!\n", cNesting));
349
350 /*
351 * Do nothing more yet. NEED AN ELF LOADER!!!
352 */
353 goto cleanup;
354 }
355
356
357 /** @sketch
358 * Java image?
359 */
360 if (*u1.pul == 0xBEBAFECAUL) //CAh FEh BAh BEh
361 {
362 char *pszName = NULL;
363 int cchName;
364
365 if (isJAVADisabled())
366 goto cleanup_noerror;
367
368 /** @sketch
369 * Java signature found.
370 * Copy the name to a temporary buffer. (only if necessary)
371 * Remove the extention (.class) and insert a space between the name and the path.
372 * (This is the needed processing of the class filename to make it a classpath
373 * entry (path) and a class name (filename).)
374 * Try find the java executor in current dir or PATH: java.exe
375 */
376 kprintf(("myldrOpen-%d: Jave image!\n", cNesting));
377
378 if (isLdrStateExecPgm() && fTkExecPgm)
379 {
380 /* Ooops we had to get the file name from the MFT. ldrpFileNameBuf is allways uppercased... */
381 /* MFT seems to hold uppercased filenames! ARG! But (by pure luck?) achTkExecPgmArguments is
382 * not uppercased (yet). Nothing could be simpler!
383 */
384 #if 1
385 psz3 = achTkExecPgmArguments;
386 #elif 0
387 psz3 = SecPathFromSFN(*phFile);
388 if (psz3 == NULL)
389 psz3 = ldrpFileNameBuf;
390 #else
391 psz3 = ldrpFileNameBuf;
392 #endif
393 cchName = strlen(psz3);
394 pszName = (char*)rmalloc(cchName + 2);
395 if (pszName == NULL)
396 {
397 rc = ERROR_NOT_ENOUGH_MEMORY;
398 goto cleanup;
399 }
400 memcpy(pszName, psz3, cchName+1);
401
402 psz = pszName + strlen(pszName) - 1;
403 while (psz > pszName && *psz != '.' && *psz != '\\' && *psz != '/')
404 psz--;
405 if (*psz == '.')
406 {
407 cchName = psz - pszName;
408 *psz-- = '\0';
409 while (psz > pszName && *psz != '\\' && *psz != '/')
410 psz--;
411
412 /* check for root and evt. make room for an extra slash. */
413 if (psz - pszName == 2)
414 {
415 memmove(psz + 1, psz, cchName - 1);
416 *psz++ = '\\';
417 }
418 }
419 /* check if no path */
420 if (psz == pszName)
421 memmove(pszName + 1, pszName, cchName + 1);
422 *psz = ' ';
423 }
424
425 ldrClose(*phFile);
426 rc = ldrOpen(phFile, ".\\JAVA.EXE", pfl);
427 if (rc != NO_ERROR)
428 rc = OpenPATH(phFile, "JAVA.EXE", pfl);
429 if (rc == NO_ERROR)
430 {
431 kprintf(("myldrOpen-%d: java - %s\n", cNesting, ldrpFileNameBuf));
432
433 /** @sketch
434 * To be able to execute any given class name we'll have to pass in the
435 * directory as -classpath. But -classpath seems to override the default
436 * and environmental CLASSPATHs. So, we'll have to pass in the value of
437 * the CLASSPATH env.var. or generate the default class path (what ever that is).
438 *
439 * TODO: spaces in class path.
440 */
441 if (isLdrStateExecPgm() && fTkExecPgm)
442 {
443 psz = u1.pach;
444
445 /*
446 * Get classpath and add it as a parameter
447 */
448 strcpy(u1.pach, "-classpath ");
449 psz = u1.pach + strlen(u1.pach);
450
451 psz3 = (char*)ScanEnv(GetEnv(TRUE), "CLASSPATH");
452 if (psz3 != NULL)
453 { /* environment variable set */
454 if (strlen(psz3) > 640 - 11 - 1 - cchName) //check for overflow
455 { // TODO? should reallocate...
456 memcpy(psz, psz3, 640 - 11 - 1 - cchName);
457 psz[640 - 11 - 1 - cchName] = '\0';
458 }
459 else
460 strcpy(psz, psz3);
461 psz += strlen(psz);
462 }
463 else
464 {
465 /* Make default classpath by taking the java.exe path + '..\lib\classes.zip' */
466 strcpy(psz, ldrpFileNameBuf);
467 psz3 = psz + strlen(psz) - 1;
468 while (psz3 > psz && *psz3 != '\\' && *psz3 != '/')
469 psz3--;
470 strcpy(++psz3, "..\\lib\\classes.zip");
471 psz = psz3 + strlen(psz3);
472 }
473
474 /*
475 * Add the class directory (as the last classpath entry) and the class name.
476 * (Note. I may happen that there is no directory, but that don't matter
477 * a space is allways preceding the class name.)
478 */
479 *psz++ = ';';
480 strcpy(psz, pszName);
481 if (pszName != NULL)
482 rfree(pszName);
483
484 /*
485 * Setup JAVA.EXE as executable with the parameters we've build.
486 */
487 rc = AddArgsToFront(2, ldrpFileNameBuf, u1.pach);
488 kprintf(("myldrOpen-%d: java - Exe: %s Args: %s\n", cNesting, ldrpFileNameBuf, u1.pach));
489 if (rc == NO_ERROR)
490 {
491 rc = SetExecName(ldrpFileNameBuf);
492 if (rc != NO_ERROR)
493 kprintf(("myldrOpen-%d: java - failed to set java.exe as execname. rc=%d\n", cNesting, rc));
494 }
495 else
496 kprintf(("myldrOpen-%d: java - failed to setup the parameters. rc=%d\n", cNesting, rc));
497
498 goto cleanup_noerror;
499 }
500 }
501 else
502 kprintf(("myldrOpen-%d: java - couldn't find/open java.exe\n", cNesting));
503
504
505 /** @sketch
506 * End of Java loading. (return)
507 */
508 if (pszName != NULL)
509 rfree(pszName);
510 goto cleanup;
511 }
512
513 }
514 else
515 {
516 /** @sketch
517 * ELSE - the reading size of a DOS header failed or file is smaller than the dos header.
518 * IF read failed or filesize is less than 4 bytes THEN
519 * return no_error to the caller.
520 * ENDIF
521 */
522 #ifdef DEBUG
523 if (rc != NO_ERROR)
524 {
525 kprintf(("myldrOpen-%d: ldrRead failed cbRead=%d, cbFile=%d, rc=%d\n", cNesting, cbRead, cbFile, rc));
526 goto cleanup_noerror;
527 }
528 if (cbRead < 4)
529 {
530 kprintf(("myldrOpen-%d: File too small! cbFile=%d\n", cNesting, cbFile));
531 goto cleanup_noerror;
532 }
533 #else
534 if (rc != NO_ERROR || cbRead < 4) //just forget files less than 4 bytes!
535 goto cleanup_noerror;
536 #endif
537 }
538 /** @sketch ENDIF (dos header read) */
539
540
541
542 /*
543 * Only unreconized files passes this point!
544 *
545 * * Fileformats with lower priority should reside here. *
546 *
547 */
548
549 /** @sketch
550 * UNIX styled script?
551 * - Starts with a hash (#)
552 * - And we're loading an EXE
553 * - And we're either in QAppType or ExecPgm state.
554 * - And that a bang (!) is the first char after the hash (ignoring blanks).
555 *
556 * FIXME: spaces script name.
557 */
558 if (*u1.pach == '#'
559 && isLdrStateLoadingEXE()
560 && (isLdrStateQAppType() || isLdrStateExecPgm())
561 )
562 {
563 if (isUNIXScriptDisabled())
564 goto cleanup_noerror;
565 /*
566 * Look for a bang (!). Tabs and spaces are skipped, anything else result in error.
567 */
568 psz = u1.pach + 1;
569 while ((*psz == ' ' || *psz == '\t') && psz - u1.pach < cbRead)
570 psz++;
571 if (*psz == '!')
572 {
573 /** @sketch Found UNIX styled script! */
574
575 /** @sketch
576 * Read more of the script if necessary. (max is 256 chars (- Linux max is 127))
577 * Terminate the string read from the file to make sure with stop somewhere!
578 */
579 if (cbRead < cbFile /*&& cbRead != 256*/)
580 {
581 cbRead = min(256, cbFile);
582 rc = ldrRead(*phFile, 0UL, u1.pach, 0UL, cbRead, NULL);
583 }
584 u1.pach[cbRead] = '\0';
585
586 if (rc == NO_ERROR)
587 {
588 /** @sketch
589 * Parse out filename and optional arguments (if any).
590 * The result of the parsing is that:
591 * psz will point at the executable name.
592 * psz2 will point at the arguments.
593 * Both strings are trimmed.
594 */
595 psz++; /* psz points to the bang, skip it. */
596 while (*psz == ' ' || *psz == '\t') /* skip blanks after bang */
597 psz++;
598 if (*psz == '\r' || *psz == '\n' || *psz == '\0') /* End-of-line? */
599 {
600 kprintf(("myldrOpen-%d: script no executable name.\n", cNesting));
601 goto cleanup_noerror; /* other error code? */
602 }
603 psz2 = psz + 1; /* Not end-of-line, so add 1 before searching for args. */
604 while (*psz2 != '\0' && *psz2 != '\n' && *psz2 != '\r' /* skip executable name. */
605 && *psz2 != ' ' && *psz2 != '\t')
606 psz2++;
607 while (*psz2 == ' ' || *psz2 == '\t') /* skip blanks after executable - pad them with '\0'! */
608 *psz2++ = '\0';
609
610 psz3 = psz2;
611 while (*psz3 != '\n' && *psz3 != '\r' && *psz3 != '\0') /* find end of parameters and terminate the string. */
612 psz3++;
613 *psz3 = '\0';
614 while (psz3 >= psz2 && (*psz3 == '\0' || *psz3 == ' ' || *psz3 == '\t')) /* trim args */
615 *psz3-- = '\0';
616
617
618 /** @sketch
619 * IF tkExecPgm THEN
620 * Correct parameters - ie. add exec name (as argv[0]),
621 * arguments (psz2) as argv[1+], old exec name, and finally
622 * the existing parameters (current argv[1+]).
623 * Set the executable name.
624 * ENDIF
625 * Open the new executable file recursively. (psz)
626 */
627 if (isLdrStateExecPgm())
628 {
629 if (*psz2)
630 rc = AddArgsToFront(3, psz, psz2, achTkExecPgmFilename);
631 else
632 rc = AddArgsToFront(2, psz, achTkExecPgmFilename);
633 if (rc != NO_ERROR)
634 {
635 kprintf(("myldrOpen-%d: AddArgsToFront failed with rc=%d\n", cNesting));
636 goto cleanup_noerror;
637 }
638 rc = SetExecName(psz);
639 if (rc != NO_ERROR)
640 kprintf(("myldrOpen-%d: SetExecName failed with rc=%d\n", cNesting));
641 }
642 ldrClose(*phFile);
643 rc = myldrOpen(phFile, psz, pfl);
644 if (rc != NO_ERROR)
645 {
646 psz2 = psz + strlen(psz);
647 if (psz + 4 >= psz2 || strcmp(psz2 - 4, ".EXE") != 0)
648 {
649 strcpy(psz2, ".EXE");
650 rc = myldrOpen(phFile, psz, pfl);
651 *psz2 = '\0';
652 }
653 else
654 psz2 = NULL;
655
656 //should we search the PATH??? For a starting, we'll do it.
657 if (rc != NO_ERROR
658 && (rc = OpenPATH(phFile, psz, pfl)) != NO_ERROR
659 && psz2 != NULL)
660 {
661 *psz2 = '.';
662 rc = OpenPATH(phFile, psz, pfl);
663 }
664 }
665 }
666 else
667 {
668 kprintf(("myldrOpen-%d: script - failed to read more of the script!, rc=%d cbRead=%d cbFile=%d.\n",
669 cNesting, rc, cbRead, cbFile));
670 }
671
672 goto cleanup;
673 }
674 else
675 {
676 kprintf(("myldrOpen-%d: script - hash found but no bang (!).\n", cNesting));
677 }
678 } /**@sketch ENDIF - UNIX styled script. */
679
680
681
682 /** @sketch
683 * REXX script?
684 * - Starts with a REXX start comment ('/','*')
685 * - And we're loading an EXE
686 * - And we're either in QAppType or ExecPgm state.
687 * - Extention:
688 * .RX and .REX are known to be pure REXX scripts.
689 * While .CMD has to invoked used the commandline OS2_SHELL or COMSPEC variable.
690 *
691 * FIXME: spaces script name.
692 */
693 psz2 = pszFilename + strlen(pszFilename) - 1;
694 while (psz2 > pszFilename && *psz2 != '.')
695 psz2--;
696 if (*psz2 == '.'
697 && *u1.pach == '/' && u1.pach[1] == '*'
698 && isLdrStateLoadingEXE()
699 && (isLdrStateQAppType() || isLdrStateExecPgm())
700 && (stricmp(psz2, ".RX") == 0 || stricmp(psz2, ".REX") == 0)
701 )
702 {
703 if (isREXXScriptDisabled())
704 goto cleanup_noerror;
705
706 /** @sketch
707 * Found REXX styled script!
708 * Find the REXX interpreter. We'll use kRx.exe to execute the REXX scripts.
709 * (This interpreter could be embedded as a child of ModuleBase as it turned out
710 * to be quite small about 700 bytes.)
711 */
712 kprintf(("myldrOpen-%d: Found REXX script\n", cNesting));
713 ldrClose(*phFile);
714 psz = "KRX.EXE";
715 rc = ldrOpen(phFile, psz, pfl);
716 if (rc != NO_ERROR)
717 rc = OpenPATH(phFile, psz, pfl);
718
719 /** @sketch
720 * IF tkExecPgm THEN
721 * Correct parameters - ie. add exec name (as argv[0]), old exec name,
722 * and finally the existing parameters (current argv[1+]).
723 * Set the executable name.
724 * ENDIF
725 */
726 if (rc == NO_ERROR && isLdrStateExecPgm())
727 {
728 rc = AddArgsToFront(2, ldrpFileNameBuf, achTkExecPgmFilename);
729 if (rc != NO_ERROR)
730 {
731 kprintf(("myldrOpen-%d: AddArgsToFront failed with rc=%d\n", cNesting));
732 goto cleanup_noerror;
733 }
734 rc = SetExecName(ldrpFileNameBuf);
735 if (rc != NO_ERROR)
736 kprintf(("myldrOpen-%d: SetExecName failed with rc=%d\n", cNesting));
737
738 goto cleanup_noerror;
739 }
740 goto cleanup;
741 } /**@sketch ENDIF - REXX styled script. */
742
743
744 /*
745 * Cleanup with rc set to NO_ERROR.
746 */
747 cleanup_noerror:
748 rc = NO_ERROR;
749
750 /*
751 * Cleanup without having rc set to NO_ERROR.
752 * Decrement the nesting count.
753 */
754 cleanup:
755 rfree(u1.pach);
756 cNesting--;
757 }
758 #ifdef DEBUG
759 else if (cNesting >= 3)
760 kprintf(("myldrOpen-%d: cNesting = %d, which is too deep!\n", cNesting, cNesting));
761 #endif
762
763ret:
764 /** @sketch
765 * Return rc.
766 */
767 return rc;
768}
769
770
771/**
772 * Adds new arguments to the front of the startup arguments for the program about to be
773 * executed.
774 *
775 * @returns OS/2 return code.
776 * @param cArgs Count of arguments to add. At least 1!!!
777 * @param ... Pointers to the arguments to add.
778 * The first argument have to be the executable name. This have to the
779 * the only argument in the first string.
780 * The other arguements are space separated, so you could add a bunch
781 * of arguments in a single string!
782 * The last argument should be the old first parameter if this is to be
783 * preserved. The old first parameter is overwritten since it's
784 * normally the executable name.
785 *
786 * @status completly implemented.
787 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
788 * @remark Implementation note:
789 * The arguments convention is as follows:
790 * First argument, which should be the executable name, is terminated with a '\0'.
791 * It starts at offset 0 into the argument buffer, of course.
792 * All other arguemnts are separated by a space and follows the immediately after the
793 * first argument.
794 * The arguments are terminated by a double nulltermination: '\0\0'.
795 */
796APIRET AddArgsToFront(int cArgs, ...)
797{
798 va_list vaarg; /* Variable length argument list. */
799 int cchOldArgs; /* Length of the old arguments (including the first argument). */
800 /* cchOldArgs = 1 means no arguments. It don't include the very last '\0' */
801 /* (remember argumets are terminated with two '\0's). */
802 int iSecondArg; /* Index of the second argument. (Used to skip the first argument.) */
803 /* Used first on the original arguments and them when adding the first */
804 /* new argument. */
805 int cchNewArgs; /* Length of the new arguments to be inserted. */
806 int i; /* Loop variable. Current function argument. */
807 char * psz; /* General string pointer. */
808
809
810 /** @sketch
811 * Assert that we're in the right state.
812 * Calc the length of the existing parameters.
813 * Calc the length of the new arguments to determin.
814 * Assert that the new arguments have length > 0.
815 */
816 #ifdef DEBUG
817 if (!isLdrStateExecPgm())
818 {
819 kprintf(("AddArgsToFront: not in tkExecPgm state.\n"));
820 return ERROR_INVALID_PARAMETER;
821 }
822 #endif
823 if (!fTkExecPgm)
824 {
825 kprintf(("AddArgsToFront: called when not in tkExecPgm data is invalid!\n"));
826 return ERROR_INVALID_PARAMETER;
827 }
828
829 iSecondArg = strlen(&achTkExecPgmArguments[0]) + 1;
830 psz = &achTkExecPgmArguments[iSecondArg];
831 while (*psz != '\0')
832 psz += strlen(psz) + 1;
833 cchOldArgs = psz - &achTkExecPgmArguments[iSecondArg];
834
835 va_start(vaarg, cArgs);
836 for (cchNewArgs = i = 0; i < cArgs; i++)
837 cchNewArgs += strlen(va_arg(vaarg, char *)) + 1; /* 1 is for space or '\0'. */
838 va_end(vaarg);
839 #ifdef DEBUG
840 if (cchNewArgs == 0)
841 {
842 kprintf(("AddArgsToFront: the size of the arguments to add is zero!\n"));
843 return ERROR_INVALID_PARAMETER;
844 }
845 #endif
846
847
848 /** @sketch
849 * Check if we have enough room for the new arguments. Fail if not enough.
850 * Move the existing arguments to make room for the new ones.
851 * !IMPORTANT! The first existing arguments (executable name) is skipped !IMPORTANT!
852 * !IMPORTANT! in this move as this have to be re-added in this call! !IMPORTANT!
853 */
854 if (cchOldArgs + cchNewArgs + 1 > CCHARGUMENTS)
855 {
856 kprintf(("AddArgsToFront: argument buffer is too small to hold the arguments to add, cchOldArgs=%d, cchNewArgs=%d\n",
857 cchOldArgs, cchNewArgs));
858 return ERROR_BAD_ARGUMENTS;
859 }
860
861 if (cchOldArgs > 0)
862 {
863 memmove(&achTkExecPgmArguments[cchNewArgs], &achTkExecPgmArguments[iSecondArg],
864 cchOldArgs + 1);
865 }
866 else
867 achTkExecPgmArguments[cchNewArgs] = '\0';
868
869
870 /** @sketch
871 * Copy new arguments.
872 * Since the first argument is special case we'll do it separately. (Uses '\0' as separator.)
873 * We assume that the entire first argument passed into this function should be the first argument!
874 * (This don't have to be true for the other arguments since these are space separated. You could
875 * pass in more than argument in a single string.)
876 * Loop thru the rest of the new arguments and add them with space as separator.
877 */
878 va_start(vaarg, cArgs);
879 psz = va_arg(vaarg, char *);
880 memcpy(&achTkExecPgmArguments[0], psz, (i = strlen(psz) + 1));
881 psz = &achTkExecPgmArguments[i];
882
883 for (i = 1; i < cArgs; i++)
884 {
885 if (i > 1) *psz++ = ' '; //Add space if not second argument.
886 strcpy(psz, va_arg(vaarg, char *));
887 psz += strlen(psz);
888 }
889 va_end(vaarg);
890 if (cchOldArgs > 0) *psz++ = ' '; //Add space if old arguments
891
892 #ifdef DEBUG /* assertion */
893 if (psz != &achTkExecPgmArguments[cchNewArgs])
894 {
895 kprintf(("AddArgsToFront: !Assertion failed! psz didn't end up where it should! (psz -> %d should be %d)\n",
896 psz - &achTkExecPgmArguments[0], cchNewArgs));
897 if (cchOldArgs <= 1)
898 psz[0] = psz[1] = '\0';
899 }
900 #endif
901
902 return NO_ERROR;
903}
904
905
906/**
907 * Sets the executable name of the module.
908 * This function is normally invoked after a different executable than the one requested was
909 * opened. It does _NOT_ set the new executable name as the first argument, since it is more
910 * convenient to this while calling AddArgsToFront to add other arguments.
911 *
912 * @returns OS/2 return code.
913 * @param pszExecName Pointer to new executable name.
914 * @status completly implemented.
915 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
916 * @remark .
917 */
918APIRET SetExecName(const char *pszExecName)
919{
920 #ifdef DEBUG
921 int cch;
922 cch = strlen(pszExecName);
923 if (cch > CCHMAXPATH)
924 {
925 kprintf(("ChangeExecName: filename is too long! cch=%d. name=%s\n", cch, pszExecName));
926 return ERROR_FILENAME_EXCED_RANGE;
927 }
928 if (!isLdrStateExecPgm())
929 {
930 kprintf(("ChangeExecName: called when not in tkExecPgm state!!! FATAL ERROR!\n"));
931 return ERROR_INVALID_PARAMETER;
932 }
933 #endif
934 if (!fTkExecPgm)
935 {
936 kprintf(("ChangeExecName: called when not in tkExecPgm data is invalid!!! FATAL ERROR!\n"));
937 return ERROR_INVALID_PARAMETER;
938 }
939
940 strcpy(achTkExecPgmFilename, pszExecName);
941
942 return 0;
943}
944
945
946/**
947 * Opens a file using the PATH environment variable of the current process.
948 * @returns OS2 return code.
949 * @param phFile Pointer to filehandle. The filehandle is set to the SFN for the opened
950 * file on successful return.
951 * The filehandle is 0 on failure.
952 * @param pszFilename Pointer to filename buffer. This will hold the filename on input.
953 * On successful return it holds the filepath found.
954 * On failiure it's undefined.
955 * @param pfl Some flags set by ldrOpen.
956 * @sketch stub
957 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
958 * @remark
959 */
960APIRET OpenPATH(PSFN phFile, char *pszFilename, PULONG pfl)
961{
962 APIRET rc;
963 USHORT TCBFailErr_save;
964 int cchFile; /* Filename length + 1. */
965 const char *pszFile; /* Pointer to filename portion. */
966 const char *pszPath = ScanEnv(GetEnv(FALSE), "PATH"); /* Current Process environment? */
967
968 /**@sketch
969 * No PATH environment.
970 */
971 if (pszPath == NULL)
972 return ERROR_FILE_NOT_FOUND;
973
974 /**@sketch
975 * Skip any paths in the filename.
976 */
977 pszFile = pszFilename + (cchFile = strlen(pszFilename));
978 while (pszFile >= pszFilename && *pszFile != '\\' && *pszFile != '/')
979 pszFile--;
980 cchFile -= pszFile - pszFilename;
981 pszFile++;
982
983 /**@sketch
984 * We'll have to save the TCBFailErr since we don't want to cause
985 * Hard Errors while searching invalid paths, etc. (ldrOpenPath does this!)
986 */
987 TCBFailErr_save = tcbGetTCBFailErr(tcbGetCur());
988
989 /**@ sketch
990 * Loop thru the PATH trying to open the specified file in each
991 * directory.
992 */
993 while (*pszPath != '\0')
994 {
995 const char * pszNext;
996 int cchPath;
997 char chEnd;
998 register char ch;
999
1000 /*
1001 * Find end of this path.
1002 */
1003 while (*pszPath == ' ') pszPath++; //skip leading spaces.
1004 if (*pszPath == '"')
1005 {
1006 chEnd = '"';
1007 pszPath++;
1008 }
1009 else
1010 chEnd = ';';
1011 pszNext = pszPath;
1012 while ((ch = *pszNext) != chEnd && ch != '\0')
1013 pszNext++;
1014
1015 cchPath = pszNext - pszPath;
1016 if (chEnd == '"')
1017 {
1018 /* Skip anything between the " and the ; or string end. */
1019 while ((ch = *pszNext) != ';' && ch != '\0')
1020 pszNext++;
1021 }
1022 else
1023 {
1024 /* Trim the string. */
1025 while (cchPath > 0 && pszPath[cchPath-1] == ' ') //??
1026 cchPath--;
1027 }
1028
1029 /*
1030 * No length? No Path! Or path'\'filename too long? => Next
1031 */
1032 if (cchPath > 0 && cchPath + cchFile + 1 < CCHMAXPATH)
1033 {
1034 static char achFilename[CCHMAXPATH];
1035 /*
1036 * Build filename
1037 */
1038 memcpy(achFilename, pszPath, cchPath);
1039 if ((ch = achFilename[cchPath - 1]) == '\\' || ch == '/')
1040 cchPath--;
1041 else
1042 achFilename[cchPath] = '\\';
1043 memcpy(&achFilename[cchPath + 1], pszFile, cchFile); /* cchFile = length + 1; hence we copy the terminator too. */
1044
1045 /*
1046 * Try open the file.
1047 */
1048 rc = myldrOpen(phFile, achFilename, pfl);
1049 switch (rc)
1050 {
1051 case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: case ERROR_ACCESS_DENIED: case ERROR_INVALID_ACCESS:
1052 case ERROR_INVALID_DRIVE: case ERROR_NOT_DOS_DISK: case ERROR_REM_NOT_LIST: case ERROR_BAD_NETPATH:
1053 case ERROR_NETWORK_BUSY: case ERROR_DEV_NOT_EXIST: case ERROR_TOO_MANY_CMDS: case ERROR_ADAP_HDW_ERR:
1054 case ERROR_UNEXP_NET_ERR: case ERROR_BAD_REM_ADAP: case ERROR_NETNAME_DELETED: case ERROR_BAD_DEV_TYPE:
1055 case ERROR_NETWORK_ACCESS_DENIED: case ERROR_BAD_NET_NAME: case ERROR_TOO_MANY_SESS: case ERROR_REQ_NOT_ACCEP:
1056 case ERROR_INVALID_PASSWORD: case ERROR_OPEN_FAILED: case ERROR_INVALID_NAME: case ERROR_FILENAME_EXCED_RANGE:
1057 case ERROR_VC_DISCONNECTED:
1058 break;
1059
1060 case NO_ERROR:
1061 strcpy(pszFilename, achFilename);
1062 default:
1063 tcbSetTCBFailErr(tcbGetCur(), TCBFailErr_save);
1064 return rc;
1065 }
1066 }
1067 #ifdef DEBUG
1068 else if (cchPath > 0) kprintf(("OpenPATH: Path component is too long\n"));
1069 #endif
1070
1071 /*
1072 * Next
1073 */
1074 if (*pszNext == '\0')
1075 break;
1076 pszPath = pszNext + 1;
1077 }
1078
1079
1080 /*
1081 * File is not found.
1082 */
1083 *phFile = 0;
1084 tcbSetTCBFailErr(tcbGetCur(), TCBFailErr_save);
1085 return ERROR_FILE_NOT_FOUND;
1086}
Note: See TracBrowser for help on using the repository browser.