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

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

Final changes for 0.9.7, i hope...

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