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

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

Merged in the Grace branch. New Win32k!

File size: 41.8 KB
Line 
1/* $Id: myldrOpen.cpp,v 1.11 2000-09-02 21:08:09 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 */
296 kprintf(("myldrOpen-%d: pe.exe - %s\n", cNesting, u1.pach));
297 if (isLdrStateExecPgm() && fTkExecPgm)
298 {
299 rc = AddArgsToFront(2, ldrpFileNameBuf, achTkExecPgmFilename);
300 if (rc == NO_ERROR)
301 {
302 rc = SetExecName(ldrpFileNameBuf);
303 if (rc != NO_ERROR)
304 kprintf(("myldrOpen-%d: pe.exe - failed to set pe.exe as execname. rc=%d\n", cNesting));
305 }
306 else
307 kprintf(("myldrOpen-%d: pe.exe - failed to add programname as argument. rc=%d\n", cNesting, rc));
308 goto cleanup_noerror;
309 }
310 }
311 else
312 kprintf(("myldrOpen-%d: pe.exe - couldn't find/open pe.exe\n", cNesting));
313 }
314 goto cleanup;
315 }
316 /** @sketch End of PE Loading. */
317
318
319 /** @sketch
320 * ELF image?
321 */
322 if (*u1.pul == ELFMAGICLSB)
323 {
324 if (isELFDisabled())
325 goto cleanup_noerror;
326
327 /*
328 * ELF signature found.
329 */
330 kprintf(("myldrOpen-%d: ELF image! - not implemented yet!\n", cNesting));
331
332 /*
333 * Do nothing more yet. NEED AN ELF LOADER!!!
334 */
335 goto cleanup;
336 }
337
338
339 /** @sketch
340 * Java image?
341 */
342 if (*u1.pul == 0xBEBAFECAUL) //CAh FEh BAh BEh
343 {
344 char *pszName = NULL;
345 int cchName;
346
347 if (isJAVADisabled())
348 goto cleanup_noerror;
349
350 /** @sketch
351 * Java signature found.
352 * Copy the name to a temporary buffer. (only if necessary)
353 * Remove the extention (.class) and insert a space between the name and the path.
354 * (This is the needed processing of the class filename to make it a classpath
355 * entry (path) and a class name (filename).)
356 * Try find the java executor in current dir or PATH: java.exe
357 */
358 kprintf(("myldrOpen-%d: Jave image!\n", cNesting));
359
360 if (isLdrStateExecPgm() && fTkExecPgm)
361 {
362 /* Ooops we had to get the file name from the MFT. ldrpFileNameBuf is allways uppercased... */
363 /* MFT seems to hold uppercased filenames! ARG! But (by pure luck?) achTkExecPgmArguments is
364 * not uppercased (yet). Nothing could be simpler!
365 */
366 #if 1
367 psz3 = achTkExecPgmArguments;
368 #elif 0
369 psz3 = SecPathFromSFN(*phFile);
370 if (psz3 == NULL)
371 psz3 = ldrpFileNameBuf;
372 #else
373 psz3 = ldrpFileNameBuf;
374 #endif
375 cchName = strlen(psz3);
376 pszName = (char*)rmalloc(cchName + 2);
377 if (pszName == NULL)
378 {
379 rc = ERROR_NOT_ENOUGH_MEMORY;
380 goto cleanup;
381 }
382 memcpy(pszName, psz3, cchName+1);
383
384 psz = pszName + strlen(pszName) - 1;
385 while (psz > pszName && *psz != '.' && *psz != '\\' && *psz != '/')
386 psz--;
387 if (*psz == '.')
388 {
389 cchName = psz - pszName;
390 *psz-- = '\0';
391 while (psz > pszName && *psz != '\\' && *psz != '/')
392 psz--;
393
394 /* check for root and evt. make room for an extra slash. */
395 if (psz - pszName == 2)
396 {
397 memmove(psz + 1, psz, cchName - 1);
398 *psz++ = '\\';
399 }
400 }
401 /* check if no path */
402 if (psz == pszName)
403 memmove(pszName + 1, pszName, cchName + 1);
404 *psz = ' ';
405 }
406
407 ldrClose(*phFile);
408 rc = ldrOpen(phFile, ".\\JAVA.EXE", pfl);
409 if (rc != NO_ERROR)
410 rc = OpenPATH(phFile, "JAVA.EXE", pfl);
411 if (rc == NO_ERROR)
412 {
413 kprintf(("myldrOpen-%d: java - %s\n", cNesting, ldrpFileNameBuf));
414
415 /** @sketch
416 * To be able to execute any given class name we'll have to pass in the
417 * directory as -classpath. But -classpath seems to override the default
418 * and environmental CLASSPATHs. So, we'll have to pass in the value of
419 * the CLASSPATH env.var. or generate the default class path (what ever that is).
420 *
421 */
422 if (isLdrStateExecPgm() && fTkExecPgm)
423 {
424 psz = u1.pach;
425
426 /*
427 * Get classpath and add it as a parameter
428 */
429 strcpy(u1.pach, "-classpath ");
430 psz = u1.pach + strlen(u1.pach);
431
432 psz3 = (char*)ScanEnv(GetEnv(TRUE), "CLASSPATH");
433 if (psz3 != NULL)
434 { /* environment variable set */
435 if (strlen(psz3) > 640 - 11 - 1 - cchName) //check for overflow
436 { // TODO? should reallocate...
437 memcpy(psz, psz3, 640 - 11 - 1 - cchName);
438 psz[640 - 11 - 1 - cchName] = '\0';
439 }
440 else
441 strcpy(psz, psz3);
442 psz += strlen(psz);
443 }
444 else
445 {
446 /* Make default classpath by taking the java.exe path + '..\lib\classes.zip' */
447 strcpy(psz, ldrpFileNameBuf);
448 psz3 = psz + strlen(psz) - 1;
449 while (psz3 > psz && *psz3 != '\\' && *psz3 != '/')
450 psz3--;
451 strcpy(++psz3, "..\\lib\\classes.zip");
452 psz = psz3 + strlen(psz3);
453 }
454
455 /*
456 * Add the class directory (as the last classpath entry) and the class name.
457 * (Note. I may happen that there is no directory, but that don't matter
458 * a space is allways preceding the class name.)
459 */
460 *psz++ = ';';
461 strcpy(psz, pszName);
462 if (pszName != NULL)
463 rfree(pszName);
464
465 /*
466 * Setup JAVA.EXE as executable with the parameters we've build.
467 */
468 rc = AddArgsToFront(2, ldrpFileNameBuf, u1.pach);
469 kprintf(("myldrOpen-%d: java - Exe: %s Args: %s\n", cNesting, ldrpFileNameBuf, u1.pach));
470 if (rc == NO_ERROR)
471 {
472 rc = SetExecName(ldrpFileNameBuf);
473 if (rc != NO_ERROR)
474 kprintf(("myldrOpen-%d: java - failed to set java.exe as execname. rc=%d\n", cNesting, rc));
475 }
476 else
477 kprintf(("myldrOpen-%d: java - failed to setup the parameters. rc=%d\n", cNesting, rc));
478
479 goto cleanup_noerror;
480 }
481 }
482 else
483 kprintf(("myldrOpen-%d: java - couldn't find/open java.exe\n", cNesting));
484
485
486 /** @sketch
487 * End of Java loading. (return)
488 */
489 if (pszName != NULL)
490 rfree(pszName);
491 goto cleanup;
492 }
493
494 }
495 else
496 {
497 /** @sketch
498 * ELSE - the reading size of a DOS header failed or file is smaller than the dos header.
499 * IF read failed or filesize is less than 4 bytes THEN
500 * return no_error to the caller.
501 * ENDIF
502 */
503 #ifdef DEBUG
504 if (rc != NO_ERROR)
505 {
506 kprintf(("myldrOpen-%d: ldrRead failed cbRead=%d, cbFile=%d, rc=%d\n", cNesting, cbRead, cbFile, rc));
507 goto cleanup_noerror;
508 }
509 if (cbRead < 4)
510 {
511 kprintf(("myldrOpen-%d: File too small! cbFile=%d\n", cNesting, cbFile));
512 goto cleanup_noerror;
513 }
514 #else
515 if (rc != NO_ERROR || cbRead < 4) //just forget files less than 4 bytes!
516 goto cleanup_noerror;
517 #endif
518 }
519 /** @sketch ENDIF (dos header read) */
520
521
522
523 /*
524 * Only unreconized files passes this point!
525 *
526 * * Fileformats with lower priority should reside here. *
527 *
528 */
529
530 /** @sketch
531 * UNIX styled script?
532 * - Starts with a hash (#)
533 * - And we're loading an EXE
534 * - And we're either in QAppType or ExecPgm state.
535 * - And that a bang (!) is the first char after the hash (ignoring blanks).
536 */
537 if (*u1.pach == '#'
538 && isLdrStateLoadingEXE()
539 && (isLdrStateQAppType() || isLdrStateExecPgm())
540 )
541 {
542 if (isUNIXScriptDisabled())
543 goto cleanup_noerror;
544 /*
545 * Look for a bang (!). Tabs and spaces are skipped, anything else result in error.
546 */
547 psz = u1.pach + 1;
548 while ((*psz == ' ' || *psz == '\t') && psz - u1.pach < cbRead)
549 psz++;
550 if (*psz == '!')
551 {
552 /** @sketch Found UNIX styled script! */
553
554 /** @sketch
555 * Read more of the script if necessary. (max is 256 chars (- Linux max is 127))
556 * Terminate the string read from the file to make sure with stop somewhere!
557 */
558 if (cbRead < cbFile /*&& cbRead != 256*/)
559 {
560 cbRead = min(256, cbFile);
561 rc = ldrRead(*phFile, 0UL, u1.pach, 0UL, cbRead, NULL);
562 }
563 u1.pach[cbRead] = '\0';
564
565 if (rc == NO_ERROR)
566 {
567 /** @sketch
568 * Parse out filename and optional arguments (if any).
569 * The result of the parsing is that:
570 * psz will point at the executable name.
571 * psz2 will point at the arguments.
572 * Both strings are trimmed.
573 */
574 psz++; /* psz points to the bang, skip it. */
575 while (*psz == ' ' || *psz == '\t') /* skip blanks after bang */
576 psz++;
577 if (*psz == '\r' || *psz == '\n' || *psz == '\0') /* End-of-line? */
578 {
579 kprintf(("myldrOpen-%d: script no executable name.\n", cNesting));
580 goto cleanup_noerror; /* other error code? */
581 }
582 psz2 = psz + 1; /* Not end-of-line, so add 1 before searching for args. */
583 while (*psz2 != '\0' && *psz2 != '\n' && *psz2 != '\r' /* skip executable name. */
584 && *psz2 != ' ' && *psz2 != '\t')
585 psz2++;
586 while (*psz2 == ' ' || *psz2 == '\t') /* skip blanks after executable - pad them with '\0'! */
587 *psz2++ = '\0';
588
589 psz3 = psz2;
590 while (*psz3 != '\n' && *psz3 != '\r' && *psz3 != '\0') /* find end of parameters and terminate the string. */
591 psz3++;
592 *psz3 = '\0';
593 while (psz3 >= psz2 && (*psz3 == '\0' || *psz3 == ' ' || *psz3 == '\t')) /* trim args */
594 *psz3-- = '\0';
595
596
597 /** @sketch
598 * IF tkExecPgm THEN
599 * Correct parameters - ie. add exec name (as argv[0]),
600 * arguments (psz2) as argv[1+], old exec name, and finally
601 * the existing parameters (current argv[1+]).
602 * Set the executable name.
603 * ENDIF
604 * Open the new executable file recursively. (psz)
605 */
606 if (isLdrStateExecPgm())
607 {
608 if (*psz2)
609 rc = AddArgsToFront(3, psz, psz2, achTkExecPgmFilename);
610 else
611 rc = AddArgsToFront(2, psz, achTkExecPgmFilename);
612 if (rc != NO_ERROR)
613 {
614 kprintf(("myldrOpen-%d: AddArgsToFront failed with rc=%d\n", cNesting));
615 goto cleanup_noerror;
616 }
617 rc = SetExecName(psz);
618 if (rc != NO_ERROR)
619 kprintf(("myldrOpen-%d: SetExecName failed with rc=%d\n", cNesting));
620 }
621 ldrClose(*phFile);
622 rc = myldrOpen(phFile, psz, pfl);
623 if (rc != NO_ERROR)
624 {
625 psz2 = psz + strlen(psz);
626 if (psz + 4 >= psz2 || strcmp(psz2 - 4, ".EXE") != 0)
627 {
628 strcpy(psz2, ".EXE");
629 rc = myldrOpen(phFile, psz, pfl);
630 *psz2 = '\0';
631 }
632 else
633 psz2 = NULL;
634
635 //should we search the PATH??? For a starting, we'll do it.
636 if (rc != NO_ERROR
637 && (rc = OpenPATH(phFile, psz, pfl)) != NO_ERROR
638 && psz2 != NULL)
639 {
640 *psz2 = '.';
641 rc = OpenPATH(phFile, psz, pfl);
642 }
643 }
644 }
645 else
646 {
647 kprintf(("myldrOpen-%d: script - failed to read more of the script!, rc=%d cbRead=%d cbFile=%d.\n",
648 cNesting, rc, cbRead, cbFile));
649 }
650
651 goto cleanup;
652 }
653 else
654 {
655 kprintf(("myldrOpen-%d: script - hash found but no bang (!).\n", cNesting));
656 }
657 } /**@sketch ENDIF - UNIX styled script. */
658
659
660
661 /** @sketch
662 * REXX script?
663 * - Starts with a REXX start comment ('/','*')
664 * - And we're loading an EXE
665 * - And we're either in QAppType or ExecPgm state.
666 * - Extention:
667 * .RX and .REX are known to be pure REXX scripts.
668 * While .CMD has to invoked used the commandline OS2_SHELL or COMSPEC variable.
669 */
670 psz2 = pszFilename + strlen(pszFilename) - 1;
671 while (psz2 > pszFilename && *psz2 != '.')
672 psz2--;
673 if (*psz2 == '.'
674 && *u1.pach == '/' && u1.pach[1] == '*'
675 && isLdrStateLoadingEXE()
676 && (isLdrStateQAppType() || isLdrStateExecPgm())
677 && (stricmp(psz2, ".RX") == 0 || stricmp(psz2, ".REX") == 0)
678 )
679 {
680 if (isREXXScriptDisabled())
681 goto cleanup_noerror;
682
683 /** @sketch
684 * Found REXX styled script!
685 * Find the REXX interpreter. We'll use kRx.exe to execute the REXX scripts.
686 * (This interpreter could be embedded as a child of ModuleBase as it turned out
687 * to be quite small about 700 bytes.)
688 */
689 kprintf(("myldrOpen-%d: Found REXX script\n", cNesting));
690 ldrClose(*phFile);
691 psz = "KRX.EXE";
692 rc = ldrOpen(phFile, psz, pfl);
693 if (rc != NO_ERROR)
694 rc = OpenPATH(phFile, psz, pfl);
695
696 /** @sketch
697 * IF tkExecPgm THEN
698 * Correct parameters - ie. add exec name (as argv[0]), old exec name,
699 * and finally the existing parameters (current argv[1+]).
700 * Set the executable name.
701 * ENDIF
702 */
703 if (rc == NO_ERROR && isLdrStateExecPgm())
704 {
705 rc = AddArgsToFront(2, ldrpFileNameBuf, achTkExecPgmFilename);
706 if (rc != NO_ERROR)
707 {
708 kprintf(("myldrOpen-%d: AddArgsToFront failed with rc=%d\n", cNesting));
709 goto cleanup_noerror;
710 }
711 rc = SetExecName(ldrpFileNameBuf);
712 if (rc != NO_ERROR)
713 kprintf(("myldrOpen-%d: SetExecName failed with rc=%d\n", cNesting));
714
715 goto cleanup_noerror;
716 }
717 goto cleanup;
718 } /**@sketch ENDIF - REXX styled script. */
719
720
721 /*
722 * Cleanup with rc set to NO_ERROR.
723 */
724 cleanup_noerror:
725 rc = NO_ERROR;
726
727 /*
728 * Cleanup without having rc set to NO_ERROR.
729 * Decrement the nesting count.
730 */
731 cleanup:
732 rfree(u1.pach);
733 cNesting--;
734 }
735 #ifdef DEBUG
736 else if (cNesting >= 3)
737 kprintf(("myldrOpen-%d: cNesting = %d, which is too deep!\n", cNesting, cNesting));
738 #endif
739
740ret:
741 /** @sketch
742 * Return rc.
743 */
744 return rc;
745}
746
747
748/**
749 * Adds new arguments to the front of the startup arguments for the program about to be
750 * executed.
751 *
752 * @returns OS/2 return code.
753 * @param cArgs Count of arguments to add. At least 1!!!
754 * @param ... Pointers to the arguments to add.
755 * The first argument have to be the executable name. This have to the
756 * the only argument in the first string.
757 * The other arguements are space separated, so you could add a bunch
758 * of arguments in a single string!
759 * The last argument should be the old first parameter if this is to be
760 * preserved. The old first parameter is overwritten since it's
761 * normally the executable name.
762 *
763 * @status completly implemented.
764 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
765 * @remark Implementation note:
766 * The arguments convention is as follows:
767 * First argument, which should be the executable name, is terminated with a '\0'.
768 * It starts at offset 0 into the argument buffer, of course.
769 * All other arguemnts are separated by a space and follows the immediately after the
770 * first argument.
771 * The arguments are terminated by a double nulltermination: '\0\0'.
772 */
773APIRET AddArgsToFront(int cArgs, ...)
774{
775 va_list vaarg; /* Variable length argument list. */
776 int cchOldArgs; /* Length of the old arguments (including the first argument). */
777 /* cchOldArgs = 1 means no arguments. It don't include the very last '\0' */
778 /* (remember argumets are terminated with two '\0's). */
779 int iSecondArg; /* Index of the second argument. (Used to skip the first argument.) */
780 /* Used first on the original arguments and them when adding the first */
781 /* new argument. */
782 int cchNewArgs; /* Length of the new arguments to be inserted. */
783 int i; /* Loop variable. Current function argument. */
784 char * psz; /* General string pointer. */
785
786
787 /** @sketch
788 * Assert that we're in the right state.
789 * Calc the length of the existing parameters.
790 * Calc the length of the new arguments to determin.
791 * Assert that the new arguments have length > 0.
792 */
793 #ifdef DEBUG
794 if (!isLdrStateExecPgm())
795 {
796 kprintf(("AddArgsToFront: not in tkExecPgm state.\n"));
797 return ERROR_INVALID_PARAMETER;
798 }
799 #endif
800 if (!fTkExecPgm)
801 {
802 kprintf(("AddArgsToFront: called when not in tkExecPgm data is invalid!\n"));
803 return ERROR_INVALID_PARAMETER;
804 }
805
806 iSecondArg = strlen(&achTkExecPgmArguments[0]) + 1;
807 psz = &achTkExecPgmArguments[iSecondArg];
808 while (*psz != '\0')
809 psz += strlen(psz) + 1;
810 cchOldArgs = psz - &achTkExecPgmArguments[iSecondArg];
811
812 va_start(vaarg, cArgs);
813 for (cchNewArgs = i = 0; i < cArgs; i++)
814 cchNewArgs += strlen(va_arg(vaarg, char *)) + 1; /* 1 is for space or '\0'. */
815 va_end(vaarg);
816 #ifdef DEBUG
817 if (cchNewArgs == 0)
818 {
819 kprintf(("AddArgsToFront: the size of the arguments to add is zero!\n"));
820 return ERROR_INVALID_PARAMETER;
821 }
822 #endif
823
824
825 /** @sketch
826 * Check if we have enough room for the new arguments. Fail if not enough.
827 * Move the existing arguments to make room for the new ones.
828 * !IMPORTANT! The first existing arguments (executable name) is skipped !IMPORTANT!
829 * !IMPORTANT! in this move as this have to be re-added in this call! !IMPORTANT!
830 */
831 if (cchOldArgs + cchNewArgs + 1 > CCHARGUMENTS)
832 {
833 kprintf(("AddArgsToFront: argument buffer is too small to hold the arguments to add, cchOldArgs=%d, cchNewArgs=%d\n",
834 cchOldArgs, cchNewArgs));
835 return ERROR_BAD_ARGUMENTS;
836 }
837
838 if (cchOldArgs > 0)
839 {
840 memmove(&achTkExecPgmArguments[cchNewArgs], &achTkExecPgmArguments[iSecondArg],
841 cchOldArgs + 1);
842 }
843 else
844 achTkExecPgmArguments[cchNewArgs] = '\0';
845
846
847 /** @sketch
848 * Copy new arguments.
849 * Since the first argument is special case we'll do it separately. (Uses '\0' as separator.)
850 * We assume that the entire first argument passed into this function should be the first argument!
851 * (This don't have to be true for the other arguments since these are space separated. You could
852 * pass in more than argument in a single string.)
853 * Loop thru the rest of the new arguments and add them with space as separator.
854 */
855 va_start(vaarg, cArgs);
856 psz = va_arg(vaarg, char *);
857 memcpy(&achTkExecPgmArguments[0], psz, (i = strlen(psz) + 1));
858 psz = &achTkExecPgmArguments[i];
859
860 for (i = 1; i < cArgs; i++)
861 {
862 if (i > 1) *psz++ = ' '; //Add space if not second argument.
863 strcpy(psz, va_arg(vaarg, char *));
864 psz += strlen(psz);
865 }
866 va_end(vaarg);
867 if (cchOldArgs > 0) *psz++ = ' '; //Add space if old arguments
868
869 #ifdef DEBUG /* assertion */
870 if (psz != &achTkExecPgmArguments[cchNewArgs])
871 {
872 kprintf(("AddArgsToFront: !Assertion failed! psz didn't end up where it should! (psz -> %d should be %d)\n",
873 psz - &achTkExecPgmArguments[0], cchNewArgs));
874 if (cchOldArgs <= 1)
875 psz[0] = psz[1] = '\0';
876 }
877 #endif
878
879 return NO_ERROR;
880}
881
882
883/**
884 * Sets the executable name of the module.
885 * This function is normally invoked after a different executable than the one requested was
886 * opened. It does _NOT_ set the new executable name as the first argument, since it is more
887 * convenient to this while calling AddArgsToFront to add other arguments.
888 *
889 * @returns OS/2 return code.
890 * @param pszExecName Pointer to new executable name.
891 * @status completly implemented.
892 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
893 * @remark .
894 */
895APIRET SetExecName(const char *pszExecName)
896{
897 #ifdef DEBUG
898 int cch;
899 cch = strlen(pszExecName);
900 if (cch > CCHMAXPATH)
901 {
902 kprintf(("ChangeExecName: filename is too long! cch=%d. name=%s\n", cch, pszExecName));
903 return ERROR_FILENAME_EXCED_RANGE;
904 }
905 if (!isLdrStateExecPgm())
906 {
907 kprintf(("ChangeExecName: called when not in tkExecPgm state!!! FATAL ERROR!\n"));
908 return ERROR_INVALID_PARAMETER;
909 }
910 #endif
911 if (!fTkExecPgm)
912 {
913 kprintf(("ChangeExecName: called when not in tkExecPgm data is invalid!!! FATAL ERROR!\n"));
914 return ERROR_INVALID_PARAMETER;
915 }
916
917 strcpy(achTkExecPgmFilename, pszExecName);
918
919 return 0;
920}
921
922
923/**
924 * Opens a file using the PATH environment variable of the current process.
925 * @returns OS2 return code.
926 * @param phFile Pointer to filehandle. The filehandle is set to the SFN for the opened
927 * file on successful return.
928 * The filehandle is 0 on failure.
929 * @param pszFilename Pointer to filename buffer. This will hold the filename on input.
930 * On successful return it holds the filepath found.
931 * On failiure it's undefined.
932 * @param pfl Some flags set by ldrOpen.
933 * @sketch stub
934 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
935 * @remark
936 */
937APIRET OpenPATH(PSFN phFile, char *pszFilename, PULONG pfl)
938{
939 APIRET rc;
940 USHORT TCBFailErr_save;
941 int cchFile; /* Filename length + 1. */
942 const char *pszFile; /* Pointer to filename portion. */
943 const char *pszPath = ScanEnv(GetEnv(FALSE), "PATH"); /* Current Process environment? */
944
945 /**@sketch
946 * No PATH environment.
947 */
948 if (pszPath == NULL)
949 return ERROR_FILE_NOT_FOUND;
950
951 /**@sketch
952 * Skip any paths in the filename.
953 */
954 pszFile = pszFilename + (cchFile = strlen(pszFilename));
955 while (pszFile >= pszFilename && *pszFile != '\\' && *pszFile != '/')
956 pszFile--;
957 cchFile -= pszFile - pszFilename;
958 pszFile++;
959
960 /**@sketch
961 * We'll have to save the TCBFailErr since we don't want to cause
962 * Hard Errors while searching invalid paths, etc. (ldrOpenPath does this!)
963 */
964 TCBFailErr_save = tcbGetTCBFailErr(tcbGetCur());
965
966 /**@ sketch
967 * Loop thru the PATH trying to open the specified file in each
968 * directory.
969 */
970 while (*pszPath != '\0')
971 {
972 const char * pszNext;
973 int cchPath;
974 char chEnd;
975 register char ch;
976
977 /*
978 * Find end of this path.
979 */
980 while (*pszPath == ' ') pszPath++; //skip leading spaces.
981 if (*pszPath == '"')
982 {
983 chEnd = '"';
984 pszPath++;
985 }
986 else
987 chEnd = ';';
988 pszNext = pszPath;
989 while ((ch = *pszNext) != chEnd && ch != '\0')
990 pszNext++;
991
992 cchPath = pszNext - pszPath;
993 if (chEnd == '"')
994 {
995 /* Skip anything between the " and the ; or string end. */
996 while ((ch = *pszNext) != ';' && ch != '\0')
997 pszNext++;
998 }
999 else
1000 {
1001 /* Trim the string. */
1002 while (cchPath > 0 && pszPath[cchPath-1] == ' ') //??
1003 cchPath--;
1004 }
1005
1006 /*
1007 * No length? No Path! Or path'\'filename too long? => Next
1008 */
1009 if (cchPath > 0 && cchPath + cchFile + 1 < CCHMAXPATH)
1010 {
1011 static char achFilename[CCHMAXPATH];
1012 /*
1013 * Build filename
1014 */
1015 memcpy(achFilename, pszPath, cchPath);
1016 if ((ch = achFilename[cchPath - 1]) == '\\' || ch == '/')
1017 cchPath--;
1018 else
1019 achFilename[cchPath] = '\\';
1020 memcpy(&achFilename[cchPath + 1], pszFile, cchFile); /* cchFile = length + 1; hence we copy the terminator too. */
1021
1022 /*
1023 * Try open the file.
1024 */
1025 rc = myldrOpen(phFile, achFilename, pfl);
1026 switch (rc)
1027 {
1028 case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: case ERROR_ACCESS_DENIED: case ERROR_INVALID_ACCESS:
1029 case ERROR_INVALID_DRIVE: case ERROR_NOT_DOS_DISK: case ERROR_REM_NOT_LIST: case ERROR_BAD_NETPATH:
1030 case ERROR_NETWORK_BUSY: case ERROR_DEV_NOT_EXIST: case ERROR_TOO_MANY_CMDS: case ERROR_ADAP_HDW_ERR:
1031 case ERROR_UNEXP_NET_ERR: case ERROR_BAD_REM_ADAP: case ERROR_NETNAME_DELETED: case ERROR_BAD_DEV_TYPE:
1032 case ERROR_NETWORK_ACCESS_DENIED: case ERROR_BAD_NET_NAME: case ERROR_TOO_MANY_SESS: case ERROR_REQ_NOT_ACCEP:
1033 case ERROR_INVALID_PASSWORD: case ERROR_OPEN_FAILED: case ERROR_INVALID_NAME: case ERROR_FILENAME_EXCED_RANGE:
1034 case ERROR_VC_DISCONNECTED:
1035 break;
1036
1037 case NO_ERROR:
1038 strcpy(pszFilename, achFilename);
1039 default:
1040 tcbSetTCBFailErr(tcbGetCur(), TCBFailErr_save);
1041 return rc;
1042 }
1043 }
1044 #ifdef DEBUG
1045 else if (cchPath > 0) kprintf(("OpenPATH: Path component is too long\n"));
1046 #endif
1047
1048 /*
1049 * Next
1050 */
1051 if (*pszNext == '\0')
1052 break;
1053 pszPath = pszNext + 1;
1054 }
1055
1056
1057 /*
1058 * File is not found.
1059 */
1060 *phFile = 0;
1061 tcbSetTCBFailErr(tcbGetCur(), TCBFailErr_save);
1062 return ERROR_FILE_NOT_FOUND;
1063}
Note: See TracBrowser for help on using the repository browser.