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

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

When invoking PE.EXE the executable name should be in quotes in case the
should be spaces in the filename or path.

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