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

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

misc changes

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 117.3 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\stringh.h"
67
68#pragma hdrstop
69
70/*
71 *@@category: Helpers\Control program helpers\Miscellaneous
72 */
73
74/* ******************************************************************
75 *
76 * Miscellaneous
77 *
78 ********************************************************************/
79
80/*
81 *@@ doshIsValidFileName:
82 * this returns NO_ERROR only if pszFile is a valid file name.
83 * This may include a full path.
84 *
85 * If a drive letter is specified, this checks for whether
86 * that drive is a FAT drive and adjust the checks accordingly,
87 * i.e. 8+3 syntax (per path component).
88 *
89 * If no drive letter is specified, this check is performed
90 * for the current drive.
91 *
92 * This also checks if pszFileNames contains characters which
93 * are invalid for the current drive.
94 *
95 * Note: this performs syntactic checks only. This does not
96 * check for whether the specified path components exist.
97 * However, it _is_ checked for whether the given drive
98 * exists.
99 *
100 * This func is especially useful to check filenames that
101 * have been entered by the user in a "Save as" dialog.
102 *
103 * If an error is found, the corresponding DOS error code
104 * is returned:
105 * -- ERROR_INVALID_DRIVE
106 * -- ERROR_FILENAME_EXCED_RANGE (on FAT: no 8+3 filename)
107 * -- ERROR_INVALID_NAME (invalid character)
108 * -- ERROR_CURRENT_DIRECTORY (if fFullyQualified: no full path specified)
109 *
110 *@@changed V0.9.2 (2000-03-11) [umoeller]: added fFullyQualified
111 */
112
113APIRET doshIsValidFileName(const char* pcszFile,
114 BOOL fFullyQualified) // in: if TRUE, pcszFile must be fully q'fied
115{
116 APIRET arc = NO_ERROR;
117 CHAR szPath[CCHMAXPATH+4] = " :";
118 CHAR szComponent[CCHMAXPATH];
119 PSZ p1, p2;
120 BOOL fIsFAT = FALSE;
121 PSZ pszInvalid;
122
123 if (fFullyQualified) // V0.9.2 (2000-03-11) [umoeller]
124 {
125 if ( (*(pcszFile + 1) != ':')
126 || (*(pcszFile + 2) != '\\')
127 )
128 arc = ERROR_CURRENT_DIRECTORY;
129 }
130
131 // check drive first
132 if (*(pcszFile + 1) == ':')
133 {
134 CHAR cDrive = toupper(*pcszFile);
135 double d;
136 // drive specified:
137 strcpy(szPath, pcszFile);
138 szPath[0] = toupper(*pcszFile);
139 arc = doshQueryDiskFree(cDrive - 'A' + 1, &d);
140 }
141 else
142 {
143 // no drive specified: take current
144 ULONG ulDriveNum = 0,
145 ulDriveMap = 0;
146 arc = DosQueryCurrentDisk(&ulDriveNum, &ulDriveMap);
147 szPath[0] = ((UCHAR)ulDriveNum) + 'A' - 1;
148 szPath[1] = ':';
149 strcpy(&szPath[2], pcszFile);
150 }
151
152 if (arc == NO_ERROR)
153 {
154 fIsFAT = doshIsFileOnFAT(szPath);
155
156 pszInvalid = (fIsFAT)
157 ? "<>|+=:;,\"/[] " // invalid characters in FAT
158 : "<>|:\"/"; // invalid characters in IFS's
159
160 // now separate path components
161 p1 = &szPath[2]; // advance past ':'
162
163 do {
164
165 if (*p1 == '\\')
166 p1++;
167
168 p2 = strchr(p1, '\\');
169 if (p2 == NULL)
170 p2 = p1 + strlen(p1);
171
172 if (p1 != p2)
173 {
174 LONG lDotOfs = -1,
175 lAfterDot = -1;
176 ULONG cbFile,
177 ul;
178 PSZ pSource = szComponent;
179
180 strncpy(szComponent, p1, p2-p1);
181 szComponent[p2-p1] = 0;
182 cbFile = strlen(szComponent);
183
184 // now check each path component
185 for (ul = 0; ul < cbFile; ul++)
186 {
187 if (fIsFAT)
188 {
189 // on FAT: only 8 characters allowed before dot
190 if (*pSource == '.')
191 {
192 lDotOfs = ul;
193 lAfterDot = 0;
194 if (ul > 7)
195 return (ERROR_FILENAME_EXCED_RANGE);
196 }
197 }
198 // and check for invalid characters
199 if (strchr(pszInvalid, *pSource) != NULL)
200 return (ERROR_INVALID_NAME);
201
202 pSource++;
203
204 // on FAT, allow only three chars after dot
205 if (fIsFAT)
206 if (lAfterDot != -1)
207 {
208 lAfterDot++;
209 if (lAfterDot > 3)
210 return (ERROR_FILENAME_EXCED_RANGE);
211 }
212 }
213
214 // we are still missing the case of a FAT file
215 // name without extension; if so, check whether
216 // the file stem is <= 8 chars
217 if (fIsFAT)
218 if (lDotOfs == -1) // dot not found:
219 if (cbFile > 8)
220 return (ERROR_FILENAME_EXCED_RANGE);
221 }
222
223 // go for next component
224 p1 = p2+1;
225 } while (*p2);
226 }
227
228 return (arc);
229}
230
231/*
232 *@@ doshMakeRealName:
233 * this copies pszSource to pszTarget, replacing
234 * all characters which are not supported by file
235 * systems with cReplace.
236 *
237 * pszTarget must be at least the same size as pszSource.
238 * If (fIsFAT), the file name will be made FAT-compliant (8+3).
239 *
240 * Returns TRUE if characters were replaced.
241 *
242 *@@changed V0.9.0 (99-11-06) [umoeller]: now replacing "*" too
243 */
244
245BOOL doshMakeRealName(PSZ pszTarget, // out: new real name
246 PSZ pszSource, // in: filename to translate
247 CHAR cReplace, // in: replacement char for invalid
248 // characters (e.g. '!')
249 BOOL fIsFAT) // in: make-FAT-compatible flag
250{
251 ULONG ul,
252 cbSource = strlen(pszSource);
253 LONG lDotOfs = -1,
254 lAfterDot = -1;
255 BOOL brc = FALSE;
256 PSZ pSource = pszSource,
257 pTarget = pszTarget;
258
259 const char *pcszInvalid = (fIsFAT)
260 ? "*<>|+=:;,\"/\\[] " // invalid characters in FAT
261 : "*<>|:\"/\\"; // invalid characters in IFS's
262
263 for (ul = 0; ul < cbSource; ul++)
264 {
265 if (fIsFAT)
266 {
267 // on FAT: truncate filename if neccessary
268 if (*pSource == '.')
269 {
270 lDotOfs = ul;
271 lAfterDot = 0;
272 if (ul > 7) {
273 // only 8 characters allowed before dot,
274 // so set target ptr to dot pos
275 pTarget = pszTarget+8;
276 }
277 }
278 }
279 // and replace invalid characters
280 if (strchr(pcszInvalid, *pSource) == NULL)
281 *pTarget = *pSource;
282 else
283 {
284 *pTarget = cReplace;
285 brc = TRUE;
286 }
287 pTarget++;
288 pSource++;
289
290 // on FAT, allow only three chars after dot
291 if (fIsFAT)
292 if (lAfterDot != -1)
293 {
294 lAfterDot++;
295 if (lAfterDot > 3)
296 break;
297 }
298 }
299 *pTarget = '\0';
300
301 if (fIsFAT)
302 {
303 // we are still missing the case of a FAT file
304 // name without extension; if so, check whether
305 // the file stem is <= 8 chars
306 if (lDotOfs == -1) // dot not found:
307 if (cbSource > 8)
308 *(pszTarget+8) = 0; // truncate
309
310 // convert to upper case
311 strupr(pszTarget);
312 }
313
314 return (brc);
315}
316
317/*
318 *@@ doshSetCurrentDir:
319 * sets the current working directory
320 * to the given path.
321 *
322 * As opposed to DosSetCurrentDir, this
323 * one will change the current drive
324 * also, if one is specified.
325 *
326 *@@changed V0.9.9 (2001-04-04) [umoeller]: this returned an error even if none occured, fixed
327 */
328
329APIRET doshSetCurrentDir(const char *pcszDir)
330{
331 APIRET arc = NO_ERROR;
332 if (!pcszDir)
333 return (ERROR_INVALID_PARAMETER);
334 {
335 if (*pcszDir != 0)
336 if (*(pcszDir+1) == ':')
337 {
338 // drive given:
339 CHAR cDrive = toupper(*(pcszDir));
340 // change drive
341 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
342 // 1 = A:, 2 = B:, ...
343 }
344
345 arc = DosSetCurrentDir((PSZ)pcszDir);
346 }
347
348 return (arc); // V0.9.9 (2001-04-04) [umoeller]
349}
350
351/*
352 *@@category: Helpers\Control program helpers\Environment management
353 * helpers for managing those ugly environment string arrays
354 * that are used with DosStartSession and WinStartApp.
355 */
356
357/* ******************************************************************
358 *
359 * Environment helpers
360 *
361 ********************************************************************/
362
363/*
364 *@@ doshParseEnvironment:
365 * this takes one of those ugly environment strings
366 * as used by DosStartSession and WinStartApp (with
367 * lots of zero-terminated strings one after another
368 * and a duplicate zero byte as a terminator) as
369 * input and splits it into an array of separate
370 * strings in pEnv.
371 *
372 * The newly allocated strings are stored in in
373 * pEnv->papszVars. The array count is stored in
374 * pEnv->cVars.
375 *
376 * Each environment variable will be copied into
377 * one newly allocated string in the array. Use
378 * doshFreeEnvironment to free the memory allocated
379 * by this function.
380 *
381 * Use the following code to browse thru the array:
382 +
383 + DOSENVIRONMENT Env = {0};
384 + if (doshParseEnvironment(pszEnv,
385 + &Env)
386 + == NO_ERROR)
387 + {
388 + if (Env.papszVars)
389 + {
390 + PSZ *ppszThis = Env.papszVars;
391 + for (ul = 0;
392 + ul < Env.cVars;
393 + ul++)
394 + {
395 + PSZ pszThis = *ppszThis;
396 + // pszThis now has something like PATH=C:\TEMP
397 + // ...
398 + // next environment string
399 + ppszThis++;
400 + }
401 + }
402 + doshFreeEnvironment(&Env);
403 + }
404 *
405 *@@added V0.9.4 (2000-08-02) [umoeller]
406 */
407
408APIRET doshParseEnvironment(const char *pcszEnv,
409 PDOSENVIRONMENT pEnv)
410{
411 APIRET arc = NO_ERROR;
412 if (!pcszEnv)
413 arc = ERROR_INVALID_PARAMETER;
414 else
415 {
416 PSZ pszVarThis = (PSZ)pcszEnv;
417 ULONG cVars = 0;
418 // count strings
419 while (*pszVarThis)
420 {
421 cVars++;
422 pszVarThis += strlen(pszVarThis) + 1;
423 }
424
425 pEnv->cVars = cVars;
426 pEnv->papszVars = 0;
427
428 if (cVars)
429 {
430 PSZ *papsz = (PSZ*)malloc(sizeof(PSZ) * cVars);
431 if (!papsz)
432 arc = ERROR_NOT_ENOUGH_MEMORY;
433 else
434 {
435 PSZ *ppszTarget = papsz;
436 memset(papsz, 0, sizeof(PSZ) * cVars);
437 pszVarThis = (PSZ)pcszEnv;
438 while (*pszVarThis)
439 {
440 *ppszTarget = strdup(pszVarThis);
441 ppszTarget++;
442 pszVarThis += strlen(pszVarThis) + 1;
443 }
444
445 pEnv->papszVars = papsz;
446 }
447 }
448 }
449
450 return (arc);
451}
452
453/*
454 *@@ doshGetEnvironment:
455 * calls doshParseEnvironment for the current
456 * process environment, which is retrieved from
457 * the info blocks.
458 *
459 *@@added V0.9.4 (2000-07-19) [umoeller]
460 */
461
462APIRET doshGetEnvironment(PDOSENVIRONMENT pEnv)
463{
464 APIRET arc = NO_ERROR;
465 if (!pEnv)
466 arc = ERROR_INVALID_PARAMETER;
467 else
468 {
469 PTIB ptib = 0;
470 PPIB ppib = 0;
471 arc = DosGetInfoBlocks(&ptib, &ppib);
472 if (arc == NO_ERROR)
473 {
474 PSZ pszEnv = ppib->pib_pchenv;
475 if (pszEnv)
476 arc = doshParseEnvironment(pszEnv, pEnv);
477 else
478 arc = ERROR_BAD_ENVIRONMENT;
479 }
480 }
481
482 return (arc);
483}
484
485/*
486 *@@ doshFindEnvironmentVar:
487 * returns the PSZ* in the pEnv->papszVars array
488 * which specifies the environment variable in pszVarName.
489 *
490 * With pszVarName, you can either specify the variable
491 * name only ("VARNAME") or a full environment string
492 * ("VARNAME=BLAH"). In any case, only the variable name
493 * is compared.
494 *
495 * Returns NULL if no such variable name was found in
496 * the array.
497 *
498 *@@added V0.9.4 (2000-07-19) [umoeller]
499 */
500
501PSZ* doshFindEnvironmentVar(PDOSENVIRONMENT pEnv,
502 PSZ pszVarName)
503{
504 PSZ *ppszRet = 0;
505 if (pEnv)
506 {
507 if ((pEnv->papszVars) && (pszVarName))
508 {
509 PSZ *ppszThis = pEnv->papszVars;
510 PSZ pszThis;
511 ULONG ul = 0;
512 ULONG ulVarNameLen = 0;
513
514 PSZ pszSearch = NULL; // receives "VARNAME="
515 PSZ pFirstEqual = strchr(pszVarName, '=');
516 if (pFirstEqual)
517 pszSearch = strhSubstr(pszVarName, pFirstEqual + 1);
518 else
519 {
520 ulVarNameLen = strlen(pszVarName);
521 pszSearch = (PSZ)malloc(ulVarNameLen + 2);
522 memcpy(pszSearch, pszVarName, ulVarNameLen);
523 *(pszSearch + ulVarNameLen) = '=';
524 *(pszSearch + ulVarNameLen + 1) = 0;
525 }
526
527 ulVarNameLen = strlen(pszSearch);
528
529 for (ul = 0;
530 ul < pEnv->cVars;
531 ul++)
532 {
533 pszThis = *ppszThis;
534
535 if (strnicmp(*ppszThis, pszSearch, ulVarNameLen) == 0)
536 {
537 ppszRet = ppszThis;
538 break;
539 }
540
541 // next environment string
542 ppszThis++;
543 }
544 }
545 }
546
547 return (ppszRet);
548}
549
550/*
551 *@@ doshSetEnvironmentVar:
552 * sets an environment variable in the specified
553 * environment, which must have been initialized
554 * using doshGetEnvironment first.
555 *
556 * pszNewEnv must be a full environment string
557 * in the form "VARNAME=VALUE".
558 *
559 * If "VARNAME" has already been set to something
560 * in the string array in pEnv, that array item
561 * is replaced.
562 *
563 * OTOH, if "VARNAME" has not been set yet, a new
564 * item is added to the array, and pEnv->cVars is
565 * raised by one. In that case, fAddFirst determines
566 * whether the new array item is added to the front
567 * or the tail of the environment list.
568 *
569 *@@added V0.9.4 (2000-07-19) [umoeller]
570 *@@changed V0.9.7 (2000-12-17) [umoeller]: added fAddFirst
571 */
572
573APIRET doshSetEnvironmentVar(PDOSENVIRONMENT pEnv,
574 PSZ pszNewEnv,
575 BOOL fAddFirst)
576{
577 APIRET arc = NO_ERROR;
578 if (!pEnv)
579 arc = ERROR_INVALID_PARAMETER;
580 else
581 {
582 if ((!pEnv->papszVars) || (!pszNewEnv))
583 arc = ERROR_INVALID_PARAMETER;
584 else
585 {
586 PSZ *ppszEnvLine = doshFindEnvironmentVar(pEnv, pszNewEnv);
587 if (ppszEnvLine)
588 {
589 // was set already: replace
590 free(*ppszEnvLine);
591 *ppszEnvLine = strdup(pszNewEnv);
592 if (!(*ppszEnvLine))
593 arc = ERROR_NOT_ENOUGH_MEMORY;
594 }
595 else
596 {
597 PSZ *ppszNew = NULL;
598 PSZ *papszNew = NULL;
599 // not set already:
600 if (fAddFirst)
601 {
602 // add as first entry:
603 papszNew = (PSZ*)malloc(sizeof(PSZ) * (pEnv->cVars + 1));
604 // overwrite first entry
605 ppszNew = papszNew;
606 // copy old entries
607 memcpy(papszNew + 1, // second new entry
608 pEnv->papszVars, // first old entry
609 sizeof(PSZ) * pEnv->cVars);
610 }
611 else
612 {
613 // append at the tail:
614 // reallocate array and add new string
615 papszNew = (PSZ*)realloc(pEnv->papszVars,
616 sizeof(PSZ) * (pEnv->cVars + 1));
617 // overwrite last entry
618 ppszNew = papszNew + pEnv->cVars;
619 }
620
621 if (!papszNew)
622 arc = ERROR_NOT_ENOUGH_MEMORY;
623 else
624 {
625 pEnv->papszVars = papszNew;
626 pEnv->cVars++;
627 *ppszNew = strdup(pszNewEnv);
628 }
629 }
630 }
631 }
632
633 return (arc);
634}
635
636/*
637 *@@ doshConvertEnvironment:
638 * converts an environment initialized by doshGetEnvironment
639 * to the string format required by WinStartApp and DosExecPgm,
640 * that is, one memory block is allocated in *ppszEnv and all
641 * strings in pEnv->papszVars are copied to that block. Each
642 * string is terminated with a null character; the last string
643 * is terminated with two null characters.
644 *
645 * Use free() to free the memory block allocated by this
646 * function in *ppszEnv.
647 *
648 *@@added V0.9.4 (2000-07-19) [umoeller]
649 */
650
651APIRET doshConvertEnvironment(PDOSENVIRONMENT pEnv,
652 PSZ *ppszEnv, // out: environment string
653 PULONG pulSize) // out: size of block allocated in *ppszEnv; ptr can be NULL
654{
655 APIRET arc = NO_ERROR;
656 if (!pEnv)
657 arc = ERROR_INVALID_PARAMETER;
658 else
659 {
660 if (!pEnv->papszVars)
661 arc = ERROR_INVALID_PARAMETER;
662 else
663 {
664 // count memory needed for all strings
665 ULONG cbNeeded = 0,
666 ul = 0;
667 PSZ *ppszThis = pEnv->papszVars;
668
669 for (ul = 0;
670 ul < pEnv->cVars;
671 ul++)
672 {
673 cbNeeded += strlen(*ppszThis) + 1; // length of string plus null terminator
674
675 // next environment string
676 ppszThis++;
677 }
678
679 cbNeeded++; // for another null terminator
680
681 *ppszEnv = (PSZ)malloc(cbNeeded);
682 if (!(*ppszEnv))
683 arc = ERROR_NOT_ENOUGH_MEMORY;
684 else
685 {
686 PSZ pTarget = *ppszEnv;
687 if (pulSize)
688 *pulSize = cbNeeded;
689 ppszThis = pEnv->papszVars;
690
691 // now copy each string
692 for (ul = 0;
693 ul < pEnv->cVars;
694 ul++)
695 {
696 PSZ pSource = *ppszThis;
697
698 while ((*pTarget++ = *pSource++))
699 ;
700
701 // *pTarget++ = 0; // append null terminator per string
702
703 // next environment string
704 ppszThis++;
705 }
706
707 *pTarget++ = 0; // append second null terminator
708 }
709 }
710 }
711
712 return (arc);
713}
714
715/*
716 *@@ doshFreeEnvironment:
717 * frees memory allocated by doshGetEnvironment.
718 *
719 *@@added V0.9.4 (2000-07-19) [umoeller]
720 */
721
722APIRET doshFreeEnvironment(PDOSENVIRONMENT pEnv)
723{
724 APIRET arc = NO_ERROR;
725 if (!pEnv)
726 arc = ERROR_INVALID_PARAMETER;
727 else
728 {
729 if (!pEnv->papszVars)
730 arc = ERROR_INVALID_PARAMETER;
731 else
732 {
733 PSZ *ppszThis = pEnv->papszVars;
734 PSZ pszThis;
735 ULONG ul = 0;
736
737 for (ul = 0;
738 ul < pEnv->cVars;
739 ul++)
740 {
741 pszThis = *ppszThis;
742 free(pszThis);
743 // *ppszThis = NULL;
744 // next environment string
745 ppszThis++;
746 }
747
748 free(pEnv->papszVars);
749 pEnv->cVars = 0;
750 }
751 }
752
753 return (arc);
754}
755
756/*
757 *@@category: Helpers\Control program helpers\Executable info
758 * these functions can retrieve BLDLEVEL information,
759 * imported modules information, exported functions information,
760 * and resources information from any executable module. See
761 * doshExecOpen.
762 */
763
764/********************************************************************
765 *
766 * Executable functions
767 *
768 ********************************************************************/
769
770/*
771 *@@ doshExecOpen:
772 * this opens the specified executable file
773 * (which can be an .EXE, .COM, .DLL, or
774 * driver file) for use with the other
775 * doshExec* functions.
776 *
777 * If no error occurs, NO_ERROR is returned
778 * and a pointer to a new EXECUTABLE structure
779 * is stored in *ppExec. Consider this pointer a
780 * handle and pass it to doshExecClose to clean
781 * up.
782 *
783 * If NO_ERROR is returned, all the fields through
784 * ulOS are set in EXECUTABLE. The psz* fields
785 * which follow afterwards require an additional
786 * call to doshExecQueryBldLevel.
787 *
788 * NOTE: If NO_ERROR is returned, the executable
789 * file has been opened by this function. It will
790 * only be closed when you call doshExecClose.
791 *
792 * If errors occur, this function returns the
793 * following error codes:
794 *
795 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
796 *
797 * -- ERROR_INVALID_EXE_SIGNATURE: specified file
798 * has no DOS EXE header, or it does, but
799 * the extended header is neither LX nor
800 * NE nor PE.
801 *
802 * -- ERROR_INVALID_PARAMETER: ppExec is NULL.
803 *
804 * plus those of DosOpen, DosSetFilePtr, and
805 * DosRead.
806 *
807 *@@added V0.9.0 [umoeller]
808 *@@changed V0.9.1 (2000-02-13) [umoeller]: fixed 32-bits flag
809 *@@changed V0.9.7 (2000-12-20) [lafaix]: fixed ulNewHeaderOfs
810 */
811
812APIRET doshExecOpen(const char* pcszExecutable,
813 PEXECUTABLE* ppExec)
814{
815 APIRET arc = NO_ERROR;
816
817 ULONG ulAction = 0;
818 HFILE hFile;
819
820 if (!ppExec)
821 return (ERROR_INVALID_PARAMETER);
822
823 *ppExec = (PEXECUTABLE)malloc(sizeof(EXECUTABLE));
824 if (!(*ppExec))
825 return (ERROR_NOT_ENOUGH_MEMORY);
826
827 memset((*ppExec), 0, sizeof(EXECUTABLE));
828
829 if (!(arc = DosOpen((PSZ)pcszExecutable,
830 &hFile,
831 &ulAction, // out: action taken
832 0, // in: new file (ignored for read-mode)
833 0, // in: new file attribs (ignored)
834 // open-flags
835 OPEN_ACTION_FAIL_IF_NEW
836 | OPEN_ACTION_OPEN_IF_EXISTS,
837 // open-mode
838 OPEN_FLAGS_FAIL_ON_ERROR // report errors to caller
839 | OPEN_FLAGS_SEQUENTIAL
840 | OPEN_FLAGS_NOINHERIT
841 | OPEN_SHARE_DENYNONE
842 | OPEN_ACCESS_READONLY, // read-only mode
843 NULL))) // no EAs
844 {
845 // file opened successfully:
846
847 ULONG ulLocal = 0;
848
849 // read old DOS EXE header
850 (*ppExec)->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER));
851 if (!((*ppExec)->pDosExeHeader))
852 arc = ERROR_NOT_ENOUGH_MEMORY;
853 else
854 {
855 ULONG ulBytesRead = 0;
856
857 if ( (!(arc = DosSetFilePtr(hFile,
858 0L,
859 FILE_BEGIN,
860 &ulLocal))) // out: new offset
861 && (!(arc = DosRead(hFile,
862 (*ppExec)->pDosExeHeader,
863 sizeof(DOSEXEHEADER),
864 &((*ppExec)->cbDosExeHeader))))
865 )
866 {
867 // now check if we really have a DOS header
868 if ((*ppExec)->pDosExeHeader->usDosExeID != 0x5a4d)
869 arc = ERROR_INVALID_EXE_SIGNATURE;
870 else
871 {
872 // we have a DOS header:
873 if ((*ppExec)->pDosExeHeader->usRelocTableOfs < 0x40)
874 {
875 // neither LX nor PE nor NE:
876 (*ppExec)->ulOS = EXEOS_DOS3;
877 (*ppExec)->ulExeFormat = EXEFORMAT_OLDDOS;
878 }
879 else
880 {
881 // either LX or PE or NE:
882 // read more bytes from position specified in header
883 CHAR achNewHeaderType[2] = "";
884
885 if ( (!(arc = DosSetFilePtr(hFile,
886 (*ppExec)->pDosExeHeader->ulNewHeaderOfs,
887 FILE_BEGIN,
888 &ulLocal)))
889 // read two chars to find out header type
890 && (!(arc = DosRead(hFile,
891 &achNewHeaderType,
892 sizeof(achNewHeaderType),
893 &ulBytesRead)))
894 )
895 {
896 PBYTE pbCheckOS = NULL;
897
898 // reset file ptr
899 DosSetFilePtr(hFile,
900 (*ppExec)->pDosExeHeader->ulNewHeaderOfs,
901 FILE_BEGIN,
902 &ulLocal);
903
904 if (memcmp(achNewHeaderType, "NE", 2) == 0)
905 {
906 // New Executable:
907 (*ppExec)->ulExeFormat = EXEFORMAT_NE;
908 // allocate NE header
909 (*ppExec)->pNEHeader = (PNEHEADER)malloc(sizeof(NEHEADER));
910 if (!((*ppExec)->pNEHeader))
911 arc = ERROR_NOT_ENOUGH_MEMORY;
912 else
913 // read in NE header
914 if (!(arc = DosRead(hFile,
915 (*ppExec)->pNEHeader,
916 sizeof(NEHEADER),
917 &((*ppExec)->cbNEHeader))))
918 if ((*ppExec)->cbNEHeader == sizeof(NEHEADER))
919 pbCheckOS = &((*ppExec)->pNEHeader->bTargetOS);
920 }
921 else if ( (memcmp(achNewHeaderType, "LX", 2) == 0)
922 || (memcmp(achNewHeaderType, "LE", 2) == 0)
923 // this is used by SMARTDRV.EXE
924 )
925 {
926 // OS/2 Linear Executable:
927 (*ppExec)->ulExeFormat = EXEFORMAT_LX;
928 // allocate LX header
929 (*ppExec)->pLXHeader = (PLXHEADER)malloc(sizeof(LXHEADER));
930 if (!((*ppExec)->pLXHeader))
931 arc = ERROR_NOT_ENOUGH_MEMORY;
932 else
933 // read in LX header
934 if (!(arc = DosRead(hFile,
935 (*ppExec)->pLXHeader,
936 sizeof(LXHEADER),
937 &((*ppExec)->cbLXHeader))))
938 if ((*ppExec)->cbLXHeader == sizeof(LXHEADER))
939 pbCheckOS = (PBYTE)(&((*ppExec)->pLXHeader->usTargetOS));
940 }
941 else if (memcmp(achNewHeaderType, "PE", 2) == 0)
942 {
943 (*ppExec)->ulExeFormat = EXEFORMAT_PE;
944 (*ppExec)->ulOS = EXEOS_WIN32;
945 (*ppExec)->f32Bits = TRUE;
946
947 // can't parse this yet
948 }
949 else
950 // strange type:
951 arc = ERROR_INVALID_EXE_SIGNATURE;
952
953 if (pbCheckOS)
954 // BYTE to check for operating system
955 // (NE and LX):
956 switch (*pbCheckOS)
957 {
958 case NEOS_OS2:
959 (*ppExec)->ulOS = EXEOS_OS2;
960 if ((*ppExec)->ulExeFormat == EXEFORMAT_LX)
961 (*ppExec)->f32Bits = TRUE;
962 break;
963
964 case NEOS_WIN16:
965 (*ppExec)->ulOS = EXEOS_WIN16;
966 break;
967
968 case NEOS_DOS4:
969 (*ppExec)->ulOS = EXEOS_DOS4;
970 break;
971
972 case NEOS_WIN386:
973 (*ppExec)->ulOS = EXEOS_WIN386;
974 (*ppExec)->f32Bits = TRUE;
975 break;
976 }
977 } // end if (!(arc = DosSetFilePtr(hFile,
978 }
979 }
980 } // end if (!(arc = DosSetFilePtr(hFile,
981 } // end if (*ppExec)->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER));
982
983 // store exec's HFILE
984 (*ppExec)->hfExe = hFile;
985 } // end if (!(arc = DosOpen((PSZ)pcszExecutable,
986
987 if (arc != NO_ERROR)
988 // error: clean up
989 doshExecClose(*ppExec);
990
991 return (arc);
992}
993
994/*
995 *@@ doshExecClose:
996 * this closes an executable opened with doshExecOpen.
997 * Always call this function if NO_ERROR was returned by
998 * doshExecOpen.
999 *
1000 *@@added V0.9.0 [umoeller]
1001 */
1002
1003APIRET doshExecClose(PEXECUTABLE pExec)
1004{
1005 APIRET arc = NO_ERROR;
1006 if (pExec)
1007 {
1008 if (pExec->pDosExeHeader)
1009 free(pExec->pDosExeHeader);
1010 if (pExec->pNEHeader)
1011 free(pExec->pNEHeader);
1012 if (pExec->pLXHeader)
1013 free(pExec->pLXHeader);
1014
1015 if (pExec->pszDescription)
1016 free(pExec->pszDescription);
1017 if (pExec->pszVendor)
1018 free(pExec->pszVendor);
1019 if (pExec->pszVersion)
1020 free(pExec->pszVersion);
1021 if (pExec->pszInfo)
1022 free(pExec->pszInfo);
1023
1024 if (pExec->hfExe)
1025 arc = DosClose(pExec->hfExe);
1026
1027 free(pExec);
1028 }
1029 else
1030 arc = ERROR_INVALID_PARAMETER;
1031
1032 return (arc);
1033}
1034
1035/*
1036 *@@ doshExecQueryBldLevel:
1037 * this retrieves buildlevel information for an
1038 * LX or NE executable previously opened with
1039 * doshExecOpen.
1040 *
1041 * BuildLevel information must be contained in the
1042 * DESCRIPTION field of an executable's module
1043 * definition (.DEF) file. In order to be readable
1044 * by BLDLEVEL.EXE (which ships with OS/2), this
1045 * string must have the following format:
1046 *
1047 + Description '@#AUTHOR:VERSION#@ DESCRIPTION'
1048 *
1049 * Example:
1050 *
1051 + Description '@#Ulrich M”ller:0.9.0#@ XWorkplace Sound Support Module'
1052 *
1053 * The "Description" entry always ends up as the
1054 * very first entry in the non-resident name table
1055 * in LX and NE executables. So this is what we retrieve
1056 * here.
1057 *
1058 * If the first entry in that table exists, NO_ERROR is
1059 * returned and at least the pszDescription field in
1060 * EXECUTABLE is set to that information.
1061 *
1062 * If that string is in IBM BLDLEVEL format, the string
1063 * is automatically parsed, and the pszVendor, pszVersion,
1064 * and pszInfo fields are also set. In the above examples,
1065 * this would return the following information:
1066 + pszVendor = "Ulrich M”ller"
1067 + pszVersion = "0.9.0"
1068 + pszInfo = "XWorkplace Sound Support Module"
1069 *
1070 * If that string is not in BLDLEVEL format, only pszDescription
1071 * will be set. The other fields remain NULL.
1072 *
1073 * This returns the following errors:
1074 *
1075 * -- ERROR_INVALID_PARAMETER: pExec invalid
1076 *
1077 * -- ERROR_INVALID_EXE_SIGNATURE (191): pExec is not in LX or NE format
1078 *
1079 * -- ERROR_INVALID_DATA (13): non-resident name table not found.
1080 *
1081 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
1082 *
1083 * plus the error codes of DosSetFilePtr and DosRead.
1084 *
1085 *@@added V0.9.0 [umoeller]
1086 *@@changed V0.9.0 (99-10-22) [umoeller]: NE format now supported
1087 *@@changed V0.9.1 (99-12-06): fixed memory leak
1088 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
1089 */
1090
1091APIRET doshExecQueryBldLevel(PEXECUTABLE pExec)
1092{
1093 APIRET arc = NO_ERROR;
1094
1095 if (!pExec)
1096 arc = ERROR_INVALID_PARAMETER;
1097 else
1098 {
1099 ULONG ulNRNTOfs = 0;
1100
1101 if (pExec->ulExeFormat == EXEFORMAT_LX)
1102 {
1103 // OK, LX format:
1104 // check if we have a non-resident name table
1105 if (pExec->pLXHeader == NULL)
1106 arc = ERROR_INVALID_DATA;
1107 else if (pExec->pLXHeader->ulNonResdNameTblOfs == 0)
1108 arc = ERROR_INVALID_DATA;
1109 else
1110 ulNRNTOfs = pExec->pLXHeader->ulNonResdNameTblOfs;
1111 }
1112 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1113 {
1114 // OK, NE format:
1115 // check if we have a non-resident name table
1116 if (pExec->pNEHeader == NULL)
1117 arc = ERROR_INVALID_DATA;
1118 else if (pExec->pNEHeader->ulNonResdTblOfs == 0)
1119 arc = ERROR_INVALID_DATA;
1120 else
1121 ulNRNTOfs = pExec->pNEHeader->ulNonResdTblOfs;
1122 }
1123 else
1124 // neither LX nor NE: stop
1125 arc = ERROR_INVALID_EXE_SIGNATURE;
1126
1127 if ( (!arc)
1128 && (ulNRNTOfs)
1129 )
1130 {
1131 ULONG ulLocal = 0,
1132 ulBytesRead = 0;
1133
1134 // move EXE file pointer to offset of non-resident name table
1135 // (from LX header)
1136 if (!(arc = DosSetFilePtr(pExec->hfExe, // file is still open
1137 ulNRNTOfs, // ofs determined above
1138 FILE_BEGIN,
1139 &ulLocal)))
1140 {
1141 // allocate memory as necessary
1142 PSZ pszNameTable = (PSZ)malloc(2001); // should suffice, because each entry
1143 // may only be 255 bytes in length
1144 if (!pszNameTable)
1145 arc = ERROR_NOT_ENOUGH_MEMORY;
1146 else
1147 {
1148 if (!(arc = DosRead(pExec->hfExe,
1149 pszNameTable,
1150 2000,
1151 &ulBytesRead)))
1152 {
1153 if (*pszNameTable == 0)
1154 // first byte (length byte) is null:
1155 arc = ERROR_INVALID_DATA;
1156 else
1157 {
1158 // now copy the string, which is in Pascal format
1159 pExec->pszDescription = (PSZ)malloc((*pszNameTable) + 1); // addt'l null byte
1160 if (!pExec->pszDescription)
1161 arc = ERROR_NOT_ENOUGH_MEMORY;
1162 else
1163 {
1164 const char *pStartOfAuthor = 0,
1165 *pStartOfVendor = 0;
1166
1167 memcpy(pExec->pszDescription,
1168 pszNameTable + 1, // skip length byte
1169 *pszNameTable); // length byte
1170 // terminate string
1171 *(pExec->pszDescription + (*pszNameTable)) = 0;
1172
1173 // now parse the damn thing:
1174 // @#VENDOR:VERSION#@ DESCRIPTION
1175 // but skip the first byte, which has the string length
1176 pStartOfVendor = strstr(pExec->pszDescription,
1177 "@#");
1178 if (pStartOfVendor)
1179 {
1180 const char *pStartOfInfo = strstr(pStartOfVendor + 2,
1181 "#@");
1182 if (pStartOfInfo)
1183 {
1184 PSZ pEndOfVendor = strchr(pStartOfVendor + 2,
1185 ':');
1186 if (pEndOfVendor)
1187 {
1188 pExec->pszVendor = strhSubstr(pStartOfVendor + 2,
1189 pEndOfVendor);
1190 pExec->pszVersion = strhSubstr(pEndOfVendor + 1,
1191 pStartOfInfo);
1192 // skip "@#" in info string
1193 pStartOfInfo += 2;
1194 // skip leading spaces in info string
1195 while (*pStartOfInfo == ' ')
1196 pStartOfInfo++;
1197 if (*pStartOfInfo) // V0.9.9 (2001-04-04) [umoeller]
1198 // and copy until end of string
1199 pExec->pszInfo = strdup(pStartOfInfo);
1200 }
1201 }
1202 }
1203 }
1204 }
1205 }
1206
1207 free(pszNameTable);
1208 } // end if PSZ pszNameTable = (PSZ)malloc(2001);
1209 }
1210 }
1211 } // end if (!pExec)
1212
1213 return (arc);
1214}
1215
1216/*
1217 *@@ doshExecQueryImportedModules:
1218 * returns an array of FSYSMODULE structure describing all
1219 * imported modules.
1220 *
1221 * *pcModules receives the # of items in the array (not the
1222 * array size!). Use doshFreeImportedModules to clean up.
1223 *
1224 * This returns a standard OS/2 error code, which might be
1225 * any of the codes returned by DosSetFilePtr and DosRead.
1226 * In addition, this may return:
1227 *
1228 * -- ERROR_NOT_ENOUGH_MEMORY
1229 *
1230 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1231 * than LX or NE, which is not understood by this function.
1232 *
1233 * Even if NO_ERROR is returned, the array pointer might still
1234 * be NULL if the module contains no such data.
1235 *
1236 *@@added V0.9.9 (2001-03-11) [lafaix]
1237 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1238 */
1239
1240APIRET doshExecQueryImportedModules(PEXECUTABLE pExec,
1241 PFSYSMODULE *ppaModules, // out: modules array
1242 PULONG pcModules) // out: array item count
1243{
1244 APIRET arc = NO_ERROR;
1245
1246 if ( (pExec)
1247 && (pExec->ulOS == EXEOS_OS2)
1248 )
1249 {
1250 ULONG cModules = 0;
1251 PFSYSMODULE paModules = NULL;
1252 int i;
1253
1254 if (pExec->ulExeFormat == EXEFORMAT_LX)
1255 {
1256 // 32-bit OS/2 executable:
1257 if (cModules = pExec->pLXHeader->ulImportModTblCnt)
1258 {
1259 ULONG cb = sizeof(FSYSMODULE) * cModules; // V0.9.9 (2001-04-03) [umoeller]
1260
1261 paModules = (PFSYSMODULE)malloc(cb);
1262 if (!paModules)
1263 arc = ERROR_NOT_ENOUGH_MEMORY; // V0.9.9 (2001-04-03) [umoeller]
1264 else
1265 {
1266 ULONG ulDummy;
1267
1268 memset(paModules, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1269
1270 // V0.9.9 (2001-04-03) [umoeller]:
1271 // Martin, I added error checking to all the below
1272 // Dos* calls. You can't just read around a file
1273 // and assume it will always be valid... especially
1274 // if you fill dynamically allocated memory.
1275
1276 if (!(arc = DosSetFilePtr(pExec->hfExe,
1277 pExec->pLXHeader->ulImportModTblOfs
1278 + pExec->pDosExeHeader->ulNewHeaderOfs,
1279 FILE_BEGIN,
1280 &ulDummy)))
1281 {
1282 for (i = 0; i < cModules; i++)
1283 {
1284 BYTE bLen = 0;
1285
1286 // reading the length of the module name
1287 if (!(arc = DosRead(pExec->hfExe,
1288 &bLen,
1289 1,
1290 &ulDummy)))
1291 {
1292 // At most 127 bytes
1293 bLen &= 0x7F;
1294
1295 // reading the module name
1296 if (!(arc = DosRead(pExec->hfExe,
1297 paModules[i].achModuleName,
1298 bLen,
1299 &ulDummy)))
1300 // module names are not null terminated, so we must
1301 // do it now
1302 paModules[i].achModuleName[bLen] = 0;
1303 }
1304
1305 if (arc)
1306 break; // V0.9.9 (2001-04-03) [umoeller]
1307
1308 } // end for
1309 }
1310 }
1311 }
1312 } // end LX
1313 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1314 {
1315 // 16-bit OS/2 executable:
1316 if (cModules = pExec->pNEHeader->usModuleTblEntries)
1317 {
1318 ULONG cb = sizeof(FSYSMODULE) * cModules;
1319
1320 paModules = (PFSYSMODULE)malloc(cb);
1321 if (!paModules)
1322 arc = ERROR_NOT_ENOUGH_MEMORY; // V0.9.9 (2001-04-03) [umoeller]
1323 else
1324 {
1325 memset(paModules, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1326
1327 for (i = 0; i < cModules; i ++)
1328 {
1329 BYTE bLen;
1330 USHORT usOfs;
1331 ULONG ulDummy;
1332
1333 // the module reference table contains offsets
1334 // relative to the import table; we hence read
1335 // the offset in the module reference table, and
1336 // then we read the name in the import table
1337
1338 if ( (!(arc = DosSetFilePtr(pExec->hfExe,
1339 pExec->pNEHeader->usModRefTblOfs
1340 + pExec->pDosExeHeader->ulNewHeaderOfs
1341 + sizeof(usOfs) * i,
1342 FILE_BEGIN,
1343 &ulDummy)))
1344 && (!(arc = DosRead(pExec->hfExe,
1345 &usOfs,
1346 2,
1347 &ulDummy)))
1348 && (!(arc = DosSetFilePtr(pExec->hfExe,
1349 pExec->pNEHeader->usImportTblOfs
1350 + pExec->pDosExeHeader->ulNewHeaderOfs
1351 + usOfs,
1352 FILE_BEGIN,
1353 &ulDummy)))
1354 && (!(arc = DosRead(pExec->hfExe,
1355 &bLen,
1356 1,
1357 &ulDummy)))
1358 )
1359 {
1360 bLen &= 0x7F;
1361
1362 if (!(arc = DosRead(pExec->hfExe,
1363 paModules[i].achModuleName,
1364 bLen,
1365 &ulDummy)))
1366 paModules[i].achModuleName[bLen] = 0;
1367 }
1368
1369 if (arc)
1370 break; // V0.9.9 (2001-04-03) [umoeller]
1371 } // end for
1372 }
1373 }
1374 }
1375 else
1376 arc = ERROR_INVALID_EXE_SIGNATURE; // V0.9.9 (2001-04-03) [umoeller]
1377
1378 if (arc)
1379 {
1380 // if we had an error above, clean up
1381 if (paModules)
1382 free(paModules);
1383 }
1384 else
1385 {
1386 // no error: output data
1387 *ppaModules = paModules;
1388 *pcModules = cModules;
1389 }
1390 }
1391 else
1392 arc = ERROR_INVALID_EXE_SIGNATURE; // V0.9.9 (2001-04-03) [umoeller]
1393
1394 return (arc);
1395}
1396
1397/*
1398 *@@ doshExecFreeImportedModules:
1399 * frees resources allocated by doshExecQueryImportedModules.
1400 *
1401 *@@added V0.9.9 (2001-03-11)
1402 */
1403
1404APIRET doshExecFreeImportedModules(PFSYSMODULE paModules)
1405{
1406 if (paModules) // V0.9.9 (2001-04-04) [umoeller]
1407 free(paModules);
1408 return (NO_ERROR);
1409}
1410
1411/*
1412 *@@ ScanLXEntryTable:
1413 * returns the number of exported entries in the entry table.
1414 *
1415 * If paFunctions is not NULL, then successive entries are
1416 * filled with the found type and ordinal values.
1417 *
1418 *@@added V0.9.9 (2001-03-30) [lafaix]
1419 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1420 */
1421
1422APIRET ScanLXEntryTable(PEXECUTABLE pExec,
1423 PFSYSFUNCTION paFunctions,
1424 PULONG pcEntries) // out: entry table entry count; ptr can be NULL
1425{
1426 APIRET arc = NO_ERROR;
1427
1428 ULONG ulDummy;
1429
1430 if (!(arc = DosSetFilePtr(pExec->hfExe,
1431 pExec->pLXHeader->ulEntryTblOfs
1432 + pExec->pDosExeHeader->ulNewHeaderOfs,
1433 FILE_BEGIN,
1434 &ulDummy)))
1435 {
1436 USHORT usOrdinal = 1,
1437 usCurrent = 0;
1438 int i;
1439
1440 while (!arc) // V0.9.9 (2001-04-03) [umoeller]
1441 {
1442 BYTE bCnt,
1443 bType,
1444 bFlag;
1445
1446 if (!(arc = DosRead(pExec->hfExe,
1447 &bCnt,
1448 1,
1449 &ulDummy)))
1450 {
1451 if (bCnt == 0)
1452 // end of the entry table
1453 break;
1454
1455 if (!(arc = DosRead(pExec->hfExe,
1456 &bType,
1457 1,
1458 &ulDummy)))
1459 {
1460 switch (bType & 0x7F)
1461 {
1462 /*
1463 * unused entries
1464 *
1465 */
1466
1467 case 0:
1468 usOrdinal += bCnt;
1469 break;
1470
1471 /*
1472 * 16-bit entries
1473 *
1474 * the bundle type is followed by the object number
1475 * and by bCnt bFlag+usOffset entries
1476 *
1477 */
1478
1479 case 1:
1480 if (!(arc = DosSetFilePtr(pExec->hfExe,
1481 sizeof(USHORT),
1482 FILE_CURRENT,
1483 &ulDummy)))
1484 {
1485 for (i = 0; i < bCnt; i ++)
1486 {
1487 if (!(arc = DosRead(pExec->hfExe,
1488 &bFlag,
1489 1,
1490 &ulDummy)))
1491 {
1492 if (bFlag & 0x01)
1493 {
1494 if (paFunctions)
1495 {
1496 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1497 paFunctions[usCurrent].ulType = 1;
1498 paFunctions[usCurrent].achFunctionName[0] = 0;
1499 }
1500 usCurrent++;
1501 }
1502
1503 usOrdinal++;
1504
1505 arc = DosSetFilePtr(pExec->hfExe,
1506 sizeof(USHORT),
1507 FILE_CURRENT,
1508 &ulDummy);
1509 }
1510
1511 if (arc)
1512 break; // V0.9.9 (2001-04-03) [umoeller]
1513
1514 } // end for
1515 }
1516 break;
1517
1518 /*
1519 * 286 call gate entries
1520 *
1521 * the bundle type is followed by the object number
1522 * and by bCnt bFlag+usOffset+usCallGate entries
1523 *
1524 */
1525
1526 case 2:
1527 if (!(arc = DosSetFilePtr(pExec->hfExe,
1528 sizeof(USHORT),
1529 FILE_CURRENT,
1530 &ulDummy)))
1531 {
1532 for (i = 0; i < bCnt; i ++)
1533 {
1534 if (!(arc = DosRead(pExec->hfExe,
1535 &bFlag,
1536 1,
1537 &ulDummy)))
1538 {
1539 if (bFlag & 0x01)
1540 {
1541 if (paFunctions)
1542 {
1543 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1544 paFunctions[usCurrent].ulType = 2;
1545 paFunctions[usCurrent].achFunctionName[0] = 0;
1546 }
1547 usCurrent++;
1548 }
1549
1550 usOrdinal++;
1551
1552 arc = DosSetFilePtr(pExec->hfExe,
1553 sizeof(USHORT) + sizeof(USHORT),
1554 FILE_CURRENT,
1555 &ulDummy);
1556 }
1557
1558 if (arc)
1559 break; // V0.9.9 (2001-04-03) [umoeller]
1560
1561 } // end for
1562 }
1563 break;
1564
1565 /*
1566 * 32-bit entries
1567 *
1568 * the bundle type is followed by the object number
1569 * and by bCnt bFlag+ulOffset entries
1570 *
1571 */
1572
1573 case 3:
1574 if (!(arc = DosSetFilePtr(pExec->hfExe,
1575 sizeof(USHORT),
1576 FILE_CURRENT,
1577 &ulDummy)))
1578 {
1579 for (i = 0; i < bCnt; i ++)
1580 {
1581 if (!(arc = DosRead(pExec->hfExe,
1582 &bFlag,
1583 1,
1584 &ulDummy)))
1585 {
1586 if (bFlag & 0x01)
1587 {
1588 if (paFunctions)
1589 {
1590 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1591 paFunctions[usCurrent].ulType = 3;
1592 paFunctions[usCurrent].achFunctionName[0] = 0;
1593 }
1594 usCurrent++;
1595 }
1596
1597 usOrdinal++;
1598
1599 arc = DosSetFilePtr(pExec->hfExe,
1600 sizeof(ULONG),
1601 FILE_CURRENT,
1602 &ulDummy);
1603 }
1604
1605 if (arc)
1606 break; // V0.9.9 (2001-04-03) [umoeller]
1607 } // end for
1608 }
1609 break;
1610
1611 /*
1612 * forwarder entries
1613 *
1614 * the bundle type is followed by a reserved word
1615 * and by bCnt bFlag+usModOrd+ulOffsOrdNum entries
1616 *
1617 */
1618
1619 case 4:
1620 if (!(arc = DosSetFilePtr(pExec->hfExe,
1621 sizeof(USHORT),
1622 FILE_CURRENT,
1623 &ulDummy)))
1624 {
1625 for (i = 0; i < bCnt; i ++)
1626 {
1627 if (!(arc = DosSetFilePtr(pExec->hfExe,
1628 sizeof(BYTE) + sizeof(USHORT) + sizeof(ULONG),
1629 FILE_CURRENT,
1630 &ulDummy)))
1631 {
1632 if (paFunctions)
1633 {
1634 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1635 paFunctions[usCurrent].ulType = 4;
1636 paFunctions[usCurrent].achFunctionName[0] = 0;
1637 }
1638 usCurrent++;
1639
1640 usOrdinal++;
1641 }
1642
1643 if (arc)
1644 break; // V0.9.9 (2001-04-03) [umoeller]
1645 } // end for
1646 }
1647 break;
1648
1649 /*
1650 * unknown bundle type
1651 *
1652 * we don't know how to handle this bundle, so we must
1653 * stop parsing the entry table here (as we don't know the
1654 * bundle size); if paFunctions is not null, we fill it with
1655 * informative data
1656 */
1657
1658 default:
1659 if (paFunctions)
1660 {
1661 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1662 paFunctions[usCurrent].ulType = bType;
1663 sprintf(paFunctions[usCurrent].achFunctionName,
1664 "Unknown bundle type encountered (%d). Aborting entry table scan.",
1665 bType);
1666
1667 arc = ERROR_INVALID_LIST_FORMAT;
1668 // whatever
1669 // V0.9.9 (2001-04-03) [umoeller]
1670 }
1671
1672 usCurrent++;
1673 } // end switch (bType & 0x7F)
1674 }
1675 }
1676 } // end while (!arc)
1677
1678 if (!arc)
1679 if (pcEntries)
1680 *pcEntries = usCurrent;
1681 }
1682
1683 return (arc);
1684}
1685
1686/*
1687 *@@ ScanNEEntryTable:
1688 * returns the number of exported entries in the entry table.
1689 *
1690 * if paFunctions is not NULL, then successive entries are
1691 * filled with the found type and ordinal values.
1692 *
1693 *@@added V0.9.9 (2001-03-30) [lafaix]
1694 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1695 */
1696
1697APIRET ScanNEEntryTable(PEXECUTABLE pExec,
1698 PFSYSFUNCTION paFunctions,
1699 PULONG pcEntries) // out: entry table entry count; ptr can be NULL
1700{
1701 APIRET arc = NO_ERROR;
1702 ULONG ulDummy;
1703
1704 if (!(arc = DosSetFilePtr(pExec->hfExe,
1705 pExec->pNEHeader->usEntryTblOfs
1706 + pExec->pDosExeHeader->ulNewHeaderOfs,
1707 FILE_BEGIN,
1708 &ulDummy)))
1709 {
1710 USHORT usOrdinal = 1,
1711 usCurrent = 0;
1712 int i;
1713
1714 while (!arc) // V0.9.9 (2001-04-03) [umoeller]
1715 {
1716 BYTE bCnt,
1717 bType,
1718 bFlag;
1719
1720 if (!(arc = DosRead(pExec->hfExe,
1721 &bCnt,
1722 1,
1723 &ulDummy)))
1724 {
1725 if (bCnt == 0)
1726 // end of the entry table
1727 break;
1728
1729 if (!(arc = DosRead(pExec->hfExe,
1730 &bType,
1731 1,
1732 &ulDummy)))
1733 {
1734 for (i = 0; i < bCnt; i++)
1735 {
1736 if (!(arc = DosRead(pExec->hfExe,
1737 &bFlag,
1738 1,
1739 &ulDummy)))
1740 {
1741 if (bFlag & 0x01)
1742 {
1743 if (paFunctions)
1744 {
1745 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1746 paFunctions[usCurrent].ulType = 1; // 16-bit entry
1747 paFunctions[usCurrent].achFunctionName[0] = 0;
1748 }
1749 usCurrent++;
1750 }
1751
1752 usOrdinal++;
1753
1754 if (bType == 0xFF)
1755 // moveable segment
1756 arc = DosSetFilePtr(pExec->hfExe,
1757 5,
1758 FILE_CURRENT,
1759 &ulDummy);
1760 else
1761 // fixed segment
1762 arc = DosSetFilePtr(pExec->hfExe,
1763 2,
1764 FILE_CURRENT,
1765 &ulDummy);
1766 }
1767
1768 if (arc)
1769 break; // V0.9.9 (2001-04-03) [umoeller]
1770
1771 } // end for
1772 }
1773 }
1774 } // end while (!arc)
1775
1776 if (!arc)
1777 if (pcEntries)
1778 *pcEntries = usCurrent;
1779 }
1780
1781 return (arc);
1782}
1783
1784/*
1785 *@@ Compare:
1786 * binary search helper
1787 *
1788 *@@added V0.9.9 (2001-04-01) [lafaix]
1789 */
1790
1791int _Optlink Compare(const void *key,
1792 const void *element)
1793{
1794 USHORT usOrdinal = *((PUSHORT) key);
1795 PFSYSFUNCTION pFunction = (PFSYSFUNCTION)element;
1796
1797 if (usOrdinal > pFunction->ulOrdinal)
1798 return (1);
1799 else if (usOrdinal < pFunction->ulOrdinal)
1800 return (-1);
1801 else
1802 return (0);
1803}
1804
1805/*
1806 *@@ ScanNameTable:
1807 * scans a resident or non-resident name table, and fills the
1808 * appropriate paFunctions entries when it encounters exported
1809 * entries names.
1810 *
1811 * This functions works for both NE and LX executables.
1812 *
1813 *@@added V0.9.9 (2001-03-30) [lafaix]
1814 *@@changed V0.9.9 (2001-04-02) [lafaix]: the first entry is special
1815 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1816 */
1817
1818APIRET ScanNameTable(PEXECUTABLE pExec,
1819 ULONG cFunctions,
1820 PFSYSFUNCTION paFunctions)
1821{
1822 APIRET arc = NO_ERROR;
1823 ULONG ulDummy;
1824
1825 USHORT usOrdinal;
1826 PFSYSFUNCTION pFunction;
1827 BOOL bFirst = TRUE;
1828
1829 while (!arc) // V0.9.9 (2001-04-03) [umoeller]
1830 {
1831 BYTE bLen;
1832 CHAR achName[256];
1833 int i;
1834
1835 if (!(arc = DosRead(pExec->hfExe,
1836 &bLen,
1837 1,
1838 &ulDummy)))
1839 {
1840 if (bLen == 0)
1841 // end of the name table
1842 break;
1843
1844 // the LX docs says that len is limited to 127 (the 8th bit being
1845 // reserved for future use); but does this applies to 16bits
1846 // tables too?
1847 // in any case, we must skip the first entry (module name in the
1848 // resident name table, and module description in non-resident
1849 // name table)
1850 if (bFirst)
1851 bFirst = FALSE;
1852 else
1853 bLen &= 0x7F;
1854
1855 if (!(arc = DosRead(pExec->hfExe,
1856 &achName,
1857 bLen,
1858 &ulDummy)))
1859 {
1860 achName[bLen] = 0;
1861
1862 if (!(arc = DosRead(pExec->hfExe,
1863 &usOrdinal,
1864 sizeof(USHORT),
1865 &ulDummy)))
1866 {
1867 if ((pFunction = (PFSYSFUNCTION)bsearch(&usOrdinal,
1868 paFunctions,
1869 cFunctions,
1870 sizeof(FSYSFUNCTION),
1871 Compare)))
1872 {
1873 memcpy(pFunction->achFunctionName,
1874 achName,
1875 bLen+1);
1876 }
1877 }
1878 }
1879 }
1880 }
1881
1882 return (arc);
1883}
1884
1885/*
1886 *@@ doshExecQueryExportedFunctions:
1887 * returns an array of FSYSFUNCTION structure describing all
1888 * exported functions.
1889 *
1890 * *pcFunctions receives the # of items in the array (not the
1891 * array size!). Use doshFreeExportedFunctions to clean up.
1892 *
1893 * Note that the returned array only contains entry for exported
1894 * functions. Empty export entries are _not_ included.
1895 *
1896 * This returns a standard OS/2 error code, which might be
1897 * any of the codes returned by DosSetFilePtr and DosRead.
1898 * In addition, this may return:
1899 *
1900 * -- ERROR_NOT_ENOUGH_MEMORY
1901 *
1902 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1903 * than LX or NE, which is not understood by this function.
1904 *
1905 * -- If ERROR_INVALID_LIST_FORMAT is returned, the format of an
1906 * export entry wasn't understood here.
1907 *
1908 * Even if NO_ERROR is returned, the array pointer might still
1909 * be NULL if the module contains no such data.
1910 *
1911 *@@added V0.9.9 (2001-03-11) [lafaix]
1912 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1913 */
1914
1915APIRET doshExecQueryExportedFunctions(PEXECUTABLE pExec,
1916 PFSYSFUNCTION *ppaFunctions, // out: functions array
1917 PULONG pcFunctions) // out: array item count
1918{
1919 APIRET arc = NO_ERROR;
1920
1921 if ( (pExec)
1922 && (pExec->ulOS == EXEOS_OS2)
1923 )
1924 {
1925 ULONG cFunctions = 0;
1926 PFSYSFUNCTION paFunctions = NULL;
1927
1928 ULONG ulDummy;
1929
1930 if (pExec->ulExeFormat == EXEFORMAT_LX)
1931 {
1932 // It's a 32bit OS/2 executable
1933
1934 // the number of exported entry points is not stored
1935 // in the executable header; we have to count them in
1936 // the entry table
1937
1938 if (!(arc = ScanLXEntryTable(pExec,
1939 NULL,
1940 &cFunctions)))
1941 {
1942 // we now have the number of exported entries; let us
1943 // build them
1944
1945 if (cFunctions)
1946 {
1947 ULONG cb = sizeof(FSYSFUNCTION) * cFunctions;
1948
1949 paFunctions = (PFSYSFUNCTION)malloc(cb);
1950 if (!paFunctions)
1951 arc = ERROR_NOT_ENOUGH_MEMORY; // V0.9.9 (2001-04-03) [umoeller]
1952 else
1953 {
1954 // we rescan the entry table (the cost is not as bad
1955 // as it may seem, due to disk caching)
1956
1957 if ( (!(arc = ScanLXEntryTable(pExec, paFunctions, NULL)))
1958 // we now scan the resident name table entries
1959 && (!(arc = DosSetFilePtr(pExec->hfExe,
1960 pExec->pLXHeader->ulResdNameTblOfs
1961 + pExec->pDosExeHeader->ulNewHeaderOfs,
1962 FILE_BEGIN,
1963 &ulDummy)))
1964 && (!(arc = ScanNameTable(pExec, cFunctions, paFunctions)))
1965 // we now scan the non-resident name table entries,
1966 // whose offset is _from the begining of the file_
1967 && (!(arc = DosSetFilePtr(pExec->hfExe,
1968 pExec->pLXHeader->ulNonResdNameTblOfs,
1969 FILE_BEGIN,
1970 &ulDummy)))
1971 )
1972 {
1973 arc = ScanNameTable(pExec, cFunctions, paFunctions);
1974 }
1975 }
1976 } // end if (cFunctions)
1977 }
1978 }
1979 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1980 {
1981 // It's a 16bit OS/2 executable
1982
1983 // here too the number of exported entry points
1984 // is not stored in the executable header; we
1985 // have to count them in the entry table
1986
1987 if (!(arc = ScanNEEntryTable(pExec,
1988 NULL,
1989 &cFunctions)))
1990 {
1991 // we now have the number of exported entries; let us
1992 // build them
1993
1994 if (cFunctions)
1995 {
1996 USHORT usOrdinal = 1,
1997 usCurrent = 0;
1998
1999 paFunctions = (PFSYSFUNCTION)malloc(sizeof(FSYSFUNCTION) * cFunctions);
2000 if (!paFunctions)
2001 arc = ERROR_NOT_ENOUGH_MEMORY;
2002 else
2003 {
2004 // we rescan the entry table (the cost is not as bad
2005 // as it may seem, due to disk caching)
2006
2007 if ( (!(arc = ScanNEEntryTable(pExec, paFunctions, NULL)))
2008 // we now scan the resident name table entries
2009 && (!(arc = DosSetFilePtr(pExec->hfExe,
2010 pExec->pNEHeader->usResdNameTblOfs
2011 + pExec->pDosExeHeader->ulNewHeaderOfs,
2012 FILE_BEGIN,
2013 &ulDummy)))
2014 && (!(arc = ScanNameTable(pExec, cFunctions, paFunctions)))
2015 // we now scan the non-resident name table entries,
2016 // whose offset is _from the begining of the file_
2017 && (!(arc = DosSetFilePtr(pExec->hfExe,
2018 pExec->pNEHeader->ulNonResdTblOfs,
2019 FILE_BEGIN,
2020 &ulDummy)))
2021 )
2022 {
2023 arc = ScanNameTable(pExec, cFunctions, paFunctions);
2024 }
2025 }
2026 }
2027 }
2028 }
2029 else
2030 arc = ERROR_INVALID_EXE_SIGNATURE; // V0.9.9 (2001-04-03) [umoeller]
2031
2032 if (arc) // V0.9.9 (2001-04-03) [umoeller]
2033 {
2034 // if we had an error above, clean up
2035 if (paFunctions)
2036 free(paFunctions);
2037 }
2038 else
2039 {
2040 // no error: output data
2041 *ppaFunctions = paFunctions;
2042 *pcFunctions = cFunctions;
2043 }
2044 }
2045 else
2046 arc = ERROR_INVALID_EXE_SIGNATURE; // V0.9.9 (2001-04-03) [umoeller]
2047
2048 return (arc);
2049}
2050
2051/*
2052 *@@ doshExecFreeExportedFunctions:
2053 * frees resources allocated by doshExecQueryExportedFunctions.
2054 *
2055 *@@added V0.9.9 (2001-03-11)
2056 */
2057
2058APIRET doshExecFreeExportedFunctions(PFSYSFUNCTION paFunctions)
2059{
2060 if (paFunctions) // V0.9.9 (2001-04-04) [umoeller]
2061 free(paFunctions);
2062 return (NO_ERROR);
2063}
2064
2065/*
2066 *@@ doshExecQueryResources:
2067 * returns an array of FSYSRESOURCE structures describing all
2068 * available resources in the module.
2069 *
2070 * *pcResources receives the no. of items in the array
2071 * (not the array size!). Use doshExecFreeResources to clean up.
2072 *
2073 * This returns a standard OS/2 error code, which might be
2074 * any of the codes returned by DosSetFilePtr and DosRead.
2075 * In addition, this may return:
2076 *
2077 * -- ERROR_NOT_ENOUGH_MEMORY
2078 *
2079 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
2080 * than LX or NE, which is not understood by this function.
2081 *
2082 * Even if NO_ERROR is returned, the array pointer might still
2083 * be NULL if the module contains no such data.
2084 *
2085 *@@added V0.9.7 (2000-12-18) [lafaix]
2086 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
2087 */
2088
2089APIRET doshExecQueryResources(PEXECUTABLE pExec, // in: executable from doshExecOpen
2090 PFSYSRESOURCE *ppaResources, // out: res's array
2091 PULONG pcResources) // out: array item count
2092{
2093 APIRET arc = NO_ERROR;
2094
2095 if ( (pExec)
2096 && (pExec->ulOS == EXEOS_OS2)
2097 )
2098 {
2099 ULONG cResources = 0;
2100 PFSYSRESOURCE paResources = NULL;
2101
2102 if (pExec->ulExeFormat == EXEFORMAT_LX)
2103 {
2104 // 32-bit OS/2 executable:
2105 if (cResources = pExec->pLXHeader->ulResTblCnt)
2106 {
2107 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2108 paResources = (PFSYSRESOURCE)malloc(cb);
2109 if (!paResources)
2110 arc = ERROR_NOT_ENOUGH_MEMORY;
2111 else
2112 {
2113 ULONG ulDummy;
2114
2115 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
2116
2117 // V0.9.9 (2001-04-03) [umoeller]:
2118 // Martin, I added error checking to all the below
2119 // Dos* calls. You can't just read around a file
2120 // and assume it will always be valid... especially
2121 // if you fill dynamically allocated memory.
2122 if (!(arc = DosSetFilePtr(pExec->hfExe,
2123 pExec->pLXHeader->ulResTblOfs
2124 + pExec->pDosExeHeader->ulNewHeaderOfs,
2125 FILE_BEGIN,
2126 &ulDummy)))
2127 {
2128 int i;
2129
2130 // V0.9.9 (2001-04-03) [umoeller]:
2131 // Besides, packing was missing.
2132 #pragma pack(1) // V0.9.9 (2001-04-02) [umoeller]
2133 struct rsrc32 /* Resource Table Entry */
2134 {
2135 unsigned short type; /* Resource type */
2136 unsigned short name; /* Resource name */
2137 unsigned long cb; /* Resource size */
2138 unsigned short obj; /* Object number */
2139 unsigned long offset; /* Offset within object */
2140 } rs;
2141
2142 struct o32_obj /* Flat .EXE object table entry */
2143 {
2144 unsigned long o32_size; /* Object virtual size */
2145 unsigned long o32_base; /* Object base virtual address */
2146 unsigned long o32_flags; /* Attribute flags */
2147 unsigned long o32_pagemap; /* Object page map index */
2148 unsigned long o32_mapsize; /* Number of entries in object page map */
2149 unsigned long o32_reserved; /* Reserved */
2150 } ot;
2151 #pragma pack() // V0.9.9 (2001-04-03) [umoeller]
2152
2153 for (i = 0; i < cResources; i++)
2154 {
2155 arc = DosRead(pExec->hfExe,
2156 &rs,
2157 14,
2158 &ulDummy);
2159 if (arc)
2160 break; // V0.9.9 (2001-04-03) [umoeller]
2161 else
2162 {
2163 paResources[i].ulID = rs.name;
2164 paResources[i].ulType = rs.type;
2165 paResources[i].ulSize = rs.cb;
2166 paResources[i].ulFlag = rs.obj; // Temp storage for Object
2167 // number. Will be filled
2168 // with resource flag
2169 // later.
2170 }
2171 }
2172
2173 if (!arc) // V0.9.9 (2001-04-03) [umoeller]
2174 {
2175 for (i = 0; i < cResources; i++)
2176 {
2177 ULONG ulOfsThis
2178 = pExec->pLXHeader->ulObjTblOfs
2179 + pExec->pDosExeHeader->ulNewHeaderOfs
2180 + ( sizeof(ot)
2181 * (paResources[i].ulFlag - 1));
2182
2183 if (!(arc = DosSetFilePtr(pExec->hfExe,
2184 ulOfsThis,
2185 FILE_BEGIN,
2186 &ulDummy)))
2187 {
2188 if (!(arc = DosRead(pExec->hfExe,
2189 &ot,
2190 sizeof(ot),
2191 &ulDummy)))
2192 {
2193 paResources[i].ulFlag = ((ot.o32_flags & OBJWRITE)
2194 ? 0
2195 : RNPURE);
2196 paResources[i].ulFlag |= ((ot.o32_flags & OBJDISCARD)
2197 ? 4096
2198 : 0);
2199 paResources[i].ulFlag |= ((ot.o32_flags & OBJSHARED)
2200 ? RNMOVE
2201 : 0);
2202 paResources[i].ulFlag |= ((ot.o32_flags & OBJPRELOAD)
2203 ? RNPRELOAD
2204 : 0);
2205 }
2206 }
2207
2208 if (arc)
2209 break; // V0.9.9 (2001-04-03) [umoeller]
2210 } // end for
2211 }
2212 } // end if !DosSetFilePtr(pExec->hfExe,
2213 } // end if paResources = (PFSYSRESOURCE)malloc(sizeof(FSYSRESOURCE) * cResources);
2214 } // end if (cResources)
2215 } // end if (pExec->ulExeFormat == EXEFORMAT_LX)
2216 else if (pExec->ulExeFormat == EXEFORMAT_NE)
2217 {
2218 // 16-bit OS/2 executable:
2219 if (cResources = pExec->pNEHeader->usResSegmCount)
2220 {
2221 #pragma pack(1) // V0.9.9 (2001-04-02) [umoeller]
2222 struct {unsigned short type; unsigned short name;} rti;
2223 struct new_seg /* New .EXE segment table entry */
2224 {
2225 unsigned short ns_sector; /* File sector of start of segment */
2226 unsigned short ns_cbseg; /* Number of bytes in file */
2227 unsigned short ns_flags; /* Attribute flags */
2228 unsigned short ns_minalloc; /* Minimum allocation in bytes */
2229 } ns;
2230 #pragma pack()
2231
2232 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2233
2234 paResources = (PFSYSRESOURCE)malloc(cb);
2235 if (!paResources)
2236 arc = ERROR_NOT_ENOUGH_MEMORY;
2237 else
2238 {
2239 ULONG ulDummy;
2240
2241 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
2242
2243 // we first read the resources IDs and types
2244 if (!(arc = DosSetFilePtr(pExec->hfExe,
2245 pExec->pNEHeader->usResTblOfs
2246 + pExec->pDosExeHeader->ulNewHeaderOfs,
2247 FILE_BEGIN,
2248 &ulDummy)))
2249 {
2250 int i;
2251
2252 for (i = 0; i < cResources; i++)
2253 {
2254 arc = DosRead(pExec->hfExe, &rti, sizeof(rti), &ulDummy);
2255 if (arc)
2256 break;
2257 else
2258 {
2259 paResources[i].ulID = rti.name;
2260 paResources[i].ulType = rti.type;
2261 }
2262 }
2263
2264 if (!arc)
2265 {
2266 // we then read their sizes and flags
2267 for (i = 0; i < cResources; i++)
2268 {
2269 if (!(arc = DosSetFilePtr(pExec->hfExe,
2270 pExec->pDosExeHeader->ulNewHeaderOfs
2271 + pExec->pNEHeader->usSegTblOfs
2272 + (sizeof(ns)
2273 * ( pExec->pNEHeader->usSegTblEntries
2274 - pExec->pNEHeader->usResSegmCount
2275 + i)),
2276 FILE_BEGIN,
2277 &ulDummy)))
2278 {
2279 if (!(arc = DosRead(pExec->hfExe,
2280 &ns,
2281 sizeof(ns),
2282 &ulDummy)))
2283 {
2284 paResources[i].ulSize = ns.ns_cbseg;
2285
2286 paResources[i].ulFlag = (ns.ns_flags & OBJPRELOAD) ? RNPRELOAD : 0;
2287 paResources[i].ulFlag |= (ns.ns_flags & OBJSHARED) ? RNPURE : 0;
2288 paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? RNMOVE : 0;
2289 paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? 4096 : 0;
2290 }
2291 }
2292
2293 if (arc)
2294 break; // V0.9.9 (2001-04-04) [umoeller]
2295 } // end for
2296 }
2297 } // end if !arc = DosSetFilePtr(pExec->hfExe,
2298 } // end if paResources = (PFSYSRESOURCE)malloc(sizeof(FSYSRESOURCE) * cResources);
2299 } // end if (cResources)
2300 } // end else if (pExec->ulExeFormat == EXEFORMAT_NE)
2301 else
2302 arc = ERROR_INVALID_EXE_SIGNATURE; // V0.9.9 (2001-04-03) [umoeller]
2303
2304 if (arc) // V0.9.9 (2001-04-03) [umoeller]
2305 {
2306 // if we had an error above, clean up
2307 if (paResources)
2308 free(paResources);
2309 }
2310 else
2311 {
2312 // no error: output data
2313 *ppaResources = paResources;
2314 *pcResources = cResources;
2315 }
2316 }
2317 else
2318 arc = ERROR_INVALID_EXE_SIGNATURE; // V0.9.9 (2001-04-03) [umoeller]
2319
2320 return (arc);
2321}
2322
2323/*
2324 *@@ doshExecFreeResources:
2325 * frees resources allocated by doshExecQueryResources.
2326 *
2327 *@@added V0.9.7 (2000-12-18) [lafaix]
2328 */
2329
2330APIRET doshExecFreeResources(PFSYSRESOURCE paResources)
2331{
2332 if (paResources) // V0.9.9 (2001-04-04) [umoeller]
2333 free(paResources);
2334 return (NO_ERROR);
2335}
2336
2337/*
2338 *@@category: Helpers\Control program helpers\Partitions info
2339 * functions for retrieving partition information directly
2340 * from the partition tables on the disk. See doshGetPartitionsList.
2341 */
2342
2343/********************************************************************
2344 *
2345 * Partition functions
2346 *
2347 ********************************************************************/
2348
2349/*
2350 *@@ doshQueryDiskCount:
2351 * returns the no. of physical disks installed
2352 * on the system.
2353 *
2354 * Based on code (C) Dmitry A. Steklenev.
2355 *
2356 *@@added V0.9.0 [umoeller]
2357 */
2358
2359UINT doshQueryDiskCount(VOID)
2360{
2361 USHORT count = 0;
2362
2363 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &count, 2, 0, 0);
2364 return (count);
2365}
2366
2367/*
2368 *@@ doshReadSector:
2369 * reads a physical disk sector.
2370 *
2371 * If NO_ERROR is returned, the sector contents
2372 * have been stored in *buff.
2373 *
2374 * Based on code (C) Dmitry A. Steklenev.
2375 *
2376 *@@added V0.9.0 [umoeller]
2377 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
2378 */
2379
2380APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
2381 void *buff,
2382 USHORT head,
2383 USHORT cylinder,
2384 USHORT sector)
2385{
2386 APIRET arc;
2387 HFILE dh = 0;
2388 char dn[256];
2389
2390 sprintf(dn, "%u:", disk);
2391 if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
2392 {
2393 TRACKLAYOUT DiskIOParm;
2394 ULONG IOCtlDataLength = sizeof(DiskIOParm);
2395 ULONG IOCtlParmLength = 512;
2396
2397 DiskIOParm.bCommand = 0;
2398 DiskIOParm.usHead = head;
2399 DiskIOParm.usCylinder = cylinder;
2400 DiskIOParm.usFirstSector = 0;
2401 DiskIOParm.cSectors = 1;
2402 DiskIOParm.TrackTable[0].usSectorNumber = sector;
2403 DiskIOParm.TrackTable[0].usSectorSize = 512;
2404
2405 arc = DosDevIOCtl(dh,
2406 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
2407 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
2408 buff , IOCtlDataLength, &IOCtlDataLength);
2409
2410 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
2411 }
2412
2413 return (arc);
2414}
2415
2416/*
2417 *@@ doshType2FSName:
2418 * this returns a static, zero-terminated string
2419 * for the given FS type. This is always 7 bytes
2420 * in length.
2421 *
2422 * Values for operating system indicator:
2423 * -- 00h empty
2424 * -- 01h DOS 12-bit FAT
2425 * -- 02h XENIX root file system
2426 * -- 03h XENIX /usr file system (obsolete)
2427 * -- 04h DOS 16-bit FAT (up to 32M)
2428 * -- 05h DOS 3.3+ extended partition
2429 * -- 06h DOS 3.31+ Large File System (16-bit FAT, over 32M)
2430 * -- 07h QNX
2431 * -- 07h OS/2 HPFS
2432 * -- 07h Windows NT NTFS
2433 * -- 07h Advanced Unix
2434 * -- 08h OS/2 (v1.0-1.3 only)
2435 * -- 08h AIX bootable partition, SplitDrive
2436 * -- 08h Commodore DOS
2437 * -- 08h DELL partition spanning multiple drives
2438 * -- 09h AIX data partition
2439 * -- 09h Coherent filesystem
2440 * -- 0Ah OS/2 Boot Manager
2441 * -- 0Ah OPUS
2442 * -- 0Ah Coherent swap partition
2443 * -- 0Bh Windows95 with 32-bit FAT
2444 * -- 0Ch Windows95 with 32-bit FAT (using LBA-mode INT 13 extensions)
2445 * -- 0Eh logical-block-addressable VFAT (same as 06h but using LBA-mode INT 13)
2446 * -- 0Fh logical-block-addressable VFAT (same as 05h but using LBA-mode INT 13)
2447 * -- 10h OPUS
2448 * -- 11h OS/2 Boot Manager hidden 12-bit FAT partition
2449 * -- 12h Compaq Diagnostics partition
2450 * -- 14h (resulted from using Novell DOS 7.0 FDISK to delete Linux Native part)
2451 * -- 14h OS/2 Boot Manager hidden sub-32M 16-bit FAT partition
2452 * -- 16h OS/2 Boot Manager hidden over-32M 16-bit FAT partition
2453 * -- 17h OS/2 Boot Manager hidden HPFS partition
2454 * -- 18h AST special Windows swap file ("Zero-Volt Suspend" partition)
2455 * -- 21h officially listed as reserved
2456 * -- 23h officially listed as reserved
2457 * -- 24h NEC MS-DOS 3.x
2458 * -- 26h officially listed as reserved
2459 * -- 31h officially listed as reserved
2460 * -- 33h officially listed as reserved
2461 * -- 34h officially listed as reserved
2462 * -- 36h officially listed as reserved
2463 * -- 38h Theos
2464 * -- 3Ch PowerQuest PartitionMagic recovery partition
2465 * -- 40h VENIX 80286
2466 * -- 41h Personal RISC Boot
2467 * -- 42h SFS (Secure File System) by Peter Gutmann
2468 * -- 50h OnTrack Disk Manager, read-only partition
2469 * -- 51h OnTrack Disk Manager, read/write partition
2470 * -- 51h NOVEL
2471 * -- 52h CP/M
2472 * -- 52h Microport System V/386
2473 * -- 53h OnTrack Disk Manager, write-only partition???
2474 * -- 54h OnTrack Disk Manager (DDO)
2475 * -- 56h GoldenBow VFeature
2476 * -- 61h SpeedStor
2477 * -- 63h Unix SysV/386, 386/ix
2478 * -- 63h Mach, MtXinu BSD 4.3 on Mach
2479 * -- 63h GNU HURD
2480 * -- 64h Novell NetWare 286
2481 * -- 65h Novell NetWare (3.11)
2482 * -- 67h Novell
2483 * -- 68h Novell
2484 * -- 69h Novell
2485 * -- 70h DiskSecure Multi-Boot
2486 * -- 71h officially listed as reserved
2487 * -- 73h officially listed as reserved
2488 * -- 74h officially listed as reserved
2489 * -- 75h PC/IX
2490 * -- 76h officially listed as reserved
2491 * -- 80h Minix v1.1 - 1.4a
2492 * -- 81h Minix v1.4b+
2493 * -- 81h Linux
2494 * -- 81h Mitac Advanced Disk Manager
2495 * -- 82h Linux Swap partition
2496 * -- 82h Prime
2497 * -- 83h Linux native file system (ext2fs/xiafs)
2498 * -- 84h OS/2-renumbered type 04h partition (related to hiding DOS C: drive)
2499 * -- 86h FAT16 volume/stripe set (Windows NT)
2500 * -- 87h HPFS Fault-Tolerant mirrored partition
2501 * -- 87h NTFS volume/stripe set
2502 * -- 93h Amoeba file system
2503 * -- 94h Amoeba bad block table
2504 * -- A0h Phoenix NoteBIOS Power Management "Save-to-Disk" partition
2505 * -- A1h officially listed as reserved
2506 * -- A3h officially listed as reserved
2507 * -- A4h officially listed as reserved
2508 * -- A5h FreeBSD, BSD/386
2509 * -- A6h officially listed as reserved
2510 * -- B1h officially listed as reserved
2511 * -- B3h officially listed as reserved
2512 * -- B4h officially listed as reserved
2513 * -- B6h officially listed as reserved
2514 * -- B7h BSDI file system (secondarily swap)
2515 * -- B8h BSDI swap partition (secondarily file system)
2516 * -- C1h DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT partition
2517 * -- C4h DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT partition
2518 * -- C6h DR DOS 6.0 LOGIN.EXE-secured Huge partition
2519 * -- C6h corrupted FAT16 volume/stripe set (Windows NT)
2520 * -- C7h Syrinx Boot
2521 * -- C7h corrupted NTFS volume/stripe set
2522 * -- D8h CP/M-86
2523 * -- DBh CP/M, Concurrent CP/M, Concurrent DOS
2524 * -- DBh CTOS (Convergent Technologies OS)
2525 * -- E1h SpeedStor 12-bit FAT extended partition
2526 * -- E3h DOS read-only
2527 * -- E3h Storage Dimensions
2528 * -- E4h SpeedStor 16-bit FAT extended partition
2529 * -- E5h officially listed as reserved
2530 * -- E6h officially listed as reserved
2531 * -- F1h Storage Dimensions
2532 * -- F2h DOS 3.3+ secondary partition
2533 * -- F3h officially listed as reserved
2534 * -- F4h SpeedStor
2535 * -- F4h Storage Dimensions
2536 * -- F6h officially listed as reserved
2537 * -- FEh LANstep
2538 * -- FEh IBM PS/2 IML
2539 * -- FFh Xenix bad block table
2540 *
2541 * Note: for partition type 07h, one should inspect the partition boot record
2542 * for the actual file system type
2543 *
2544 * Based on code (C) Dmitry A. Steklenev.
2545 *
2546 *@@added V0.9.0 [umoeller]
2547 */
2548
2549const char* doshType2FSName(unsigned char bFSType) // in: FS type
2550{
2551 PSZ zFSName = NULL;
2552
2553 switch (bFSType)
2554 {
2555 case PAR_UNUSED:
2556 zFSName = "UNUSED ";
2557 break;
2558 case PAR_FAT12SMALL:
2559 zFSName = "FAT-12 ";
2560 break;
2561 case PAR_XENIXROOT:
2562 zFSName = "XENIX ";
2563 break;
2564 case PAR_XENIXUSER:
2565 zFSName = "XENIX ";
2566 break;
2567 case PAR_FAT16SMALL:
2568 zFSName = "FAT-16 ";
2569 break;
2570 case PAR_EXTENDED:
2571 zFSName = "EXTEND ";
2572 break;
2573 case PAR_FAT16BIG:
2574 zFSName = "BIGDOS ";
2575 break;
2576 case PAR_HPFS:
2577 zFSName = "HPFS ";
2578 break;
2579 case PAR_AIXBOOT:
2580 zFSName = "AIX ";
2581 break;
2582 case PAR_AIXDATA:
2583 zFSName = "AIX ";
2584 break;
2585 case PAR_BOOTMANAGER:
2586 zFSName = "BOOTMNG";
2587 break;
2588 case PAR_WINDOWS95:
2589 zFSName = "WIN95 ";
2590 break;
2591 case PAR_WINDOWS95LB:
2592 zFSName = "WIN95 ";
2593 break;
2594 case PAR_VFAT16BIG:
2595 zFSName = "VFAT ";
2596 break;
2597 case PAR_VFAT16EXT:
2598 zFSName = "VFAT ";
2599 break;
2600 case PAR_OPUS:
2601 zFSName = "OPUS ";
2602 break;
2603 case PAR_HID12SMALL:
2604 zFSName = "FAT-12*";
2605 break;
2606 case PAR_COMPAQDIAG:
2607 zFSName = "COMPAQ ";
2608 break;
2609 case PAR_HID16SMALL:
2610 zFSName = "FAT-16*";
2611 break;
2612 case PAR_HID16BIG:
2613 zFSName = "BIGDOS*";
2614 break;
2615 case PAR_HIDHPFS:
2616 zFSName = "HPFS* ";
2617 break;
2618 case PAR_WINDOWSSWP:
2619 zFSName = "WINSWAP";
2620 break;
2621 case PAR_NECDOS:
2622 zFSName = "NECDOS ";
2623 break;
2624 case PAR_THEOS:
2625 zFSName = "THEOS ";
2626 break;
2627 case PAR_VENIX:
2628 zFSName = "VENIX ";
2629 break;
2630 case PAR_RISCBOOT:
2631 zFSName = "RISC ";
2632 break;
2633 case PAR_SFS:
2634 zFSName = "SFS ";
2635 break;
2636 case PAR_ONTRACK:
2637 zFSName = "ONTRACK";
2638 break;
2639 case PAR_ONTRACKEXT:
2640 zFSName = "ONTRACK";
2641 break;
2642 case PAR_CPM:
2643 zFSName = "CP/M ";
2644 break;
2645 case PAR_UNIXSYSV:
2646 zFSName = "UNIX ";
2647 break;
2648 case PAR_NOVELL_64:
2649 zFSName = "NOVELL ";
2650 break;
2651 case PAR_NOVELL_65:
2652 zFSName = "NOVELL ";
2653 break;
2654 case PAR_NOVELL_67:
2655 zFSName = "NOVELL ";
2656 break;
2657 case PAR_NOVELL_68:
2658 zFSName = "NOVELL ";
2659 break;
2660 case PAR_NOVELL_69:
2661 zFSName = "NOVELL ";
2662 break;
2663 case PAR_PCIX:
2664 zFSName = "PCIX ";
2665 break;
2666 case PAR_MINIX:
2667 zFSName = "MINIX ";
2668 break;
2669 case PAR_LINUX:
2670 zFSName = "LINUX ";
2671 break;
2672 case PAR_LINUXSWAP:
2673 zFSName = "LNXSWP ";
2674 break;
2675 case PAR_LINUXFILE:
2676 zFSName = "LINUX ";
2677 break;
2678 case PAR_FREEBSD:
2679 zFSName = "FREEBSD";
2680 break;
2681 case PAR_BBT:
2682 zFSName = "BBT ";
2683 break;
2684
2685 default:
2686 zFSName = " ";
2687 break;
2688 }
2689 return zFSName;
2690}
2691
2692/*
2693 * AppendPartition:
2694 * this appends the given partition information to
2695 * the given partition list. To do this, a new
2696 * PARTITIONINFO structure is created and appended
2697 * in a list (managed thru the PARTITIONINFO.pNext
2698 * items).
2699 *
2700 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
2701 * With each call of this function, this pointer is advanced
2702 * to point to the newly created PARTITIONINFO, so before
2703 * calling this function for the first time,
2704 *
2705 * Based on code (C) Dmitry A. Steklenev.
2706 *
2707 *@@added V0.9.0 [umoeller]
2708 */
2709
2710APIRET AppendPartition(PARTITIONINFO **pppiFirst,
2711 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
2712 PUSHORT posCount, // in/out: partition count
2713 BYTE bDisk, // in: disk of partition
2714 char *pszBootName, // in: boot partition name
2715 CHAR cLetter, // in/out: drive letter
2716 BYTE bFsType, // in: file system type
2717 BOOL fPrimary, // in: primary?
2718 BOOL fBootable,
2719 ULONG ulSectors) // in: no. of sectors
2720{
2721 APIRET arc = NO_ERROR;
2722 PPARTITIONINFO ppiNew = (PPARTITIONINFO)malloc(sizeof(PARTITIONINFO));
2723 if (ppiNew)
2724 {
2725 // store data
2726 ppiNew->bDisk = bDisk;
2727 if (fBootable)
2728 {
2729 memcpy(ppiNew->szBootName, pszBootName, 8);
2730 ppiNew->szBootName[8] = 0;
2731 }
2732 else
2733 ppiNew->szBootName[0] = 0;
2734 ppiNew->cLetter = cLetter;
2735 ppiNew->bFSType = bFsType;
2736 strcpy(ppiNew->szFSType,
2737 doshType2FSName(bFsType));
2738 ppiNew->fPrimary = fPrimary;
2739 ppiNew->fBootable = fBootable;
2740 ppiNew->ulSize = ulSectors / 2048;
2741
2742 ppiNew->pNext = NULL;
2743
2744 (*posCount)++;
2745
2746 if (*pppiFirst == (PPARTITIONINFO)NULL)
2747 {
2748 // first call:
2749 *pppiFirst = ppiNew;
2750 *pppiThis = ppiNew;
2751 }
2752 else
2753 {
2754 // append to list
2755 (**pppiThis).pNext = ppiNew;
2756 *pppiThis = ppiNew;
2757 }
2758 }
2759 else
2760 arc = ERROR_NOT_ENOUGH_MEMORY;
2761
2762 return (arc);
2763}
2764
2765// Sector and Cylinder values are actually 6 bits and 10 bits:
2766//
2767// 1 1 1 1 1 1
2768// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
2769// ³c c c c c c c c C c S s s s s s³
2770// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
2771//
2772// The high two bits of the second byte are used as the high bits
2773// of a 10-bit value. This allows for as many as 1024 cylinders
2774// and 64 sectors per cylinder.
2775
2776/*
2777 * GetCyl:
2778 * get cylinder number.
2779 *
2780 * Based on code (C) Dmitry A. Steklenev.
2781 *
2782 *@@added V0.9.0 [umoeller]
2783 */
2784
2785static USHORT GetCyl(USHORT rBeginSecCyl)
2786{
2787 return ( (rBeginSecCyl & 0x00C0) << 2)
2788 + ((rBeginSecCyl & 0xFF00) >> 8);
2789}
2790
2791/*
2792 * GetSec:
2793 * get sector number.
2794 *
2795 * Based on code (C) Dmitry A. Steklenev.
2796 *
2797 *@@added V0.9.0 [umoeller]
2798 */
2799
2800static USHORT GetSec(USHORT rBeginSecCyl)
2801{
2802 return rBeginSecCyl & 0x003F;
2803}
2804
2805/*
2806 *@@ doshGetBootManager:
2807 * this goes thru the master boot records on all
2808 * disks to find the boot manager partitions.
2809 *
2810 * Returns:
2811 * -- NO_ERROR: boot manager found; in that case,
2812 * information about the boot manager
2813 * is written into *pusDisk, *pusPart,
2814 * *BmInfo. Any of these pointers can
2815 * be NULL if you're not interested.
2816 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
2817 *
2818 * Based on code (C) Dmitry A. Steklenev.
2819 *
2820 *@@added V0.9.0 [umoeller]
2821 */
2822
2823APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
2824 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
2825 PAR_INFO *BmInfo) // out: if != NULL, boot manager partition info
2826{
2827 APIRET arc = NO_ERROR;
2828 USHORT count = doshQueryDiskCount(); // Physical disk number
2829 MBR_INFO MBoot; // Master Boot
2830 USHORT usDisk;
2831
2832 if (count > 8) // Not above 8 disks
2833 count = 8;
2834
2835 for (usDisk = 1; usDisk <= count; usDisk++)
2836 {
2837 USHORT usPrim = 0;
2838
2839 // for each disk, read the MBR, which has the
2840 // primary partitions
2841 if ((arc = doshReadSector(usDisk, &MBoot,
2842 0, 0, 1)))
2843 return (arc);
2844
2845 // Lookup BootManager partition
2846 for (usPrim = 0; usPrim < 4; usPrim++)
2847 {
2848 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
2849 {
2850 if (BmInfo)
2851 *BmInfo = MBoot.sPrtnInfo[usPrim];
2852 if (pusPart)
2853 *pusPart = usPrim;
2854 if (pusDisk)
2855 *pusDisk = usDisk;
2856 return (NO_ERROR);
2857 }
2858 }
2859 }
2860
2861 return (ERROR_NOT_SUPPORTED);
2862}
2863
2864/*
2865 * GetPrimaryPartitions:
2866 * this returns the primary partitions.
2867 *
2868 * This gets called from doshGetPartitionsList.
2869 *
2870 * Returns 0 upon errors.
2871 *
2872 * Based on code (C) Dmitry A. Steklenev.
2873 *
2874 *@@added V0.9.0 [umoeller]
2875 */
2876
2877APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
2878 PARTITIONINFO **pppiThis,
2879 PUSHORT posCount, // in/out: partition count
2880 PCHAR pcLetter, // in/out: drive letter counter
2881 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
2882 PAR_INFO* BmInfo, // in: info returned by doshGetBootManager or NULL
2883 UINT iDisk) // in: system's physical disk count
2884{
2885 APIRET arc = NO_ERROR;
2886
2887 if (!BmInfo)
2888 arc = ERROR_INVALID_PARAMETER;
2889 else
2890 {
2891 SYS_INFO MName[32]; // Name Space from Boot Manager
2892 memset(&MName, 0, sizeof(MName));
2893
2894 // read boot manager name table
2895 if (!(arc = doshReadSector(BmDisk,
2896 &MName,
2897 BmInfo->bBeginHead,
2898 GetCyl(BmInfo->rBeginSecCyl),
2899 GetSec(BmInfo->rBeginSecCyl) + 3)))
2900 {
2901 MBR_INFO MBoot; // Master Boot
2902 USHORT i;
2903
2904 // read master boot record of this disk
2905 if (!(arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
2906 {
2907 for (i = 0;
2908 i < 4; // there can be only four primary partitions
2909 i++)
2910 {
2911 // skip unused partition, BootManager or Extended partition
2912 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
2913 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER) // skip boot manager
2914 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_EXTENDED) // skip extended
2915 )
2916 {
2917 BOOL fBootable = ( (BmInfo)
2918 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
2919 );
2920 // store this partition
2921 if ((arc = AppendPartition(pppiFirst,
2922 pppiThis,
2923 posCount,
2924 iDisk,
2925 (fBootable)
2926 ? (char*)&MName[(iDisk - 1) * 4 + i].name
2927 : "",
2928 *pcLetter,
2929 MBoot.sPrtnInfo[i].bFileSysCode,
2930 TRUE, // primary
2931 fBootable,
2932 MBoot.sPrtnInfo[i].lTotalSects)))
2933 return (arc);
2934 }
2935 }
2936 }
2937 }
2938 }
2939
2940 return (arc);
2941}
2942
2943/*
2944 * GetLogicalDrives:
2945 * this returns info for the logical drives
2946 * in the extended partition. This gets called
2947 * from GetExtendedPartition.
2948 *
2949 * This gets called from GetExtendedPartition.
2950 *
2951 * Based on code (C) Dmitry A. Steklenev.
2952 *
2953 *@@added V0.9.0 [umoeller]
2954 */
2955
2956APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
2957 PARTITIONINFO **pppiThis,
2958 PUSHORT posCount,
2959 PCHAR pcLetter,
2960 PAR_INFO* PrInfo, // in: MBR entry of extended partition
2961 UINT PrDisk,
2962 PAR_INFO* BmInfo)
2963{
2964 APIRET arc = NO_ERROR;
2965 EXT_INFO MBoot; // Master Boot
2966 USHORT i;
2967
2968 if ((arc = doshReadSector(PrDisk,
2969 &MBoot,
2970 PrInfo->bBeginHead,
2971 GetCyl(PrInfo->rBeginSecCyl),
2972 GetSec(PrInfo->rBeginSecCyl))))
2973 return (arc);
2974
2975 for (i = 0; i < 4; i++)
2976 {
2977 // skip unused partition or BootManager partition
2978 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
2979 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER)
2980 )
2981 {
2982 BOOL fBootable = FALSE;
2983 BOOL fAssignLetter = FALSE;
2984
2985 // special work around extended partition
2986 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
2987 {
2988 if ((arc = GetLogicalDrives(pppiFirst,
2989 pppiThis,
2990 posCount,
2991 pcLetter,
2992 &MBoot.sPrtnInfo[i],
2993 PrDisk,
2994 BmInfo)))
2995 return (arc);
2996
2997 continue;
2998 }
2999
3000 // raise driver letter if OS/2 would recognize this drive
3001 if ( (MBoot.sPrtnInfo[i].bFileSysCode < PAR_PCIX)
3002 )
3003 fAssignLetter = TRUE;
3004
3005 if (fAssignLetter)
3006 (*pcLetter)++;
3007
3008 fBootable = ( (BmInfo)
3009 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
3010 );
3011
3012 if ((arc = AppendPartition(pppiFirst,
3013 pppiThis,
3014 posCount,
3015 PrDisk,
3016 (fBootable)
3017 ? (char*)&MBoot.sBmNames[i].name
3018 : "",
3019 (fAssignLetter)
3020 ? *pcLetter
3021 : ' ',
3022 MBoot.sPrtnInfo[i].bFileSysCode,
3023 FALSE, // primary
3024 fBootable, // bootable
3025 MBoot.sPrtnInfo[i].lTotalSects)))
3026 return (arc);
3027 }
3028 }
3029
3030 return (NO_ERROR);
3031}
3032
3033/*
3034 * GetExtendedPartition:
3035 * this finds the extended partition on the given
3036 * drive and calls GetLogicalDrives in turn.
3037 *
3038 * This gets called from doshGetPartitionsList.
3039 *
3040 * Based on code (C) Dmitry A. Steklenev.
3041 *
3042 *@@added V0.9.0 [umoeller]
3043 */
3044
3045APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
3046 PARTITIONINFO **pppiThis,
3047 PUSHORT posCount,
3048 PCHAR pcLetter,
3049 PAR_INFO* BmInfo,
3050 UINT iDisk) // in: disk to query
3051{
3052 APIRET arc = NO_ERROR;
3053 MBR_INFO MBoot; // Master Boot
3054 USHORT i;
3055
3056 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
3057 return (arc);
3058
3059 // go thru MBR entries to find extended partition
3060 for (i = 0;
3061 i < 4;
3062 i++)
3063 {
3064 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
3065 {
3066 if ((arc = GetLogicalDrives(pppiFirst,
3067 pppiThis,
3068 posCount,
3069 pcLetter,
3070 &MBoot.sPrtnInfo[i],
3071 iDisk,
3072 BmInfo)))
3073 return (arc);
3074 }
3075 }
3076
3077 return (NO_ERROR);
3078}
3079
3080/*
3081 *@@ doshGetPartitionsList:
3082 * this returns lots of information about the
3083 * partitions on all physical disks, which is
3084 * read directly from the MBRs and partition
3085 * tables.
3086 *
3087 * If NO_ERROR is returned by this function,
3088 * *ppPartitionInfo points to a linked list of
3089 * PARTITIONINFO structures, which has
3090 * *pusPartitionCount items.
3091 *
3092 * In that case, use doshFreePartitionsList to
3093 * free the resources allocated by this function.
3094 *
3095 * The linked list starts out with all the primary
3096 * partitions, followed by the logical drives in
3097 * the extended partitions. This function attempts
3098 * to guess the correct drive letters and stores
3099 * these with the PARTITIONINFO items, but there's
3100 * no guarantee that this is correct. We correctly
3101 * ignore Linux partitions here and give all primary
3102 * partitions the C: letter, but I have no idea
3103 * what happens with NTFS partitions, since I have
3104 * none.
3105 *
3106 * If an error != NO_ERROR is returned, *pusContext
3107 * will be set to one of the following:
3108 *
3109 * -- 1: boot manager not found
3110 *
3111 * -- 2: primary partitions error
3112 *
3113 * -- 3: secondary partitions error
3114 *
3115 * Based on code (C) Dmitry A. Steklenev.
3116 *
3117 *@@added V0.9.0 [umoeller]
3118 */
3119
3120APIRET doshGetPartitionsList(PPARTITIONINFO *ppPartitionInfo, // out: partition info array
3121 PUSHORT pusPartitionCount, // out: count of items in **ppPartitionInfo
3122 PUSHORT pusContext) // out: error context
3123{
3124 APIRET arc = NO_ERROR;
3125 PAR_INFO BmInfo; // BootManager partition
3126 USHORT usBmDisk; // BootManager disk
3127 USHORT cDisks = doshQueryDiskCount(); // physical disks count
3128 USHORT i;
3129
3130 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
3131 *ppiTemp = NULL;
3132 USHORT osCount; // bootable partition count
3133 CHAR cLetter = 'C'; // first drive letter
3134
3135 if (cDisks > 8) // Not above 8 disks
3136 cDisks = 8;
3137
3138 // get boot manager disk and info
3139 if ((arc = doshGetBootManager(&usBmDisk,
3140 NULL,
3141 &BmInfo)) != NO_ERROR)
3142 {
3143 *pusContext = 1;
3144 return (arc);
3145 }
3146 // on each disk, read primary partitions
3147 for (i = 1; i <= cDisks; i++)
3148 {
3149 if ((arc = GetPrimaryPartitions(&pPartitionInfos,
3150 &ppiTemp,
3151 &osCount,
3152 &cLetter,
3153 usBmDisk,
3154 usBmDisk ? &BmInfo : 0,
3155 i)))
3156 {
3157 *pusContext = 2;
3158 return (arc);
3159 }
3160 }
3161
3162 if (usBmDisk)
3163 {
3164 // boot manager found:
3165 // on each disk, read extended partition
3166 // with logical drives
3167 for (i = 1; i <= cDisks; i++)
3168 {
3169 if ((arc = GetExtendedPartition(&pPartitionInfos,
3170 &ppiTemp,
3171 &osCount,
3172 &cLetter,
3173 &BmInfo,
3174 i)))
3175 {
3176 *pusContext = 3;
3177 return (arc);
3178 }
3179 }
3180 }
3181
3182 *ppPartitionInfo = pPartitionInfos;
3183 *pusPartitionCount = osCount;
3184
3185 return (NO_ERROR); // 0
3186}
3187
3188/*
3189 *@@ doshFreePartitionsList:
3190 * this frees the resources allocated by
3191 * doshGetPartitionsList.
3192 *
3193 *@@added V0.9.0 [umoeller]
3194 */
3195
3196APIRET doshFreePartitionsList(PPARTITIONINFO pPartitionInfo)
3197{
3198 PPARTITIONINFO ppiThis = NULL;
3199 ppiThis = pPartitionInfo;
3200 while (ppiThis)
3201 {
3202 PPARTITIONINFO ppiNext = ppiThis->pNext;
3203 free(ppiThis);
3204 ppiThis = ppiNext;
3205 }
3206 return (NO_ERROR);
3207}
3208
3209
Note: See TracBrowser for help on using the repository browser.