source: trunk/src/helpers/dosh2.c@ 127

Last change on this file since 127 was 127, checked in by umoeller, 24 years ago

Tons of updates for turbo folders and replacement icons.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 154.8 KB
Line 
1
2/*
3 *@@sourcefile dosh2.c:
4 * dosh.c contains more Control Program helper functions.
5 *
6 * This file is new with V0.9.4 (2000-07-26) [umoeller].
7 *
8 * As opposed to the functions in dosh.c, these require
9 * linking against other helpers. As a result, these have
10 * been separated from dosh.c to allow linking against
11 * dosh.obj only.
12 *
13 * Function prefixes:
14 * -- dosh* Dos (Control Program) helper functions
15 *
16 * This has the same header as dosh.c, dosh.h.
17 *
18 * The partition functions in this file are based on
19 * code which has kindly been provided by Dmitry A. Steklenev.
20 * See doshGetPartitionsList for how to use these.
21 *
22 * Note: Version numbering in this file relates to XWorkplace version
23 * numbering.
24 *
25 *@@header "helpers\dosh.h"
26 *@@added V0.9.4 (2000-07-27) [umoeller]
27 */
28
29/*
30 * This file Copyright (C) 1997-2000 Ulrich M”ller,
31 * Dmitry A. Steklenev.
32 * This file is part of the "XWorkplace helpers" source package.
33 * This is free software; you can redistribute it and/or modify
34 * it under the terms of the GNU General Public License as published
35 * by the Free Software Foundation, in version 2 as it comes in the
36 * "COPYING" file of the XWorkplace main distribution.
37 * This program is distributed in the hope that it will be useful,
38 * but WITHOUT ANY WARRANTY; without even the implied warranty of
39 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40 * GNU General Public License for more details.
41 */
42
43#define OS2EMX_PLAIN_CHAR
44 // this is needed for "os2emx.h"; if this is defined,
45 // emx will define PSZ as _signed_ char, otherwise
46 // as unsigned char
47
48#define INCL_DOSMODULEMGR
49#define INCL_DOSPROCESS
50#define INCL_DOSSESMGR
51#define INCL_DOSQUEUES
52#define INCL_DOSMISC
53#define INCL_DOSDEVICES
54#define INCL_DOSDEVIOCTL
55#define INCL_DOSERRORS
56#include <os2.h>
57
58#include <stdlib.h>
59#include <string.h>
60#include <stdio.h>
61#include <ctype.h>
62
63#include "setup.h" // code generation and debugging options
64
65#include "helpers\dosh.h"
66#include "helpers\ensure.h"
67#include "helpers\nls.h"
68#include "helpers\standards.h"
69#include "helpers\stringh.h"
70
71#pragma hdrstop
72
73/*
74 *@@category: Helpers\Control program helpers\Miscellaneous
75 */
76
77/* ******************************************************************
78 *
79 * Miscellaneous
80 *
81 ********************************************************************/
82
83/*
84 *@@ doshIsValidFileName:
85 * this returns NO_ERROR only if pszFile is a valid file name.
86 * This may include a full path.
87 *
88 * If a drive letter is specified, this checks for whether
89 * that drive is a FAT drive and adjust the checks accordingly,
90 * i.e. 8+3 syntax (per path component).
91 *
92 * If no drive letter is specified, this check is performed
93 * for the current drive.
94 *
95 * This also checks if pszFileNames contains characters which
96 * are invalid for the current drive.
97 *
98 * Note: this performs syntactic checks only. This does not
99 * check for whether the specified path components exist.
100 * However, it _is_ checked for whether the given drive
101 * exists.
102 *
103 * This func is especially useful to check filenames that
104 * have been entered by the user in a "Save as" dialog.
105 *
106 * If an error is found, the corresponding DOS error code
107 * is returned:
108 * -- ERROR_INVALID_DRIVE
109 * -- ERROR_FILENAME_EXCED_RANGE (on FAT: no 8+3 filename)
110 * -- ERROR_INVALID_NAME (invalid character)
111 * -- ERROR_CURRENT_DIRECTORY (if fFullyQualified: no full path specified)
112 *
113 *@@changed V0.9.2 (2000-03-11) [umoeller]: added fFullyQualified
114 */
115
116APIRET doshIsValidFileName(const char* pcszFile,
117 BOOL fFullyQualified) // in: if TRUE, pcszFile must be fully q'fied
118{
119 APIRET arc = NO_ERROR;
120 CHAR szPath[CCHMAXPATH+4] = " :";
121 CHAR szComponent[CCHMAXPATH];
122 PSZ p1, p2;
123 BOOL fIsFAT = FALSE;
124 PSZ pszInvalid;
125
126 if (fFullyQualified) // V0.9.2 (2000-03-11) [umoeller]
127 {
128 if ( (*(pcszFile + 1) != ':')
129 || (*(pcszFile + 2) != '\\')
130 )
131 arc = ERROR_CURRENT_DIRECTORY;
132 }
133
134 // check drive first
135 if (*(pcszFile + 1) == ':')
136 {
137 CHAR cDrive = toupper(*pcszFile);
138 double d;
139 // drive specified:
140 strcpy(szPath, pcszFile);
141 szPath[0] = toupper(*pcszFile);
142 arc = doshQueryDiskFree(cDrive - 'A' + 1, &d);
143 }
144 else
145 {
146 // no drive specified: take current
147 ULONG ulDriveNum = 0,
148 ulDriveMap = 0;
149 arc = DosQueryCurrentDisk(&ulDriveNum, &ulDriveMap);
150 szPath[0] = ((UCHAR)ulDriveNum) + 'A' - 1;
151 szPath[1] = ':';
152 strcpy(&szPath[2], pcszFile);
153 }
154
155 if (arc == NO_ERROR)
156 {
157 fIsFAT = doshIsFileOnFAT(szPath);
158
159 pszInvalid = (fIsFAT)
160 ? "<>|+=:;,\"/[] " // invalid characters in FAT
161 : "<>|:\"/"; // invalid characters in IFS's
162
163 // now separate path components
164 p1 = &szPath[2]; // advance past ':'
165
166 do {
167
168 if (*p1 == '\\')
169 p1++;
170
171 p2 = strchr(p1, '\\');
172 if (p2 == NULL)
173 p2 = p1 + strlen(p1);
174
175 if (p1 != p2)
176 {
177 LONG lDotOfs = -1,
178 lAfterDot = -1;
179 ULONG cbFile,
180 ul;
181 PSZ pSource = szComponent;
182
183 strncpy(szComponent, p1, p2-p1);
184 szComponent[p2-p1] = 0;
185 cbFile = strlen(szComponent);
186
187 // now check each path component
188 for (ul = 0; ul < cbFile; ul++)
189 {
190 if (fIsFAT)
191 {
192 // on FAT: only 8 characters allowed before dot
193 if (*pSource == '.')
194 {
195 lDotOfs = ul;
196 lAfterDot = 0;
197 if (ul > 7)
198 return (ERROR_FILENAME_EXCED_RANGE);
199 }
200 }
201 // and check for invalid characters
202 if (strchr(pszInvalid, *pSource) != NULL)
203 return (ERROR_INVALID_NAME);
204
205 pSource++;
206
207 // on FAT, allow only three chars after dot
208 if (fIsFAT)
209 if (lAfterDot != -1)
210 {
211 lAfterDot++;
212 if (lAfterDot > 3)
213 return (ERROR_FILENAME_EXCED_RANGE);
214 }
215 }
216
217 // we are still missing the case of a FAT file
218 // name without extension; if so, check whether
219 // the file stem is <= 8 chars
220 if (fIsFAT)
221 if (lDotOfs == -1) // dot not found:
222 if (cbFile > 8)
223 return (ERROR_FILENAME_EXCED_RANGE);
224 }
225
226 // go for next component
227 p1 = p2+1;
228 } while (*p2);
229 }
230
231 return (arc);
232}
233
234/*
235 *@@ doshMakeRealName:
236 * this copies pszSource to pszTarget, replacing
237 * all characters which are not supported by file
238 * systems with cReplace.
239 *
240 * pszTarget must be at least the same size as pszSource.
241 * If (fIsFAT), the file name will be made FAT-compliant (8+3).
242 *
243 * Returns TRUE if characters were replaced.
244 *
245 *@@changed V0.9.0 (99-11-06) [umoeller]: now replacing "*" too
246 */
247
248BOOL doshMakeRealName(PSZ pszTarget, // out: new real name
249 PSZ pszSource, // in: filename to translate
250 CHAR cReplace, // in: replacement char for invalid
251 // characters (e.g. '!')
252 BOOL fIsFAT) // in: make-FAT-compatible flag
253{
254 ULONG ul,
255 cbSource = strlen(pszSource);
256 LONG lDotOfs = -1,
257 lAfterDot = -1;
258 BOOL brc = FALSE;
259 PSZ pSource = pszSource,
260 pTarget = pszTarget;
261
262 const char *pcszInvalid = (fIsFAT)
263 ? "*<>|+=:;,\"/\\[] " // invalid characters in FAT
264 : "*<>|:\"/\\"; // invalid characters in IFS's
265
266 for (ul = 0; ul < cbSource; ul++)
267 {
268 if (fIsFAT)
269 {
270 // on FAT: truncate filename if neccessary
271 if (*pSource == '.')
272 {
273 lDotOfs = ul;
274 lAfterDot = 0;
275 if (ul > 7) {
276 // only 8 characters allowed before dot,
277 // so set target ptr to dot pos
278 pTarget = pszTarget+8;
279 }
280 }
281 }
282 // and replace invalid characters
283 if (strchr(pcszInvalid, *pSource) == NULL)
284 *pTarget = *pSource;
285 else
286 {
287 *pTarget = cReplace;
288 brc = TRUE;
289 }
290 pTarget++;
291 pSource++;
292
293 // on FAT, allow only three chars after dot
294 if (fIsFAT)
295 if (lAfterDot != -1)
296 {
297 lAfterDot++;
298 if (lAfterDot > 3)
299 break;
300 }
301 }
302 *pTarget = '\0';
303
304 if (fIsFAT)
305 {
306 // we are still missing the case of a FAT file
307 // name without extension; if so, check whether
308 // the file stem is <= 8 chars
309 if (lDotOfs == -1) // dot not found:
310 if (cbSource > 8)
311 *(pszTarget+8) = 0; // truncate
312
313 // convert to upper case
314 strupr(pszTarget);
315 }
316
317 return (brc);
318}
319
320/*
321 *@@ doshSetCurrentDir:
322 * sets the current working directory
323 * to the given path.
324 *
325 * As opposed to DosSetCurrentDir, this
326 * one will change the current drive
327 * also, if one is specified.
328 *
329 *@@changed V0.9.9 (2001-04-04) [umoeller]: this returned an error even if none occured, fixed
330 */
331
332APIRET doshSetCurrentDir(const char *pcszDir)
333{
334 APIRET arc = NO_ERROR;
335 if (!pcszDir)
336 return (ERROR_INVALID_PARAMETER);
337 {
338 if (*pcszDir != 0)
339 if (*(pcszDir+1) == ':')
340 {
341 // drive given:
342 CHAR cDrive = toupper(*(pcszDir));
343 // change drive
344 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
345 // 1 = A:, 2 = B:, ...
346 }
347
348 arc = DosSetCurrentDir((PSZ)pcszDir);
349 }
350
351 return (arc); // V0.9.9 (2001-04-04) [umoeller]
352}
353
354/*
355 *@@category: Helpers\Control program helpers\Executable info
356 * these functions can retrieve BLDLEVEL information,
357 * imported modules information, exported functions information,
358 * and resources information from any executable module. See
359 * doshExecOpen.
360 */
361
362/********************************************************************
363 *
364 * Executable functions
365 *
366 ********************************************************************/
367
368/*
369 *@@ doshExecOpen:
370 * this opens the specified executable file
371 * (which can be an .EXE, .COM, .DLL, or
372 * driver file) for use with the other
373 * doshExec* functions.
374 *
375 * If no error occurs, NO_ERROR is returned
376 * and a pointer to a new EXECUTABLE structure
377 * is stored in *ppExec. Consider this pointer a
378 * handle and pass it to doshExecClose to clean
379 * up.
380 *
381 * If NO_ERROR is returned, all the fields through
382 * ulOS are set in EXECUTABLE. The psz* fields
383 * which follow afterwards require an additional
384 * call to doshExecQueryBldLevel.
385 *
386 * NOTE: If NO_ERROR is returned, the executable
387 * file has been opened by this function. It will
388 * only be closed when you call doshExecClose.
389 *
390 * If errors occur, this function returns the
391 * following error codes:
392 *
393 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
394 *
395 * -- ERROR_INVALID_EXE_SIGNATURE (191): header is
396 * neither plain DOS, nor NE, nor LX, nor PE.
397 * The given file probably isn't even an
398 * executable. This you will get if you
399 * pass in COM, BAT, or CMD files.
400 *
401 * -- ERROR_BAD_EXE_FORMAT (193): header was
402 * recognized, but the header data was
403 * not understood.
404 *
405 * -- ERROR_INVALID_PARAMETER: ppExec is NULL.
406 *
407 * plus those of DosOpen, DosSetFilePtr, and
408 * DosRead.
409 *
410 * The following executable types are supported
411 * (see EXECUTABLE for details):
412 *
413 * -- Plain DOS 3.x executable without new header.
414 *
415 * -- New Executable (NE), used by Win16 and
416 * 16-bit OS/2 and still many of today's drivers.
417 *
418 * -- Linear Executable (LX), OS/2 2.x and above.
419 *
420 * -- Portable Executable (PE), used by Win32.
421 *
422 * V0.9.12 adds support for NOSTUB executables,
423 * which are new-style executables (NE or LX)
424 * without a leading DOS header. The executable
425 * then starts directly with the NE or LX header.
426 * I am not sure whether PE supports such things
427 * as well... if so, it should be supported too.
428 *
429 * @@todo:
430 *
431 * win95 \WINDOWS\extract.exe is NE with a non-standard format
432 * win16 \WINDOWS\EXPAND.EXE
433 * win16 \WINDOWS\MSD.EXE"
434 *
435 *@@added V0.9.0 [umoeller]
436 *@@changed V0.9.1 (2000-02-13) [umoeller]: fixed 32-bits flag
437 *@@changed V0.9.7 (2000-12-20) [lafaix]: fixed ulNewHeaderOfs
438 *@@changed V0.9.10 (2001-04-08) [lafaix]: added PE support
439 *@@changed V0.9.10 (2001-04-08) [umoeller]: now setting ppExec only if NO_ERROR is returned
440 *@@changed V0.9.12 (2001-05-03) [umoeller]: added support for NOSTUB newstyle executables
441 *@@changed V0.9.16 (2001-12-08) [umoeller]: now using OPEN_SHARE_DENYWRITE
442 *@@changed V0.9.16 (2001-12-08) [umoeller]: fLibrary was never set, works for LX and NE now
443 *@@changed V0.9.16 (2001-12-08) [umoeller]: some speed optimizations, changed some return codes
444 *@@changed V0.9.16 (2002-01-04) [umoeller]: added fixes for COM, BAT, CMD extensions
445 */
446
447APIRET doshExecOpen(const char* pcszExecutable,
448 PEXECUTABLE* ppExec)
449{
450 APIRET arc = NO_ERROR;
451
452 PEXECUTABLE pExec = NULL;
453
454 PXFILE pFile = NULL;
455 ULONG cbFile = 0;
456 PCSZ pExt;
457 BOOL fOpenFile = FALSE;
458
459 if (!ppExec)
460 return (ERROR_INVALID_PARAMETER);
461
462 if (!(pExec = (PEXECUTABLE)malloc(sizeof(EXECUTABLE))))
463 return (ERROR_NOT_ENOUGH_MEMORY);
464
465 memset(pExec, 0, sizeof(EXECUTABLE));
466
467 // check some of the default extensions
468 // V0.9.16 (2002-01-04) [umoeller]
469 if (pExt = doshGetExtension(pcszExecutable))
470 {
471 if (!stricmp(pExt, "COM"))
472 {
473 // I am not willing to find out more about the
474 // .COM executable format, so for this one case,
475 // let OS/2 determine what we have here
476 ULONG ulDosAppType = 0;
477 if (!(arc = DosQueryAppType((PSZ)pcszExecutable, &ulDosAppType)))
478 {
479 if (ulDosAppType & FAPPTYP_DOS) // 0x20
480 pExec->ulOS = EXEOS_DOS3;
481 else
482 pExec->ulOS = EXEOS_OS2;
483
484 pExec->ulExeFormat = EXEFORMAT_COM;
485 }
486 }
487 else if (!stricmp(pExt, "BAT"))
488 {
489 pExec->ulOS = EXEOS_DOS3;
490 pExec->ulExeFormat = EXEFORMAT_TEXT_BATCH;
491 }
492 else if (!stricmp(pExt, "CMD"))
493 {
494 pExec->ulOS = EXEOS_OS2;
495 pExec->ulExeFormat = EXEFORMAT_TEXT_CMD;
496 }
497 else
498 fOpenFile = TRUE;
499 }
500
501 if ( (fOpenFile)
502 && (!(arc = doshOpen((PSZ)pcszExecutable,
503 XOPEN_READ_EXISTING,
504 &cbFile,
505 &pFile)))
506 )
507 {
508 // file opened successfully:
509 pExec->pFile = pFile;
510
511 // read old DOS EXE header
512 if (!(pExec->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER))))
513 arc = ERROR_NOT_ENOUGH_MEMORY;
514 else
515 {
516 pExec->cbDosExeHeader = sizeof(DOSEXEHEADER);
517 if (!(arc = doshReadAt(pFile,
518 0,
519 &pExec->cbDosExeHeader, // in/out
520 (PBYTE)pExec->pDosExeHeader)))
521 {
522 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
523 BOOL fLoadNewHeader = FALSE;
524
525 // now check if we really have a DOS header
526 if (pExec->pDosExeHeader->usDosExeID != 0x5a4d)
527 {
528 // arc = ERROR_INVALID_EXE_SIGNATURE;
529
530 // V0.9.12 (2001-05-03) [umoeller]
531 // try loading new header directly; there are
532 // drivers which were built with NOSTUB, and
533 // the exe image starts out with the NE or LX
534 // image directly
535 fLoadNewHeader = TRUE;
536 // ulNewHeaderOfs is 0 now
537
538 // remove the DOS header info, since we have none
539 // V0.9.12 (2001-05-03) [umoeller]
540 FREE(pExec->pDosExeHeader);
541 }
542 else
543 {
544 // we have a DOS header:
545 if (pExec->pDosExeHeader->usRelocTableOfs < 0x40)
546 {
547 // neither LX nor PE nor NE:
548 pExec->ulOS = EXEOS_DOS3;
549 pExec->ulExeFormat = EXEFORMAT_OLDDOS;
550 }
551 else
552 {
553 // we have a new header offset:
554 fLoadNewHeader = TRUE;
555 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
556 }
557 }
558
559 if (fLoadNewHeader)
560 {
561 // either LX or PE or NE:
562 // read in new header...
563 // ulNewHeaderOfs is now either 0 (if no DOS header
564 // was found) or pDosExeHeader->ulNewHeaderOfs
565 // V0.9.12 (2001-05-03) [umoeller]
566 ULONG cbRead;
567 PBYTE pbHeader;
568
569 // now, we used to read in the first two chars
570 // to check out if we have PE or LX or NE and
571 // then read the header accordingly... but
572 // that wasn't terribly efficient. So load
573 // a chunk of data and then do a realloc()
574 // instead.
575
576 // take the largest of LXHEADER and NEHEADER and PEHEADER
577 cbRead = sizeof(LXHEADER);
578 if (sizeof(NEHEADER) > cbRead)
579 cbRead = sizeof(NEHEADER);
580 if (sizeof(PEHEADER) > cbRead)
581 cbRead = sizeof(PEHEADER);
582
583 if (!(pbHeader = (PBYTE)malloc(cbRead)))
584 arc = ERROR_NOT_ENOUGH_MEMORY;
585 else if (!(arc = doshReadAt(pFile,
586 ulNewHeaderOfs,
587 &cbRead,
588 pbHeader)))
589 {
590 PBYTE pbCheckOS = NULL;
591
592 PSZ achNewHeaderType = (PSZ)pbHeader;
593
594 if (!memcmp(achNewHeaderType, "NE", 2))
595 {
596 // New Executable:
597 pExec->ulExeFormat = EXEFORMAT_NE;
598
599 if (cbRead < sizeof(NEHEADER))
600 {
601 arc = ERROR_BAD_EXE_FORMAT;
602 FREE(pbHeader);
603 }
604 else
605 {
606 pExec->pNEHeader = (PNEHEADER)realloc(pbHeader,
607 sizeof(NEHEADER));
608 pExec->cbNEHeader = sizeof(NEHEADER);
609 pbCheckOS = &pExec->pNEHeader->bTargetOS;
610 // set library flag V0.9.16 (2001-12-08) [umoeller]
611 if (pExec->pNEHeader->usFlags & 0x8000)
612 // library:
613 pExec->fLibrary = TRUE;
614 }
615 }
616 else if ( (!memcmp(achNewHeaderType, "LX", 2))
617 || (!memcmp(achNewHeaderType, "LE", 2))
618 // this is used by SMARTDRV.EXE
619 )
620 {
621 // OS/2 Linear Executable:
622 pExec->ulExeFormat = EXEFORMAT_LX;
623
624 if (cbRead < sizeof(LXHEADER))
625 {
626 arc = ERROR_BAD_EXE_FORMAT;
627 FREE(pbHeader);
628 }
629 else
630 {
631 pExec->pLXHeader = (PLXHEADER)realloc(pbHeader,
632 sizeof(LXHEADER));
633 // read in LX header
634 pExec->cbLXHeader = sizeof(LXHEADER);
635 pbCheckOS = (PBYTE)(&pExec->pLXHeader->usTargetOS);
636 // set library flag V0.9.16 (2001-12-08) [umoeller]
637 if (pExec->pLXHeader->ulFlags & 0x8000)
638 // library:
639 pExec->fLibrary = TRUE;
640 }
641 }
642 else if (!memcmp(achNewHeaderType, "PE", 2))
643 {
644 pExec->ulExeFormat = EXEFORMAT_PE;
645
646 if (cbRead < sizeof(PEHEADER))
647 {
648 arc = ERROR_BAD_EXE_FORMAT;
649 FREE(pbHeader);
650 }
651 else
652 {
653 // PE has a standard header of 24 bytes
654 // plus an extended header, so check what
655 // we've got
656 ULONG cbPE = sizeof(PEHEADER); // 24
657 // + ((PPEHEADER)pbHeader)->usHeaderSize;
658 pExec->pPEHeader = (PPEHEADER)realloc(pbHeader,
659 cbPE);
660
661 pExec->cbPEHeader = cbPE;
662 pExec->ulOS = EXEOS_WIN32;
663 pExec->f32Bits = TRUE;
664
665 /*
666 // we have the first 24 bytes already, so
667 // go for the next chunk, if this is more
668 // than we have in PEHEADER
669 if ( (cbRead < cbPE)
670 && (cbRead = pExec->pPEHeader->usHeaderSize)
671 )
672 {
673 _Pmpf((" usHdrSize %d, sizeof(PEHEADER) %d, cbRead %d, cbPE %d --> reading extended header",
674 pExec->pPEHeader->usHeaderSize,
675 sizeof(PEHEADER),
676 cbRead,
677 cbPE));
678 if (!(arc = doshReadAt(hFile,
679 ulNewHeaderOfs + 24,
680 FILE_BEGIN,
681 &cbRead,
682 (PBYTE)pExec->pPEHeader + 24)))
683 {
684 }
685 else
686 {
687 arc = ERROR_BAD_EXE_FORMAT;
688 FREE(pExec->pPEHeader);
689 }
690 }
691 else
692 _Pmpf((" already got extended header"));
693 */
694 }
695 }
696 else
697 {
698 // strange type:
699 arc = ERROR_INVALID_EXE_SIGNATURE;
700 FREE(pbHeader);
701 }
702
703 if ((!arc) && (pbCheckOS))
704 {
705 // BYTE to check for operating system
706 // (NE and LX):
707 switch (*pbCheckOS)
708 {
709 case NEOS_OS2:
710 pExec->ulOS = EXEOS_OS2;
711 if (pExec->ulExeFormat == EXEFORMAT_LX)
712 pExec->f32Bits = TRUE;
713 break;
714
715 case NEOS_WIN16:
716 pExec->ulOS = EXEOS_WIN16;
717 break;
718
719 case NEOS_DOS4:
720 pExec->ulOS = EXEOS_DOS4;
721 break;
722
723 case NEOS_WIN386:
724 pExec->ulOS = EXEOS_WIN386;
725 pExec->f32Bits = TRUE;
726 break;
727 }
728 }
729 } // end if (!(arc = doshReadAt(hFile,
730 } // end if (fLoadNewHeader)
731 } // end if (!(arc = doshReadAt(hFile,
732 } // end else if (!(pExec->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER))))
733
734 } // end if (!(arc = DosOpen((PSZ)pcszExecutable,
735
736 if (arc != NO_ERROR)
737 // error: clean up
738 doshExecClose(&pExec);
739 else
740 *ppExec = pExec;
741
742 return (arc);
743}
744
745/*
746 *@@ ParseBldLevel:
747 * called from doshExecQueryBldLevel to parse
748 * the BLDLEVEL string.
749 *
750 * On entry, caller has copied the string into
751 * pExec->pszDescription. The string is
752 * null-terminated.
753 *
754 * The BLDLEVEL string comes in two flavors.
755 *
756 * -- The standard format is:
757 *
758 + @#VENDOR:VERSION#@DESCRIPTION
759 *
760 * DESCRIPTION can have leading spaces, but
761 * need to have them.
762 *
763 * -- However, there is an extended version
764 * in that the DESCRIPTION field is split
765 * up even more. The marker for this seems
766 * to be that the description starts out
767 * with "##1##".
768 *
769 + ##1## DATETIME BUILDMACHINE:ASD:LANG:CTRY:REVISION:UNKNOWN:FIXPAK@@DESCRIPTION
770 *
771 * The problem is that the DATETIME field comes
772 * in several flavors. IBM uses things like
773 *
774 + "Thu Nov 30 15:30:37 2000 BWBLD228"
775 *
776 * while DANIS506.ADD has
777 *
778 + "15.12.2000 18:22:57 Nachtigall"
779 *
780 * Looks like the date/time string is standardized
781 * to have 24 characters then.
782 *
783 *@@added V0.9.12 (2001-05-18) [umoeller]
784 *@@changed V0.9.12 (2001-05-19) [umoeller]: added extended BLDLEVEL support
785 */
786
787VOID ParseBldLevel(PEXECUTABLE pExec)
788{
789 const char // *pStartOfAuthor = 0,
790 *pStartOfVendor = 0;
791
792 // @#VENDOR:VERSION#@ DESCRIPTION
793 // but skip the first byte, which has the string length
794 pStartOfVendor = strstr(pExec->pszDescription,
795 "@#");
796 if (pStartOfVendor)
797 {
798 const char *pStartOfInfo = strstr(pStartOfVendor + 2,
799 "#@");
800 if (pStartOfInfo)
801 {
802 const char *pEndOfVendor = strchr(pStartOfVendor + 2,
803 ':');
804 if (pEndOfVendor)
805 {
806 pExec->pszVendor = strhSubstr(pStartOfVendor + 2,
807 pEndOfVendor);
808 pExec->pszVersion = strhSubstr(pEndOfVendor + 1,
809 pStartOfInfo);
810 // skip "@#" in DESCRIPTION string
811 pStartOfInfo += 2;
812
813 // now check if we have extended DESCRIPTION V0.9.12 (2001-05-19) [umoeller]
814 if ( (strlen(pStartOfInfo) > 6)
815 && (!memcmp(pStartOfInfo, "##1##", 5))
816 )
817 {
818 // yes: parse that beast
819 const char *p = pStartOfInfo + 5;
820
821 // get build date/time
822 if (strlen(p) > 24)
823 {
824 // date/time seems to be fixed 24 chars in length
825 if (pExec->pszBuildDateTime = (PSZ)malloc(25))
826 {
827 memcpy(pExec->pszBuildDateTime,
828 p,
829 24);
830 pExec->pszBuildDateTime[24] = '\0';
831
832 p += 24;
833
834 // now we're at the colon-separated
835 // strings, first of which is the
836 // build machine;
837 // skip leading spaces
838 while (*p == ' ')
839 p++;
840
841 if (*p)
842 {
843 char **papsz[] =
844 {
845 &pExec->pszBuildMachine,
846 &pExec->pszASD,
847 &pExec->pszLanguage,
848 &pExec->pszCountry,
849 &pExec->pszRevision,
850 &pExec->pszUnknown,
851 &pExec->pszFixpak
852 };
853 ULONG ul;
854
855 for (ul = 0;
856 ul < sizeof(papsz) / sizeof(papsz[0]);
857 ul++)
858 {
859 BOOL fStop = FALSE;
860 const char *pNextColon = strchr(p, ':'),
861 *pDoubleAt = strstr(p, "@@");
862 if (!pNextColon)
863 {
864 // last item:
865 if (pDoubleAt)
866 pNextColon = pDoubleAt;
867 else
868 pNextColon = p + strlen(p);
869
870 fStop = TRUE;
871 }
872
873 if ( (fStop)
874 || ( (pNextColon)
875 && ( (!pDoubleAt)
876 || (pNextColon < pDoubleAt)
877 )
878 )
879 )
880 {
881 if (pNextColon > p + 1)
882 *(papsz[ul]) = strhSubstr(p, pNextColon);
883 }
884 else
885 break;
886
887 if (fStop)
888 break;
889
890 p = pNextColon + 1;
891 }
892 }
893 }
894 }
895
896 pStartOfInfo = strstr(p,
897 "@@");
898 if (pStartOfInfo)
899 pStartOfInfo += 2;
900 }
901
902 // -- if we had no extended DESCRIPTION,
903 // pStartOfInfo points to regular description now
904 // -- if we parse the extended DESCRIPTION above,
905 // pStartOfInfo points to after @@ now
906 // -- if we had an error, pStartOfInfo is NULL
907 if (pStartOfInfo)
908 {
909 // add the regular DESCRIPTION then
910 // skip leading spaces in info string
911 while (*pStartOfInfo == ' ')
912 pStartOfInfo++;
913 if (*pStartOfInfo) // V0.9.9 (2001-04-04) [umoeller]
914 // and copy until end of string
915 pExec->pszInfo = strdup(pStartOfInfo);
916 }
917 }
918 }
919 }
920}
921
922/*
923 *@@ doshExecQueryBldLevel:
924 * this retrieves buildlevel information for an
925 * LX or NE executable previously opened with
926 * doshExecOpen.
927 *
928 * BuildLevel information must be contained in the
929 * DESCRIPTION field of an executable's module
930 * definition (.DEF) file. In order to be readable
931 * by BLDLEVEL.EXE (which ships with OS/2), this
932 * string must have the following format:
933 *
934 + Description '@#AUTHOR:VERSION#@ DESCRIPTION'
935 *
936 * Example:
937 *
938 + Description '@#Ulrich M”ller:0.9.0#@ XWorkplace Sound Support Module'
939 *
940 * The "Description" entry always ends up as the
941 * very first entry in the non-resident name table
942 * in LX and NE executables. So this is what we retrieve
943 * here.
944 *
945 * If the first entry in that table exists, NO_ERROR is
946 * returned and at least the pszDescription field in
947 * EXECUTABLE is set to that information.
948 *
949 * If that string is in IBM BLDLEVEL format, the string
950 * is automatically parsed, and the pszVendor, pszVersion,
951 * and pszInfo fields are also set. In the above examples,
952 * this would return the following information:
953 + pszVendor = "Ulrich M”ller"
954 + pszVersion = "0.9.0"
955 + pszInfo = "XWorkplace Sound Support Module"
956 *
957 * If that string is not in BLDLEVEL format, only pszDescription
958 * will be set. The other fields remain NULL.
959 *
960 * This returns the following errors:
961 *
962 * -- ERROR_INVALID_PARAMETER: pExec invalid
963 *
964 * -- ERROR_INVALID_EXE_SIGNATURE (191): pExec is not in LX or NE format
965 *
966 * -- ERROR_INVALID_DATA (13): non-resident name table not found.
967 *
968 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
969 *
970 * plus the error codes of DosSetFilePtr and DosRead.
971 *
972 *@@added V0.9.0 [umoeller]
973 *@@changed V0.9.0 (99-10-22) [umoeller]: NE format now supported
974 *@@changed V0.9.1 (99-12-06): fixed memory leak
975 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
976 *@@changed V0.9.12 (2001-05-18) [umoeller]: extracted ParseBldLevel
977 */
978
979APIRET doshExecQueryBldLevel(PEXECUTABLE pExec)
980{
981 APIRET arc = NO_ERROR;
982
983 if (!pExec)
984 arc = ERROR_INVALID_PARAMETER;
985 else
986 {
987 PXFILE pFile = pExec->pFile;
988
989 ULONG ulNRNTOfs = 0;
990
991 if (pExec->ulExeFormat == EXEFORMAT_LX)
992 {
993 // OK, LX format:
994 // check if we have a non-resident name table
995 if (pExec->pLXHeader == NULL)
996 arc = ERROR_INVALID_DATA;
997 else if (pExec->pLXHeader->ulNonResdNameTblOfs == 0)
998 arc = ERROR_INVALID_DATA;
999 else
1000 ulNRNTOfs = pExec->pLXHeader->ulNonResdNameTblOfs;
1001 }
1002 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1003 {
1004 // OK, NE format:
1005 // check if we have a non-resident name table
1006 if (pExec->pNEHeader == NULL)
1007 arc = ERROR_INVALID_DATA;
1008 else if (pExec->pNEHeader->ulNonResdTblOfs == 0)
1009 arc = ERROR_INVALID_DATA;
1010 else
1011 ulNRNTOfs = pExec->pNEHeader->ulNonResdTblOfs;
1012 }
1013 else
1014 // neither LX nor NE: stop
1015 arc = ERROR_INVALID_EXE_SIGNATURE;
1016
1017 if ( (!arc)
1018 && (ulNRNTOfs)
1019 )
1020 {
1021 ULONG ulLocal = 0,
1022 ulBytesRead = 0;
1023
1024 // move EXE file pointer to offset of non-resident name table
1025 // (from LX header)
1026 if (!(arc = DosSetFilePtr(pFile->hf, // file is still open
1027 ulNRNTOfs, // ofs determined above
1028 FILE_BEGIN,
1029 &ulLocal)))
1030 {
1031 // allocate memory as necessary
1032 PSZ pszNameTable = (PSZ)malloc(2001); // should suffice, because each entry
1033 // may only be 255 bytes in length
1034 if (!pszNameTable)
1035 arc = ERROR_NOT_ENOUGH_MEMORY;
1036 else
1037 {
1038 if (!(arc = DosRead(pFile->hf,
1039 pszNameTable,
1040 2000,
1041 &ulBytesRead)))
1042 {
1043 if (*pszNameTable == 0)
1044 // first byte (length byte) is null:
1045 arc = ERROR_INVALID_DATA;
1046 else
1047 {
1048 // now copy the string, which is in Pascal format
1049 pExec->pszDescription = (PSZ)malloc((*pszNameTable) + 1); // addt'l null byte
1050 if (!pExec->pszDescription)
1051 arc = ERROR_NOT_ENOUGH_MEMORY;
1052 else
1053 {
1054 memcpy(pExec->pszDescription,
1055 pszNameTable + 1, // skip length byte
1056 *pszNameTable); // length byte
1057 // terminate string
1058 *(pExec->pszDescription + (*pszNameTable)) = 0;
1059
1060 ParseBldLevel(pExec);
1061 }
1062 }
1063 }
1064
1065 free(pszNameTable);
1066 } // end if PSZ pszNameTable = (PSZ)malloc(2001);
1067 }
1068 }
1069 } // end if (!pExec)
1070
1071 return (arc);
1072}
1073
1074/*
1075 *@@ doshExecQueryImportedModules:
1076 * returns an array of FSYSMODULE structure describing all
1077 * imported modules.
1078 *
1079 * *pcModules receives the # of items in the array (not the
1080 * array size!). Use doshFreeImportedModules to clean up.
1081 *
1082 * This returns a standard OS/2 error code, which might be
1083 * any of the codes returned by DosSetFilePtr and DosRead.
1084 * In addition, this may return:
1085 *
1086 * -- ERROR_NOT_ENOUGH_MEMORY
1087 *
1088 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1089 * than LX or NE, which is not understood by this function.
1090 *
1091 * Even if NO_ERROR is returned, the array pointer might still
1092 * be NULL if the module contains no such data.
1093 *
1094 *@@added V0.9.9 (2001-03-11) [lafaix]
1095 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1096 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1097 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
1098 *@@changed V0.9.10 (2001-04-13) [lafaix]: removed 127 characters limit
1099 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1100 */
1101
1102APIRET doshExecQueryImportedModules(PEXECUTABLE pExec,
1103 PFSYSMODULE *ppaModules, // out: modules array
1104 PULONG pcModules) // out: array item count
1105{
1106 if ( (pExec)
1107 && ( (pExec->ulOS == EXEOS_OS2)
1108 || (pExec->ulOS == EXEOS_WIN16)
1109 || (pExec->ulOS == EXEOS_WIN386)
1110 )
1111 )
1112 {
1113 ENSURE_BEGIN;
1114 ULONG cModules = 0;
1115 PFSYSMODULE paModules = NULL;
1116 int i;
1117 HFILE hfExe = pExec->pFile->hf;
1118
1119 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
1120
1121 if (pExec->pDosExeHeader)
1122 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1123 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1124
1125 if (pExec->ulExeFormat == EXEFORMAT_LX)
1126 {
1127 // 32-bit OS/2 executable:
1128 cModules = pExec->pLXHeader->ulImportModTblCnt;
1129
1130 if (cModules)
1131 {
1132 ULONG cb = sizeof(FSYSMODULE) * cModules; // V0.9.9 (2001-04-03) [umoeller]
1133 ULONG ulDummy;
1134
1135 paModules = (PFSYSMODULE)malloc(cb);
1136 if (!paModules)
1137 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY); // V0.9.9 (2001-04-03) [umoeller]
1138
1139 memset(paModules, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1140
1141 ENSURE_SAFE(DosSetFilePtr(hfExe,
1142 pExec->pLXHeader->ulImportModTblOfs
1143 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1144 FILE_BEGIN,
1145 &ulDummy));
1146
1147 for (i = 0; i < cModules; i++)
1148 {
1149 BYTE bLen = 0;
1150
1151 // reading the length of the module name
1152 ENSURE_SAFE(DosRead(hfExe, &bLen, 1, &ulDummy));
1153
1154 // reading the module name
1155 ENSURE_SAFE(DosRead(hfExe,
1156 paModules[i].achModuleName,
1157 bLen,
1158 &ulDummy));
1159
1160 // module names are not null terminated, so we must
1161 // do it now
1162 paModules[i].achModuleName[bLen] = 0;
1163 } // end for
1164 }
1165 } // end LX
1166 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1167 {
1168 // 16-bit executable:
1169 cModules = pExec->pNEHeader->usModuleTblEntries;
1170
1171 if (cModules)
1172 {
1173 ULONG cb = sizeof(FSYSMODULE) * cModules;
1174
1175 paModules = (PFSYSMODULE)malloc(cb);
1176 if (!paModules)
1177 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY); // V0.9.9 (2001-04-03) [umoeller]
1178
1179 memset(paModules, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1180
1181 for (i = 0; i < cModules; i ++)
1182 {
1183 BYTE bLen;
1184 USHORT usOfs;
1185 ULONG ulDummy;
1186
1187 // the module reference table contains offsets
1188 // relative to the import table; we hence read
1189 // the offset in the module reference table, and
1190 // then we read the name in the import table
1191
1192 ENSURE_SAFE(DosSetFilePtr(hfExe,
1193 pExec->pNEHeader->usModRefTblOfs
1194 + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
1195 + sizeof(usOfs) * i,
1196 FILE_BEGIN,
1197 &ulDummy));
1198
1199 ENSURE_SAFE(DosRead(hfExe, &usOfs, 2, &ulDummy));
1200
1201 ENSURE_SAFE(DosSetFilePtr(hfExe,
1202 pExec->pNEHeader->usImportTblOfs
1203 + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
1204 + usOfs,
1205 FILE_BEGIN,
1206 &ulDummy));
1207
1208 ENSURE_SAFE(DosRead(hfExe, &bLen, 1, &ulDummy));
1209
1210 ENSURE_SAFE(DosRead(hfExe,
1211 paModules[i].achModuleName,
1212 bLen,
1213 &ulDummy));
1214
1215 paModules[i].achModuleName[bLen] = 0;
1216 } // end for
1217 }
1218 } // end NE
1219 else
1220 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1221
1222 // no error: output data
1223 *ppaModules = paModules;
1224 *pcModules = cModules;
1225
1226 ENSURE_FINALLY;
1227 // if we had an error above, clean up
1228 free(paModules);
1229 ENSURE_END;
1230 }
1231 else
1232 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1233
1234 ENSURE_OK;
1235}
1236
1237/*
1238 *@@ doshExecFreeImportedModules:
1239 * frees resources allocated by doshExecQueryImportedModules.
1240 *
1241 *@@added V0.9.9 (2001-03-11)
1242 */
1243
1244APIRET doshExecFreeImportedModules(PFSYSMODULE paModules)
1245{
1246 free(paModules);
1247 return (NO_ERROR);
1248}
1249
1250/*
1251 *@@ ScanLXEntryTable:
1252 * returns the number of exported entries in the entry table.
1253 *
1254 * If paFunctions is not NULL, then successive entries are
1255 * filled with the found type and ordinal values.
1256 *
1257 *@@added V0.9.9 (2001-03-30) [lafaix]
1258 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1259 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1260 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1261 */
1262
1263APIRET ScanLXEntryTable(PEXECUTABLE pExec,
1264 PFSYSFUNCTION paFunctions,
1265 PULONG pcEntries) // out: entry table entry count; ptr can be NULL
1266{
1267 ULONG ulDummy;
1268 USHORT usOrdinal = 1,
1269 usCurrent = 0;
1270 int i;
1271
1272 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
1273 HFILE hfExe = pExec->pFile->hf;
1274
1275 if (pExec->pDosExeHeader)
1276 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1277 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1278
1279 ENSURE(DosSetFilePtr(hfExe,
1280 pExec->pLXHeader->ulEntryTblOfs
1281 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1282 FILE_BEGIN,
1283 &ulDummy));
1284
1285 while (TRUE)
1286 {
1287 BYTE bCnt,
1288 bType,
1289 bFlag;
1290
1291 ENSURE(DosRead(hfExe, &bCnt, 1, &ulDummy));
1292
1293 if (bCnt == 0)
1294 // end of the entry table
1295 break;
1296
1297 ENSURE(DosRead(hfExe, &bType, 1, &ulDummy));
1298
1299 switch (bType & 0x7F)
1300 {
1301 /*
1302 * unused entries
1303 *
1304 */
1305
1306 case 0:
1307 usOrdinal += bCnt;
1308 break;
1309
1310 /*
1311 * 16-bit entries
1312 *
1313 * the bundle type is followed by the object number
1314 * and by bCnt bFlag+usOffset entries
1315 *
1316 */
1317
1318 case 1:
1319 ENSURE(DosSetFilePtr(hfExe,
1320 sizeof(USHORT),
1321 FILE_CURRENT,
1322 &ulDummy));
1323
1324 for (i = 0; i < bCnt; i ++)
1325 {
1326 ENSURE(DosRead(hfExe, &bFlag, 1, &ulDummy));
1327
1328 if (bFlag & 0x01)
1329 {
1330 if (paFunctions)
1331 {
1332 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1333 paFunctions[usCurrent].ulType = 1;
1334 paFunctions[usCurrent].achFunctionName[0] = 0;
1335 }
1336 usCurrent++;
1337 }
1338
1339 usOrdinal++;
1340
1341 ENSURE(DosSetFilePtr(hfExe,
1342 sizeof(USHORT),
1343 FILE_CURRENT,
1344 &ulDummy));
1345
1346 } // end for
1347 break;
1348
1349 /*
1350 * 286 call gate entries
1351 *
1352 * the bundle type is followed by the object number
1353 * and by bCnt bFlag+usOffset+usCallGate entries
1354 *
1355 */
1356
1357 case 2:
1358 ENSURE(DosSetFilePtr(hfExe,
1359 sizeof(USHORT),
1360 FILE_CURRENT,
1361 &ulDummy));
1362
1363 for (i = 0; i < bCnt; i ++)
1364 {
1365 ENSURE(DosRead(hfExe, &bFlag, 1, &ulDummy));
1366
1367 if (bFlag & 0x01)
1368 {
1369 if (paFunctions)
1370 {
1371 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1372 paFunctions[usCurrent].ulType = 2;
1373 paFunctions[usCurrent].achFunctionName[0] = 0;
1374 }
1375 usCurrent++;
1376 }
1377
1378 usOrdinal++;
1379
1380 ENSURE(DosSetFilePtr(hfExe,
1381 sizeof(USHORT) + sizeof(USHORT),
1382 FILE_CURRENT,
1383 &ulDummy));
1384
1385 } // end for
1386 break;
1387
1388 /*
1389 * 32-bit entries
1390 *
1391 * the bundle type is followed by the object number
1392 * and by bCnt bFlag+ulOffset entries
1393 *
1394 */
1395
1396 case 3:
1397 ENSURE(DosSetFilePtr(hfExe,
1398 sizeof(USHORT),
1399 FILE_CURRENT,
1400 &ulDummy));
1401
1402 for (i = 0; i < bCnt; i ++)
1403 {
1404 ENSURE(DosRead(hfExe, &bFlag, 1, &ulDummy));
1405
1406 if (bFlag & 0x01)
1407 {
1408 if (paFunctions)
1409 {
1410 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1411 paFunctions[usCurrent].ulType = 3;
1412 paFunctions[usCurrent].achFunctionName[0] = 0;
1413 }
1414 usCurrent++;
1415 }
1416
1417 usOrdinal++;
1418
1419 ENSURE(DosSetFilePtr(hfExe,
1420 sizeof(ULONG),
1421 FILE_CURRENT,
1422 &ulDummy));
1423 } // end for
1424 break;
1425
1426 /*
1427 * forwarder entries
1428 *
1429 * the bundle type is followed by a reserved word
1430 * and by bCnt bFlag+usModOrd+ulOffsOrdNum entries
1431 *
1432 */
1433
1434 case 4:
1435 ENSURE(DosSetFilePtr(hfExe,
1436 sizeof(USHORT),
1437 FILE_CURRENT,
1438 &ulDummy));
1439
1440 for (i = 0; i < bCnt; i ++)
1441 {
1442 ENSURE(DosSetFilePtr(hfExe,
1443 sizeof(BYTE) + sizeof(USHORT) + sizeof(ULONG),
1444 FILE_CURRENT,
1445 &ulDummy));
1446
1447 if (paFunctions)
1448 {
1449 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1450 paFunctions[usCurrent].ulType = 4;
1451 paFunctions[usCurrent].achFunctionName[0] = 0;
1452 }
1453 usCurrent++;
1454
1455 usOrdinal++;
1456 } // end for
1457 break;
1458
1459 /*
1460 * unknown bundle type
1461 *
1462 * we don't know how to handle this bundle, so we must
1463 * stop parsing the entry table here (as we don't know the
1464 * bundle size); if paFunctions is not null, we fill it with
1465 * informative data
1466 */
1467
1468 default:
1469 if (paFunctions)
1470 {
1471 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1472 paFunctions[usCurrent].ulType = bType;
1473 sprintf(paFunctions[usCurrent].achFunctionName,
1474 "Unknown bundle type encountered (%d). Aborting entry table scan.",
1475 bType);
1476
1477 usCurrent++;
1478 }
1479 ENSURE_FAIL(ERROR_INVALID_LIST_FORMAT);
1480 // whatever
1481 // V0.9.9 (2001-04-03) [umoeller]
1482 } // end switch (bType & 0x7F)
1483 } // end while (TRUE)
1484
1485 if (pcEntries)
1486 *pcEntries = usCurrent;
1487
1488 ENSURE_OK;
1489}
1490
1491/*
1492 *@@ ScanNEEntryTable:
1493 * returns the number of exported entries in the entry table.
1494 *
1495 * if paFunctions is not NULL, then successive entries are
1496 * filled with the found type and ordinal values.
1497 *
1498 *@@added V0.9.9 (2001-03-30) [lafaix]
1499 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1500 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1501 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1502 */
1503
1504APIRET ScanNEEntryTable(PEXECUTABLE pExec,
1505 PFSYSFUNCTION paFunctions,
1506 PULONG pcEntries) // out: entry table entry count; ptr can be NULL
1507{
1508 ULONG ulDummy;
1509 USHORT usOrdinal = 1,
1510 usCurrent = 0;
1511 int i;
1512
1513 ULONG ulNewHeaderOfs = 0;
1514 HFILE hfExe = pExec->pFile->hf;
1515
1516 if (pExec->pDosExeHeader)
1517 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1518 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1519
1520 ENSURE(DosSetFilePtr(hfExe,
1521 pExec->pNEHeader->usEntryTblOfs
1522 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1523 FILE_BEGIN,
1524 &ulDummy));
1525
1526 while (TRUE)
1527 {
1528 BYTE bCnt,
1529 bType,
1530 bFlag;
1531
1532 ENSURE(DosRead(hfExe, &bCnt, 1, &ulDummy));
1533
1534 if (bCnt == 0)
1535 // end of the entry table
1536 break;
1537
1538 ENSURE(DosRead(hfExe, &bType, 1, &ulDummy));
1539
1540 if (bType)
1541 {
1542 for (i = 0; i < bCnt; i++)
1543 {
1544 ENSURE(DosRead(hfExe,
1545 &bFlag,
1546 1,
1547 &ulDummy));
1548
1549 if (bFlag & 0x01)
1550 {
1551 if (paFunctions)
1552 {
1553 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1554 paFunctions[usCurrent].ulType = 1; // 16-bit entry
1555 paFunctions[usCurrent].achFunctionName[0] = 0;
1556 }
1557 usCurrent++;
1558 }
1559
1560 usOrdinal++;
1561
1562 if (bType == 0xFF)
1563 {
1564 // moveable segment
1565 ENSURE(DosSetFilePtr(hfExe,
1566 5,
1567 FILE_CURRENT,
1568 &ulDummy));
1569 }
1570 else
1571 {
1572 // fixed segment or constant (0xFE)
1573 ENSURE(DosSetFilePtr(hfExe,
1574 2,
1575 FILE_CURRENT,
1576 &ulDummy));
1577 }
1578
1579 } // end for
1580 }
1581 else
1582 usOrdinal += bCnt;
1583 } // end while (TRUE)
1584
1585 if (pcEntries)
1586 *pcEntries = usCurrent;
1587
1588 ENSURE_OK;
1589}
1590
1591/*
1592 *@@ Compare:
1593 * binary search helper
1594 *
1595 *@@added V0.9.9 (2001-04-01) [lafaix]
1596 *@@changed V0.9.9 (2001-04-07) [umoeller]: added _Optlink, or this won't compile as C++
1597 */
1598
1599int _Optlink Compare(const void *key,
1600 const void *element)
1601{
1602 USHORT usOrdinal = *((PUSHORT) key);
1603 PFSYSFUNCTION pFunction = (PFSYSFUNCTION)element;
1604
1605 if (usOrdinal > pFunction->ulOrdinal)
1606 return (1);
1607 else if (usOrdinal < pFunction->ulOrdinal)
1608 return (-1);
1609 else
1610 return (0);
1611}
1612
1613/*
1614 *@@ ScanNameTable:
1615 * scans a resident or non-resident name table, and fills the
1616 * appropriate paFunctions entries when it encounters exported
1617 * entries names.
1618 *
1619 * This functions works for both NE and LX executables.
1620 *
1621 *@@added V0.9.9 (2001-03-30) [lafaix]
1622 *@@changed V0.9.9 (2001-04-02) [lafaix]: the first entry is special
1623 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1624 *@@changed V0.9.9 (2001-04-05) [lafaix]: removed the 127 char limit
1625 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1626 */
1627
1628APIRET ScanNameTable(PEXECUTABLE pExec,
1629 ULONG cFunctions,
1630 PFSYSFUNCTION paFunctions)
1631{
1632 ULONG ulDummy;
1633
1634 USHORT usOrdinal;
1635 PFSYSFUNCTION pFunction;
1636 HFILE hfExe = pExec->pFile->hf;
1637
1638 while (TRUE)
1639 {
1640 BYTE bLen;
1641 CHAR achName[256];
1642 // int i;
1643
1644 ENSURE(DosRead(hfExe, &bLen, 1, &ulDummy));
1645
1646 if (bLen == 0)
1647 // end of the name table
1648 break;
1649
1650 ENSURE(DosRead(hfExe, &achName, bLen, &ulDummy));
1651 achName[bLen] = 0;
1652
1653 ENSURE(DosRead(hfExe, &usOrdinal, sizeof(USHORT), &ulDummy));
1654
1655 if ((pFunction = (PFSYSFUNCTION)bsearch(&usOrdinal,
1656 paFunctions,
1657 cFunctions,
1658 sizeof(FSYSFUNCTION),
1659 Compare)))
1660 {
1661 memcpy(pFunction->achFunctionName,
1662 achName,
1663 bLen+1);
1664 }
1665 }
1666
1667 ENSURE_OK;
1668}
1669
1670/*
1671 *@@ doshExecQueryExportedFunctions:
1672 * returns an array of FSYSFUNCTION structure describing all
1673 * exported functions.
1674 *
1675 * *pcFunctions receives the # of items in the array (not the
1676 * array size!). Use doshFreeExportedFunctions to clean up.
1677 *
1678 * Note that the returned array only contains entry for exported
1679 * functions. Empty export entries are _not_ included.
1680 *
1681 * This returns a standard OS/2 error code, which might be
1682 * any of the codes returned by DosSetFilePtr and DosRead.
1683 * In addition, this may return:
1684 *
1685 * -- ERROR_NOT_ENOUGH_MEMORY
1686 *
1687 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1688 * than LX or NE, which is not understood by this function.
1689 *
1690 * -- If ERROR_INVALID_LIST_FORMAT is returned, the format of an
1691 * export entry wasn't understood here.
1692 *
1693 * Even if NO_ERROR is returned, the array pointer might still
1694 * be NULL if the module contains no such data.
1695 *
1696 *@@added V0.9.9 (2001-03-11) [lafaix]
1697 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1698 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1699 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
1700 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1701 */
1702
1703APIRET doshExecQueryExportedFunctions(PEXECUTABLE pExec,
1704 PFSYSFUNCTION *ppaFunctions, // out: functions array
1705 PULONG pcFunctions) // out: array item count
1706{
1707 if ( (pExec)
1708 && ( (pExec->ulOS == EXEOS_OS2)
1709 || (pExec->ulOS == EXEOS_WIN16)
1710 || (pExec->ulOS == EXEOS_WIN386)
1711 )
1712 )
1713 {
1714 ENSURE_BEGIN;
1715 ULONG cFunctions = 0;
1716 PFSYSFUNCTION paFunctions = NULL;
1717
1718 ULONG ulDummy;
1719
1720 HFILE hfExe = pExec->pFile->hf;
1721 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
1722
1723 if (pExec->pDosExeHeader)
1724 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1725 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1726
1727 if (pExec->ulExeFormat == EXEFORMAT_LX)
1728 {
1729 // It's a 32bit OS/2 executable
1730
1731 // the number of exported entry points is not stored
1732 // in the executable header; we have to count them in
1733 // the entry table
1734
1735 ENSURE(ScanLXEntryTable(pExec, NULL, &cFunctions));
1736
1737 // we now have the number of exported entries; let us
1738 // build them
1739
1740 if (cFunctions)
1741 {
1742 ULONG cb = sizeof(FSYSFUNCTION) * cFunctions;
1743
1744 paFunctions = (PFSYSFUNCTION)malloc(cb);
1745 if (!paFunctions)
1746 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
1747
1748 // we rescan the entry table (the cost is not as bad
1749 // as it may seem, due to disk caching)
1750
1751 ENSURE_SAFE(ScanLXEntryTable(pExec, paFunctions, NULL));
1752
1753 // we now scan the resident name table entries
1754
1755 ENSURE_SAFE(DosSetFilePtr(hfExe,
1756 pExec->pLXHeader->ulResdNameTblOfs
1757 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1758 FILE_BEGIN,
1759 &ulDummy));
1760
1761 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1762
1763 // we now scan the non-resident name table entries,
1764 // whose offset is _from the begining of the file_
1765
1766 ENSURE_SAFE(DosSetFilePtr(hfExe,
1767 pExec->pLXHeader->ulNonResdNameTblOfs,
1768 FILE_BEGIN,
1769 &ulDummy));
1770
1771 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1772 } // end if (cFunctions)
1773 }
1774 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1775 {
1776 // it's a "new" segmented 16bit executable
1777
1778 // here too the number of exported entry points
1779 // is not stored in the executable header; we
1780 // have to count them in the entry table
1781
1782 ENSURE(ScanNEEntryTable(pExec, NULL, &cFunctions));
1783
1784 // we now have the number of exported entries; let us
1785 // build them
1786
1787 if (cFunctions)
1788 {
1789 // USHORT usOrdinal = 1;
1790 // usCurrent = 0;
1791
1792 paFunctions = (PFSYSFUNCTION)malloc(sizeof(FSYSFUNCTION) * cFunctions);
1793 if (!paFunctions)
1794 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
1795
1796 // we rescan the entry table (the cost is not as bad
1797 // as it may seem, due to disk caching)
1798
1799 ENSURE_SAFE(ScanNEEntryTable(pExec, paFunctions, NULL));
1800
1801 // we now scan the resident name table entries
1802
1803 ENSURE_SAFE(DosSetFilePtr(hfExe,
1804 pExec->pNEHeader->usResdNameTblOfs
1805 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1806 FILE_BEGIN,
1807 &ulDummy));
1808
1809 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1810
1811 // we now scan the non-resident name table entries,
1812 // whose offset is _from the begining of the file_
1813
1814 ENSURE_SAFE(DosSetFilePtr(hfExe,
1815 pExec->pNEHeader->ulNonResdTblOfs,
1816 FILE_BEGIN,
1817 &ulDummy));
1818
1819 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1820 }
1821 }
1822 else
1823 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1824
1825 // no error: output data
1826 *ppaFunctions = paFunctions;
1827 *pcFunctions = cFunctions;
1828
1829 ENSURE_FINALLY;
1830 // if we had an error above, clean up
1831 free(paFunctions);
1832 ENSURE_END;
1833 }
1834 else
1835 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1836
1837 ENSURE_OK;
1838}
1839
1840/*
1841 *@@ doshExecFreeExportedFunctions:
1842 * frees resources allocated by doshExecQueryExportedFunctions.
1843 *
1844 *@@added V0.9.9 (2001-03-11)
1845 */
1846
1847APIRET doshExecFreeExportedFunctions(PFSYSFUNCTION paFunctions)
1848{
1849 free(paFunctions);
1850
1851 return (NO_ERROR);
1852}
1853
1854/*
1855 *@@ doshExecQueryResources:
1856 * returns an array of FSYSRESOURCE structures describing all
1857 * available resources in the module.
1858 *
1859 * *pcResources receives the no. of items in the array
1860 * (not the array size!). Use doshExecFreeResources to clean up.
1861 *
1862 * This returns a standard OS/2 error code, which might be
1863 * any of the codes returned by DosSetFilePtr and DosRead.
1864 * In addition, this may return:
1865 *
1866 * -- ERROR_NOT_ENOUGH_MEMORY
1867 *
1868 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1869 * than LX or NE, which is not understood by this function.
1870 *
1871 * Even if NO_ERROR is returned, the array pointer might still
1872 * be NULL if the module contains no such data.
1873 *
1874 *@@added V0.9.7 (2000-12-18) [lafaix]
1875 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1876 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
1877 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1878 */
1879
1880APIRET doshExecQueryResources(PEXECUTABLE pExec, // in: executable from doshExecOpen
1881 PFSYSRESOURCE *ppaResources, // out: res's array
1882 PULONG pcResources) // out: array item count
1883{
1884 if ( (pExec)
1885 && ( (pExec->ulOS == EXEOS_OS2)
1886 || (pExec->ulOS == EXEOS_WIN16)
1887 || (pExec->ulOS == EXEOS_WIN386)
1888 )
1889 )
1890 {
1891 ENSURE_BEGIN;
1892 ULONG cResources = 0;
1893 PFSYSRESOURCE paResources = NULL;
1894
1895 HFILE hfExe = pExec->pFile->hf;
1896 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
1897
1898 if (pExec->pDosExeHeader)
1899 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1900 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1901
1902 if (pExec->ulExeFormat == EXEFORMAT_LX)
1903 {
1904 // 32-bit OS/2 executable:
1905 PLXHEADER pLXHeader = pExec->pLXHeader;
1906 if (cResources = pLXHeader->ulResTblCnt)
1907 {
1908 #pragma pack(1) // V0.9.9 (2001-04-02) [umoeller]
1909 struct rsrc32 // Resource Table Entry
1910 {
1911 unsigned short type; // Resource type
1912 unsigned short name; // Resource name
1913 unsigned long cb; // Resource size
1914 unsigned short obj; // Object number
1915 unsigned long offset; // Offset within object
1916 } rs;
1917
1918 struct o32_obj // Flat .EXE object table entry
1919 {
1920 unsigned long o32_size; // Object virtual size
1921 unsigned long o32_base; // Object base virtual address
1922 unsigned long o32_flags; // Attribute flags
1923 unsigned long o32_pagemap; // Object page map index
1924 unsigned long o32_mapsize; // Number of entries in object page map
1925 unsigned long o32_reserved; // Reserved
1926 } ot;
1927 #pragma pack() // V0.9.9 (2001-04-03) [umoeller]
1928
1929 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
1930 ULONG ulDummy;
1931 int i;
1932 ULONG ulCurOfs;
1933
1934 paResources = (PFSYSRESOURCE)malloc(cb);
1935 if (!paResources)
1936 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
1937
1938 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1939
1940 ENSURE_SAFE(DosSetFilePtr(hfExe,
1941 pLXHeader->ulResTblOfs
1942 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1943 FILE_BEGIN,
1944 &ulDummy));
1945
1946 for (i = 0; i < cResources; i++)
1947 {
1948 ENSURE_SAFE(DosRead(hfExe, &rs, 14, &ulDummy));
1949
1950 paResources[i].ulID = rs.name;
1951 paResources[i].ulType = rs.type;
1952 paResources[i].ulSize = rs.cb;
1953 paResources[i].ulFlag = rs.obj; // Temp storage for Object
1954 // number. Will be filled
1955 // with resource flag
1956 // later.
1957 }
1958
1959 for (i = 0; i < cResources; i++)
1960 {
1961 ULONG ulOfsThis = pLXHeader->ulObjTblOfs
1962 + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
1963 + ( sizeof(ot)
1964 * (paResources[i].ulFlag - 1));
1965
1966 ENSURE_SAFE(DosSetFilePtr(hfExe,
1967 ulOfsThis,
1968 FILE_BEGIN,
1969 &ulDummy));
1970
1971 ENSURE_SAFE(DosRead(hfExe, &ot, sizeof(ot), &ulDummy));
1972
1973 paResources[i].ulFlag = ((ot.o32_flags & OBJWRITE)
1974 ? 0
1975 : RNPURE);
1976 paResources[i].ulFlag |= ((ot.o32_flags & OBJDISCARD)
1977 ? 4096
1978 : 0);
1979 paResources[i].ulFlag |= ((ot.o32_flags & OBJSHARED)
1980 ? RNMOVE
1981 : 0);
1982 paResources[i].ulFlag |= ((ot.o32_flags & OBJPRELOAD)
1983 ? RNPRELOAD
1984 : 0);
1985 } // end for
1986 } // end if (cResources)
1987 } // end if (pExec->ulExeFormat == EXEFORMAT_LX)
1988 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1989 {
1990 PNEHEADER pNEHeader = pExec->pNEHeader;
1991
1992 if (pExec->ulOS == EXEOS_OS2)
1993 {
1994 // 16-bit OS/2 executable:
1995 cResources = pNEHeader->usResSegmCount;
1996
1997 if (cResources)
1998 {
1999 #pragma pack(1) // V0.9.9 (2001-04-02) [umoeller]
2000 struct {unsigned short type; unsigned short name;} rti;
2001 struct new_seg // New .EXE segment table entry
2002 {
2003 unsigned short ns_sector; // File sector of start of segment
2004 unsigned short ns_cbseg; // Number of bytes in file
2005 unsigned short ns_flags; // Attribute flags
2006 unsigned short ns_minalloc; // Minimum allocation in bytes
2007 } ns;
2008 #pragma pack()
2009
2010 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2011 ULONG ulDummy;
2012 int i;
2013
2014 paResources = (PFSYSRESOURCE)malloc(cb);
2015 if (!paResources)
2016 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2017
2018 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
2019
2020 // we first read the resources IDs and types
2021
2022 ENSURE_SAFE(DosSetFilePtr(hfExe,
2023 pNEHeader->usResTblOfs
2024 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
2025 FILE_BEGIN,
2026 &ulDummy));
2027
2028 for (i = 0; i < cResources; i++)
2029 {
2030 ENSURE_SAFE(DosRead(hfExe, &rti, sizeof(rti), &ulDummy));
2031
2032 paResources[i].ulID = rti.name;
2033 paResources[i].ulType = rti.type;
2034 }
2035
2036 // we then read their sizes and flags
2037
2038 for (i = 0; i < cResources; i++)
2039 {
2040 ENSURE_SAFE(DosSetFilePtr(hfExe,
2041 ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
2042 + pNEHeader->usSegTblOfs
2043 + (sizeof(ns)
2044 * ( pNEHeader->usSegTblEntries
2045 - pNEHeader->usResSegmCount
2046 + i)),
2047 FILE_BEGIN,
2048 &ulDummy));
2049
2050 ENSURE_SAFE(DosRead(hfExe, &ns, sizeof(ns), &ulDummy));
2051
2052 paResources[i].ulSize = ns.ns_cbseg;
2053
2054 paResources[i].ulFlag = (ns.ns_flags & OBJPRELOAD) ? RNPRELOAD : 0;
2055 paResources[i].ulFlag |= (ns.ns_flags & OBJSHARED) ? RNPURE : 0;
2056 paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? RNMOVE : 0;
2057 paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? 4096 : 0;
2058 }
2059 } // end if (cResources)
2060 }
2061 else
2062 {
2063 // 16-bit Windows executable
2064 USHORT usAlignShift;
2065 ULONG ulDummy;
2066
2067 ENSURE(DosSetFilePtr(hfExe,
2068 pNEHeader->usResTblOfs
2069 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
2070 FILE_BEGIN,
2071 &ulDummy));
2072
2073 ENSURE(DosRead(hfExe,
2074 &usAlignShift,
2075 sizeof(usAlignShift),
2076 &ulDummy));
2077
2078 while (TRUE)
2079 {
2080 USHORT usTypeID;
2081 USHORT usCount;
2082
2083 ENSURE(DosRead(hfExe,
2084 &usTypeID,
2085 sizeof(usTypeID),
2086 &ulDummy));
2087
2088 if (usTypeID == 0)
2089 break;
2090
2091 ENSURE(DosRead(hfExe,
2092 &usCount,
2093 sizeof(usCount),
2094 &ulDummy));
2095
2096 ENSURE(DosSetFilePtr(hfExe,
2097 sizeof(ULONG),
2098 FILE_CURRENT,
2099 &ulDummy));
2100
2101 cResources += usCount;
2102
2103 // first pass, skip NAMEINFO table
2104 ENSURE(DosSetFilePtr(hfExe,
2105 usCount*6*sizeof(USHORT),
2106 FILE_CURRENT,
2107 &ulDummy));
2108 }
2109
2110 if (cResources)
2111 {
2112 USHORT usCurrent = 0;
2113 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2114
2115 paResources = (PFSYSRESOURCE)malloc(cb);
2116 if (!paResources)
2117 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2118
2119 memset(paResources, 0, cb);
2120
2121 ENSURE_SAFE(DosSetFilePtr(hfExe,
2122 pNEHeader->usResTblOfs
2123 + ulNewHeaderOfs,
2124 FILE_BEGIN,
2125 &ulDummy));
2126
2127 ENSURE_SAFE(DosRead(hfExe,
2128 &usAlignShift,
2129 sizeof(usAlignShift),
2130 &ulDummy));
2131
2132 while (TRUE)
2133 {
2134 USHORT usTypeID;
2135 USHORT usCount;
2136 int i;
2137
2138 ENSURE_SAFE(DosRead(hfExe,
2139 &usTypeID,
2140 sizeof(usTypeID),
2141 &ulDummy));
2142
2143 if (usTypeID == 0)
2144 break;
2145
2146 ENSURE_SAFE(DosRead(hfExe,
2147 &usCount,
2148 sizeof(usCount),
2149 &ulDummy));
2150
2151 ENSURE_SAFE(DosSetFilePtr(hfExe,
2152 sizeof(ULONG),
2153 FILE_CURRENT,
2154 &ulDummy));
2155
2156 // second pass, read NAMEINFO table
2157 for (i = 0; i < usCount; i++)
2158 {
2159 USHORT usLength,
2160 usFlags,
2161 usID;
2162
2163 ENSURE_SAFE(DosSetFilePtr(hfExe,
2164 sizeof(USHORT),
2165 FILE_CURRENT,
2166 &ulDummy));
2167
2168 ENSURE_SAFE(DosRead(hfExe,
2169 &usLength,
2170 sizeof(USHORT),
2171 &ulDummy));
2172 ENSURE_SAFE(DosRead(hfExe,
2173 &usFlags,
2174 sizeof(USHORT),
2175 &ulDummy));
2176 ENSURE_SAFE(DosRead(hfExe,
2177 &usID,
2178 sizeof(USHORT),
2179 &ulDummy));
2180
2181 ENSURE_SAFE(DosSetFilePtr(hfExe,
2182 2*sizeof(USHORT),
2183 FILE_CURRENT,
2184 &ulDummy));
2185
2186 // !!! strings ids and types not handled yet
2187 // !!! 15th bit is used to denotes strings
2188 // !!! offsets [lafaix]
2189 paResources[usCurrent].ulType = usTypeID ^ 0x8000;
2190 paResources[usCurrent].ulID = usID ^ 0x8000;
2191 paResources[usCurrent].ulSize = usLength << usAlignShift;
2192 paResources[usCurrent].ulFlag = usFlags & 0x70;
2193
2194 usCurrent++;
2195 }
2196 }
2197 }
2198 }
2199 } // end else if (pExec->ulExeFormat == EXEFORMAT_NE)
2200 else
2201 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
2202
2203 *ppaResources = paResources;
2204 *pcResources = cResources;
2205
2206 ENSURE_FINALLY;
2207 // if we had an error above, clean up
2208 free(paResources);
2209 ENSURE_END;
2210 }
2211 else
2212 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
2213
2214 ENSURE_OK;
2215}
2216
2217/*
2218 *@@ doshExecFreeResources:
2219 * frees resources allocated by doshExecQueryResources.
2220 *
2221 *@@added V0.9.7 (2000-12-18) [lafaix]
2222 */
2223
2224APIRET doshExecFreeResources(PFSYSRESOURCE paResources)
2225{
2226 free(paResources);
2227 return (NO_ERROR);
2228}
2229
2230/*
2231 *@@ doshLoadLXMaps:
2232 * loads the three main LX maps into the given
2233 * EXECUTABLE structure.
2234 *
2235 * This loads:
2236 *
2237 * 1) the LX resource table;
2238 *
2239 * 2) the LX object table;
2240 *
2241 * 3) the LX object _page_ table (object map).
2242 *
2243 * Note that this is not automatically called
2244 * by doshExecOpen to save time, since the LX
2245 * maps are not needed for all the other exe
2246 * functions.
2247 *
2248 * This returns:
2249 *
2250 * -- NO_ERROR: all three LX maps were loaded,
2251 * and pExec->fLXMapsLoaded was set to TRUE.
2252 *
2253 * -- ERROR_INVALID_PARAMETER
2254 *
2255 * -- ERROR_INVALID_EXE_SIGNATURE: pExec does
2256 * not specify an LX executable.
2257 *
2258 * -- ERROR_NO_DATA: at least one of the structs
2259 * does not exist.
2260 *
2261 * -- ERROR_NOT_ENOUGH_MEMORY
2262 *
2263 * plus the error codes of doshReadAt.
2264 *
2265 * Call doshFreeLXMaps to clean up explicitly, but
2266 * that func automatically gets called by doshExecClose.
2267 *
2268 *@@added V0.9.16 (2001-12-08) [umoeller]
2269 */
2270
2271APIRET doshLoadLXMaps(PEXECUTABLE pExec)
2272{
2273 APIRET arc;
2274
2275 PLXHEADER pLXHeader;
2276
2277 if (!pExec)
2278 arc = ERROR_INVALID_PARAMETER;
2279 else if (pExec->fLXMapsLoaded)
2280 // already loaded:
2281 arc = NO_ERROR;
2282 else if ( (pExec->ulExeFormat != EXEFORMAT_LX)
2283 || (!(pLXHeader = pExec->pLXHeader))
2284 )
2285 arc = ERROR_INVALID_EXE_SIGNATURE;
2286 else
2287 {
2288 PXFILE pFile = pExec->pFile;
2289 ULONG ulNewHeaderOfs = 0;
2290 ULONG cb;
2291
2292 if (pExec->pDosExeHeader)
2293 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
2294 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
2295
2296 // resource table
2297 if ( (!(arc = doshAllocArray(pLXHeader->ulResTblCnt,
2298 sizeof(RESOURCETABLEENTRY),
2299 (PBYTE*)&pExec->pRsTbl,
2300 &cb)))
2301 && (!(arc = doshReadAt(pFile,
2302 pLXHeader->ulResTblOfs
2303 + ulNewHeaderOfs,
2304 &cb,
2305 (PBYTE)pExec->pRsTbl)))
2306 )
2307 {
2308 // object table
2309 if ( (!(arc = doshAllocArray(pLXHeader->ulObjCount,
2310 sizeof(OBJECTTABLEENTRY),
2311 (PBYTE*)&pExec->pObjTbl,
2312 &cb)))
2313 && (!(arc = doshReadAt(pFile,
2314 pLXHeader->ulObjTblOfs
2315 + ulNewHeaderOfs,
2316 &cb,
2317 (PBYTE)pExec->pObjTbl)))
2318 )
2319 {
2320 // object page table
2321 if ( (!(arc = doshAllocArray(pLXHeader->ulPageCount,
2322 sizeof(OBJECTPAGETABLEENTRY),
2323 (PBYTE*)&pExec->pObjPageTbl,
2324 &cb)))
2325 && (!(arc = doshReadAt(pFile,
2326 pLXHeader->ulObjPageTblOfs
2327 + ulNewHeaderOfs,
2328 &cb,
2329 (PBYTE)pExec->pObjPageTbl)))
2330 )
2331 {
2332 }
2333 }
2334 }
2335
2336 if (!arc)
2337 pExec->fLXMapsLoaded = TRUE;
2338 else
2339 doshFreeLXMaps(pExec);
2340 }
2341
2342 return (arc);
2343}
2344
2345/*
2346 *@@ doshFreeLXMaps:
2347 * frees only the LX maps allocated by doshLoadLXMaps.
2348 * This gets called automatically by doshExecClose.
2349 *
2350 *@@added V0.9.16 (2001-12-08) [umoeller]
2351 */
2352
2353VOID doshFreeLXMaps(PEXECUTABLE pExec)
2354{
2355 FREE(pExec->pRsTbl);
2356 FREE(pExec->pObjTbl);
2357 FREE(pExec->pObjPageTbl);
2358 pExec->fLXMapsLoaded = FALSE;
2359}
2360
2361/*
2362 *@@ doshLoadOS2NEMaps:
2363 *
2364 * This works only if
2365 *
2366 * -- ulExeFormat == EXEFORMAT_NE and
2367 *
2368 * -- ulOS == EXEOS_OS2,
2369 *
2370 * but not with Win16 NE executables.
2371 *
2372 *@@added V0.9.16 (2001-12-08) [umoeller]
2373 */
2374
2375APIRET doshLoadOS2NEMaps(PEXECUTABLE pExec)
2376{
2377 APIRET arc;
2378
2379 PNEHEADER pNEHeader;
2380
2381 if (!pExec)
2382 arc = ERROR_INVALID_PARAMETER;
2383 else if (pExec->fOS2NEMapsLoaded)
2384 // already loaded:
2385 arc = NO_ERROR;
2386 else if ( (pExec->ulExeFormat != EXEFORMAT_NE)
2387 || (pExec->ulOS != EXEOS_OS2)
2388 || (!(pNEHeader = pExec->pNEHeader))
2389 )
2390 arc = ERROR_INVALID_EXE_SIGNATURE;
2391 else
2392 {
2393 PXFILE pFile = pExec->pFile;
2394 ULONG ulNewHeaderOfs = 0;
2395 ULONG cb;
2396
2397 if (pExec->pDosExeHeader)
2398 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
2399 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
2400
2401 // resource table
2402 if ( (!(arc = doshAllocArray(pNEHeader->usResSegmCount,
2403 sizeof(OS2NERESTBLENTRY),
2404 (PBYTE*)&pExec->paOS2NEResTblEntry,
2405 &cb)))
2406 && (!(arc = doshReadAt(pFile,
2407 pNEHeader->usResTblOfs
2408 + ulNewHeaderOfs,
2409 &cb,
2410 (PBYTE)pExec->paOS2NEResTblEntry)))
2411 )
2412 {
2413 // resource segments
2414 if ( (!(arc = doshAllocArray(pNEHeader->usResSegmCount,
2415 sizeof(OS2NESEGMENT),
2416 (PBYTE*)&pExec->paOS2NESegments,
2417 &cb)))
2418 && (!(arc = doshReadAt(pFile,
2419 pNEHeader->usResTblOfs
2420 + ulNewHeaderOfs
2421 - cb, // pNEHeader->usResSegmCount * sizeof(struct new_seg)
2422 &cb,
2423 (PBYTE)pExec->paOS2NESegments)))
2424 )
2425 {
2426 }
2427 }
2428
2429 if (!arc)
2430 pExec->fOS2NEMapsLoaded = TRUE;
2431 else
2432 doshFreeNEMaps(pExec);
2433 }
2434
2435 return (arc);
2436}
2437
2438/*
2439 *@@ doshFreeNEMaps:
2440 *
2441 *@@added V0.9.16 (2001-12-08) [umoeller]
2442 */
2443
2444VOID doshFreeNEMaps(PEXECUTABLE pExec)
2445{
2446 FREE(pExec->paOS2NEResTblEntry);
2447 FREE(pExec->paOS2NESegments);
2448 pExec->fOS2NEMapsLoaded = FALSE;
2449}
2450
2451/*
2452 *@@ doshExecClose:
2453 * this closes an executable opened with doshExecOpen.
2454 * Always call this function if NO_ERROR was returned by
2455 * doshExecOpen.
2456 *
2457 * This automaticall calls doshFreeLXMaps.
2458 *
2459 *@@added V0.9.0 [umoeller]
2460 *@@changed V0.9.16 (2001-12-08) [umoeller]: fixed memory leaks
2461 *@@changed V0.9.16 (2001-12-08) [umoeller]: changed prototype to null the pExec ptr
2462 */
2463
2464APIRET doshExecClose(PEXECUTABLE *ppExec)
2465{
2466 APIRET arc = NO_ERROR;
2467 PEXECUTABLE pExec;
2468 if ( (ppExec)
2469 && (pExec = *ppExec)
2470 )
2471 {
2472 char **papsz[] =
2473 {
2474 (char**)&pExec->pDosExeHeader,
2475 (char**)&pExec->pNEHeader,
2476 (char**)&pExec->pLXHeader,
2477 (char**)&pExec->pPEHeader,
2478
2479 &pExec->pszDescription,
2480 &pExec->pszVendor,
2481 &pExec->pszVersion,
2482 &pExec->pszInfo,
2483
2484 &pExec->pszBuildDateTime,
2485 &pExec->pszBuildMachine,
2486 &pExec->pszASD,
2487 &pExec->pszLanguage,
2488 &pExec->pszCountry,
2489 &pExec->pszRevision,
2490 &pExec->pszUnknown,
2491 &pExec->pszFixpak
2492 };
2493 ULONG ul;
2494
2495 doshFreeLXMaps(pExec);
2496 doshFreeNEMaps(pExec);
2497
2498 // fixed the memory leaks with the missing fields,
2499 // turned this into a loop
2500 for (ul = 0;
2501 ul < sizeof(papsz) / sizeof(papsz[0]);
2502 ul++)
2503 {
2504 PSZ pThis;
2505 if (pThis = *papsz[ul])
2506 {
2507 free(pThis);
2508 pThis = NULL;
2509 }
2510 }
2511
2512 doshClose(&pExec->pFile);
2513
2514 free(pExec);
2515 *ppExec = NULL;
2516 }
2517 else
2518 arc = ERROR_INVALID_PARAMETER;
2519
2520 return (arc);
2521}
2522
2523/*
2524 *@@ CopyToBuffer:
2525 * little helper for copying a string to
2526 * a target buffer with length checking.
2527 *
2528 * Returns:
2529 *
2530 * -- NO_ERROR
2531 *
2532 * -- ERROR_BUFFER_OVERFLOW if pszTarget does
2533 * not have enough room to hold pcszSource
2534 * (including the null terminator).
2535 *
2536 *@@added V0.9.16 (2001-10-08) [umoeller]
2537 */
2538
2539APIRET CopyToBuffer(PSZ pszTarget, // out: target buffer
2540 PCSZ pcszSource, // in: source string
2541 ULONG cbTarget) // in: size of target buffer
2542{
2543 ULONG ulLength = strlen(pcszSource);
2544 if (ulLength < cbTarget)
2545 {
2546 memcpy(pszTarget,
2547 pcszSource,
2548 ulLength + 1);
2549 return (NO_ERROR);
2550 }
2551
2552 return(ERROR_BUFFER_OVERFLOW);
2553}
2554
2555/*
2556 *@@ doshSearchPath:
2557 * replacement for DosSearchPath.
2558 *
2559 * This looks along all directories which are
2560 * specified in the value of the given environment
2561 * variable if pcszFile is found.
2562 *
2563 * As opposed to the stupid DosSearchPath, this
2564 * ignores subdirectories in the path particles.
2565 * For example, DosSearchPath would usually not
2566 * find an INSTALL file because \OS2 contains
2567 * an INSTALL directory, or NETSCAPE because
2568 * \OS2\INSTALL contains a NETSCAPE directory.
2569 *
2570 * Returns:
2571 *
2572 * -- NO_ERROR: pszExecutable has received the
2573 * full path of pcszFile.
2574 *
2575 * -- ERROR_FILE_NOT_FOUND: pcszFile was not found
2576 * in the specified path (or is a directory).
2577 *
2578 * -- ERROR_BUFFER_OVERFLOW: pcszFile was found, but
2579 * the pszExecutable buffer is too small to hold
2580 * the full path.
2581 *
2582 *@@added V0.9.16 (2001-10-08) [umoeller]
2583 */
2584
2585APIRET doshSearchPath(const char *pcszPath, // in: path variable name (e.g. "PATH")
2586 const char *pcszFile, // in: file to look for (e.g. "LVM.EXE")
2587 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
2588 ULONG cbExecutable) // in: sizeof (*pszExecutable)
2589{
2590 APIRET arc = NO_ERROR;
2591
2592 // get the PATH value
2593 PCSZ pcszPathValue;
2594 if (!(arc = DosScanEnv((PSZ)pcszPath,
2595#if __cplusplus
2596 &pcszPathValue)))
2597#else
2598 (PSZ*)&pcszPathValue)))
2599#endif
2600 {
2601 // run thru the path components
2602 PSZ pszPathCopy;
2603 if (pszPathCopy = strdup(pcszPathValue))
2604 {
2605 PSZ pszToken = strtok(pszPathCopy, ";");
2606 while (pszToken)
2607 {
2608 CHAR szFileMask[2*CCHMAXPATH];
2609 FILESTATUS3 fs3;
2610
2611 sprintf(szFileMask,
2612 "%s\\%s",
2613 pszToken, // path particle
2614 pcszFile); // e.g. "netscape"
2615
2616 if ( (!(arc = DosQueryPathInfo(szFileMask,
2617 FIL_STANDARD,
2618 &fs3,
2619 sizeof(fs3))))
2620 // make sure it's not a directory
2621 // and that it's not hidden
2622 && (!(fs3.attrFile & (FILE_DIRECTORY | FILE_HIDDEN)))
2623 )
2624 {
2625 // copy
2626 arc = CopyToBuffer(pszExecutable,
2627 szFileMask,
2628 cbExecutable);
2629 // and stop
2630 break;
2631 }
2632 else
2633 arc = ERROR_FILE_NOT_FOUND;
2634 // and search on
2635
2636 pszToken = strtok(NULL, ";");
2637 };
2638
2639 free(pszPathCopy);
2640 }
2641 else
2642 arc = ERROR_NOT_ENOUGH_MEMORY;
2643 }
2644
2645 return (arc);
2646}
2647
2648/*
2649 * FindFile:
2650 * helper for doshFindExecutable.
2651 *
2652 *added V0.9.11 (2001-04-25) [umoeller]
2653 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewrote second half for DosSearchPath replacement, which returns directories too
2654 */
2655
2656APIRET FindFile(const char *pcszCommand, // in: command (e.g. "lvm")
2657 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
2658 ULONG cbExecutable) // in: sizeof (*pszExecutable)
2659{
2660 APIRET arc = NO_ERROR;
2661 FILESTATUS3 fs3;
2662
2663 if ( (strchr(pcszCommand, '\\'))
2664 || (strchr(pcszCommand, ':'))
2665 )
2666 {
2667 // looks like this is qualified:
2668 arc = DosQueryPathInfo((PSZ)pcszCommand,
2669 FIL_STANDARD,
2670 &fs3,
2671 sizeof(fs3));
2672 if (!arc)
2673 if (!(fs3.attrFile & FILE_DIRECTORY))
2674 arc = CopyToBuffer(pszExecutable,
2675 pcszCommand,
2676 cbExecutable);
2677 else
2678 // directory:
2679 arc = ERROR_INVALID_EXE_SIGNATURE;
2680 }
2681 else
2682 {
2683 // non-qualified:
2684 /* arc = DosSearchPath(SEARCH_IGNORENETERRS
2685 | SEARCH_ENVIRONMENT
2686 | SEARCH_CUR_DIRECTORY,
2687 "PATH",
2688 (PSZ)pcszCommand,
2689 pszExecutable,
2690 cbExecutable); */
2691 // The above is not useable. It returns directories
2692 // on the path... for example, it returns \OS2\INSTALL\NETSCAPE
2693 // if netscape is looked for. So we search manually... sigh.
2694 // V0.9.16 (2001-10-08) [umoeller]
2695 arc = doshSearchPath("PATH",
2696 pcszCommand,
2697 pszExecutable,
2698 cbExecutable);
2699 }
2700
2701 return (arc);
2702}
2703
2704/*
2705 *@@ doshFindExecutable:
2706 * this attempts to find an executable by doing the
2707 * following:
2708 *
2709 * 1) If pcszCommand appears to be qualified (i.e. contains
2710 * a backslash), this checks for whether the file exists.
2711 * If it is a directory, ERROR_INVALID_EXE_SIGNATURE is
2712 * returned.
2713 *
2714 * 2) If pcszCommand contains no backslash, this searches
2715 * all directories on the PATH in order to find the full
2716 * path of the executable. Starting with V0.9.16, we
2717 * use doshSearchPath for that.
2718 *
2719 * papcszExtensions determines if additional searches are to be
2720 * performed if the file doesn't exist (case 1) or doshSearchPath
2721 * returned ERROR_FILE_NOT_FOUND (case 2).
2722 * This must point to an array of strings specifying the extra
2723 * extensions to search for.
2724 *
2725 * If both papcszExtensions and cExtensions are null, no
2726 * extra searches are performed.
2727 *
2728 * Returns:
2729 *
2730 * -- NO_ERROR: pszExecutable has received the full path of
2731 * the executable found by DosSearchPath.
2732 *
2733 * -- ERROR_FILE_NOT_FOUND
2734 *
2735 * -- ERROR_BUFFER_OVERFLOW: pcszCommand was found, but
2736 * the pszExecutable buffer is too small to hold
2737 * the full path.
2738 *
2739 * Example:
2740 *
2741 + const char *aExtensions[] = { "EXE",
2742 + "COM",
2743 + "CMD"
2744 + };
2745 + CHAR szExecutable[CCHMAXPATH];
2746 + APIRET arc = doshFindExecutable("lvm",
2747 + szExecutable,
2748 + sizeof(szExecutable),
2749 + aExtensions,
2750 + 3);
2751 *
2752 *@@added V0.9.9 (2001-03-07) [umoeller]
2753 *@@changed V0.9.11 (2001-04-25) [umoeller]: this never worked for qualified pcszCommand's, fixed
2754 */
2755
2756APIRET doshFindExecutable(const char *pcszCommand, // in: command (e.g. "lvm")
2757 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
2758 ULONG cbExecutable, // in: sizeof (*pszExecutable)
2759 const char **papcszExtensions, // in: array of extensions (without dots)
2760 ULONG cExtensions) // in: array item count
2761{
2762 APIRET arc = FindFile(pcszCommand,
2763 pszExecutable,
2764 cbExecutable);
2765
2766 if ( (arc == ERROR_FILE_NOT_FOUND) // not found?
2767 && (cExtensions) // any extra searches wanted?
2768 )
2769 {
2770 // try additional things then
2771 PSZ psz2 = (PSZ)malloc(strlen(pcszCommand) + 20);
2772 if (psz2)
2773 {
2774 ULONG ul;
2775 for (ul = 0;
2776 ul < cExtensions;
2777 ul++)
2778 {
2779 const char *pcszExtThis = papcszExtensions[ul];
2780 sprintf(psz2,
2781 "%s.%s",
2782 pcszCommand,
2783 pcszExtThis);
2784 arc = FindFile(psz2,
2785 pszExecutable,
2786 cbExecutable);
2787 if (arc != ERROR_FILE_NOT_FOUND)
2788 break;
2789 }
2790
2791 free(psz2);
2792 }
2793 else
2794 arc = ERROR_NOT_ENOUGH_MEMORY;
2795 }
2796
2797 return (arc);
2798}
2799
2800/*
2801 *@@category: Helpers\Control program helpers\Partitions info
2802 * functions for retrieving partition information directly
2803 * from the partition tables on the disk. See doshGetPartitionsList.
2804 */
2805
2806/********************************************************************
2807 *
2808 * Partition functions
2809 *
2810 ********************************************************************/
2811
2812/*
2813 *@@ doshQueryDiskCount:
2814 * returns the no. of physical disks installed
2815 * on the system.
2816 *
2817 *@@added V0.9.0 [umoeller]
2818 */
2819
2820UINT doshQueryDiskCount(VOID)
2821{
2822 USHORT usCount = 0;
2823 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &usCount, 2, 0, 0);
2824 return (usCount);
2825}
2826
2827/*
2828 *@@ doshType2FSName:
2829 * this returns a static, zero-terminated string
2830 * for the given FS type, or NULL if the type
2831 * is unknown.
2832 *
2833 *@@added V0.9.0 [umoeller]
2834 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewritten
2835 */
2836
2837const char* doshType2FSName(unsigned char bFSType) // in: FS type
2838{
2839 switch (bFSType)
2840 {
2841 case 0x00: return "empty";
2842 case 0x01: return "DOS 12-bit FAT < 10 Mb";
2843 case 0x02: return "XENIX root file system";
2844 case 0x03: return "XENIX /usr file system (obsolete)";
2845 case 0x04: return "DOS 16-bit FAT < 32 Mb";
2846 case 0x05: return "DOS 3.3+ extended partition";
2847 case 0x06: return "DOS 3.31+ 16-bit FAT > 32 Mb";
2848 case 0x07: return "HPFS/NTFS/QNX/Advanced Unix";
2849 case 0x08: return "OS/2 1.0-1.3/AIX/Commodore/DELL";
2850 case 0x09: return "AIX data/Coherent";
2851 case 0x0A: return "OS/2 Boot Manager/OPUS/Coherent Swap";
2852 case 0x0B: return "Windows95 with 32-bit FAT";
2853 case 0x0C: return "Windows95 with 32-bit FAT (LBA)";
2854 case 0x0E: return "Windows 95 VFAT (06h plus LBA)";
2855 case 0x0F: return "Windows 95 VFAT (05h plus LBA)";
2856 case 0x10: return "OPUS";
2857 case 0x11: return "OS/2 Boot Manager hidden 12-bit FAT";
2858 case 0x12: return "Compaq Diagnostics";
2859 case 0x14: return "OS/2 Boot Manager hidden sub-32M 16-bit FAT";
2860 case 0x16: return "OS/2 Boot Manager hidden over-32M 16-bit FAT";
2861 case 0x17: return "OS/2 Boot Manager hidden HPFS";
2862 case 0x18: return "AST special Windows swap file (\"Zero-Volt Suspend\")";
2863 // case 0x21: reserved
2864 // case 0x23: reserved
2865 case 0x24: return "NEC MS-DOS 3.x";
2866 // case 0x26: reserved
2867 // case 0x31: reserved
2868 // case 0x33: reserved
2869 // case 0x34: reserved
2870 // case 0x36: reserved
2871 case 0x38: return "Theos";
2872 case 0x3C: return "PowerQuest PartitionMagic recovery partition";
2873 case 0x40: return "VENIX 80286";
2874 case 0x41: return "Personal RISC Boot";
2875 case 0x42: return "SFS (Secure File System) by Peter Gutmann";
2876 case 0x50: return "OnTrack Disk Manager, read-only";
2877 case 0x51: return "OnTrack Disk Manager, read/write";
2878 case 0x52: return "CP/M or Microport System V/386";
2879 case 0x53: return "OnTrack Disk Manager, write-only???";
2880 case 0x54: return "OnTrack Disk Manager (DDO)";
2881 case 0x56: return "GoldenBow VFeature";
2882 case 0x61: return "SpeedStor";
2883 case 0x63: return "Unix SysV/386, 386/ix or Mach, MtXinu BSD 4.3 on Mach or GNU HURD";
2884 case 0x64: return "Novell NetWare 286";
2885 case 0x65: return "Novell NetWare (3.11)";
2886 case 0x67:
2887 case 0x68:
2888 case 0x69: return "Novell";
2889 case 0x70: return "DiskSecure Multi-Boot";
2890 // case 0x71: reserved
2891 // case 0x73: reserved
2892 // case 0x74: reserved
2893 case 0x75: return "PC/IX";
2894 // case 0x76: reserved
2895 case 0x80: return "Minix v1.1 - 1.4a";
2896 case 0x81: return "Minix v1.4b+ or Linux or Mitac Advanced Disk Manager";
2897 case 0x82: return "Linux Swap or Prime";
2898 case 0x83: return "Linux native file system (ext2fs/xiafs)";
2899 case 0x84: return "OS/2-renumbered type 04h (hidden DOS C: drive)";
2900 case 0x86: return "FAT16 volume/stripe set (Windows NT)";
2901 case 0x87: return "HPFS Fault-Tolerant mirrored partition or NTFS volume/stripe set";
2902 case 0x93: return "Amoeba file system";
2903 case 0x94: return "Amoeba bad block table";
2904 case 0xA0: return "Phoenix NoteBIOS Power Management \"Save-to-Disk\" partition";
2905 // case 0xA1: reserved
2906 // case 0xA3: reserved
2907 // case 0xA4: reserved
2908 case 0xA5: return "FreeBSD, BSD/386";
2909 // case 0xA6: reserved
2910 // case 0xB1: reserved
2911 // case 0xB3: reserved
2912 // case 0xB4: reserved
2913 // case 0xB6: reserved
2914 case 0xB7: return "BSDI file system (secondarily swap)";
2915 case 0xB8: return "BSDI swap (secondarily file system)";
2916 case 0xC1: return "DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT";
2917 case 0xC4: return "DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT";
2918 case 0xC6: return "DR DOS 6.0 LOGIN.EXE-secured Huge partition or NT corrupted FAT16 volume/stripe set";
2919 case 0xC7: return "Syrinx Boot or corrupted NTFS volume/stripe set";
2920 case 0xD8: return "CP/M-86";
2921 case 0xDB: return "CP/M, Concurrent CP/M, Concurrent DOS, Convergent Technologies OS";
2922 case 0xE1: return "SpeedStor 12-bit FAT extended partition";
2923 case 0xE3: return "DOS read-only or Storage Dimensions";
2924 case 0xE4: return "SpeedStor 16-bit FAT extended partition";
2925 // case 0xE5: reserved
2926 // case 0xE6: reserved
2927 case 0xF1: return "Storage Dimensions";
2928 case 0xF2: return "DOS 3.3+ secondary partition";
2929 // case 0xF3: reserved
2930 case 0xF4: return "SpeedStor or Storage Dimensions";
2931 // case 0xF6: reserved
2932 case 0xFE: return "LANstep or IBM PS/2 IML";
2933 case 0xFF: return "Xenix bad block table";
2934 }
2935
2936 return NULL;
2937}
2938
2939/*
2940 * AppendPartition:
2941 * this appends the given partition information to
2942 * the given partition list. To do this, a new
2943 * PARTITIONINFO structure is created and appended
2944 * in a list (managed thru the PARTITIONINFO.pNext
2945 * items).
2946 *
2947 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
2948 * With each call of this function, this pointer is advanced
2949 * to point to the newly created PARTITIONINFO, so before
2950 * calling this function for the first time,
2951 *
2952 *@@added V0.9.0 [umoeller]
2953 */
2954
2955APIRET AppendPartition(PARTITIONINFO **pppiFirst,
2956 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
2957 PUSHORT posCount, // in/out: partition count
2958 BYTE bDisk, // in: disk of partition
2959 const char *pszBootName, // in: boot partition name
2960 CHAR cLetter, // in/out: drive letter
2961 BYTE bFsType, // in: file system type
2962 BOOL fPrimary, // in: primary?
2963 BOOL fBootable,
2964 ULONG ulSectors) // in: no. of sectors
2965{
2966 APIRET arc = NO_ERROR;
2967 PPARTITIONINFO ppiNew = NEW(PARTITIONINFO);
2968 if (ppiNew)
2969 {
2970 ZERO(ppiNew);
2971
2972 // store data
2973 ppiNew->bDisk = bDisk;
2974 if ((fBootable) && (pszBootName) )
2975 {
2976 memcpy(ppiNew->szBootName, pszBootName, 8);
2977 ppiNew->szBootName[8] = 0;
2978 }
2979 else
2980 ppiNew->szBootName[0] = 0;
2981 ppiNew->cLetter = cLetter;
2982 ppiNew->bFSType = bFsType;
2983 ppiNew->pcszFSType = doshType2FSName(bFsType);
2984 ppiNew->fPrimary = fPrimary;
2985 ppiNew->fBootable = fBootable;
2986 ppiNew->ulSize = ulSectors / 2048;
2987
2988 ppiNew->pNext = NULL;
2989
2990 (*posCount)++;
2991
2992 if (*pppiFirst == (PPARTITIONINFO)NULL)
2993 {
2994 // first call:
2995 *pppiFirst = ppiNew;
2996 *pppiThis = ppiNew;
2997 }
2998 else
2999 {
3000 // append to list
3001 (**pppiThis).pNext = ppiNew;
3002 *pppiThis = ppiNew;
3003 }
3004 }
3005 else
3006 arc = ERROR_NOT_ENOUGH_MEMORY;
3007
3008 return (arc);
3009}
3010
3011#ifndef __XWPLITE__
3012
3013/*
3014 *@@ doshReadSector:
3015 * reads a physical disk sector.
3016 *
3017 * If NO_ERROR is returned, the sector contents
3018 * have been stored in *buff.
3019 *
3020 * Originally contributed by Dmitry A. Steklenev.
3021 *
3022 *@@added V0.9.0 [umoeller]
3023 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
3024 */
3025
3026APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
3027 void *buff,
3028 USHORT head,
3029 USHORT cylinder,
3030 USHORT sector)
3031{
3032 APIRET arc;
3033 HFILE dh = 0;
3034 char dn[256];
3035
3036 sprintf(dn, "%u:", disk);
3037 if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
3038 {
3039 TRACKLAYOUT DiskIOParm;
3040 ULONG IOCtlDataLength = sizeof(DiskIOParm);
3041 ULONG IOCtlParmLength = 512;
3042
3043 DiskIOParm.bCommand = 0;
3044 DiskIOParm.usHead = head;
3045 DiskIOParm.usCylinder = cylinder;
3046 DiskIOParm.usFirstSector = 0;
3047 DiskIOParm.cSectors = 1;
3048 DiskIOParm.TrackTable[0].usSectorNumber = sector;
3049 DiskIOParm.TrackTable[0].usSectorSize = 512;
3050
3051 arc = DosDevIOCtl(dh,
3052 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
3053 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
3054 buff , IOCtlDataLength, &IOCtlDataLength);
3055
3056 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
3057 }
3058
3059 return (arc);
3060}
3061
3062// Sector and Cylinder values are actually 6 bits and 10 bits:
3063//
3064// 1 1 1 1 1 1
3065// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
3066// ³c c c c c c c c C c S s s s s s³
3067// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
3068//
3069// The high two bits of the second byte are used as the high bits
3070// of a 10-bit value. This allows for as many as 1024 cylinders
3071// and 64 sectors per cylinder.
3072
3073/*
3074 * GetCyl:
3075 * get cylinder number.
3076 *
3077 * Originally contributed by Dmitry A. Steklenev.
3078 *
3079 *@@added V0.9.0 [umoeller]
3080 */
3081
3082static USHORT GetCyl(USHORT rBeginSecCyl)
3083{
3084 return ( (rBeginSecCyl & 0x00C0) << 2)
3085 + ((rBeginSecCyl & 0xFF00) >> 8);
3086}
3087
3088/*
3089 * GetSec:
3090 * get sector number.
3091 *
3092 * Originally contributed by Dmitry A. Steklenev.
3093 *
3094 *@@added V0.9.0 [umoeller]
3095 */
3096
3097static USHORT GetSec(USHORT rBeginSecCyl)
3098{
3099 return rBeginSecCyl & 0x003F;
3100}
3101
3102/*
3103 *@@ doshGetBootManager:
3104 * this goes thru the master boot records on all
3105 * disks to find the boot manager partition.
3106 *
3107 * Returns:
3108 *
3109 * -- NO_ERROR: boot manager found; in that case,
3110 * information about the boot manager
3111 * is written into *pusDisk, *pusPart,
3112 * *BmInfo. Any of these pointers can
3113 * be NULL if you're not interested.
3114 *
3115 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
3116 *
3117 * Originally contributed by Dmitry A. Steklenev.
3118 *
3119 *@@added V0.9.0 [umoeller]
3120 */
3121
3122APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
3123 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
3124 PAR_INFO *pBmInfo) // out: if != NULL, boot manager partition info
3125{
3126 APIRET arc = NO_ERROR;
3127 USHORT count = doshQueryDiskCount(); // Physical disk number
3128 MBR_INFO MBoot; // Master Boot
3129 USHORT usDisk;
3130
3131 if (count > 8) // Not above 8 disks
3132 count = 8;
3133
3134 for (usDisk = 1; usDisk <= count; usDisk++)
3135 {
3136 USHORT usPrim = 0;
3137
3138 // for each disk, read the MBR, which has the
3139 // primary partitions
3140 if ((arc = doshReadSector(usDisk,
3141 &MBoot,
3142 0, // head
3143 0, // cylinder
3144 1))) // sector
3145 return (arc);
3146
3147 // scan primary partitions for whether
3148 // BootManager partition exists
3149 for (usPrim = 0; usPrim < 4; usPrim++)
3150 {
3151 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
3152 {
3153 // this is boot manager:
3154 if (pBmInfo)
3155 *pBmInfo = MBoot.sPrtnInfo[usPrim];
3156 if (pusPart)
3157 *pusPart = usPrim;
3158 if (pusDisk)
3159 *pusDisk = usDisk;
3160 // stop scanning
3161 return (NO_ERROR);
3162 }
3163 }
3164 }
3165
3166 return (ERROR_NOT_SUPPORTED);
3167}
3168
3169/*
3170 * GetPrimaryPartitions:
3171 * this returns the primary partitions.
3172 *
3173 * This gets called from doshGetPartitionsList.
3174 *
3175 * Returns:
3176 *
3177 * -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
3178 *
3179 * Originally contributed by Dmitry A. Steklenev.
3180 *
3181 *@@added V0.9.0 [umoeller]
3182 */
3183
3184APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
3185 PARTITIONINFO **pppiThis,
3186 PUSHORT posCount, // in/out: partition count
3187 PCHAR pcLetter, // in/out: drive letter counter
3188 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
3189 PAR_INFO* pBmInfo, // in: info returned by doshGetBootManager or NULL
3190 UINT iDisk) // in: system's physical disk count
3191{
3192 APIRET arc = NO_ERROR;
3193
3194 if (!pBmInfo)
3195 arc = ERROR_INVALID_PARAMETER;
3196 else
3197 {
3198 SYS_INFO MName[32]; // Name Space from Boot Manager
3199 memset(&MName, 0, sizeof(MName));
3200
3201 // read boot manager name table;
3202 // this is in the boot manager primary partition
3203 // at sector offset 3 (?!?)
3204 if (!(arc = doshReadSector(BmDisk,
3205 &MName,
3206 // head, cylinder, sector of bmgr primary partition:
3207 pBmInfo->bBeginHead,
3208 GetCyl(pBmInfo->rBeginSecCyl),
3209 GetSec(pBmInfo->rBeginSecCyl) + 3)))
3210 {
3211 // got bmgr name table:
3212 MBR_INFO MBoot; // Master Boot
3213 USHORT i;
3214
3215 // read master boot record of this disk
3216 if (!(arc = doshReadSector(iDisk,
3217 &MBoot,
3218 0, // head
3219 0, // cylinder
3220 1))) // sector
3221 {
3222 for (i = 0;
3223 i < 4; // there can be only four primary partitions
3224 i++)
3225 {
3226 // skip unused partition, BootManager or Extended partition
3227 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
3228 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A) // skip boot manager
3229 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x05) // skip extended partition
3230 )
3231 {
3232 BOOL fBootable = ( (pBmInfo)
3233 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
3234 );
3235 // store this partition
3236 if ((arc = AppendPartition(pppiFirst,
3237 pppiThis,
3238 posCount,
3239 iDisk,
3240 (fBootable)
3241 ? (char*)&MName[(iDisk - 1) * 4 + i].name
3242 : "",
3243 *pcLetter,
3244 MBoot.sPrtnInfo[i].bFileSysCode,
3245 TRUE, // primary
3246 fBootable,
3247 MBoot.sPrtnInfo[i].lTotalSects)))
3248 return (arc);
3249 }
3250 }
3251 }
3252 }
3253 }
3254
3255 return (arc);
3256}
3257
3258/*
3259 * GetLogicalDrives:
3260 * this returns info for the logical drives
3261 * in the extended partition. This gets called
3262 * from GetExtendedPartition.
3263 *
3264 * This gets called from GetExtendedPartition.
3265 *
3266 * Originally contributed by Dmitry A. Steklenev.
3267 *
3268 *@@added V0.9.0 [umoeller]
3269 */
3270
3271APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
3272 PARTITIONINFO **pppiThis,
3273 PUSHORT posCount,
3274 PCHAR pcLetter,
3275 PAR_INFO* PrInfo, // in: MBR entry of extended partition
3276 UINT PrDisk,
3277 PAR_INFO* BmInfo)
3278{
3279 APIRET arc = NO_ERROR;
3280 EXT_INFO MBoot; // Master Boot
3281 USHORT i;
3282
3283 if ((arc = doshReadSector(PrDisk,
3284 &MBoot,
3285 PrInfo->bBeginHead,
3286 GetCyl(PrInfo->rBeginSecCyl),
3287 GetSec(PrInfo->rBeginSecCyl))))
3288 return (arc);
3289
3290 for (i = 0; i < 4; i++)
3291 {
3292 // skip unused partition or BootManager partition
3293 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
3294 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A)
3295 )
3296 {
3297 BOOL fBootable = FALSE;
3298 BOOL fAssignLetter = FALSE;
3299
3300 // special work around extended partition
3301 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
3302 {
3303 if ((arc = GetLogicalDrives(pppiFirst,
3304 pppiThis,
3305 posCount,
3306 pcLetter,
3307 &MBoot.sPrtnInfo[i],
3308 PrDisk,
3309 BmInfo)))
3310 return (arc);
3311
3312 continue;
3313 }
3314
3315 // raise driver letter if OS/2 would recognize this drive
3316 if ( (MBoot.sPrtnInfo[i].bFileSysCode < 0x75)
3317 )
3318 fAssignLetter = TRUE;
3319
3320 if (fAssignLetter)
3321 (*pcLetter)++;
3322
3323 fBootable = ( (BmInfo)
3324 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
3325 );
3326
3327 if ((arc = AppendPartition(pppiFirst,
3328 pppiThis,
3329 posCount,
3330 PrDisk,
3331 (fBootable)
3332 ? (char*)&MBoot.sBmNames[i].name
3333 : "",
3334 (fAssignLetter)
3335 ? *pcLetter
3336 : ' ',
3337 MBoot.sPrtnInfo[i].bFileSysCode,
3338 FALSE, // primary
3339 fBootable, // bootable
3340 MBoot.sPrtnInfo[i].lTotalSects)))
3341 return (arc);
3342 }
3343 }
3344
3345 return (NO_ERROR);
3346}
3347
3348/*
3349 * GetExtendedPartition:
3350 * this finds the extended partition on the given
3351 * drive and calls GetLogicalDrives in turn.
3352 *
3353 * This gets called from doshGetPartitionsList.
3354 *
3355 * Originally contributed by Dmitry A. Steklenev.
3356 *
3357 *@@added V0.9.0 [umoeller]
3358 */
3359
3360APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
3361 PARTITIONINFO **pppiThis,
3362 PUSHORT posCount,
3363 PCHAR pcLetter,
3364 PAR_INFO* BmInfo,
3365 UINT iDisk) // in: disk to query
3366{
3367 APIRET arc = NO_ERROR;
3368 MBR_INFO MBoot; // Master Boot
3369 USHORT i;
3370
3371 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
3372 return (arc);
3373
3374 // go thru MBR entries to find extended partition
3375 for (i = 0;
3376 i < 4;
3377 i++)
3378 {
3379 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
3380 {
3381 if ((arc = GetLogicalDrives(pppiFirst,
3382 pppiThis,
3383 posCount,
3384 pcLetter,
3385 &MBoot.sPrtnInfo[i],
3386 iDisk,
3387 BmInfo)))
3388 return (arc);
3389 }
3390 }
3391
3392 return (NO_ERROR);
3393}
3394
3395/*
3396 *@@ ReadFDiskPartitions:
3397 * helper for doshGetPartitionsList for non-LVM
3398 * systems.
3399 *
3400 * Originally contributed by Dmitry A. Steklenev.
3401 *
3402 *@@added V0.9.16 (2001-10-08) [umoeller]
3403 */
3404
3405APIRET ReadFDiskPartitions(PARTITIONINFO **ppPartitionInfos,
3406 USHORT *pcPartitions,
3407 PUSHORT pusContext) // out: error context
3408{
3409 APIRET arc = NO_ERROR;
3410
3411 PAR_INFO BmInfo; // BootManager partition
3412 USHORT usBmDisk; // BootManager disk
3413 USHORT cDisks = doshQueryDiskCount(); // physical disks count
3414 USHORT i;
3415
3416 CHAR cLetter = 'C'; // first drive letter
3417
3418 PARTITIONINFO *ppiTemp = NULL;
3419
3420 if (cDisks > 8) // Not above 8 disks
3421 cDisks = 8;
3422
3423 // get boot manager disk and info
3424 if ((arc = doshGetBootManager(&usBmDisk,
3425 NULL,
3426 &BmInfo)) != NO_ERROR)
3427 {
3428 *pusContext = 1;
3429 }
3430 else
3431 {
3432 // on each disk, read primary partitions
3433 for (i = 1; i <= cDisks; i++)
3434 {
3435 if ((arc = GetPrimaryPartitions(ppPartitionInfos,
3436 &ppiTemp,
3437 pcPartitions,
3438 &cLetter,
3439 usBmDisk,
3440 usBmDisk ? &BmInfo : 0,
3441 i)))
3442 {
3443 *pusContext = 2;
3444 }
3445 }
3446
3447 if (!arc && usBmDisk)
3448 {
3449 // boot manager found:
3450 // on each disk, read extended partition
3451 // with logical drives
3452 for (i = 1; i <= cDisks; i++)
3453 {
3454 if ((arc = GetExtendedPartition(ppPartitionInfos,
3455 &ppiTemp,
3456 pcPartitions,
3457 &cLetter,
3458 &BmInfo,
3459 i)))
3460 {
3461 *pusContext = 3;
3462 }
3463 }
3464 }
3465 } // end else if ((arc = doshGetBootManager(&usBmDisk,
3466
3467 return (arc);
3468}
3469
3470#endif
3471
3472/*
3473 *@@ CleanPartitionInfos:
3474 *
3475 *@@added V0.9.9 (2001-04-07) [umoeller]
3476 */
3477
3478VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
3479{
3480 while (ppiThis)
3481 {
3482 PPARTITIONINFO ppiNext = ppiThis->pNext;
3483 free(ppiThis);
3484 ppiThis = ppiNext;
3485 }
3486}
3487
3488/*
3489 *@@ doshGetPartitionsList:
3490 * this returns lots of information about the
3491 * partitions on all physical disks, which is
3492 * read directly from the MBRs and partition
3493 * tables.
3494 *
3495 * If NO_ERROR is returned by this function,
3496 * *ppPartitionInfo points to a linked list of
3497 * PARTITIONINFO structures, which has
3498 * *pusPartitionCount items.
3499 *
3500 * In that case, use doshFreePartitionsList to
3501 * free the resources allocated by this function.
3502 *
3503 * What this function returns depends on whether
3504 * LVM is installed.
3505 *
3506 * -- If LVM.DLL is found on the LIBPATH, this opens
3507 * the LVM engine and returns the info from the
3508 * LVM engine in the PARTITIONINFO structures.
3509 * The partitions are then sorted by disk in
3510 * ascending order.
3511 *
3512 * -- Otherwise, we parse the partition tables
3513 * manually. The linked list then starts out with
3514 * all the primary partitions, followed by the
3515 * logical drives in the extended partitions.
3516 * This function attempts to guess the correct drive
3517 * letters and stores these with the PARTITIONINFO
3518 * items, but there's no guarantee that this is
3519 * correct. We correctly ignore Linux partitions here
3520 * and give all primary partitions the C: letter, but
3521 * I have no idea what happens with NTFS partitions,
3522 * since I have none.
3523 *
3524 * If an error != NO_ERROR is returned, *pusContext
3525 * will be set to one of the following:
3526 *
3527 * -- 1: boot manager not found
3528 *
3529 * -- 2: primary partitions error
3530 *
3531 * -- 3: secondary partitions error
3532 *
3533 * -- 0: something else.
3534 *
3535 * Originally contributed by Dmitry A. Steklenev.
3536 *
3537 *@@added V0.9.0 [umoeller]
3538 *@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
3539 *@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
3540 */
3541
3542APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
3543 PUSHORT pusContext) // out: error context
3544{
3545 APIRET arc = NO_ERROR;
3546
3547 PLVMINFO pLVMInfo = NULL;
3548
3549 PARTITIONINFO *pPartitionInfos = NULL; // linked list of all partitions
3550 USHORT cPartitions = 0; // bootable partition count
3551
3552 if (!ppList)
3553 return (ERROR_INVALID_PARAMETER);
3554
3555 if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
3556 {
3557 // LVM installed:
3558 arc = doshReadLVMPartitions(pLVMInfo, // in: LVM info
3559 &pPartitionInfos, // out: partitions array
3560 &cPartitions); // out: partitions count
3561 // copied to output below
3562
3563 if (arc)
3564 {
3565 // error: start over
3566 doshFreeLVMInfo(pLVMInfo);
3567 CleanPartitionInfos(pPartitionInfos);
3568 pPartitionInfos = NULL;
3569 cPartitions = 0;
3570 }
3571 }
3572
3573#ifndef __XWPLITE__
3574 if (arc)
3575 // LVM not installed, or failed:
3576 // parse partitions manually
3577 arc = ReadFDiskPartitions(&pPartitionInfos,
3578 &cPartitions,
3579 pusContext);
3580#endif
3581
3582 if (!arc)
3583 {
3584 // no error so far:
3585 *pusContext = 0;
3586
3587 *ppList = NEW(PARTITIONSLIST);
3588 if (!(*ppList))
3589 arc = ERROR_NOT_ENOUGH_MEMORY;
3590 else
3591 {
3592 ZERO(*ppList);
3593
3594 (*ppList)->pPartitionInfo = pPartitionInfos;
3595 (*ppList)->cPartitions = cPartitions;
3596
3597 _Pmpf((__FUNCTION__ ": returning %d partitions", cPartitions));
3598 }
3599 }
3600
3601 if (arc)
3602 CleanPartitionInfos(pPartitionInfos);
3603
3604 _Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
3605
3606 return (arc);
3607}
3608
3609/*
3610 *@@ doshFreePartitionsList:
3611 * this frees the resources allocated by
3612 * doshGetPartitionsList.
3613 *
3614 *@@added V0.9.0 [umoeller]
3615 */
3616
3617APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
3618{
3619 if (!ppList)
3620 return (ERROR_INVALID_PARAMETER);
3621 else
3622 {
3623 CleanPartitionInfos(ppList->pPartitionInfo);
3624 doshFreeLVMInfo(ppList->pLVMInfo);
3625 free(ppList);
3626 }
3627
3628 return (NO_ERROR);
3629}
3630
3631/********************************************************************
3632 *
3633 * LVM declarations
3634 *
3635 ********************************************************************/
3636
3637/*
3638 *@@category: Helpers\Control program helpers\Partitions info\Quick LVM Interface
3639 * functions for transparently interfacing LVM.DLL.
3640 */
3641
3642typedef unsigned char BOOLEAN;
3643typedef unsigned short int CARDINAL16;
3644typedef unsigned long CARDINAL32;
3645typedef unsigned int CARDINAL;
3646typedef unsigned long DoubleWord;
3647
3648#ifdef ADDRESS
3649#undef ADDRESS
3650#endif
3651
3652typedef void* ADDRESS;
3653
3654#pragma pack(1)
3655
3656#define DISK_NAME_SIZE 20
3657#define FILESYSTEM_NAME_SIZE 20
3658#define PARTITION_NAME_SIZE 20
3659#define VOLUME_NAME_SIZE 20
3660
3661/*
3662 *@@ Drive_Control_Record:
3663 * invariant for a disk drive.
3664 *
3665 *@@added V0.9.9 (2001-04-07) [umoeller]
3666 */
3667
3668typedef struct _Drive_Control_Record
3669{
3670 CARDINAL32 Drive_Number; // OS/2 Drive Number for this drive.
3671 CARDINAL32 Drive_Size; // The total number of sectors on the drive.
3672 DoubleWord Drive_Serial_Number; // The serial number assigned to this drive. For info. purposes only.
3673 ADDRESS Drive_Handle; // Handle used for operations on the disk that this record corresponds to.
3674 CARDINAL32 Cylinder_Count; // The number of cylinders on the drive.
3675 CARDINAL32 Heads_Per_Cylinder; // The number of heads per cylinder for this drive.
3676 CARDINAL32 Sectors_Per_Track; // The number of sectors per track for this drive.
3677 BOOLEAN Drive_Is_PRM; // Set to TRUE if this drive is a PRM.
3678 BYTE Reserved[3]; // Alignment.
3679} Drive_Control_Record;
3680
3681/*
3682 *@@ Drive_Control_Array:
3683 * returned by the Get_Drive_Control_Data function
3684 *
3685 *@@added V0.9.9 (2001-04-07) [umoeller]
3686 */
3687
3688typedef struct _Drive_Control_Array
3689{
3690 Drive_Control_Record * Drive_Control_Data; // An array of drive control records.
3691 CARDINAL32 Count; // The number of entries in the array of drive control records.
3692} Drive_Control_Array;
3693
3694/*
3695 *@@ Drive_Information_Record:
3696 * defines the information that can be changed for a specific disk drive.
3697 *
3698 *@@added V0.9.9 (2001-04-07) [umoeller]
3699 */
3700
3701typedef struct _Drive_Information_Record
3702{
3703 CARDINAL32 Total_Available_Sectors; // The number of sectors on the disk which are not currently assigned to a partition.
3704 CARDINAL32 Largest_Free_Block_Of_Sectors; // The number of sectors in the largest contiguous block of available sectors.
3705 BOOLEAN Corrupt_Partition_Table; // If TRUE, then the partitioning information found on the drive is incorrect!
3706 BOOLEAN Unusable; // If TRUE, the drive's MBR is not accessible and the drive can not be partitioned.
3707 BOOLEAN IO_Error; // If TRUE, then the last I/O operation on this drive failed!
3708 BOOLEAN Is_Big_Floppy; // If TRUE, then the drive is a PRM formatted as a big floppy (i.e. the old style removable media support).
3709 char Drive_Name[DISK_NAME_SIZE]; // User assigned name for this disk drive.
3710} Drive_Information_Record;
3711
3712/*
3713 *@@ Partition_Information_Record:
3714 *
3715 *@@added V0.9.9 (2001-04-07) [umoeller]
3716 */
3717
3718typedef struct _Partition_Information_Record
3719{
3720 ADDRESS Partition_Handle;
3721 // The handle used to perform operations on this partition.
3722 ADDRESS Volume_Handle;
3723 // If this partition is part of a volume, this will be the handle of
3724 // the volume. If this partition is NOT part of a volume, then this
3725 // handle will be 0.
3726 ADDRESS Drive_Handle;
3727 // The handle for the drive this partition resides on.
3728 DoubleWord Partition_Serial_Number;
3729 // The serial number assigned to this partition.
3730 CARDINAL32 Partition_Start;
3731 // The LBA of the first sector of the partition.
3732 CARDINAL32 True_Partition_Size;
3733 // The total number of sectors comprising the partition.
3734 CARDINAL32 Usable_Partition_Size;
3735 // The size of the partition as reported to the IFSM. This is the
3736 // size of the partition less any LVM overhead.
3737 CARDINAL32 Boot_Limit;
3738 // The maximum number of sectors from this block of free space that
3739 // can be used to create a bootable partition if you allocate from the
3740 // beginning of the block of free space.
3741 BOOLEAN Spanned_Volume;
3742 // TRUE if this partition is part of a multi-partition volume.
3743 BOOLEAN Primary_Partition;
3744 // True or False. Any non-zero value here indicates that this partition
3745 // is a primary partition. Zero here indicates that this partition is
3746 // a "logical drive" - i.e. it resides inside of an extended partition.
3747 BYTE Active_Flag;
3748 // 80 = Partition is marked as being active.
3749 // 0 = Partition is not active.
3750 BYTE OS_Flag;
3751 // This field is from the partition table. It is known as the OS flag,
3752 // the Partition Type Field, Filesystem Type, and various other names.
3753 // Values of interest
3754 // If this field is: (values are in hex)
3755 // 07 = The partition is a compatibility partition formatted for use
3756 // with an installable filesystem, such as HPFS or JFS.
3757 // 00 = Unformatted partition
3758 // 01 = FAT12 filesystem is in use on this partition.
3759 // 04 = FAT16 filesystem is in use on this partition.
3760 // 0A = OS/2 Boot Manager Partition
3761 // 35 = LVM partition
3762 // 84 = OS/2 FAT16 partition which has been relabeled by Boot Manager to "Hide" it.
3763 BYTE Partition_Type;
3764 // 0 = Free Space
3765 // 1 = LVM Partition (Part of an LVM Volume.)
3766 // 2 = Compatibility Partition
3767 // All other values are reserved for future use.
3768 BYTE Partition_Status;
3769 // 0 = Free Space
3770 // 1 = In Use - i.e. already assigned to a volume.
3771 // 2 = Available - i.e. not currently assigned to a volume.
3772 BOOLEAN On_Boot_Manager_Menu;
3773 // Set to TRUE if this partition is not part of a Volume yet is on the
3774 // Boot Manager Menu.
3775 BYTE Reserved;
3776 // Alignment.
3777 char Volume_Drive_Letter;
3778 // The drive letter assigned to the volume that this partition is a part of.
3779 char Drive_Name[DISK_NAME_SIZE];
3780 // User assigned name for this disk drive.
3781 char File_System_Name[FILESYSTEM_NAME_SIZE];
3782 // The name of the filesystem in use on this partition, if it is known.
3783 char Partition_Name[PARTITION_NAME_SIZE];
3784 // The user assigned name for this partition.
3785 char Volume_Name[VOLUME_NAME_SIZE];
3786 // If this partition is part of a volume, then this will be the
3787 // name of the volume that this partition is a part of. If this
3788 // record represents free space, then the Volume_Name will be
3789 // "FREE SPACE xx", where xx is a unique numeric ID generated by
3790 // LVM.DLL. Otherwise it will be an empty string.
3791} Partition_Information_Record;
3792
3793// The following defines are for use with the Partition_Type field in the
3794// Partition_Information_Record.
3795#define FREE_SPACE_PARTITION 0
3796#define LVM_PARTITION 1
3797#define COMPATIBILITY_PARTITION 2
3798
3799// The following defines are for use with the Partition_Status field in the
3800// Partition_Information_Record.
3801#define PARTITION_IS_IN_USE 1
3802#define PARTITION_IS_AVAILABLE 2
3803#define PARTITION_IS_FREE_SPACE 0
3804
3805/*
3806 *@@ Partition_Information_Array:
3807 * returned by various functions in the LVM Engine.
3808 *
3809 *@@added V0.9.9 (2001-04-07) [umoeller]
3810 */
3811
3812typedef struct _Partition_Information_Array
3813{
3814 Partition_Information_Record * Partition_Array; // An array of Partition_Information_Records.
3815 CARDINAL32 Count; // The number of entries in the Partition_Array.
3816} Partition_Information_Array;
3817
3818/*
3819 *@@ Volume_Information_Record:
3820 * variable information for a volume.
3821 *
3822 *@@added V0.9.9 (2001-04-07) [umoeller]
3823 */
3824
3825typedef struct _Volume_Information_Record
3826{
3827 CARDINAL32 Volume_Size;
3828 // The number of sectors comprising the volume.
3829 CARDINAL32 Partition_Count;
3830 // The number of partitions which comprise this volume.
3831 CARDINAL32 Drive_Letter_Conflict;
3832 // 0 indicates that the drive letter preference for this volume is unique.
3833 // 1 indicates that the drive letter preference for this volume
3834 // is not unique, but this volume got its preferred drive letter anyway.
3835 // 2 indicates that the drive letter preference for this volume
3836 // is not unique, and this volume did NOT get its preferred drive letter.
3837 // 4 indicates that this volume is currently "hidden" - i.e. it has
3838 // no drive letter preference at the current time.
3839 BOOLEAN Compatibility_Volume;
3840 // TRUE if this is for a compatibility volume, FALSE otherwise.
3841 BOOLEAN Bootable;
3842 // Set to TRUE if this volume appears on the Boot Manager menu, or if it is
3843 // a compatibility volume and its corresponding partition is the first active
3844 // primary partition on the first drive.
3845 char Drive_Letter_Preference;
3846 // The drive letter that this volume desires to be.
3847 char Current_Drive_Letter;
3848 // The drive letter currently used to access this volume.
3849 // May be different than Drive_Letter_Preference if there was a conflict ( i.e. Drive_Letter_Preference
3850 // is already in use by another volume ).
3851 char Initial_Drive_Letter;
3852 // The drive letter assigned to this volume by the operating system
3853 // when LVM was started. This may be different from the
3854 // Drive_Letter_Preference if there were conflicts, and
3855 // may be different from the Current_Drive_Letter. This
3856 // will be 0x0 if the Volume did not exist when the LVM Engine
3857 // was opened (i.e. it was created during this LVM session).
3858 BOOLEAN New_Volume;
3859 // Set to FALSE if this volume existed before the LVM Engine was
3860 // opened. Set to TRUE if this volume was created after the LVM
3861 // Engine was opened.
3862 BYTE Status;
3863 // 0 = None.
3864 // 1 = Bootable
3865 // 2 = Startable
3866 // 3 = Installable.
3867 BYTE Reserved_1;
3868 char Volume_Name[VOLUME_NAME_SIZE];
3869 // The user assigned name for this volume.
3870 char File_System_Name[FILESYSTEM_NAME_SIZE];
3871 // The name of the filesystem in use on this partition, if it
3872 // is known.
3873} Volume_Information_Record;
3874
3875#pragma pack()
3876
3877/********************************************************************
3878 *
3879 * Quick LVM Interface API
3880 *
3881 ********************************************************************/
3882
3883/*
3884 *@@ LVMINFOPRIVATE:
3885 * private structure used by doshQueryLVMInfo.
3886 * This is what the LVMINFO pointer really
3887 * points to.
3888 *
3889 *@@added V0.9.9 (2001-04-07) [umoeller]
3890 */
3891
3892typedef struct _LVMINFOPRIVATE
3893{
3894 LVMINFO LVMInfo; // public structure (dosh.h)
3895
3896 // function pointers resolved from LVM.DLL
3897
3898 void (* _System Open_LVM_Engine)(BOOLEAN Ignore_CHS,
3899 CARDINAL32 *Error_Code);
3900
3901 void (* _System Free_Engine_Memory)(ADDRESS Object);
3902
3903 void (* _System Close_LVM_Engine)(void);
3904
3905 Drive_Control_Array (* _System
3906 Get_Drive_Control_Data)(CARDINAL32 *Error_Code);
3907
3908 Drive_Information_Record (* _System
3909 Get_Drive_Status)(ADDRESS Drive_Handle,
3910 CARDINAL32 *Error_Code);
3911
3912 Partition_Information_Array (* _System
3913 Get_Partitions)(ADDRESS Handle,
3914 CARDINAL32 *Error_Code);
3915
3916 Volume_Information_Record (*_System
3917 Get_Volume_Information)(ADDRESS Volume_Handle,
3918 CARDINAL32 *Error_Code);
3919
3920} LVMINFOPRIVATE, *PLVMINFOPRIVATE;
3921
3922#define LVM_ERROR_FIRST 20000
3923
3924/*
3925 *@@ doshQueryLVMInfo:
3926 * creates an LVMINFO structure if LVM is installed.
3927 * Returns that structure (which the caller must free
3928 * using doshFreeLVMInfo) or NULL if LVM.DLL was not
3929 * found along the LIBPATH.
3930 *
3931 *@@added V0.9.9 (2001-04-07) [umoeller]
3932 */
3933
3934APIRET doshQueryLVMInfo(PLVMINFO *ppLVMInfo)
3935{
3936 APIRET arc = NO_ERROR;
3937 CHAR szError[100];
3938 PLVMINFOPRIVATE pLVMInfo = NULL;
3939 HMODULE hmodLVM = NULLHANDLE;
3940
3941 if (!(arc = DosLoadModule(szError,
3942 sizeof(szError),
3943 "LVM",
3944 &hmodLVM)))
3945 {
3946 // got LVM.DLL:
3947 pLVMInfo = NEW(LVMINFOPRIVATE);
3948 if (!pLVMInfo)
3949 arc = ERROR_NOT_ENOUGH_MEMORY;
3950 else
3951 {
3952 // array of function pointers to be resolved from LVM.DLL
3953 RESOLVEFUNCTION aFunctions[] =
3954 {
3955 "Open_LVM_Engine", (PFN*)&pLVMInfo->Open_LVM_Engine,
3956 "Free_Engine_Memory", (PFN*)&pLVMInfo->Free_Engine_Memory,
3957 "Close_LVM_Engine", (PFN*)&pLVMInfo->Close_LVM_Engine,
3958 "Get_Drive_Control_Data", (PFN*)&pLVMInfo->Get_Drive_Control_Data,
3959 "Get_Drive_Status", (PFN*)&pLVMInfo->Get_Drive_Status,
3960 "Get_Partitions", (PFN*)&pLVMInfo->Get_Partitions,
3961 "Get_Volume_Information", (PFN*)&pLVMInfo->Get_Volume_Information
3962 };
3963 ULONG ul;
3964
3965 ZERO(pLVMInfo);
3966
3967 pLVMInfo->LVMInfo.hmodLVM = hmodLVM;
3968
3969 // now resolve function pointers
3970 for (ul = 0;
3971 ul < ARRAYITEMCOUNT(aFunctions);
3972 ul++)
3973 {
3974 PRESOLVEFUNCTION pFuncThis = &aFunctions[ul];
3975 arc = DosQueryProcAddr(hmodLVM,
3976 0, // ordinal, ignored
3977 (PSZ)pFuncThis->pcszFunctionName,
3978 pFuncThis->ppFuncAddress);
3979 if (!pFuncThis->ppFuncAddress)
3980 arc = ERROR_INVALID_NAME;
3981
3982 if (arc)
3983 break;
3984 }
3985 }
3986 }
3987
3988 if (arc)
3989 doshFreeLVMInfo((PLVMINFO)pLVMInfo);
3990 else
3991 *ppLVMInfo = (PLVMINFO)pLVMInfo;
3992
3993 return (arc);
3994}
3995
3996/*
3997 *@@ doshReadLVMPartitions:
3998 * using the LVMINFO parameter from doshQueryLVMInfo,
3999 * builds an array of PARTITIONINFO structures with
4000 * the data returned from LVM.DLL.
4001 *
4002 *@@added V0.9.9 (2001-04-07) [umoeller]
4003 */
4004
4005APIRET doshReadLVMPartitions(PLVMINFO pInfo, // in: LVM info
4006 PPARTITIONINFO *ppPartitionInfo, // out: partitions array
4007 PUSHORT pcPartitions) // out: partitions count
4008{
4009 APIRET arc = NO_ERROR;
4010 CARDINAL32 Error = 0;
4011
4012 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
4013 *ppiTemp = NULL;
4014 USHORT cPartitions = 0; // bootable partition count
4015 PLVMINFOPRIVATE pLVMInfo = (PLVMINFOPRIVATE)pInfo;
4016
4017 _Pmpf((__FUNCTION__ ": entering"));
4018
4019 if (!pLVMInfo)
4020 return (ERROR_INVALID_PARAMETER);
4021
4022 // initialize LVM engine
4023 pLVMInfo->Open_LVM_Engine(TRUE,
4024 &Error);
4025
4026 _Pmpf((" Open_LVM_Engine Error: %d"));
4027
4028 if (!Error)
4029 {
4030 Drive_Control_Array DCA = pLVMInfo->Get_Drive_Control_Data(&Error);
4031 // member records to be freed
4032
4033 _Pmpf((" Get_Drive_Control_Data Error: %d, drive count: %d", Error, DCA.Count));
4034
4035 if ( (!Error)
4036 && (DCA.Count)
4037 )
4038 {
4039 // DCA.Drive_Control_Data now contains drive information records;
4040 // this must be freed
4041 ULONG ulDisk;
4042
4043 for (ulDisk = 0;
4044 ulDisk < DCA.Count;
4045 ulDisk++)
4046 {
4047 Drive_Control_Record *pDriveControlRecord
4048 = &DCA.Drive_Control_Data[ulDisk];
4049 ADDRESS hDrive = pDriveControlRecord->Drive_Handle;
4050
4051 /* Drive_Information_Record pDriveInfoRecord
4052 = pLVMInfo->Get_Drive_Status(hDrive,
4053 &Error);
4054
4055 _Pmpf((" drive %d Get_Drive_Status Error: %d", ulDisk, Error));
4056
4057 if (!Error) */
4058 {
4059 Partition_Information_Array PIA
4060 = pLVMInfo->Get_Partitions(hDrive,
4061 &Error);
4062
4063 _Pmpf((" Get_Partitions Error: %d", Error));
4064
4065 if (!Error)
4066 {
4067 // PIA.Partition_Array now contains
4068 // Partition_Information_Record; must be freed
4069
4070 // now go thru partitions of this drive
4071 ULONG ulPart;
4072 for (ulPart = 0;
4073 ulPart < PIA.Count;
4074 ulPart++)
4075 {
4076 Partition_Information_Record *pPartition
4077 = &PIA.Partition_Array[ulPart];
4078 Volume_Information_Record VolumeInfo;
4079
4080 const char *pcszBootName = NULL; // for now
4081 BOOL fBootable = FALSE;
4082
4083 if (pPartition->Volume_Handle)
4084 {
4085 // this partition is part of a volume:
4086 // only then can it be bootable...
4087 // get the volume info
4088 VolumeInfo
4089 = pLVMInfo->Get_Volume_Information(pPartition->Volume_Handle,
4090 &Error);
4091 pcszBootName = VolumeInfo.Volume_Name;
4092
4093 fBootable = (VolumeInfo.Status == 1);
4094 }
4095
4096
4097 if (arc = AppendPartition(&pPartitionInfos,
4098 &ppiTemp,
4099 &cPartitions,
4100 ulDisk + 1,
4101 pcszBootName,
4102 pPartition->Volume_Drive_Letter,
4103 pPartition->OS_Flag, // FS type
4104 pPartition->Primary_Partition,
4105 fBootable,
4106 pPartition->True_Partition_Size))
4107 break;
4108 }
4109
4110 // clean up partitions
4111 pLVMInfo->Free_Engine_Memory(PIA.Partition_Array);
4112 }
4113 }
4114 /* else
4115 // error:
4116 break; */
4117 }
4118
4119 // clean up drive data
4120 pLVMInfo->Free_Engine_Memory(DCA.Drive_Control_Data);
4121 }
4122 }
4123
4124 // close LVM
4125 pLVMInfo->Close_LVM_Engine();
4126
4127 if (Error)
4128 {
4129 // if we got an error, return it with the
4130 // LVM error offset
4131 arc = LVM_ERROR_FIRST + Error;
4132
4133 CleanPartitionInfos(pPartitionInfos);
4134 }
4135
4136 if (!arc)
4137 {
4138 *ppPartitionInfo = pPartitionInfos;
4139 *pcPartitions = cPartitions;
4140 }
4141
4142 _Pmpf((__FUNCTION__ ": exiting, arg = %d", arc));
4143
4144 return (arc);
4145}
4146
4147/*
4148 *@@ doshFreeLVMInfo:
4149 *
4150 *@@added V0.9.9 (2001-04-07) [umoeller]
4151 */
4152
4153VOID doshFreeLVMInfo(PLVMINFO pInfo)
4154{
4155 if (pInfo)
4156 {
4157 if (pInfo->hmodLVM)
4158 DosFreeModule(pInfo->hmodLVM);
4159
4160 free(pInfo);
4161 }
4162}
4163
4164/*
4165 *@@category: Helpers\Control program helpers\Wildcard matching
4166 * See doshMatch.
4167 */
4168
4169/* ******************************************************************
4170 *
4171 * Wildcard matching
4172 *
4173 ********************************************************************/
4174
4175/*
4176 * PerformMatch:
4177 * compares a single path component. The input strings must
4178 * not have slashes or backslashes in them.
4179 *
4180 * fHasDot must be true if pName contains at least one dot.
4181 *
4182 * Note that this function is recursive.
4183 */
4184
4185BOOL PerformMatch(PCSZ pMask,
4186 PCSZ pName,
4187 int fHasDot)
4188{
4189 while (TRUE)
4190 {
4191 // go thru the pMask char by char
4192 switch (*pMask)
4193 {
4194 case 0:
4195 // if we've reached the end of the mask,
4196 // we better have the end of the name too
4197 if (*pName == 0)
4198 return TRUE;
4199 return FALSE;
4200
4201 case '?':
4202 // a question mark matches one single character;
4203 // it does _not_ match a dot;
4204 // at the end of the component, it also matches
4205 // no characters
4206 if ( (*pName != '.')
4207 && (*pName != 0)
4208 )
4209 ++pName;
4210 ++pMask;
4211 break;
4212
4213 case '*':
4214 // asterisk matches zero or more characters
4215
4216 // skip extra asterisks
4217 do
4218 {
4219 ++pMask;
4220 } while (*pMask == '*');
4221
4222 // pMask points to after '*';
4223 // pName is unchanged... so for each pName
4224 // that follows, check if it matches
4225 while (TRUE)
4226 {
4227 if (PerformMatch(pMask, pName, fHasDot))
4228 // the remainder matched:
4229 // then everything matches
4230 return TRUE;
4231
4232 if (*pName == 0)
4233 return FALSE;
4234
4235 // didn't match: try next pName
4236 ++pName;
4237 }
4238
4239 case '.':
4240 // a dot matches a dot only, even if the name doesn't
4241 // have one at the end
4242 ++pMask;
4243 if (*pName == '.')
4244 ++pName;
4245 else if ( (fHasDot)
4246 || (*pName != 0)
4247 )
4248 return FALSE;
4249 break;
4250
4251 default:
4252 if (*pMask++ != *pName++)
4253 return FALSE;
4254 break;
4255 }
4256 }
4257}
4258
4259/*
4260 *@@ doshMatch:
4261 * this matches '*' and '?' wildcards, similar to what
4262 * DosEditName does. It returns TRUE if the given name
4263 * matches the given mask.
4264 *
4265 * However, this does not require a file to be present, but
4266 * works on strings only.
4267 *
4268 * This accepts both short and fully qualified masks and
4269 * names, but the following rules apply:
4270 *
4271 * -- Either both the mask and the name must be fully
4272 * qualified, or both must not. Otherwise the match fails.
4273 *
4274 * -- If fully qualified, only the last component may contain
4275 * wildcards.
4276 *
4277 * -- This compares without respect to case always.
4278 *
4279 * -- As opposed to the WPS, this handles multiple dots in
4280 * filenames correctly. For example, the WPS will not
4281 * match "*.ZIP" against "whatever-0.9.3.zip", but this
4282 * one will.
4283 *
4284 * This replaces strhMatchOS2 which has been removed with
4285 * V0.9.16 and is a lot faster than the old code, which has
4286 * been completely rewritten.
4287 *
4288 *@@added V0.9.16 (2002-01-01) [umoeller]
4289 */
4290
4291BOOL doshMatch(const char *pcszMask, // in: mask (e.g. "*.txt")
4292 const char *pcszName) // in: string to check (e.g. "test.txt")
4293{
4294 BOOL brc = FALSE;
4295
4296 int iMaskDrive = -1,
4297 iNameDrive = -1;
4298
4299 ULONG cbMask = strlen(pcszMask),
4300 cbName = strlen(pcszName);
4301 PSZ pszMask = (PSZ)_alloca(cbMask + 1),
4302 pszName = (PSZ)_alloca(cbName + 1);
4303
4304 PCSZ pLastMaskComponent,
4305 pLastNameComponent;
4306
4307 ULONG cbMaskPath = 0,
4308 cbNamePath = 0;
4309
4310 CHAR c;
4311
4312 memcpy(pszMask, pcszMask, cbMask + 1);
4313 nlsUpper(pszMask, cbMask);
4314 memcpy(pszName, pcszName, cbName + 1);
4315 nlsUpper(pszName, cbName);
4316
4317 if (pLastMaskComponent = strrchr(pszMask, '\\'))
4318 {
4319 // length of path component
4320 cbMaskPath = pLastMaskComponent - pszMask;
4321 pLastMaskComponent++;
4322 }
4323 else
4324 pLastMaskComponent = pszMask;
4325
4326 if (pLastNameComponent = strrchr(pszName, '\\'))
4327 {
4328 // length of path component
4329 cbNamePath = pLastNameComponent - pszName;
4330 pLastNameComponent++;
4331 }
4332 else
4333 pLastNameComponent = pszName;
4334
4335 // compare paths; if the lengths are different
4336 // or memcmp fails, we can't match
4337 if ( (cbMaskPath == cbNamePath) // can both be null
4338 && ( (cbMaskPath == 0)
4339 || (!memcmp(pszMask, pszName, cbMaskPath))
4340 )
4341 )
4342 {
4343 // alright, paths match:
4344 brc = PerformMatch(pLastMaskComponent,
4345 pLastNameComponent,
4346 // has dot?
4347 (strchr(pLastNameComponent, '.') != NULL));
4348
4349 }
4350
4351 return brc;
4352}
4353
4354
Note: See TracBrowser for help on using the repository browser.