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

Last change on this file since 8 was 8, checked in by umoeller, 25 years ago

Initial checkin of helpers code which used to be in WarpIN.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 69.8 KB
Line 
1
2/*
3 *@@sourcefile dosh2.c:
4 * dosh.c contains more Control Program helper functions.
5 *
6 * This file is new with V0.9.4 (2000-07-26) [umoeller].
7 *
8 * As opposed to the functions in dosh.c, these require
9 * linking against other helpers. As a result, these have
10 * been separated from dosh.c to allow linking against
11 * dosh.obj only.
12 *
13 * Function prefixes:
14 * -- dosh* Dos (Control Program) helper functions
15 *
16 * This has the same header as dosh.c, dosh.h.
17 *
18 * The partition functions in this file are based on
19 * code which has kindly been provided by Dmitry A. Steklenev.
20 * See doshGetPartitionsList for how to use these.
21 *
22 * Note: Version numbering in this file relates to XWorkplace version
23 * numbering.
24 *
25 *@@header "helpers\dosh.h"
26 *@@added V0.9.4 (2000-07-27) [umoeller]
27 */
28
29/*
30 * This file Copyright (C) 1997-2000 Ulrich M”ller,
31 * Dmitry A. Steklenev.
32 * This file is part of the XWorkplace source package.
33 * XWorkplace 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_DOSDEVIOCTL
49#define INCL_DOS
50#define INCL_DOSERRORS
51// #define INCL_GPI
52#include <os2.h>
53#include <stdlib.h>
54#include <string.h>
55#include <stdio.h>
56#include <ctype.h>
57
58#include "setup.h" // code generation and debugging options
59
60#include "helpers\dosh.h"
61#include "helpers\stringh.h"
62
63#pragma hdrstop
64
65/*
66 *@@category: Helpers\Control program helpers\Miscellaneous
67 */
68
69/* ******************************************************************
70 * *
71 * Miscellaneous *
72 * *
73 ********************************************************************/
74
75/*
76 *@@ doshIsValidFileName:
77 * this returns NO_ERROR only if pszFile is a valid file name.
78 * This may include a full path.
79 *
80 * If a drive letter is specified, this checks for whether
81 * that drive is a FAT drive and adjust the checks accordingly,
82 * i.e. 8+3 syntax (per path component).
83 *
84 * If no drive letter is specified, this check is performed
85 * for the current drive.
86 *
87 * This also checks if pszFileNames contains characters which
88 * are invalid for the current drive.
89 *
90 * Note: this performs syntactic checks only. This does not
91 * check for whether the specified path components exist.
92 * However, it _is_ checked for whether the given drive
93 * exists.
94 *
95 * This func is especially useful to check filenames that
96 * have been entered by the user in a "Save as" dialog.
97 *
98 * If an error is found, the corresponding DOS error code
99 * is returned:
100 * -- ERROR_INVALID_DRIVE
101 * -- ERROR_FILENAME_EXCED_RANGE (on FAT: no 8+3 filename)
102 * -- ERROR_INVALID_NAME (invalid character)
103 * -- ERROR_CURRENT_DIRECTORY (if fFullyQualified: no full path specified)
104 *
105 *@@changed V0.9.2 (2000-03-11) [umoeller]: added fFullyQualified
106 */
107
108APIRET doshIsValidFileName(const char* pcszFile,
109 BOOL fFullyQualified) // in: if TRUE, pcszFile must be fully q'fied
110{
111 CHAR szPath[CCHMAXPATH+4] = " :";
112 CHAR szComponent[CCHMAXPATH];
113 PSZ p1, p2;
114 BOOL fIsFAT = FALSE;
115 PSZ pszInvalid;
116
117 if (fFullyQualified) // V0.9.2 (2000-03-11) [umoeller]
118 {
119 if ( (*(pcszFile + 1) != ':')
120 || (*(pcszFile + 2) != '\\')
121 )
122 return (ERROR_CURRENT_DIRECTORY);
123 }
124
125 // check drive first
126 if (*(pcszFile + 1) == ':')
127 {
128 CHAR cDrive = toupper(*pcszFile);
129 // drive specified:
130 strcpy(szPath, pcszFile);
131 szPath[0] = toupper(*pcszFile);
132 if (doshQueryDiskFree(cDrive - 'A' + 1) == -1)
133 return (ERROR_INVALID_DRIVE);
134 }
135 else
136 {
137 // no drive specified: take current
138 ULONG ulDriveNum = 0,
139 ulDriveMap = 0;
140 DosQueryCurrentDisk(&ulDriveNum, &ulDriveMap);
141 szPath[0] = ((UCHAR)ulDriveNum) + 'A' - 1;
142 szPath[1] = ':';
143 strcpy(&szPath[2], pcszFile);
144 }
145
146 fIsFAT = doshIsFileOnFAT(szPath);
147
148 pszInvalid = (fIsFAT)
149 ? "<>|+=:;,\"/[] " // invalid characters in FAT
150 : "<>|:\"/"; // invalid characters in IFS's
151
152 // now separate path components
153 p1 = &szPath[2]; // advance past ':'
154
155 do {
156
157 if (*p1 == '\\')
158 p1++;
159
160 p2 = strchr(p1, '\\');
161 if (p2 == NULL)
162 p2 = p1 + strlen(p1);
163
164 if (p1 != p2)
165 {
166 LONG lDotOfs = -1,
167 lAfterDot = -1;
168 ULONG cbFile,
169 ul;
170 PSZ pSource = szComponent;
171
172 strncpy(szComponent, p1, p2-p1);
173 szComponent[p2-p1] = 0;
174 cbFile = strlen(szComponent);
175
176 // now check each path component
177 for (ul = 0; ul < cbFile; ul++)
178 {
179 if (fIsFAT)
180 {
181 // on FAT: only 8 characters allowed before dot
182 if (*pSource == '.')
183 {
184 lDotOfs = ul;
185 lAfterDot = 0;
186 if (ul > 7)
187 return (ERROR_FILENAME_EXCED_RANGE);
188 }
189 }
190 // and check for invalid characters
191 if (strchr(pszInvalid, *pSource) != NULL)
192 return (ERROR_INVALID_NAME);
193
194 pSource++;
195
196 // on FAT, allow only three chars after dot
197 if (fIsFAT)
198 if (lAfterDot != -1)
199 {
200 lAfterDot++;
201 if (lAfterDot > 3)
202 return(ERROR_FILENAME_EXCED_RANGE);
203 }
204 }
205
206 // we are still missing the case of a FAT file
207 // name without extension; if so, check whether
208 // the file stem is <= 8 chars
209 if (fIsFAT)
210 if (lDotOfs == -1) // dot not found:
211 if (cbFile > 8)
212 return (ERROR_FILENAME_EXCED_RANGE);
213 }
214
215 // go for next component
216 p1 = p2+1;
217 } while (*p2);
218
219 return (NO_ERROR);
220}
221
222/*
223 *@@ doshMakeRealName:
224 * this copies pszSource to pszTarget, replacing
225 * all characters which are not supported by file
226 * systems with cReplace.
227 * pszTarget must be at least the same size as pszSource.
228 * If (fIsFAT), the file name will be made FAT-compliant (8+3).
229 * Returns TRUE if characters were replaced.
230 *
231 *@@changed V0.9.0 (99-11-06) [umoeller]: now replacing "*" too
232 */
233
234BOOL doshMakeRealName(PSZ pszTarget, // out: new real name
235 PSZ pszSource, // in: filename to translate
236 CHAR cReplace, // in: replacement char for invalid
237 // characters (e.g. '!')
238 BOOL fIsFAT) // in: make-FAT-compatible flag
239{
240 ULONG ul,
241 cbSource = strlen(pszSource);
242 LONG lDotOfs = -1,
243 lAfterDot = -1;
244 BOOL brc = FALSE;
245 PSZ pSource = pszSource,
246 pTarget = pszTarget,
247 pszInvalid = (fIsFAT)
248 ? "*<>|+=:;,\"/\\[] " // invalid characters in FAT
249 : "*<>|:\"/\\"; // invalid characters in IFS's
250
251 for (ul = 0; ul < cbSource; ul++)
252 {
253 if (fIsFAT)
254 {
255 // on FAT: truncate filename if neccessary
256 if (*pSource == '.')
257 {
258 lDotOfs = ul;
259 lAfterDot = 0;
260 if (ul > 7) {
261 // only 8 characters allowed before dot,
262 // so set target ptr to dot pos
263 pTarget = pszTarget+8;
264 }
265 }
266 }
267 // and replace invalid characters
268 if (strchr(pszInvalid, *pSource) == NULL)
269 *pTarget = *pSource;
270 else
271 {
272 *pTarget = cReplace;
273 brc = TRUE;
274 }
275 pTarget++;
276 pSource++;
277
278 // on FAT, allow only three chars after dot
279 if (fIsFAT)
280 if (lAfterDot != -1)
281 {
282 lAfterDot++;
283 if (lAfterDot > 3)
284 break;
285 }
286 }
287 *pTarget = '\0';
288
289 if (fIsFAT)
290 {
291 // we are still missing the case of a FAT file
292 // name without extension; if so, check whether
293 // the file stem is <= 8 chars
294 if (lDotOfs == -1) // dot not found:
295 if (cbSource > 8)
296 *(pszTarget+8) = 0; // truncate
297
298 // convert to upper case
299 strupr(pszTarget);
300 }
301
302 return (brc);
303}
304
305/*
306 *@@ doshSetCurrentDir:
307 * sets the current working directory
308 * to the given path.
309 *
310 * As opposed to DosSetCurrentDir, this
311 * one will change the current drive
312 * also, if one is specified.
313 */
314
315APIRET doshSetCurrentDir(const char *pcszDir)
316{
317 if (pcszDir)
318 {
319 if (*pcszDir != 0)
320 if (*(pcszDir+1) == ':')
321 {
322 // drive given:
323 CHAR cDrive = toupper(*(pcszDir));
324 APIRET arc;
325 // change drive
326 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
327 // 1 = A:, 2 = B:, ...
328 if (arc != NO_ERROR)
329 return (arc);
330 }
331
332 return (DosSetCurrentDir((PSZ)pcszDir));
333 }
334 return (ERROR_INVALID_PARAMETER);
335}
336
337/*
338 *@@category: Helpers\Control program helpers\Environment management
339 */
340
341/* ******************************************************************
342 * *
343 * Environment helpers *
344 * *
345 ********************************************************************/
346
347/*
348 *@@ doshParseEnvironment:
349 * this takes one of those ugly environment strings
350 * as used by DosStartSession and WinStartApp (with
351 * lots of zero-terminated strings one after another
352 * and a duplicate zero byte as a terminator) as
353 * input and splits it into an array of separate
354 * strings in pEnv.
355 *
356 * The newly allocated strings are stored in in
357 * pEnv->papszVars. The array count is stored in
358 * pEnv->cVars.
359 *
360 * Each environment variable will be copied into
361 * one newly allocated string in the array. Use
362 * doshFreeEnvironment to free the memory allocated
363 * by this function.
364 *
365 * Use the following code to browse thru the array:
366 +
367 + DOSENVIRONMENT Env = {0};
368 + if (doshParseEnvironment(pszEnv,
369 + &Env)
370 + == NO_ERROR)
371 + {
372 + if (Env.papszVars)
373 + {
374 + PSZ *ppszThis = Env.papszVars;
375 + for (ul = 0;
376 + ul < Env.cVars;
377 + ul++)
378 + {
379 + PSZ pszThis = *ppszThis;
380 + // pszThis now has something like PATH=C:\TEMP
381 + // ...
382 + // next environment string
383 + ppszThis++;
384 + }
385 + }
386 + doshFreeEnvironment(&Env);
387 + }
388 *
389 *@@added V0.9.4 (2000-08-02) [umoeller]
390 */
391
392APIRET doshParseEnvironment(const char *pcszEnv,
393 PDOSENVIRONMENT pEnv)
394{
395 APIRET arc = NO_ERROR;
396 if (!pcszEnv)
397 arc = ERROR_INVALID_PARAMETER;
398 else
399 {
400 PSZ pszVarThis = (PSZ)pcszEnv;
401 ULONG cVars = 0;
402 // count strings
403 while (*pszVarThis)
404 {
405 cVars++;
406 pszVarThis += strlen(pszVarThis) + 1;
407 }
408
409 pEnv->cVars = cVars;
410 pEnv->papszVars = 0;
411
412 if (cVars)
413 {
414 PSZ *papsz = (PSZ*)malloc(sizeof(PSZ) * cVars);
415 if (!papsz)
416 arc = ERROR_NOT_ENOUGH_MEMORY;
417 else
418 {
419 PSZ *ppszTarget = papsz;
420 memset(papsz, 0, sizeof(PSZ) * cVars);
421 pszVarThis = (PSZ)pcszEnv;
422 while (*pszVarThis)
423 {
424 *ppszTarget = strdup(pszVarThis);
425 ppszTarget++;
426 pszVarThis += strlen(pszVarThis) + 1;
427 }
428
429 pEnv->papszVars = papsz;
430 }
431 }
432 }
433
434 return (arc);
435}
436
437/*
438 *@@ doshGetEnvironment:
439 * calls doshParseEnvironment for the current
440 * process environment, which is retrieved from
441 * the info blocks.
442 *
443 *@@added V0.9.4 (2000-07-19) [umoeller]
444 */
445
446APIRET doshGetEnvironment(PDOSENVIRONMENT pEnv)
447{
448 APIRET arc = NO_ERROR;
449 if (!pEnv)
450 arc = ERROR_INVALID_PARAMETER;
451 else
452 {
453 PTIB ptib = 0;
454 PPIB ppib = 0;
455 arc = DosGetInfoBlocks(&ptib, &ppib);
456 if (arc == NO_ERROR)
457 {
458 PSZ pszEnv = ppib->pib_pchenv;
459 if (pszEnv)
460 arc = doshParseEnvironment(pszEnv, pEnv);
461 else
462 arc = ERROR_BAD_ENVIRONMENT;
463 }
464 }
465
466 return (arc);
467}
468
469/*
470 *@@ doshFindEnvironmentVar:
471 * returns the PSZ* in the pEnv->papszVars array
472 * which specifies the environment variable in pszVarName.
473 *
474 * With pszVarName, you can either specify the variable
475 * name only ("VARNAME") or a full environment string
476 * ("VARNAME=BLAH"). In any case, only the variable name
477 * is compared.
478 *
479 * Returns NULL if no such variable name was found in
480 * the array.
481 *
482 *@@added V0.9.4 (2000-07-19) [umoeller]
483 */
484
485PSZ* doshFindEnvironmentVar(PDOSENVIRONMENT pEnv,
486 PSZ pszVarName)
487{
488 PSZ *ppszRet = 0;
489 if (pEnv)
490 {
491 if ((pEnv->papszVars) && (pszVarName))
492 {
493 PSZ *ppszThis = pEnv->papszVars;
494 PSZ pszThis;
495 ULONG ul = 0;
496 ULONG ulVarNameLen = 0;
497
498 PSZ pszSearch = NULL; // receives "VARNAME="
499 PSZ pFirstEqual = strchr(pszVarName, '=');
500 if (pFirstEqual)
501 pszSearch = strhSubstr(pszVarName, pFirstEqual + 1);
502 else
503 {
504 ulVarNameLen = strlen(pszVarName);
505 pszSearch = (PSZ)malloc(ulVarNameLen + 2);
506 memcpy(pszSearch, pszVarName, ulVarNameLen);
507 *(pszSearch + ulVarNameLen) = '=';
508 *(pszSearch + ulVarNameLen + 1) = 0;
509 }
510
511 ulVarNameLen = strlen(pszSearch);
512
513 for (ul = 0;
514 ul < pEnv->cVars;
515 ul++)
516 {
517 pszThis = *ppszThis;
518
519 if (strnicmp(*ppszThis, pszSearch, ulVarNameLen) == 0)
520 {
521 ppszRet = ppszThis;
522 break;
523 }
524
525 // next environment string
526 ppszThis++;
527 }
528 }
529 }
530
531 return (ppszRet);
532}
533
534/*
535 *@@ doshSetEnvironmentVar:
536 * sets an environment variable in the specified
537 * environment, which must have been initialized
538 * using doshGetEnvironment first.
539 *
540 * pszNewEnv must be a full environment string
541 * in the form "VARNAME=VALUE".
542 *
543 * If "VARNAME" has already been set to something
544 * in the string array in pEnv, that array item
545 * is replaced. Otherwise a new item is added to
546 * the array, and pEnv->cVars is raised by one.
547 *
548 *@@added V0.9.4 (2000-07-19) [umoeller]
549 */
550
551APIRET doshSetEnvironmentVar(PDOSENVIRONMENT pEnv,
552 PSZ pszNewEnv)
553{
554 APIRET arc = NO_ERROR;
555 if (!pEnv)
556 arc = ERROR_INVALID_PARAMETER;
557 else
558 {
559 if ((!pEnv->papszVars) || (!pszNewEnv))
560 arc = ERROR_INVALID_PARAMETER;
561 else
562 {
563 PSZ *ppszEnvLine = doshFindEnvironmentVar(pEnv, pszNewEnv);
564 if (ppszEnvLine)
565 {
566 // was set already: replace
567 free(*ppszEnvLine);
568 *ppszEnvLine = strdup(pszNewEnv);
569 }
570 else
571 {
572 PSZ *ppszNew;
573 PSZ *papszNew;
574 // not set already:
575 // append
576 // reallocate array and add new string
577 papszNew = (PSZ*)realloc(pEnv->papszVars, sizeof(PSZ) * (pEnv->cVars + 1));
578 if (!papszNew)
579 arc = ERROR_NOT_ENOUGH_MEMORY;
580 else
581 {
582 pEnv->papszVars = papszNew;
583 ppszNew = pEnv->papszVars + pEnv->cVars;
584 pEnv->cVars++;
585 *ppszNew = strdup(pszNewEnv);
586 }
587 }
588 }
589 }
590
591 return (arc);
592}
593
594/*
595 *@@ doshConvertEnvironment:
596 * converts an environment initialized by doshGetEnvironment
597 * to the string format required by WinStartApp and DosExecPgm,
598 * that is, one memory block is allocated in *ppszEnv and all
599 * strings in pEnv->papszVars are copied to that block. Each
600 * string is terminated with a null character; the last string
601 * is terminated with two null characters.
602 *
603 * Use free() to free the memory block allocated by this
604 * function in *ppszEnv.
605 *
606 *@@added V0.9.4 (2000-07-19) [umoeller]
607 */
608
609APIRET doshConvertEnvironment(PDOSENVIRONMENT pEnv,
610 PSZ *ppszEnv, // out: environment string
611 PULONG pulSize) // out: size of block allocated in *ppszEnv
612{
613 APIRET arc = NO_ERROR;
614 if (!pEnv)
615 arc = ERROR_INVALID_PARAMETER;
616 else
617 {
618 if (!pEnv->papszVars)
619 arc = ERROR_INVALID_PARAMETER;
620 else
621 {
622 // count memory needed for all strings
623 ULONG cbNeeded = 0,
624 ul = 0;
625 PSZ *ppszThis = pEnv->papszVars;
626
627 for (ul = 0;
628 ul < pEnv->cVars;
629 ul++)
630 {
631 cbNeeded += strlen(*ppszThis) + 1; // length of string plus null terminator
632
633 // next environment string
634 ppszThis++;
635 }
636
637 cbNeeded++; // for another null terminator
638
639 *ppszEnv = (PSZ)malloc(cbNeeded);
640 if (!(*ppszEnv))
641 arc = ERROR_NOT_ENOUGH_MEMORY;
642 else
643 {
644 PSZ pTarget = *ppszEnv;
645 *pulSize = cbNeeded;
646 ppszThis = pEnv->papszVars;
647
648 // now copy each string
649 for (ul = 0;
650 ul < pEnv->cVars;
651 ul++)
652 {
653 PSZ pSource = *ppszThis;
654
655 while ((*pTarget++ = *pSource++))
656 ;
657
658 // *pTarget++ = 0; // append null terminator per string
659
660 // next environment string
661 ppszThis++;
662 }
663
664 *pTarget++ = 0; // append second null terminator
665 }
666 }
667 }
668
669 return (arc);
670}
671
672/*
673 *@@ doshFreeEnvironment:
674 * frees memory allocated by doshGetEnvironment.
675 *
676 *@@added V0.9.4 (2000-07-19) [umoeller]
677 */
678
679APIRET doshFreeEnvironment(PDOSENVIRONMENT pEnv)
680{
681 APIRET arc = NO_ERROR;
682 if (!pEnv)
683 arc = ERROR_INVALID_PARAMETER;
684 else
685 {
686 if (!pEnv->papszVars)
687 arc = ERROR_INVALID_PARAMETER;
688 else
689 {
690 PSZ *ppszThis = pEnv->papszVars;
691 PSZ pszThis;
692 ULONG ul = 0;
693
694 for (ul = 0;
695 ul < pEnv->cVars;
696 ul++)
697 {
698 pszThis = *ppszThis;
699 free(pszThis);
700 // *ppszThis = NULL;
701 // next environment string
702 ppszThis++;
703 }
704
705 free(pEnv->papszVars);
706 pEnv->cVars = 0;
707 }
708 }
709
710 return (arc);
711}
712
713/*
714 *@@category: Helpers\Control program helpers\Module handling
715 */
716
717/* ******************************************************************
718 * *
719 * Module handling helpers *
720 * *
721 ********************************************************************/
722
723/*
724 *@@ doshResolveImports:
725 * this function loads the module called pszModuleName
726 * and resolves imports dynamically using DosQueryProcAddress.
727 *
728 * To specify the functions to be imported, a RESOLVEFUNCTION
729 * array is used. In each of the array items, specify the
730 * name of the function and a pointer to a function pointer
731 * where to store the resolved address.
732 *
733 *@@added V0.9.3 (2000-04-29) [umoeller]
734 */
735
736APIRET doshResolveImports(PSZ pszModuleName, // in: DLL to load
737 HMODULE *phmod, // out: module handle
738 PRESOLVEFUNCTION paResolves, // in/out: function resolves
739 ULONG cResolves) // in: array item count (not array size!)
740{
741 CHAR szName[CCHMAXPATH];
742 APIRET arc = DosLoadModule(szName,
743 sizeof(szName),
744 pszModuleName,
745 phmod);
746 if (arc == NO_ERROR)
747 {
748 ULONG ul;
749 for (ul = 0;
750 ul < cResolves;
751 ul++)
752 {
753 arc = DosQueryProcAddr(*phmod,
754 0, // ordinal, ignored
755 (PSZ)paResolves[ul].pcszFunctionName,
756 paResolves[ul].ppFuncAddress);
757
758 /* _Pmpf(("Resolved %s to 0x%lX, rc: %d",
759 paResolves[ul].pcszFunctionName,
760 *paResolves[ul].ppFuncAddress,
761 arc)); */
762 if (arc != NO_ERROR)
763 break;
764 }
765 }
766
767 return (arc);
768}
769
770/*
771 *@@category: Helpers\Control program helpers\Executable info
772 */
773
774/********************************************************************
775 * *
776 * Executable functions *
777 * *
778 ********************************************************************/
779
780/*
781 *@@ doshExecOpen:
782 * this opens the specified executable file
783 * (which can be an .EXE, .COM, .DLL, or
784 * driver file) for use with the other
785 * doshExec* functions.
786 *
787 * If no error occurs, NO_ERROR is returned
788 * and a pointer to a new EXECUTABLE structure
789 * is stored in *ppExec. Consider this pointer a
790 * handle and pass it to doshExecClose when the
791 * executable is no longer needed to free
792 * resources.
793 *
794 * If NO_ERROR is returned, all the fields through
795 * ulOS are set in EXECUTABLE. The psz* fields
796 * which follow afterwards require an additional
797 * call to doshExecQueryBldLevel.
798 *
799 * If errors occur, this function returns the
800 * following error codes:
801 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
802 * -- ERROR_INVALID_EXE_SIGNATURE: specified file
803 * has no DOS EXE header.
804 * -- ERROR_INVALID_PARAMETER: ppExec is NULL.
805 *
806 * plus those of DosOpen, DosSetFilePtr, and
807 * DosRead.
808 *
809 *@@added V0.9.0 [umoeller]
810 *@@changed V0.9.1 (2000-02-13) [umoeller]: fixed 32-bits flag
811 */
812
813APIRET doshExecOpen(const char* pcszExecutable,
814 PEXECUTABLE* ppExec)
815{
816 ULONG ulAction = 0;
817 HFILE hFile;
818 APIRET arc = DosOpen((PSZ)pcszExecutable,
819 &hFile,
820 &ulAction, // out: action taken
821 0, // in: new file (ignored for read-mode)
822 0, // in: new file attribs (ignored)
823 // open-flags
824 OPEN_ACTION_FAIL_IF_NEW
825 | OPEN_ACTION_OPEN_IF_EXISTS,
826 // open-mode
827 OPEN_FLAGS_FAIL_ON_ERROR // report errors to caller
828 | OPEN_FLAGS_SEQUENTIAL
829 | OPEN_FLAGS_NOINHERIT
830 | OPEN_SHARE_DENYNONE
831 | OPEN_ACCESS_READONLY, // read-only mode
832 NULL); // no EAs
833
834 if (arc == NO_ERROR)
835 {
836 // file opened successfully:
837 // create EXECUTABLE structure
838
839 if (ppExec)
840 {
841 *ppExec = (PEXECUTABLE)malloc(sizeof(EXECUTABLE));
842 if (*ppExec)
843 {
844 ULONG ulLocal = 0;
845
846 memset((*ppExec), 0, sizeof(EXECUTABLE));
847
848 // read old DOS EXE header
849 (*ppExec)->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER));
850 if ((*ppExec)->pDosExeHeader == NULL)
851 arc = ERROR_NOT_ENOUGH_MEMORY;
852 else
853 {
854 ULONG ulBytesRead = 0;
855 arc = DosSetFilePtr(hFile,
856 0L,
857 FILE_BEGIN,
858 &ulLocal); // out: new offset
859 arc = DosRead(hFile,
860 (*ppExec)->pDosExeHeader,
861 sizeof(DOSEXEHEADER),
862 &((*ppExec)->cbDosExeHeader));
863 // now check if we really have a DOS header
864 if ((*ppExec)->pDosExeHeader->usDosExeID != 0x5a4d)
865 arc = ERROR_INVALID_EXE_SIGNATURE;
866 else
867 {
868 // we have a DOS header:
869 if ((*ppExec)->pDosExeHeader->usRelocTableOfs < 0x40)
870 {
871 // not LX or PE or NE:
872 (*ppExec)->ulOS = EXEOS_DOS3;
873 (*ppExec)->ulExeFormat = EXEFORMAT_OLDDOS;
874 }
875 else
876 {
877 // either LX or PE or NE:
878 // read more bytes from position
879 // specified in header
880 arc = DosSetFilePtr(hFile,
881 (*ppExec)->pDosExeHeader->usNewHeaderOfs,
882 FILE_BEGIN,
883 &ulLocal);
884
885 if (arc == NO_ERROR)
886 {
887 PBYTE pbCheckOS = NULL;
888
889 // read two chars to find out header type
890 CHAR achNewHeaderType[2] = "";
891 arc = DosRead(hFile,
892 &achNewHeaderType,
893 sizeof(achNewHeaderType),
894 &ulBytesRead);
895 // reset file ptr
896 DosSetFilePtr(hFile,
897 (*ppExec)->pDosExeHeader->usNewHeaderOfs,
898 FILE_BEGIN,
899 &ulLocal);
900
901 if (memcmp(achNewHeaderType, "NE", 2) == 0)
902 {
903 // New Executable:
904 (*ppExec)->ulExeFormat = EXEFORMAT_NE;
905 // read NE header
906 (*ppExec)->pNEHeader = (PNEHEADER)malloc(sizeof(NEHEADER));
907 DosRead(hFile,
908 (*ppExec)->pNEHeader,
909 sizeof(NEHEADER),
910 &((*ppExec)->cbNEHeader));
911 if ((*ppExec)->cbNEHeader == sizeof(NEHEADER))
912 pbCheckOS = &((*ppExec)->pNEHeader->bTargetOS);
913 }
914 else if ( (memcmp(achNewHeaderType, "LX", 2) == 0)
915 || (memcmp(achNewHeaderType, "LE", 2) == 0)
916 // this is used by SMARTDRV.EXE
917 )
918 {
919 // OS/2 Linear Executable:
920 (*ppExec)->ulExeFormat = EXEFORMAT_LX;
921 // read LX header
922 (*ppExec)->pLXHeader = (PLXHEADER)malloc(sizeof(LXHEADER));
923 DosRead(hFile,
924 (*ppExec)->pLXHeader,
925 sizeof(LXHEADER),
926 &((*ppExec)->cbLXHeader));
927 if ((*ppExec)->cbLXHeader == sizeof(LXHEADER))
928 pbCheckOS = (PBYTE)(&((*ppExec)->pLXHeader->usTargetOS));
929 }
930 else if (memcmp(achNewHeaderType, "PE", 2) == 0)
931 {
932 (*ppExec)->ulExeFormat = EXEFORMAT_PE;
933 (*ppExec)->ulOS = EXEOS_WIN32;
934 (*ppExec)->f32Bits = TRUE;
935 }
936 else
937 arc = ERROR_INVALID_EXE_SIGNATURE;
938
939 if (pbCheckOS)
940 // BYTE to check for operating system
941 // (NE and LX):
942 switch (*pbCheckOS)
943 {
944 case NEOS_OS2:
945 (*ppExec)->ulOS = EXEOS_OS2;
946 if ((*ppExec)->ulExeFormat == EXEFORMAT_LX)
947 (*ppExec)->f32Bits = TRUE;
948 break;
949 case NEOS_WIN16:
950 (*ppExec)->ulOS = EXEOS_WIN16;
951 break;
952 case NEOS_DOS4:
953 (*ppExec)->ulOS = EXEOS_DOS4;
954 break;
955 case NEOS_WIN386:
956 (*ppExec)->ulOS = EXEOS_WIN386;
957 (*ppExec)->f32Bits = TRUE;
958 break;
959 }
960 }
961 }
962 }
963
964 // store exec's HFILE
965 (*ppExec)->hfExe = hFile;
966 }
967
968 if (arc != NO_ERROR)
969 // error: clean up
970 doshExecClose(*ppExec);
971
972 } // end if (*ppExec)
973 else
974 arc = ERROR_NOT_ENOUGH_MEMORY;
975 } // end if (ppExec)
976 else
977 arc = ERROR_INVALID_PARAMETER;
978 } // end if (arc == NO_ERROR)
979
980 return (arc);
981}
982
983/*
984 *@@ doshExecClose:
985 * this closes an executable opened with doshExecOpen.
986 * Always call this function if NO_ERROR was returned by
987 * doshExecOpen.
988 *
989 *@@added V0.9.0 [umoeller]
990 */
991
992APIRET doshExecClose(PEXECUTABLE pExec)
993{
994 APIRET arc = NO_ERROR;
995 if (pExec)
996 {
997 if (pExec->pDosExeHeader)
998 free(pExec->pDosExeHeader);
999 if (pExec->pNEHeader)
1000 free(pExec->pNEHeader);
1001 if (pExec->pLXHeader)
1002 free(pExec->pLXHeader);
1003
1004 if (pExec->pszDescription)
1005 free(pExec->pszDescription);
1006 if (pExec->pszVendor)
1007 free(pExec->pszVendor);
1008 if (pExec->pszVersion)
1009 free(pExec->pszVersion);
1010 if (pExec->pszInfo)
1011 free(pExec->pszInfo);
1012
1013 DosClose(pExec->hfExe);
1014
1015 free(pExec);
1016 }
1017 else
1018 arc = ERROR_INVALID_PARAMETER;
1019
1020 return (arc);
1021}
1022
1023/*
1024 *@@ doshExecQueryBldLevel:
1025 * this retrieves buildlevel information for an
1026 * LX or NE executable previously opened with
1027 * doshExecOpen.
1028 *
1029 * BuildLevel information must be contained in the
1030 * DESCRIPTION field of an executable's module
1031 * definition (.DEF) file. In order to be readable
1032 * by BLDLEVEL.EXE (which ships with OS/2), this
1033 * string must have the following format:
1034 *
1035 + Description '@#AUTHOR:VERSION#@ DESCRIPTION'
1036 *
1037 * Example:
1038 *
1039 + Description '@#Ulrich M”ller:0.9.0#@ XWorkplace Sound Support Module'
1040 *
1041 * The "Description" entry always ends up as the
1042 * very first entry in the non-resident name table
1043 * in LX and NE executables. So this is what we retrieve
1044 * here.
1045 *
1046 * If the first entry in that table exists, NO_ERROR is
1047 * returned and at least the pszDescription field in
1048 * EXECUTABLE is set to that information.
1049 *
1050 * If that string is in IBM BLDLEVEL format, the string
1051 * is automatically parsed, and the pszVendor, pszVersion,
1052 * and pszInfo fields are also set. In the above examples,
1053 * this would return the following information:
1054 + pszVendor = "Ulrich M”ller"
1055 + pszVersion = "0.9.0"
1056 + pszInfo = "XWorkplace Sound Support Module"
1057 *
1058 * If that string is not in BLDLEVEL format, only pszDescription
1059 * will be set. The other fields remain NULL.
1060 *
1061 * This returns the following errors:
1062 * -- ERROR_INVALID_PARAMETER: pExec invalid
1063 * -- ERROR_INVALID_EXE_SIGNATURE (191): pExec is not in LX or NE format
1064 * -- ERROR_INVALID_DATA (13): non-resident name table not found.
1065 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
1066 *
1067 * plus the error codes of DosSetFilePtr and DosRead.
1068 *
1069 *@@added V0.9.0 [umoeller]
1070 *@@changed V0.9.0 (99-10-22) [umoeller]: NE format now supported
1071 *@@changed V0.9.1 (99-12-06): fixed memory leak
1072 */
1073
1074APIRET doshExecQueryBldLevel(PEXECUTABLE pExec)
1075{
1076 APIRET arc = NO_ERROR;
1077 PSZ pszNameTable = NULL;
1078 ULONG ulNRNTOfs = 0;
1079
1080 do
1081 {
1082 ULONG ulLocal = 0,
1083 ulBytesRead = 0;
1084 PSZ pStartOfAuthor = NULL;
1085
1086 if (pExec == NULL)
1087 {
1088 arc = ERROR_INVALID_PARAMETER;
1089 break;
1090 }
1091
1092 if (pExec->ulExeFormat == EXEFORMAT_LX)
1093 {
1094 // OK, LX format:
1095 // check if we have a non-resident name table
1096 if (pExec->pLXHeader == NULL)
1097 {
1098 arc = ERROR_INVALID_DATA;
1099 break;
1100 }
1101 if (pExec->pLXHeader->ulNonResdNameTblOfs == 0)
1102 {
1103 arc = ERROR_INVALID_DATA;
1104 break;
1105 }
1106
1107 ulNRNTOfs = pExec->pLXHeader->ulNonResdNameTblOfs;
1108 }
1109 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1110 {
1111 // OK, NE format:
1112 // check if we have a non-resident name table
1113 if (pExec->pNEHeader == NULL)
1114 {
1115 arc = ERROR_INVALID_DATA;
1116 break;
1117 }
1118 if (pExec->pNEHeader->ulNonResdTblOfs == 0)
1119 {
1120 arc = ERROR_INVALID_DATA;
1121 break;
1122 }
1123
1124 ulNRNTOfs = pExec->pNEHeader->ulNonResdTblOfs;
1125 }
1126 else
1127 {
1128 // neither LX nor NE: stop
1129 arc = ERROR_INVALID_EXE_SIGNATURE;
1130 break;
1131 }
1132
1133 if (ulNRNTOfs == 0)
1134 {
1135 // shouldn't happen
1136 arc = ERROR_INVALID_DATA;
1137 break;
1138 }
1139
1140 // move EXE file pointer to offset of non-resident name table
1141 // (from LX header)
1142 arc = DosSetFilePtr(pExec->hfExe, // file is still open
1143 ulNRNTOfs, // ofs determined above
1144 FILE_BEGIN,
1145 &ulLocal);
1146 if (arc != NO_ERROR)
1147 break;
1148
1149 // allocate memory as necessary
1150 pszNameTable = (PSZ)malloc(2001); // should suffice, because each entry
1151 // may only be 255 bytes in length
1152 if (pszNameTable)
1153 {
1154 arc = DosRead(pExec->hfExe,
1155 pszNameTable,
1156 2000,
1157 &ulBytesRead);
1158 if (arc != NO_ERROR)
1159 break;
1160 if (*pszNameTable == 0)
1161 {
1162 // first byte (length byte) is null:
1163 arc = ERROR_INVALID_DATA;
1164 free (pszNameTable); // fixed V0.9.1 (99-12-06)
1165 break;
1166 }
1167
1168 // now copy the string, which is in Pascal format
1169 pExec->pszDescription = (PSZ)malloc((*pszNameTable) + 1); // addt'l null byte
1170 memcpy(pExec->pszDescription,
1171 pszNameTable + 1, // skip length byte
1172 *pszNameTable); // length byte
1173 // terminate string
1174 *(pExec->pszDescription + (*pszNameTable)) = 0;
1175
1176 // _Pmpf(("pszDescription: %s", pExec->pszDescription));
1177
1178 // now parse the damn thing:
1179 // @#AUTHOR:VERSION#@ DESCRIPTION
1180 // but skip the first byte, which has the string length
1181 pStartOfAuthor = strstr(pExec->pszDescription, "@#");
1182 if (pStartOfAuthor)
1183 {
1184 PSZ pStartOfInfo = strstr(pStartOfAuthor + 2, "#@");
1185 // _Pmpf(("Testing"));
1186 if (pStartOfInfo)
1187 {
1188 PSZ pEndOfAuthor = strchr(pStartOfAuthor + 2, ':');
1189 // _Pmpf(("pStartOfinfo: %s", pStartOfInfo));
1190 if (pEndOfAuthor)
1191 {
1192 // _Pmpf(("pEndOfAuthor: %s", pEndOfAuthor));
1193 pExec->pszVendor = strhSubstr(pStartOfAuthor + 2, pEndOfAuthor);
1194 pExec->pszVersion = strhSubstr(pEndOfAuthor + 1, pStartOfInfo);
1195 // skip "@#" in info string
1196 pStartOfInfo += 2;
1197 // skip leading spaces in info string
1198 while (*pStartOfInfo == ' ')
1199 pStartOfInfo++;
1200 // and copy until end of string
1201 pExec->pszInfo = strdup(pStartOfInfo);
1202 }
1203 }
1204 }
1205
1206 free(pszNameTable);
1207 }
1208 else
1209 arc = ERROR_NOT_ENOUGH_MEMORY;
1210 } while (FALSE);
1211
1212
1213 return (arc);
1214}
1215
1216/*
1217 *@@category: Helpers\Control program helpers\Partitions info
1218 */
1219
1220/********************************************************************
1221 * *
1222 * Partition functions *
1223 * *
1224 ********************************************************************/
1225
1226/*
1227 *@@ doshQueryDiskCount:
1228 * returns the no. of physical disks installed
1229 * on the system.
1230 *
1231 * Based on code (C) Dmitry A. Steklenev.
1232 *
1233 *@@added V0.9.0 [umoeller]
1234 */
1235
1236UINT doshQueryDiskCount(VOID)
1237{
1238 USHORT count = 0;
1239
1240 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &count, 2, 0, 0);
1241 return (count);
1242}
1243
1244/*
1245 *@@ doshReadSector:
1246 * reads a physical disk sector.
1247 *
1248 * If NO_ERROR is returned, the sector contents
1249 * have been stored in *buff.
1250 *
1251 * Based on code (C) Dmitry A. Steklenev.
1252 *
1253 *@@added V0.9.0 [umoeller]
1254 */
1255
1256APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
1257 void *buff,
1258 USHORT head,
1259 USHORT cylinder,
1260 USHORT sector)
1261{
1262 UINT arc;
1263 HFILE dh = 0;
1264 char dn[256];
1265 // char ms[256];
1266
1267 sprintf( dn, "%u:", disk );
1268 arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3);
1269
1270 if (arc)
1271 // error:
1272 return (arc);
1273 else
1274 {
1275 TRACKLAYOUT DiskIOParm;
1276 ULONG IOCtlDataLength = sizeof(DiskIOParm);
1277 ULONG IOCtlParmLength = 512;
1278
1279 DiskIOParm.bCommand = 0;
1280 DiskIOParm.usHead = head;
1281 DiskIOParm.usCylinder = cylinder;
1282 DiskIOParm.usFirstSector = 0;
1283 DiskIOParm.cSectors = 1;
1284 DiskIOParm.TrackTable[0].usSectorNumber = sector;
1285 DiskIOParm.TrackTable[0].usSectorSize = 512;
1286
1287 arc = DosDevIOCtl(dh,
1288 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
1289 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
1290 buff , IOCtlDataLength, &IOCtlDataLength);
1291
1292 if(arc)
1293 {
1294 // error:
1295 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
1296 return (arc);
1297 }
1298
1299 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
1300 }
1301 return (NO_ERROR);
1302}
1303
1304/*
1305 *@@ doshType2FSName:
1306 * this returns a static, zero-terminated string
1307 * for the given FS type. This is always 7 bytes
1308 * in length.
1309 *
1310 * Values for operating system indicator:
1311 * -- 00h empty
1312 * -- 01h DOS 12-bit FAT
1313 * -- 02h XENIX root file system
1314 * -- 03h XENIX /usr file system (obsolete)
1315 * -- 04h DOS 16-bit FAT (up to 32M)
1316 * -- 05h DOS 3.3+ extended partition
1317 * -- 06h DOS 3.31+ Large File System (16-bit FAT, over 32M)
1318 * -- 07h QNX
1319 * -- 07h OS/2 HPFS
1320 * -- 07h Windows NT NTFS
1321 * -- 07h Advanced Unix
1322 * -- 08h OS/2 (v1.0-1.3 only)
1323 * -- 08h AIX bootable partition, SplitDrive
1324 * -- 08h Commodore DOS
1325 * -- 08h DELL partition spanning multiple drives
1326 * -- 09h AIX data partition
1327 * -- 09h Coherent filesystem
1328 * -- 0Ah OS/2 Boot Manager
1329 * -- 0Ah OPUS
1330 * -- 0Ah Coherent swap partition
1331 * -- 0Bh Windows95 with 32-bit FAT
1332 * -- 0Ch Windows95 with 32-bit FAT (using LBA-mode INT 13 extensions)
1333 * -- 0Eh logical-block-addressable VFAT (same as 06h but using LBA-mode INT 13)
1334 * -- 0Fh logical-block-addressable VFAT (same as 05h but using LBA-mode INT 13)
1335 * -- 10h OPUS
1336 * -- 11h OS/2 Boot Manager hidden 12-bit FAT partition
1337 * -- 12h Compaq Diagnostics partition
1338 * -- 14h (resulted from using Novell DOS 7.0 FDISK to delete Linux Native part)
1339 * -- 14h OS/2 Boot Manager hidden sub-32M 16-bit FAT partition
1340 * -- 16h OS/2 Boot Manager hidden over-32M 16-bit FAT partition
1341 * -- 17h OS/2 Boot Manager hidden HPFS partition
1342 * -- 18h AST special Windows swap file ("Zero-Volt Suspend" partition)
1343 * -- 21h officially listed as reserved
1344 * -- 23h officially listed as reserved
1345 * -- 24h NEC MS-DOS 3.x
1346 * -- 26h officially listed as reserved
1347 * -- 31h officially listed as reserved
1348 * -- 33h officially listed as reserved
1349 * -- 34h officially listed as reserved
1350 * -- 36h officially listed as reserved
1351 * -- 38h Theos
1352 * -- 3Ch PowerQuest PartitionMagic recovery partition
1353 * -- 40h VENIX 80286
1354 * -- 41h Personal RISC Boot
1355 * -- 42h SFS (Secure File System) by Peter Gutmann
1356 * -- 50h OnTrack Disk Manager, read-only partition
1357 * -- 51h OnTrack Disk Manager, read/write partition
1358 * -- 51h NOVEL
1359 * -- 52h CP/M
1360 * -- 52h Microport System V/386
1361 * -- 53h OnTrack Disk Manager, write-only partition???
1362 * -- 54h OnTrack Disk Manager (DDO)
1363 * -- 56h GoldenBow VFeature
1364 * -- 61h SpeedStor
1365 * -- 63h Unix SysV/386, 386/ix
1366 * -- 63h Mach, MtXinu BSD 4.3 on Mach
1367 * -- 63h GNU HURD
1368 * -- 64h Novell NetWare 286
1369 * -- 65h Novell NetWare (3.11)
1370 * -- 67h Novell
1371 * -- 68h Novell
1372 * -- 69h Novell
1373 * -- 70h DiskSecure Multi-Boot
1374 * -- 71h officially listed as reserved
1375 * -- 73h officially listed as reserved
1376 * -- 74h officially listed as reserved
1377 * -- 75h PC/IX
1378 * -- 76h officially listed as reserved
1379 * -- 80h Minix v1.1 - 1.4a
1380 * -- 81h Minix v1.4b+
1381 * -- 81h Linux
1382 * -- 81h Mitac Advanced Disk Manager
1383 * -- 82h Linux Swap partition
1384 * -- 82h Prime
1385 * -- 83h Linux native file system (ext2fs/xiafs)
1386 * -- 84h OS/2-renumbered type 04h partition (related to hiding DOS C: drive)
1387 * -- 86h FAT16 volume/stripe set (Windows NT)
1388 * -- 87h HPFS Fault-Tolerant mirrored partition
1389 * -- 87h NTFS volume/stripe set
1390 * -- 93h Amoeba file system
1391 * -- 94h Amoeba bad block table
1392 * -- A0h Phoenix NoteBIOS Power Management "Save-to-Disk" partition
1393 * -- A1h officially listed as reserved
1394 * -- A3h officially listed as reserved
1395 * -- A4h officially listed as reserved
1396 * -- A5h FreeBSD, BSD/386
1397 * -- A6h officially listed as reserved
1398 * -- B1h officially listed as reserved
1399 * -- B3h officially listed as reserved
1400 * -- B4h officially listed as reserved
1401 * -- B6h officially listed as reserved
1402 * -- B7h BSDI file system (secondarily swap)
1403 * -- B8h BSDI swap partition (secondarily file system)
1404 * -- C1h DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT partition
1405 * -- C4h DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT partition
1406 * -- C6h DR DOS 6.0 LOGIN.EXE-secured Huge partition
1407 * -- C6h corrupted FAT16 volume/stripe set (Windows NT)
1408 * -- C7h Syrinx Boot
1409 * -- C7h corrupted NTFS volume/stripe set
1410 * -- D8h CP/M-86
1411 * -- DBh CP/M, Concurrent CP/M, Concurrent DOS
1412 * -- DBh CTOS (Convergent Technologies OS)
1413 * -- E1h SpeedStor 12-bit FAT extended partition
1414 * -- E3h DOS read-only
1415 * -- E3h Storage Dimensions
1416 * -- E4h SpeedStor 16-bit FAT extended partition
1417 * -- E5h officially listed as reserved
1418 * -- E6h officially listed as reserved
1419 * -- F1h Storage Dimensions
1420 * -- F2h DOS 3.3+ secondary partition
1421 * -- F3h officially listed as reserved
1422 * -- F4h SpeedStor
1423 * -- F4h Storage Dimensions
1424 * -- F6h officially listed as reserved
1425 * -- FEh LANstep
1426 * -- FEh IBM PS/2 IML
1427 * -- FFh Xenix bad block table
1428 *
1429 * Note: for partition type 07h, one should inspect the partition boot record
1430 * for the actual file system type
1431 *
1432 * Based on code (C) Dmitry A. Steklenev.
1433 *
1434 *@@added V0.9.0 [umoeller]
1435 */
1436
1437char* doshType2FSName(unsigned char bFSType) // in: FS type
1438{
1439 PSZ zFSName = NULL;
1440
1441 switch (bFSType)
1442 {
1443 case PAR_UNUSED:
1444 zFSName = "UNUSED ";
1445 break;
1446 case PAR_FAT12SMALL:
1447 zFSName = "FAT-12 ";
1448 break;
1449 case PAR_XENIXROOT:
1450 zFSName = "XENIX ";
1451 break;
1452 case PAR_XENIXUSER:
1453 zFSName = "XENIX ";
1454 break;
1455 case PAR_FAT16SMALL:
1456 zFSName = "FAT-16 ";
1457 break;
1458 case PAR_EXTENDED:
1459 zFSName = "EXTEND ";
1460 break;
1461 case PAR_FAT16BIG:
1462 zFSName = "BIGDOS ";
1463 break;
1464 case PAR_HPFS:
1465 zFSName = "HPFS ";
1466 break;
1467 case PAR_AIXBOOT:
1468 zFSName = "AIX ";
1469 break;
1470 case PAR_AIXDATA:
1471 zFSName = "AIX ";
1472 break;
1473 case PAR_BOOTMANAGER:
1474 zFSName = "BOOTMNG";
1475 break;
1476 case PAR_WINDOWS95:
1477 zFSName = "WIN95 ";
1478 break;
1479 case PAR_WINDOWS95LB:
1480 zFSName = "WIN95 ";
1481 break;
1482 case PAR_VFAT16BIG:
1483 zFSName = "VFAT ";
1484 break;
1485 case PAR_VFAT16EXT:
1486 zFSName = "VFAT ";
1487 break;
1488 case PAR_OPUS:
1489 zFSName = "OPUS ";
1490 break;
1491 case PAR_HID12SMALL:
1492 zFSName = "FAT-12*";
1493 break;
1494 case PAR_COMPAQDIAG:
1495 zFSName = "COMPAQ ";
1496 break;
1497 case PAR_HID16SMALL:
1498 zFSName = "FAT-16*";
1499 break;
1500 case PAR_HID16BIG:
1501 zFSName = "BIGDOS*";
1502 break;
1503 case PAR_HIDHPFS:
1504 zFSName = "HPFS* ";
1505 break;
1506 case PAR_WINDOWSSWP:
1507 zFSName = "WINSWAP";
1508 break;
1509 case PAR_NECDOS:
1510 zFSName = "NECDOS ";
1511 break;
1512 case PAR_THEOS:
1513 zFSName = "THEOS ";
1514 break;
1515 case PAR_VENIX:
1516 zFSName = "VENIX ";
1517 break;
1518 case PAR_RISCBOOT:
1519 zFSName = "RISC ";
1520 break;
1521 case PAR_SFS:
1522 zFSName = "SFS ";
1523 break;
1524 case PAR_ONTRACK:
1525 zFSName = "ONTRACK";
1526 break;
1527 case PAR_ONTRACKEXT:
1528 zFSName = "ONTRACK";
1529 break;
1530 case PAR_CPM:
1531 zFSName = "CP/M ";
1532 break;
1533 case PAR_UNIXSYSV:
1534 zFSName = "UNIX ";
1535 break;
1536 case PAR_NOVELL_64:
1537 zFSName = "NOVELL ";
1538 break;
1539 case PAR_NOVELL_65:
1540 zFSName = "NOVELL ";
1541 break;
1542 case PAR_NOVELL_67:
1543 zFSName = "NOVELL ";
1544 break;
1545 case PAR_NOVELL_68:
1546 zFSName = "NOVELL ";
1547 break;
1548 case PAR_NOVELL_69:
1549 zFSName = "NOVELL ";
1550 break;
1551 case PAR_PCIX:
1552 zFSName = "PCIX ";
1553 break;
1554 case PAR_MINIX:
1555 zFSName = "MINIX ";
1556 break;
1557 case PAR_LINUX:
1558 zFSName = "LINUX ";
1559 break;
1560 case PAR_LINUXSWAP:
1561 zFSName = "LNXSWP ";
1562 break;
1563 case PAR_LINUXFILE:
1564 zFSName = "LINUX ";
1565 break;
1566 case PAR_FREEBSD:
1567 zFSName = "FREEBSD";
1568 break;
1569 case PAR_BBT:
1570 zFSName = "BBT ";
1571 break;
1572
1573 default:
1574 zFSName = " ";
1575 break;
1576 }
1577 return zFSName;
1578}
1579
1580/*
1581 * AppendPartition:
1582 * this appends the given partition information to
1583 * the given partition list. To do this, a new
1584 * PARTITIONINFO structure is created and appended
1585 * in a list (managed thru the PARTITIONINFO.pNext
1586 * items).
1587 *
1588 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
1589 * With each call of this function, this pointer is advanced
1590 * to point to the newly created PARTITIONINFO, so before
1591 * calling this function for the first time,
1592 *
1593 * Based on code (C) Dmitry A. Steklenev.
1594 *
1595 *@@added V0.9.0 [umoeller]
1596 */
1597
1598APIRET AppendPartition(PARTITIONINFO **pppiFirst,
1599 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
1600 PUSHORT posCount, // in/out: partition count
1601 BYTE bDisk, // in: disk of partition
1602 char *pszBootName, // in: boot partition name
1603 CHAR cLetter, // in/out: drive letter
1604 BYTE bFsType, // in: file system type
1605 BOOL fPrimary, // in: primary?
1606 BOOL fBootable,
1607 ULONG ulSectors) // in: no. of sectors
1608{
1609 APIRET arc = NO_ERROR;
1610 PPARTITIONINFO ppiNew = (PPARTITIONINFO)malloc(sizeof(PARTITIONINFO));
1611 if (ppiNew)
1612 {
1613 // store data
1614 ppiNew->bDisk = bDisk;
1615 if (fBootable)
1616 {
1617 memcpy(ppiNew->szBootName, pszBootName, 8);
1618 ppiNew->szBootName[8] = 0;
1619 }
1620 else
1621 ppiNew->szBootName[0] = 0;
1622 ppiNew->cLetter = cLetter;
1623 ppiNew->bFSType = bFsType;
1624 strcpy(ppiNew->szFSType,
1625 doshType2FSName(bFsType));
1626 ppiNew->fPrimary = fPrimary;
1627 ppiNew->fBootable = fBootable;
1628 ppiNew->ulSize = ulSectors / 2048;
1629
1630 ppiNew->pNext = NULL;
1631
1632 (*posCount)++;
1633
1634 if (*pppiFirst == (PPARTITIONINFO)NULL)
1635 {
1636 // first call:
1637 *pppiFirst = ppiNew;
1638 *pppiThis = ppiNew;
1639 }
1640 else
1641 {
1642 // append to list
1643 (**pppiThis).pNext = ppiNew;
1644 *pppiThis = ppiNew;
1645 }
1646 }
1647 else
1648 arc = ERROR_NOT_ENOUGH_MEMORY;
1649
1650 return (arc);
1651}
1652
1653// Sector and Cylinder values are actually 6 bits and 10 bits:
1654//
1655// 1 1 1 1 1 1
1656// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
1657// ³c c c c c c c c C c S s s s s s³
1658// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
1659//
1660// The high two bits of the second byte are used as the high bits
1661// of a 10-bit value. This allows for as many as 1024 cylinders
1662// and 64 sectors per cylinder.
1663
1664/*
1665 * GetCyl:
1666 * get cylinder number.
1667 *
1668 * Based on code (C) Dmitry A. Steklenev.
1669 *
1670 *@@added V0.9.0 [umoeller]
1671 */
1672
1673static USHORT GetCyl(USHORT rBeginSecCyl)
1674{
1675 return ((rBeginSecCyl & 0x00C0) << 2) +
1676 ((rBeginSecCyl & 0xFF00) >> 8);
1677}
1678
1679/*
1680 * GetSec:
1681 * get sector number.
1682 *
1683 * Based on code (C) Dmitry A. Steklenev.
1684 *
1685 *@@added V0.9.0 [umoeller]
1686 */
1687
1688static USHORT GetSec(USHORT rBeginSecCyl)
1689{
1690 return rBeginSecCyl & 0x003F;
1691}
1692
1693/*
1694 *@@ doshGetBootManager:
1695 * this goes thru the master boot records on all
1696 * disks to find the boot manager partitions.
1697 *
1698 * Returns:
1699 * -- NO_ERROR: boot manager found; in that case,
1700 * information about the boot manager
1701 * is written into *pusDisk, *pusPart,
1702 * *BmInfo. Any of these pointers can
1703 * be NULL if you're not interested.
1704 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
1705 *
1706 * Based on code (C) Dmitry A. Steklenev.
1707 *
1708 *@@added V0.9.0 [umoeller]
1709 */
1710
1711APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
1712 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
1713 PAR_INFO *BmInfo) // out: if != NULL, boot manager partition info
1714{
1715 APIRET arc = NO_ERROR;
1716 USHORT count = doshQueryDiskCount(); // Physical disk number
1717 MBR_INFO MBoot; // Master Boot
1718 USHORT usDisk;
1719
1720 if (count > 8) // Not above 8 disks
1721 count = 8;
1722
1723 for (usDisk = 1; usDisk <= count; usDisk++)
1724 {
1725 USHORT usPrim = 0;
1726
1727 // for each disk, read the MBR, which has the
1728 // primary partitions
1729 if ((arc = doshReadSector(usDisk, &MBoot,
1730 0, 0, 1)))
1731 return (arc);
1732
1733 // Lookup BootManager partition
1734 for (usPrim = 0; usPrim < 4; usPrim++)
1735 {
1736 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
1737 {
1738 if (BmInfo)
1739 *BmInfo = MBoot.sPrtnInfo[usPrim];
1740 if (pusPart)
1741 *pusPart = usPrim;
1742 if (pusDisk)
1743 *pusDisk = usDisk;
1744 return (NO_ERROR);
1745 }
1746 }
1747 }
1748
1749 return (ERROR_NOT_SUPPORTED);
1750}
1751
1752/*
1753 * GetPrimaryPartitions:
1754 * this returns the primary partitions.
1755 *
1756 * This gets called from doshGetPartitionsList.
1757 *
1758 * Returns 0 upon errors.
1759 *
1760 * Based on code (C) Dmitry A. Steklenev.
1761 *
1762 *@@added V0.9.0 [umoeller]
1763 */
1764
1765APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
1766 PARTITIONINFO **pppiThis,
1767 PUSHORT posCount, // in/out: partition count
1768 PCHAR pcLetter, // in/out: drive letter counter
1769 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
1770 PAR_INFO* BmInfo, // in: info returned by doshGetBootManager or NULL
1771 UINT iDisk) // in: system's physical disk count
1772{
1773 APIRET arc = NO_ERROR;
1774 MBR_INFO MBoot; // Master Boot
1775 SYS_INFO MName[32]; // Name Space from Boot Manager
1776 USHORT i;
1777
1778 memset(&MName, 0, sizeof(MName));
1779
1780 if (BmInfo)
1781 {
1782 // read boot manager name table
1783 if ((arc = doshReadSector(BmDisk, &MName, BmInfo->bBeginHead,
1784 GetCyl(BmInfo->rBeginSecCyl),
1785 GetSec(BmInfo->rBeginSecCyl) + 3)))
1786 return (arc);
1787 }
1788
1789 // read master boot record
1790 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
1791 return (arc);
1792
1793 for (i = 0;
1794 i < 4; // there can be only four primary partitions
1795 i++)
1796 {
1797 // skip unused partition, BootManager or Extended partition
1798 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
1799 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER) // skip boot manager
1800 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_EXTENDED) // skip extended
1801 )
1802 {
1803 BOOL fBootable = ( (BmInfo)
1804 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
1805 );
1806 // store this partition
1807 if ((arc = AppendPartition(pppiFirst,
1808 pppiThis,
1809 posCount,
1810 iDisk,
1811 (fBootable)
1812 ? (char*)&MName[(iDisk - 1) * 4 + i].name
1813 : "",
1814 *pcLetter,
1815 MBoot.sPrtnInfo[i].bFileSysCode,
1816 TRUE, // primary
1817 fBootable,
1818 MBoot.sPrtnInfo[i].lTotalSects)))
1819 return (arc);
1820 }
1821 }
1822 return (NO_ERROR);
1823}
1824
1825/*
1826 * GetLogicalDrives:
1827 * this returns info for the logical drives
1828 * in the extended partition. This gets called
1829 * from GetExtendedPartition.
1830 *
1831 * This gets called from GetExtendedPartition.
1832 *
1833 * Based on code (C) Dmitry A. Steklenev.
1834 *
1835 *@@added V0.9.0 [umoeller]
1836 */
1837
1838APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
1839 PARTITIONINFO **pppiThis,
1840 PUSHORT posCount,
1841 PCHAR pcLetter,
1842 PAR_INFO* PrInfo, // in: MBR entry of extended partition
1843 UINT PrDisk,
1844 PAR_INFO* BmInfo)
1845{
1846 APIRET arc = NO_ERROR;
1847 EXT_INFO MBoot; // Master Boot
1848 USHORT i;
1849
1850 if ((arc = doshReadSector(PrDisk, &MBoot, PrInfo->bBeginHead,
1851 GetCyl(PrInfo->rBeginSecCyl),
1852 GetSec(PrInfo->rBeginSecCyl))))
1853 return (arc);
1854
1855 for (i = 0; i < 4; i++)
1856 {
1857 // skip unused partition or BootManager partition
1858 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
1859 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER)
1860 )
1861 {
1862 BOOL fBootable = FALSE;
1863 BOOL fAssignLetter = FALSE;
1864
1865 // special work around extended partition
1866 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
1867 {
1868 if ((arc = GetLogicalDrives(pppiFirst,
1869 pppiThis,
1870 posCount,
1871 pcLetter,
1872 &MBoot.sPrtnInfo[i],
1873 PrDisk,
1874 BmInfo)))
1875 return (arc);
1876
1877 continue;
1878 }
1879
1880 // raise driver letter if OS/2 would recognize this drive
1881 if ( (MBoot.sPrtnInfo[i].bFileSysCode < PAR_PCIX)
1882 )
1883 fAssignLetter = TRUE;
1884
1885 if (fAssignLetter)
1886 (*pcLetter)++;
1887
1888 fBootable = ( (BmInfo)
1889 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
1890 );
1891
1892 if ((arc = AppendPartition(pppiFirst,
1893 pppiThis,
1894 posCount,
1895 PrDisk,
1896 (fBootable)
1897 ? (char*)&MBoot.sBmNames[i].name
1898 : "",
1899 (fAssignLetter)
1900 ? *pcLetter
1901 : ' ',
1902 MBoot.sPrtnInfo[i].bFileSysCode,
1903 FALSE, // primary
1904 fBootable, // bootable
1905 MBoot.sPrtnInfo[i].lTotalSects)))
1906 return (arc);
1907 }
1908
1909 /* // if BootManager installed and partition is bootable
1910 if (BmInfo)
1911 {
1912 if (MBoot.sBmNames[i].bootable & 0x01)
1913 {
1914 }
1915 }
1916
1917 // if BootManager not installed
1918 else
1919 {
1920 if (arc = AppendPartition(pppiFirst,
1921 pppiThis,
1922 posCount,
1923 PrDisk,
1924 "",
1925 *pcLetter,
1926 MBoot.sPrtnInfo[i].bFileSysCode,
1927 FALSE,
1928 MBoot.sPrtnInfo[i].lTotalSects))
1929 return (arc);
1930 } */
1931 }
1932
1933 return (NO_ERROR);
1934}
1935
1936/*
1937 * GetExtendedPartition:
1938 * this finds the extended partition on the given
1939 * drive and calls GetLogicalDrives in turn.
1940 *
1941 * This gets called from doshGetPartitionsList.
1942 *
1943 * Based on code (C) Dmitry A. Steklenev.
1944 *
1945 *@@added V0.9.0 [umoeller]
1946 */
1947
1948APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
1949 PARTITIONINFO **pppiThis,
1950 PUSHORT posCount,
1951 PCHAR pcLetter,
1952 PAR_INFO* BmInfo,
1953 UINT iDisk) // in: disk to query
1954{
1955 APIRET arc = NO_ERROR;
1956 MBR_INFO MBoot; // Master Boot
1957 USHORT i;
1958
1959 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
1960 return (arc);
1961
1962 // go thru MBR entries to find extended partition
1963 for (i = 0;
1964 i < 4;
1965 i++)
1966 {
1967 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
1968 {
1969 if ((arc = GetLogicalDrives(pppiFirst,
1970 pppiThis,
1971 posCount,
1972 pcLetter,
1973 &MBoot.sPrtnInfo[i],
1974 iDisk,
1975 BmInfo)))
1976 return (arc);
1977 }
1978 }
1979
1980 return (NO_ERROR);
1981}
1982
1983/*
1984 *@@ doshGetPartitionsList:
1985 * this returns lots of information about the
1986 * partitions on all physical disks, which is
1987 * read directly from the MBRs and partition
1988 * tables.
1989 *
1990 * If NO_ERROR is returned by this function,
1991 * *ppPartitionInfo points to a linked list of
1992 * PARTITIONINFO structures, which has
1993 * *pusPartitionCount items.
1994 *
1995 * In that case, use doshFreePartitionsList to
1996 * free the resources allocated by this function.
1997 *
1998 * The linked list starts out with all the primary
1999 * partitions, followed by the logical drives in
2000 * the extended partitions. This function attempts
2001 * to guess the correct drive letters and stores
2002 * these with the PARTITIONINFO items, but there's
2003 * no guarantee that this is correct. We correctly
2004 * ignore Linux partitions here and give all primary
2005 * partitions the C: letter, but I have no idea
2006 * what happens with NTFS partitions, since I have
2007 * none.
2008 *
2009 * If an error != NO_ERROR is returned, *pusContext
2010 * will be set to one of the following:
2011 * -- 1: boot manager not found
2012 * -- 2: primary partitions error
2013 * -- 3: secondary partitions error
2014 *
2015 * Based on code (C) Dmitry A. Steklenev.
2016 *
2017 *@@added V0.9.0 [umoeller]
2018 */
2019
2020APIRET doshGetPartitionsList(PPARTITIONINFO *ppPartitionInfo, // out: partition info array
2021 PUSHORT pusPartitionCount, // out: count of items in **ppPartitionInfo
2022 PUSHORT pusContext) // out: error context
2023{
2024 APIRET arc = NO_ERROR;
2025 PAR_INFO BmInfo; // BootManager partition
2026 USHORT usBmDisk; // BootManager disk
2027 USHORT cDisks = doshQueryDiskCount(); // physical disks count
2028 USHORT i;
2029
2030 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
2031 *ppiTemp = NULL;
2032 USHORT osCount; // bootable partition count
2033 CHAR cLetter = 'C'; // first drive letter
2034
2035 if (cDisks > 8) // Not above 8 disks
2036 cDisks = 8;
2037
2038 // get boot manager disk and info
2039 if ((arc = doshGetBootManager(&usBmDisk,
2040 NULL,
2041 &BmInfo)) != NO_ERROR)
2042 {
2043 *pusContext = 1;
2044 return (arc);
2045 }
2046 // on each disk, read primary partitions
2047 for (i = 1; i <= cDisks; i++)
2048 if ((arc = GetPrimaryPartitions(&pPartitionInfos,
2049 &ppiTemp,
2050 &osCount,
2051 &cLetter,
2052 usBmDisk,
2053 usBmDisk ? &BmInfo : 0,
2054 i)))
2055 {
2056 *pusContext = 2;
2057 return (arc);
2058 }
2059
2060 if (usBmDisk)
2061 {
2062 // boot manager found:
2063 // on each disk, read extended partition
2064 // with logical drives
2065 for (i = 1; i <= cDisks; i++)
2066 if ((arc = GetExtendedPartition(&pPartitionInfos,
2067 &ppiTemp,
2068 &osCount,
2069 &cLetter,
2070 &BmInfo,
2071 i)))
2072 {
2073 *pusContext = 3;
2074 return (arc);
2075 }
2076 }
2077
2078 *ppPartitionInfo = pPartitionInfos;
2079 *pusPartitionCount = osCount;
2080
2081 return (NO_ERROR); // 0
2082}
2083
2084/*
2085 *@@ doshFreePartitionsList:
2086 * this frees the resources allocated by
2087 * doshGetPartitionsList.
2088 *
2089 *@@added V0.9.0 [umoeller]
2090 */
2091
2092APIRET doshFreePartitionsList(PPARTITIONINFO pPartitionInfo)
2093{
2094 PPARTITIONINFO ppiThis = NULL;
2095 ppiThis = pPartitionInfo;
2096 while (ppiThis)
2097 {
2098 PPARTITIONINFO ppiNext = ppiThis->pNext;
2099 free(ppiThis);
2100 ppiThis = ppiNext;
2101 }
2102 return (NO_ERROR);
2103}
2104
2105
Note: See TracBrowser for help on using the repository browser.