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

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