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

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

Tons of updates.

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