source: branches/branch-1-0/src/helpers/dosh2.c@ 286

Last change on this file since 286 was 286, checked in by pr, 20 years ago

doshSearchDirs implementation

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 79.4 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\ensure.h"
67#include "helpers\nls.h"
68#include "helpers\standards.h"
69#include "helpers\stringh.h"
70
71#pragma hdrstop
72
73/*
74 *@@category: Helpers\Control program helpers\Miscellaneous
75 */
76
77/* ******************************************************************
78 *
79 * Miscellaneous
80 *
81 ********************************************************************/
82
83/*
84 *@@ doshIsValidFileName:
85 * this returns NO_ERROR only if pszFile is a valid file name.
86 * This may include a full path.
87 *
88 * If a drive letter is specified, this checks for whether
89 * that drive is a FAT drive and adjust the checks accordingly,
90 * i.e. 8+3 syntax (per path component).
91 *
92 * If no drive letter is specified, this check is performed
93 * for the current drive.
94 *
95 * This also checks if pszFileNames contains characters which
96 * are invalid for the current drive.
97 *
98 * Note: this performs syntactic checks only. This does not
99 * check for whether the specified path components exist.
100 * However, it _is_ checked for whether the given drive
101 * exists.
102 *
103 * This func is especially useful to check filenames that
104 * have been entered by the user in a "Save as" dialog.
105 *
106 * If an error is found, the corresponding DOS error code
107 * is returned:
108 * -- ERROR_INVALID_DRIVE
109 * -- ERROR_FILENAME_EXCED_RANGE (on FAT: no 8+3 filename)
110 * -- ERROR_INVALID_NAME (invalid character)
111 * -- ERROR_CURRENT_DIRECTORY (if fFullyQualified: no full path specified)
112 *
113 *@@changed V0.9.2 (2000-03-11) [umoeller]: added fFullyQualified
114 */
115
116APIRET doshIsValidFileName(const char* pcszFile,
117 BOOL fFullyQualified) // in: if TRUE, pcszFile must be fully q'fied
118{
119 APIRET arc = NO_ERROR;
120 CHAR szPath[CCHMAXPATH+4] = " :";
121 CHAR szComponent[CCHMAXPATH];
122 PSZ p1, p2;
123 BOOL fIsFAT = FALSE;
124 PSZ pszInvalid;
125
126 if (fFullyQualified) // V0.9.2 (2000-03-11) [umoeller]
127 {
128 if ( (*(pcszFile + 1) != ':')
129 || (*(pcszFile + 2) != '\\')
130 )
131 arc = ERROR_CURRENT_DIRECTORY;
132 }
133
134 // check drive first
135 if (*(pcszFile + 1) == ':')
136 {
137 CHAR cDrive = toupper(*pcszFile);
138 double d;
139 // drive specified:
140 strcpy(szPath, pcszFile);
141 szPath[0] = toupper(*pcszFile);
142 arc = doshQueryDiskFree(cDrive - 'A' + 1, &d);
143 }
144 else
145 {
146 // no drive specified: take current
147 ULONG ulDriveNum = 0,
148 ulDriveMap = 0;
149 arc = DosQueryCurrentDisk(&ulDriveNum, &ulDriveMap);
150 szPath[0] = ((UCHAR)ulDriveNum) + 'A' - 1;
151 szPath[1] = ':';
152 strcpy(&szPath[2], pcszFile);
153 }
154
155 if (arc == NO_ERROR)
156 {
157 fIsFAT = doshIsFileOnFAT(szPath);
158
159 pszInvalid = (fIsFAT)
160 ? "<>|+=:;,\"/[] " // invalid characters in FAT
161 : "<>|:\"/"; // invalid characters in IFS's
162
163 // now separate path components
164 p1 = &szPath[2]; // advance past ':'
165
166 do {
167
168 if (*p1 == '\\')
169 p1++;
170
171 p2 = strchr(p1, '\\');
172 if (p2 == NULL)
173 p2 = p1 + strlen(p1);
174
175 if (p1 != p2)
176 {
177 LONG lDotOfs = -1,
178 lAfterDot = -1;
179 ULONG cbFile,
180 ul;
181 PSZ pSource = szComponent;
182
183 strncpy(szComponent, p1, p2-p1);
184 szComponent[p2-p1] = 0;
185 cbFile = strlen(szComponent);
186
187 // now check each path component
188 for (ul = 0; ul < cbFile; ul++)
189 {
190 if (fIsFAT)
191 {
192 // on FAT: only 8 characters allowed before dot
193 if (*pSource == '.')
194 {
195 lDotOfs = ul;
196 lAfterDot = 0;
197 if (ul > 7)
198 return (ERROR_FILENAME_EXCED_RANGE);
199 }
200 }
201 // and check for invalid characters
202 if (strchr(pszInvalid, *pSource) != NULL)
203 return (ERROR_INVALID_NAME);
204
205 pSource++;
206
207 // on FAT, allow only three chars after dot
208 if (fIsFAT)
209 if (lAfterDot != -1)
210 {
211 lAfterDot++;
212 if (lAfterDot > 3)
213 return (ERROR_FILENAME_EXCED_RANGE);
214 }
215 }
216
217 // we are still missing the case of a FAT file
218 // name without extension; if so, check whether
219 // the file stem is <= 8 chars
220 if (fIsFAT)
221 if (lDotOfs == -1) // dot not found:
222 if (cbFile > 8)
223 return (ERROR_FILENAME_EXCED_RANGE);
224 }
225
226 // go for next component
227 p1 = p2+1;
228 } while (*p2);
229 }
230
231 return arc;
232}
233
234/*
235 *@@ doshMakeRealName:
236 * this copies pszSource to pszTarget, replacing
237 * all characters which are not supported by file
238 * systems with cReplace.
239 *
240 * pszTarget must be at least the same size as pszSource.
241 * If (fIsFAT), the file name will be made FAT-compliant (8+3).
242 *
243 * Returns TRUE if characters were replaced.
244 *
245 *@@changed V0.9.0 (99-11-06) [umoeller]: now replacing "*" too
246 */
247
248BOOL doshMakeRealName(PSZ pszTarget, // out: new real name
249 PSZ pszSource, // in: filename to translate
250 CHAR cReplace, // in: replacement char for invalid
251 // characters (e.g. '!')
252 BOOL fIsFAT) // in: make-FAT-compatible flag
253{
254 ULONG ul,
255 cbSource = strlen(pszSource);
256 LONG lDotOfs = -1,
257 lAfterDot = -1;
258 BOOL brc = FALSE;
259 PSZ pSource = pszSource,
260 pTarget = pszTarget;
261
262 const char *pcszInvalid = (fIsFAT)
263 ? "*<>|+=:;,\"/\\[] " // invalid characters in FAT
264 : "*<>|:\"/\\"; // invalid characters in IFS's
265
266 for (ul = 0; ul < cbSource; ul++)
267 {
268 if (fIsFAT)
269 {
270 // on FAT: truncate filename if neccessary
271 if (*pSource == '.')
272 {
273 lDotOfs = ul;
274 lAfterDot = 0;
275 if (ul > 7) {
276 // only 8 characters allowed before dot,
277 // so set target ptr to dot pos
278 pTarget = pszTarget+8;
279 }
280 }
281 }
282 // and replace invalid characters
283 if (strchr(pcszInvalid, *pSource) == NULL)
284 *pTarget = *pSource;
285 else
286 {
287 *pTarget = cReplace;
288 brc = TRUE;
289 }
290 pTarget++;
291 pSource++;
292
293 // on FAT, allow only three chars after dot
294 if (fIsFAT)
295 if (lAfterDot != -1)
296 {
297 lAfterDot++;
298 if (lAfterDot > 3)
299 break;
300 }
301 }
302 *pTarget = '\0';
303
304 if (fIsFAT)
305 {
306 // we are still missing the case of a FAT file
307 // name without extension; if so, check whether
308 // the file stem is <= 8 chars
309 if (lDotOfs == -1) // dot not found:
310 if (cbSource > 8)
311 *(pszTarget+8) = 0; // truncate
312
313 // convert to upper case
314 strupr(pszTarget);
315 }
316
317 return brc;
318}
319
320/*
321 *@@ doshSetCurrentDir:
322 * sets the current working directory
323 * to the given path.
324 *
325 * As opposed to DosSetCurrentDir, this
326 * one will change the current drive
327 * also, if one is specified.
328 *
329 *@@changed V0.9.9 (2001-04-04) [umoeller]: this returned an error even if none occurred, fixed
330 */
331
332APIRET doshSetCurrentDir(const char *pcszDir)
333{
334 APIRET arc = NO_ERROR;
335 if (!pcszDir)
336 return (ERROR_INVALID_PARAMETER);
337 {
338 if (*pcszDir != 0)
339 if (*(pcszDir+1) == ':')
340 {
341 // drive given:
342 CHAR cDrive = toupper(*(pcszDir));
343 // change drive
344 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
345 // 1 = A:, 2 = B:, ...
346 }
347
348 arc = DosSetCurrentDir((PSZ)pcszDir);
349 }
350
351 return arc; // V0.9.9 (2001-04-04) [umoeller]
352}
353
354/*
355 *@@ CopyToBuffer:
356 * little helper for copying a string to
357 * a target buffer with length checking.
358 *
359 * Returns:
360 *
361 * -- NO_ERROR
362 *
363 * -- ERROR_BUFFER_OVERFLOW if pszTarget does
364 * not have enough room to hold pcszSource
365 * (including the null terminator).
366 *
367 *@@added V0.9.16 (2001-10-08) [umoeller]
368 */
369
370STATIC APIRET CopyToBuffer(PSZ pszTarget, // out: target buffer
371 PCSZ pcszSource, // in: source string
372 ULONG cbTarget) // in: size of target buffer
373{
374 ULONG ulLength = strlen(pcszSource);
375 if (ulLength < cbTarget)
376 {
377 memcpy(pszTarget,
378 pcszSource,
379 ulLength + 1);
380 return NO_ERROR;
381 }
382
383 return(ERROR_BUFFER_OVERFLOW);
384}
385
386/*
387 *@@ doshSearchPath:
388 * replacement for DosSearchPath.
389 *
390 * This looks along all directories which are
391 * specified in the value of the given environment
392 * variable if pcszFile is found.
393 *
394 * As opposed to the stupid DosSearchPath, this
395 * ignores subdirectories in the path particles.
396 * For example, DosSearchPath would usually not
397 * find an INSTALL file because \OS2 contains
398 * an INSTALL directory, or NETSCAPE because
399 * \OS2\INSTALL contains a NETSCAPE directory.
400 *
401 * Returns:
402 *
403 * -- NO_ERROR: pszExecutable has received the
404 * full path of pcszFile.
405 *
406 * -- ERROR_FILE_NOT_FOUND: pcszFile was not found
407 * in the specified path (or is a directory).
408 *
409 * -- ERROR_BUFFER_OVERFLOW: pcszFile was found, but
410 * the pszExecutable buffer is too small to hold
411 * the full path.
412 *
413 *@@added V0.9.16 (2001-10-08) [umoeller]
414 *@@changed V1.0.0 (2002-11-23) [umoeller]: allowing NULL pcszPath to search for "PATH"
415 */
416
417APIRET doshSearchPath(const char *pcszPath, // in: path variable name; if NULL, we use "PATH"
418 const char *pcszFile, // in: file to look for (e.g. "LVM.EXE")
419 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
420 ULONG cbExecutable) // in: sizeof (*pszExecutable)
421{
422 APIRET arc = NO_ERROR;
423
424 // get the PATH value
425 PCSZ pcszPathValue;
426
427 if (!pcszPath)
428 pcszPath = "PATH"; // V1.0.0 (2002-11-23) [umoeller]
429
430 if (!(arc = DosScanEnv((PSZ)pcszPath,
431#if __cplusplus
432 &pcszPathValue)))
433#else
434 (PSZ*)&pcszPathValue)))
435#endif
436 {
437 // run thru the path components
438 PSZ pszPathCopy;
439 if (pszPathCopy = strdup(pcszPathValue))
440 {
441 PSZ pszToken = strtok(pszPathCopy, ";");
442 while (pszToken)
443 {
444 CHAR szFileMask[2*CCHMAXPATH];
445 FILESTATUS3 fs3;
446
447 sprintf(szFileMask,
448 "%s\\%s",
449 pszToken, // path particle
450 pcszFile); // e.g. "netscape"
451
452 if ( (!(arc = DosQueryPathInfo(szFileMask,
453 FIL_STANDARD,
454 &fs3,
455 sizeof(fs3))))
456 // make sure it's not a directory
457 // and that it's not hidden
458 && (!(fs3.attrFile & (FILE_DIRECTORY | FILE_HIDDEN)))
459 )
460 {
461 // copy
462 arc = CopyToBuffer(pszExecutable,
463 szFileMask,
464 cbExecutable);
465 // and stop
466 break;
467 }
468 else
469 arc = ERROR_FILE_NOT_FOUND;
470 // and search on
471
472 pszToken = strtok(NULL, ";");
473 };
474
475 free(pszPathCopy);
476 }
477 else
478 arc = ERROR_NOT_ENOUGH_MEMORY;
479 }
480
481 return arc;
482}
483
484/*
485 *@@ doshSearchDirs:
486 * This looks along all directories which are
487 * specified in the passed directory list
488 * if pcszFile is found.
489 *
490 * As opposed to the stupid DosSearchPath, this
491 * ignores subdirectories in the path particles.
492 * For example, DosSearchPath would usually not
493 * find an INSTALL file because \OS2 contains
494 * an INSTALL directory, or NETSCAPE because
495 * \OS2\INSTALL contains a NETSCAPE directory.
496 *
497 * Returns:
498 *
499 * -- NO_ERROR: pszExecutable has received the
500 * full path of pcszFile.
501 *
502 * -- ERROR_FILE_NOT_FOUND: pcszFile was not found
503 * in the specified path (or is a directory).
504 *
505 * -- ERROR_BUFFER_OVERFLOW: pcszFile was found, but
506 * the pszExecutable buffer is too small to hold
507 * the full path.
508 *
509 *@@added V1.0.4 (2005-06-16) [chennecke]: blatantly stolen from doshSearchPath
510 */
511
512APIRET doshSearchDirs(const char *pcszDirList, // in: list of directories in PATH style
513 const char *pcszFile, // in: file to look for (e.g. "LVM.EXE")
514 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
515 ULONG cbExecutable) // in: sizeof (*pszExecutable)
516{
517 APIRET arc = NO_ERROR;
518
519 // run thru the path components
520 PSZ pszPathCopy;
521 if (pszPathCopy = strdup(pcszDirList))
522 {
523 PSZ pszToken = strtok(pszPathCopy, ";");
524 while (pszToken)
525 {
526 CHAR szFileMask[2*CCHMAXPATH];
527 FILESTATUS3 fs3;
528
529 sprintf(szFileMask,
530 "%s\\%s",
531 pszToken, // path particle
532 pcszFile); // e.g. "netscape"
533
534 if ( (!(arc = DosQueryPathInfo(szFileMask,
535 FIL_STANDARD,
536 &fs3,
537 sizeof(fs3))))
538 // make sure it's not a directory
539 // and that it's not hidden
540 && (!(fs3.attrFile & (FILE_DIRECTORY | FILE_HIDDEN)))
541 )
542 {
543 // copy
544 arc = CopyToBuffer(pszExecutable,
545 szFileMask,
546 cbExecutable);
547 // and stop
548 break;
549 }
550 else
551 arc = ERROR_FILE_NOT_FOUND;
552 // and search on
553
554 pszToken = strtok(NULL, ";");
555 };
556
557 free(pszPathCopy);
558 }
559 else
560 arc = ERROR_NOT_ENOUGH_MEMORY;
561
562 return arc;
563}
564
565/*
566 * FindFile:
567 * helper for doshFindExecutable.
568 *
569 *added V0.9.11 (2001-04-25) [umoeller]
570 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewrote second half for DosSearchPath replacement, which returns directories too
571 */
572
573STATIC APIRET FindFile(const char *pcszCommand, // in: command (e.g. "lvm")
574 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
575 ULONG cbExecutable) // in: sizeof (*pszExecutable)
576{
577 APIRET arc = NO_ERROR;
578 FILESTATUS3 fs3;
579
580 if ( (strchr(pcszCommand, '\\'))
581 || (strchr(pcszCommand, ':'))
582 )
583 {
584 // looks like this is qualified:
585 arc = DosQueryPathInfo((PSZ)pcszCommand,
586 FIL_STANDARD,
587 &fs3,
588 sizeof(fs3));
589 if (!arc)
590 if (!(fs3.attrFile & FILE_DIRECTORY))
591 arc = CopyToBuffer(pszExecutable,
592 pcszCommand,
593 cbExecutable);
594 else
595 // directory:
596 arc = ERROR_INVALID_EXE_SIGNATURE;
597 }
598 else
599 {
600 // non-qualified:
601 /* arc = DosSearchPath(SEARCH_IGNORENETERRS
602 | SEARCH_ENVIRONMENT
603 | SEARCH_CUR_DIRECTORY,
604 "PATH",
605 (PSZ)pcszCommand,
606 pszExecutable,
607 cbExecutable); */
608 // The above is not useable. It returns directories
609 // on the path... for example, it returns \OS2\INSTALL\NETSCAPE
610 // if netscape is looked for. So we search manually... sigh.
611 // V0.9.16 (2001-10-08) [umoeller]
612 arc = doshSearchPath("PATH",
613 pcszCommand,
614 pszExecutable,
615 cbExecutable);
616 }
617
618 return arc;
619}
620
621/*
622 *@@ doshFindExecutable:
623 * this attempts to find an executable by doing the
624 * following:
625 *
626 * 1) If pcszCommand appears to be qualified (i.e. contains
627 * a backslash), this checks for whether the file exists.
628 * If it is a directory, ERROR_INVALID_EXE_SIGNATURE is
629 * returned.
630 *
631 * 2) If pcszCommand contains no backslash, this searches
632 * all directories on the PATH in order to find the full
633 * path of the executable. Starting with V0.9.16, we
634 * use doshSearchPath for that.
635 *
636 * papcszExtensions determines if additional searches are to be
637 * performed if the file doesn't exist (case 1) or doshSearchPath
638 * returned ERROR_FILE_NOT_FOUND (case 2).
639 * This must point to an array of strings specifying the extra
640 * extensions to search for.
641 *
642 * If both papcszExtensions and cExtensions are null, no
643 * extra searches are performed.
644 *
645 * Returns:
646 *
647 * -- NO_ERROR: pszExecutable has received the full path of
648 * the executable found by DosSearchPath.
649 *
650 * -- ERROR_FILE_NOT_FOUND
651 *
652 * -- ERROR_BUFFER_OVERFLOW: pcszCommand was found, but
653 * the pszExecutable buffer is too small to hold
654 * the full path.
655 *
656 * Example:
657 *
658 + const char *aExtensions[] = { "EXE",
659 + "COM",
660 + "CMD"
661 + };
662 + CHAR szExecutable[CCHMAXPATH];
663 + APIRET arc = doshFindExecutable("lvm",
664 + szExecutable,
665 + sizeof(szExecutable),
666 + aExtensions,
667 + 3);
668 *
669 *@@added V0.9.9 (2001-03-07) [umoeller]
670 *@@changed V0.9.11 (2001-04-25) [umoeller]: this never worked for qualified pcszCommand's, fixed
671 */
672
673APIRET doshFindExecutable(const char *pcszCommand, // in: command (e.g. "lvm")
674 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
675 ULONG cbExecutable, // in: sizeof (*pszExecutable)
676 const char **papcszExtensions, // in: array of extensions (without dots)
677 ULONG cExtensions) // in: array item count
678{
679 APIRET arc = FindFile(pcszCommand,
680 pszExecutable,
681 cbExecutable);
682
683 if ( (arc == ERROR_FILE_NOT_FOUND) // not found?
684 && (cExtensions) // any extra searches wanted?
685 )
686 {
687 // try additional things then
688 PSZ psz2 = (PSZ)malloc(strlen(pcszCommand) + 20);
689 if (psz2)
690 {
691 ULONG ul;
692 for (ul = 0;
693 ul < cExtensions;
694 ul++)
695 {
696 const char *pcszExtThis = papcszExtensions[ul];
697 sprintf(psz2,
698 "%s.%s",
699 pcszCommand,
700 pcszExtThis);
701 arc = FindFile(psz2,
702 pszExecutable,
703 cbExecutable);
704 if (arc != ERROR_FILE_NOT_FOUND)
705 break;
706 }
707
708 free(psz2);
709 }
710 else
711 arc = ERROR_NOT_ENOUGH_MEMORY;
712 }
713
714 return arc;
715}
716
717/*
718 *@@category: Helpers\Control program helpers\Partitions info
719 * functions for retrieving partition information directly
720 * from the partition tables on the disk. See doshGetPartitionsList.
721 */
722
723/********************************************************************
724 *
725 * Partition functions
726 *
727 ********************************************************************/
728
729/*
730 *@@ doshQueryDiskCount:
731 * returns the no. of physical disks installed
732 * on the system.
733 *
734 *@@added V0.9.0 [umoeller]
735 */
736
737UINT doshQueryDiskCount(VOID)
738{
739 USHORT usCount = 0;
740 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &usCount, 2, 0, 0);
741 return (usCount);
742}
743
744/*
745 *@@ doshType2FSName:
746 * this returns a static, zero-terminated string
747 * for the given FS type, or NULL if the type
748 * is unknown.
749 *
750 *@@added V0.9.0 [umoeller]
751 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewritten
752 */
753
754const char* doshType2FSName(unsigned char bFSType) // in: FS type
755{
756 switch (bFSType)
757 {
758 case 0x00: return "empty";
759 case 0x01: return "DOS 12-bit FAT < 10 Mb";
760 case 0x02: return "XENIX root file system";
761 case 0x03: return "XENIX /usr file system (obsolete)";
762 case 0x04: return "DOS 16-bit FAT < 32 Mb";
763 case 0x05: return "DOS 3.3+ extended partition";
764 case 0x06: return "DOS 3.31+ 16-bit FAT > 32 Mb";
765 case 0x07: return "HPFS/NTFS/QNX/Advanced Unix";
766 case 0x08: return "OS/2 1.0-1.3/AIX/Commodore/DELL";
767 case 0x09: return "AIX data/Coherent";
768 case 0x0A: return "OS/2 Boot Manager/OPUS/Coherent Swap";
769 case 0x0B: return "Windows95 with 32-bit FAT";
770 case 0x0C: return "Windows95 with 32-bit FAT (LBA)";
771 case 0x0E: return "Windows 95 VFAT (06h plus LBA)";
772 case 0x0F: return "Windows 95 VFAT (05h plus LBA)";
773 case 0x10: return "OPUS";
774 case 0x11: return "OS/2 Boot Manager hidden 12-bit FAT";
775 case 0x12: return "Compaq Diagnostics";
776 case 0x14: return "OS/2 Boot Manager hidden sub-32M 16-bit FAT";
777 case 0x16: return "OS/2 Boot Manager hidden over-32M 16-bit FAT";
778 case 0x17: return "OS/2 Boot Manager hidden HPFS";
779 case 0x18: return "AST special Windows swap file (\"Zero-Volt Suspend\")";
780 // case 0x21: reserved
781 // case 0x23: reserved
782 case 0x24: return "NEC MS-DOS 3.x";
783 // case 0x26: reserved
784 // case 0x31: reserved
785 // case 0x33: reserved
786 // case 0x34: reserved
787 // case 0x36: reserved
788 case 0x38: return "Theos";
789 case 0x3C: return "PowerQuest PartitionMagic recovery partition";
790 case 0x40: return "VENIX 80286";
791 case 0x41: return "Personal RISC Boot";
792 case 0x42: return "SFS (Secure File System) by Peter Gutmann";
793 case 0x50: return "OnTrack Disk Manager, read-only";
794 case 0x51: return "OnTrack Disk Manager, read/write";
795 case 0x52: return "CP/M or Microport System V/386";
796 case 0x53: return "OnTrack Disk Manager, write-only???";
797 case 0x54: return "OnTrack Disk Manager (DDO)";
798 case 0x56: return "GoldenBow VFeature";
799 case 0x61: return "SpeedStor";
800 case 0x63: return "Unix SysV/386, 386/ix or Mach, MtXinu BSD 4.3 on Mach or GNU HURD";
801 case 0x64: return "Novell NetWare 286";
802 case 0x65: return "Novell NetWare (3.11)";
803 case 0x67:
804 case 0x68:
805 case 0x69: return "Novell";
806 case 0x70: return "DiskSecure Multi-Boot";
807 // case 0x71: reserved
808 // case 0x73: reserved
809 // case 0x74: reserved
810 case 0x75: return "PC/IX";
811 // case 0x76: reserved
812 case 0x80: return "Minix v1.1 - 1.4a";
813 case 0x81: return "Minix v1.4b+ or Linux or Mitac Advanced Disk Manager";
814 case 0x82: return "Linux Swap or Prime";
815 case 0x83: return "Linux native file system (ext2fs/xiafs)";
816 case 0x84: return "OS/2-renumbered type 04h (hidden DOS C: drive)";
817 case 0x86: return "FAT16 volume/stripe set (Windows NT)";
818 case 0x87: return "HPFS Fault-Tolerant mirrored partition or NTFS volume/stripe set";
819 case 0x93: return "Amoeba file system";
820 case 0x94: return "Amoeba bad block table";
821 case 0xA0: return "Phoenix NoteBIOS Power Management \"Save-to-Disk\" partition";
822 // case 0xA1: reserved
823 // case 0xA3: reserved
824 // case 0xA4: reserved
825 case 0xA5: return "FreeBSD, BSD/386";
826 // case 0xA6: reserved
827 // case 0xB1: reserved
828 // case 0xB3: reserved
829 // case 0xB4: reserved
830 // case 0xB6: reserved
831 case 0xB7: return "BSDI file system (secondarily swap)";
832 case 0xB8: return "BSDI swap (secondarily file system)";
833 case 0xC1: return "DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT";
834 case 0xC4: return "DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT";
835 case 0xC6: return "DR DOS 6.0 LOGIN.EXE-secured Huge partition or NT corrupted FAT16 volume/stripe set";
836 case 0xC7: return "Syrinx Boot or corrupted NTFS volume/stripe set";
837 case 0xD8: return "CP/M-86";
838 case 0xDB: return "CP/M, Concurrent CP/M, Concurrent DOS, Convergent Technologies OS";
839 case 0xE1: return "SpeedStor 12-bit FAT extended partition";
840 case 0xE3: return "DOS read-only or Storage Dimensions";
841 case 0xE4: return "SpeedStor 16-bit FAT extended partition";
842 // case 0xE5: reserved
843 // case 0xE6: reserved
844 case 0xF1: return "Storage Dimensions";
845 case 0xF2: return "DOS 3.3+ secondary partition";
846 // case 0xF3: reserved
847 case 0xF4: return "SpeedStor or Storage Dimensions";
848 // case 0xF6: reserved
849 case 0xFE: return "LANstep or IBM PS/2 IML";
850 case 0xFF: return "Xenix bad block table";
851 }
852
853 return NULL;
854}
855
856/*
857 * AppendPartition:
858 * this appends the given partition information to
859 * the given partition list. To do this, a new
860 * PARTITIONINFO structure is created and appended
861 * in a list (managed thru the PARTITIONINFO.pNext
862 * items).
863 *
864 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
865 * With each call of this function, this pointer is advanced
866 * to point to the newly created PARTITIONINFO, so before
867 * calling this function for the first time,
868 *
869 *@@added V0.9.0 [umoeller]
870 *@@changed V0.9.20 (2002-08-10) [umoeller]: fixed truncated LVM names
871 */
872
873STATIC APIRET AppendPartition(PARTITIONINFO **pppiFirst,
874 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
875 PUSHORT posCount, // in/out: partition count
876 BYTE bDisk, // in: disk of partition
877 const char *pszBootName, // in: boot partition name
878 CHAR cLetter, // in/out: drive letter
879 BYTE bFsType, // in: file system type
880 BOOL fPrimary, // in: primary?
881 BOOL fBootable,
882 ULONG ulSectors) // in: no. of sectors
883{
884 APIRET arc = NO_ERROR;
885 PPARTITIONINFO ppiNew;
886 if (ppiNew = NEW(PARTITIONINFO))
887 {
888 ZERO(ppiNew);
889
890 // store data
891 ppiNew->bDisk = bDisk;
892 if ( (fBootable)
893 && (pszBootName)
894 )
895 {
896 // fixed truncated LVM names V0.9.20 (2002-08-10) [umoeller]
897 strhncpy0(ppiNew->szBootName,
898 pszBootName,
899 sizeof(ppiNew->szBootName));
900 }
901 else
902 ppiNew->szBootName[0] = 0;
903 ppiNew->cLetter = cLetter;
904 ppiNew->bFSType = bFsType;
905 ppiNew->pcszFSType = doshType2FSName(bFsType);
906 ppiNew->fPrimary = fPrimary;
907 ppiNew->fBootable = fBootable;
908 ppiNew->ulSize = ulSectors / 2048;
909
910 ppiNew->pNext = NULL;
911
912 (*posCount)++;
913
914 if (*pppiFirst == (PPARTITIONINFO)NULL)
915 {
916 // first call:
917 *pppiFirst = ppiNew;
918 *pppiThis = ppiNew;
919 }
920 else
921 {
922 // append to list
923 (**pppiThis).pNext = ppiNew;
924 *pppiThis = ppiNew;
925 }
926 }
927 else
928 arc = ERROR_NOT_ENOUGH_MEMORY;
929
930 return arc;
931}
932
933#ifndef __XWPLITE__
934
935/*
936 *@@ doshReadSector:
937 * reads a physical disk sector.
938 *
939 * If NO_ERROR is returned, the sector contents
940 * have been stored in *buff.
941 *
942 * Originally contributed by Dmitry A. Steklenev.
943 *
944 *@@added V0.9.0 [umoeller]
945 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
946 */
947
948APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
949 void *buff,
950 USHORT head,
951 USHORT cylinder,
952 USHORT sector)
953{
954 APIRET arc;
955 HFILE dh = 0;
956 char dn[256];
957
958 sprintf(dn, "%u:", disk);
959 if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
960 {
961 TRACKLAYOUT DiskIOParm;
962 ULONG IOCtlDataLength = sizeof(DiskIOParm);
963 ULONG IOCtlParmLength = 512;
964
965 DiskIOParm.bCommand = 0;
966 DiskIOParm.usHead = head;
967 DiskIOParm.usCylinder = cylinder;
968 DiskIOParm.usFirstSector = 0;
969 DiskIOParm.cSectors = 1;
970 DiskIOParm.TrackTable[0].usSectorNumber = sector;
971 DiskIOParm.TrackTable[0].usSectorSize = 512;
972
973 arc = DosDevIOCtl(dh,
974 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
975 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
976 buff , IOCtlDataLength, &IOCtlDataLength);
977
978 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
979 }
980
981 return arc;
982}
983
984// Sector and Cylinder values are actually 6 bits and 10 bits:
985//
986// 1 1 1 1 1 1
987// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
988// ³c c c c c c c c C c S s s s s s³
989// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
990//
991// The high two bits of the second byte are used as the high bits
992// of a 10-bit value. This allows for as many as 1024 cylinders
993// and 64 sectors per cylinder.
994
995/*
996 * GetCyl:
997 * get cylinder number.
998 *
999 * Originally contributed by Dmitry A. Steklenev.
1000 *
1001 *@@added V0.9.0 [umoeller]
1002 */
1003
1004STATIC USHORT GetCyl(USHORT rBeginSecCyl)
1005{
1006 return ( (rBeginSecCyl & 0x00C0) << 2)
1007 + ((rBeginSecCyl & 0xFF00) >> 8);
1008}
1009
1010/*
1011 * GetSec:
1012 * get sector number.
1013 *
1014 * Originally contributed by Dmitry A. Steklenev.
1015 *
1016 *@@added V0.9.0 [umoeller]
1017 */
1018
1019STATIC USHORT GetSec(USHORT rBeginSecCyl)
1020{
1021 return rBeginSecCyl & 0x003F;
1022}
1023
1024/*
1025 *@@ doshGetBootManager:
1026 * this goes thru the master boot records on all
1027 * disks to find the boot manager partition.
1028 *
1029 * Returns:
1030 *
1031 * -- NO_ERROR: boot manager found; in that case,
1032 * information about the boot manager
1033 * is written into *pusDisk, *pusPart,
1034 * *BmInfo. Any of these pointers can
1035 * be NULL if you're not interested.
1036 *
1037 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
1038 *
1039 * Originally contributed by Dmitry A. Steklenev.
1040 *
1041 *@@added V0.9.0 [umoeller]
1042 */
1043
1044APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
1045 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
1046 PAR_INFO *pBmInfo) // out: if != NULL, boot manager partition info
1047{
1048 APIRET arc = NO_ERROR;
1049 USHORT count = doshQueryDiskCount(); // Physical disk number
1050 MBR_INFO MBoot; // Master Boot
1051 USHORT usDisk;
1052
1053 if (count > 8) // Not above 8 disks
1054 count = 8;
1055
1056 for (usDisk = 1; usDisk <= count; usDisk++)
1057 {
1058 USHORT usPrim = 0;
1059
1060 // for each disk, read the MBR, which has the
1061 // primary partitions
1062 if ((arc = doshReadSector(usDisk,
1063 &MBoot,
1064 0, // head
1065 0, // cylinder
1066 1))) // sector
1067 return arc;
1068
1069 // scan primary partitions for whether
1070 // BootManager partition exists
1071 for (usPrim = 0; usPrim < 4; usPrim++)
1072 {
1073 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
1074 {
1075 // this is boot manager:
1076 if (pBmInfo)
1077 *pBmInfo = MBoot.sPrtnInfo[usPrim];
1078 if (pusPart)
1079 *pusPart = usPrim;
1080 if (pusDisk)
1081 *pusDisk = usDisk;
1082 // stop scanning
1083 return NO_ERROR;
1084 }
1085 }
1086 }
1087
1088 return (ERROR_NOT_SUPPORTED);
1089}
1090
1091/*
1092 * GetPrimaryPartitions:
1093 * this returns the primary partitions.
1094 *
1095 * This gets called from doshGetPartitionsList.
1096 *
1097 * Returns:
1098 *
1099 * -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
1100 *
1101 * Originally contributed by Dmitry A. Steklenev.
1102 *
1103 *@@added V0.9.0 [umoeller]
1104 */
1105
1106STATIC APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
1107 PARTITIONINFO **pppiThis,
1108 PUSHORT posCount, // in/out: partition count
1109 PCHAR pcLetter, // in/out: drive letter counter
1110 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
1111 PAR_INFO* pBmInfo, // in: info returned by doshGetBootManager or NULL
1112 UINT iDisk) // in: system's physical disk count
1113{
1114 APIRET arc = NO_ERROR;
1115
1116 if (!pBmInfo)
1117 arc = ERROR_INVALID_PARAMETER;
1118 else
1119 {
1120 SYS_INFO MName[32]; // Name Space from Boot Manager
1121 memset(&MName, 0, sizeof(MName));
1122
1123 // read boot manager name table;
1124 // this is in the boot manager primary partition
1125 // at sector offset 3 (?!?)
1126 if (!(arc = doshReadSector(BmDisk,
1127 &MName,
1128 // head, cylinder, sector of bmgr primary partition:
1129 pBmInfo->bBeginHead,
1130 GetCyl(pBmInfo->rBeginSecCyl),
1131 GetSec(pBmInfo->rBeginSecCyl) + 3)))
1132 {
1133 // got bmgr name table:
1134 MBR_INFO MBoot; // Master Boot
1135 USHORT i;
1136
1137 // read master boot record of this disk
1138 if (!(arc = doshReadSector(iDisk,
1139 &MBoot,
1140 0, // head
1141 0, // cylinder
1142 1))) // sector
1143 {
1144 for (i = 0;
1145 i < 4; // there can be only four primary partitions
1146 i++)
1147 {
1148 // skip unused partition, BootManager or Extended partition
1149 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
1150 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A) // skip boot manager
1151 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x05) // skip extended partition
1152 )
1153 {
1154 BOOL fBootable = ( (pBmInfo)
1155 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
1156 );
1157 // store this partition
1158 if ((arc = AppendPartition(pppiFirst,
1159 pppiThis,
1160 posCount,
1161 iDisk,
1162 (fBootable)
1163 ? (char*)&MName[(iDisk - 1) * 4 + i].name
1164 : "",
1165 *pcLetter,
1166 MBoot.sPrtnInfo[i].bFileSysCode,
1167 TRUE, // primary
1168 fBootable,
1169 MBoot.sPrtnInfo[i].lTotalSects)))
1170 return arc;
1171 }
1172 }
1173 }
1174 }
1175 }
1176
1177 return arc;
1178}
1179
1180/*
1181 * GetLogicalDrives:
1182 * this returns info for the logical drives
1183 * in the extended partition. This gets called
1184 * from GetExtendedPartition.
1185 *
1186 * This gets called from GetExtendedPartition.
1187 *
1188 * Originally contributed by Dmitry A. Steklenev.
1189 *
1190 *@@added V0.9.0 [umoeller]
1191 */
1192
1193STATIC APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
1194 PARTITIONINFO **pppiThis,
1195 PUSHORT posCount,
1196 PCHAR pcLetter,
1197 PAR_INFO* PrInfo, // in: MBR entry of extended partition
1198 UINT PrDisk,
1199 PAR_INFO* BmInfo)
1200{
1201 APIRET arc = NO_ERROR;
1202 EXT_INFO MBoot; // Master Boot
1203 USHORT i;
1204
1205 if ((arc = doshReadSector(PrDisk,
1206 &MBoot,
1207 PrInfo->bBeginHead,
1208 GetCyl(PrInfo->rBeginSecCyl),
1209 GetSec(PrInfo->rBeginSecCyl))))
1210 return arc;
1211
1212 for (i = 0; i < 4; i++)
1213 {
1214 // skip unused partition or BootManager partition
1215 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
1216 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A)
1217 )
1218 {
1219 BOOL fBootable = FALSE;
1220 BOOL fAssignLetter = FALSE;
1221
1222 // special work around extended partition
1223 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
1224 {
1225 if ((arc = GetLogicalDrives(pppiFirst,
1226 pppiThis,
1227 posCount,
1228 pcLetter,
1229 &MBoot.sPrtnInfo[i],
1230 PrDisk,
1231 BmInfo)))
1232 return arc;
1233
1234 continue;
1235 }
1236
1237 // raise driver letter if OS/2 would recognize this drive
1238 if ( (MBoot.sPrtnInfo[i].bFileSysCode < 0x75)
1239 )
1240 fAssignLetter = TRUE;
1241
1242 if (fAssignLetter)
1243 (*pcLetter)++;
1244
1245 fBootable = ( (BmInfo)
1246 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
1247 );
1248
1249 if ((arc = AppendPartition(pppiFirst,
1250 pppiThis,
1251 posCount,
1252 PrDisk,
1253 (fBootable)
1254 ? (char*)&MBoot.sBmNames[i].name
1255 : "",
1256 (fAssignLetter)
1257 ? *pcLetter
1258 : ' ',
1259 MBoot.sPrtnInfo[i].bFileSysCode,
1260 FALSE, // primary
1261 fBootable, // bootable
1262 MBoot.sPrtnInfo[i].lTotalSects)))
1263 return arc;
1264 }
1265 }
1266
1267 return NO_ERROR;
1268}
1269
1270/*
1271 * GetExtendedPartition:
1272 * this finds the extended partition on the given
1273 * drive and calls GetLogicalDrives in turn.
1274 *
1275 * This gets called from doshGetPartitionsList.
1276 *
1277 * Originally contributed by Dmitry A. Steklenev.
1278 *
1279 *@@added V0.9.0 [umoeller]
1280 */
1281
1282STATIC APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
1283 PARTITIONINFO **pppiThis,
1284 PUSHORT posCount,
1285 PCHAR pcLetter,
1286 PAR_INFO* BmInfo,
1287 UINT iDisk) // in: disk to query
1288{
1289 APIRET arc = NO_ERROR;
1290 MBR_INFO MBoot; // Master Boot
1291 USHORT i;
1292
1293 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
1294 return arc;
1295
1296 // go thru MBR entries to find extended partition
1297 for (i = 0;
1298 i < 4;
1299 i++)
1300 {
1301 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
1302 {
1303 if ((arc = GetLogicalDrives(pppiFirst,
1304 pppiThis,
1305 posCount,
1306 pcLetter,
1307 &MBoot.sPrtnInfo[i],
1308 iDisk,
1309 BmInfo)))
1310 return arc;
1311 }
1312 }
1313
1314 return NO_ERROR;
1315}
1316
1317/*
1318 *@@ ReadFDiskPartitions:
1319 * helper for doshGetPartitionsList for non-LVM
1320 * systems.
1321 *
1322 * Originally contributed by Dmitry A. Steklenev.
1323 *
1324 *@@added V0.9.16 (2001-10-08) [umoeller]
1325 */
1326
1327STATIC APIRET ReadFDiskPartitions(PARTITIONINFO **ppPartitionInfos,
1328 USHORT *pcPartitions,
1329 PUSHORT pusContext) // out: error context
1330{
1331 APIRET arc = NO_ERROR;
1332
1333 PAR_INFO BmInfo; // BootManager partition
1334 USHORT usBmDisk; // BootManager disk
1335 USHORT cDisks = doshQueryDiskCount(); // physical disks count
1336 USHORT i;
1337
1338 CHAR cLetter = 'C'; // first drive letter
1339
1340 PARTITIONINFO *ppiTemp = NULL;
1341
1342 if (cDisks > 8) // Not above 8 disks
1343 cDisks = 8;
1344
1345 // get boot manager disk and info
1346 if ((arc = doshGetBootManager(&usBmDisk,
1347 NULL,
1348 &BmInfo)) != NO_ERROR)
1349 {
1350 *pusContext = 1;
1351 }
1352 else
1353 {
1354 // on each disk, read primary partitions
1355 for (i = 1; i <= cDisks; i++)
1356 {
1357 if ((arc = GetPrimaryPartitions(ppPartitionInfos,
1358 &ppiTemp,
1359 pcPartitions,
1360 &cLetter,
1361 usBmDisk,
1362 usBmDisk ? &BmInfo : 0,
1363 i)))
1364 {
1365 *pusContext = 2;
1366 }
1367 }
1368
1369 if (!arc && usBmDisk)
1370 {
1371 // boot manager found:
1372 // on each disk, read extended partition
1373 // with logical drives
1374 for (i = 1; i <= cDisks; i++)
1375 {
1376 if ((arc = GetExtendedPartition(ppPartitionInfos,
1377 &ppiTemp,
1378 pcPartitions,
1379 &cLetter,
1380 &BmInfo,
1381 i)))
1382 {
1383 *pusContext = 3;
1384 }
1385 }
1386 }
1387 } // end else if ((arc = doshGetBootManager(&usBmDisk,
1388
1389 return arc;
1390}
1391
1392#endif
1393
1394/*
1395 *@@ CleanPartitionInfos:
1396 *
1397 *@@added V0.9.9 (2001-04-07) [umoeller]
1398 */
1399
1400STATIC VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
1401{
1402 while (ppiThis)
1403 {
1404 PPARTITIONINFO ppiNext = ppiThis->pNext;
1405 free(ppiThis);
1406 ppiThis = ppiNext;
1407 }
1408}
1409
1410/*
1411 *@@ doshGetPartitionsList:
1412 * this returns lots of information about the
1413 * partitions on all physical disks, which is
1414 * read directly from the MBRs and partition
1415 * tables.
1416 *
1417 * If NO_ERROR is returned by this function,
1418 * *ppPartitionInfo points to a linked list of
1419 * PARTITIONINFO structures, which has
1420 * *pusPartitionCount items.
1421 *
1422 * In that case, use doshFreePartitionsList to
1423 * free the resources allocated by this function.
1424 *
1425 * What this function returns depends on whether
1426 * LVM is installed.
1427 *
1428 * -- If LVM.DLL is found on the LIBPATH, this opens
1429 * the LVM engine and returns the info from the
1430 * LVM engine in the PARTITIONINFO structures.
1431 * The partitions are then sorted by disk in
1432 * ascending order.
1433 *
1434 * -- Otherwise, we parse the partition tables
1435 * manually. The linked list then starts out with
1436 * all the primary partitions, followed by the
1437 * logical drives in the extended partitions.
1438 * This function attempts to guess the correct drive
1439 * letters and stores these with the PARTITIONINFO
1440 * items, but there's no guarantee that this is
1441 * correct. We correctly ignore Linux partitions here
1442 * and give all primary partitions the C: letter, but
1443 * I have no idea what happens with NTFS partitions,
1444 * since I have none.
1445 *
1446 * If an error != NO_ERROR is returned, *pusContext
1447 * will be set to one of the following:
1448 *
1449 * -- 1: boot manager not found
1450 *
1451 * -- 2: primary partitions error
1452 *
1453 * -- 3: secondary partitions error
1454 *
1455 * -- 0: something else.
1456 *
1457 * Originally contributed by Dmitry A. Steklenev.
1458 *
1459 *@@added V0.9.0 [umoeller]
1460 *@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
1461 *@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
1462 */
1463
1464APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
1465 PUSHORT pusContext) // out: error context
1466{
1467 APIRET arc = NO_ERROR;
1468
1469 PLVMINFO pLVMInfo = NULL;
1470
1471 PARTITIONINFO *pPartitionInfos = NULL; // linked list of all partitions
1472 USHORT cPartitions = 0; // bootable partition count
1473
1474 if (!ppList)
1475 return (ERROR_INVALID_PARAMETER);
1476
1477 if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
1478 {
1479 // LVM installed:
1480 arc = doshReadLVMPartitions(pLVMInfo, // in: LVM info
1481 &pPartitionInfos, // out: partitions array
1482 &cPartitions); // out: partitions count
1483 // copied to output below
1484
1485 if (arc)
1486 {
1487 // error: start over
1488 doshFreeLVMInfo(pLVMInfo);
1489 CleanPartitionInfos(pPartitionInfos);
1490 pPartitionInfos = NULL;
1491 cPartitions = 0;
1492 }
1493 }
1494
1495#ifndef __XWPLITE__
1496 if (arc)
1497 // LVM not installed, or failed:
1498 // parse partitions manually
1499 arc = ReadFDiskPartitions(&pPartitionInfos,
1500 &cPartitions,
1501 pusContext);
1502#endif
1503
1504 if (!arc)
1505 {
1506 // no error so far:
1507 *pusContext = 0;
1508
1509 *ppList = NEW(PARTITIONSLIST);
1510 if (!(*ppList))
1511 arc = ERROR_NOT_ENOUGH_MEMORY;
1512 else
1513 {
1514 ZERO(*ppList);
1515
1516 (*ppList)->pPartitionInfo = pPartitionInfos;
1517 (*ppList)->cPartitions = cPartitions;
1518
1519 _Pmpf((__FUNCTION__ ": returning %d partitions", cPartitions));
1520 }
1521 }
1522
1523 if (arc)
1524 CleanPartitionInfos(pPartitionInfos);
1525
1526 _Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
1527
1528 return arc;
1529}
1530
1531/*
1532 *@@ doshFreePartitionsList:
1533 * this frees the resources allocated by
1534 * doshGetPartitionsList.
1535 *
1536 *@@added V0.9.0 [umoeller]
1537 */
1538
1539APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
1540{
1541 if (!ppList)
1542 return (ERROR_INVALID_PARAMETER);
1543 else
1544 {
1545 CleanPartitionInfos(ppList->pPartitionInfo);
1546 doshFreeLVMInfo(ppList->pLVMInfo);
1547 free(ppList);
1548 }
1549
1550 return NO_ERROR;
1551}
1552
1553/********************************************************************
1554 *
1555 * LVM declarations
1556 *
1557 ********************************************************************/
1558
1559/*
1560 *@@category: Helpers\Control program helpers\Partitions info\Quick LVM Interface
1561 * functions for transparently interfacing LVM.DLL.
1562 */
1563
1564typedef unsigned char BOOLEAN;
1565typedef unsigned short int CARDINAL16;
1566typedef unsigned long CARDINAL32;
1567typedef unsigned int CARDINAL;
1568typedef unsigned long DoubleWord;
1569
1570#ifdef ADDRESS
1571#undef ADDRESS
1572#endif
1573
1574typedef void* ADDRESS;
1575
1576#pragma pack(1)
1577
1578#define DISK_NAME_SIZE 20
1579#define FILESYSTEM_NAME_SIZE 20
1580#define PARTITION_NAME_SIZE 20
1581#define VOLUME_NAME_SIZE 20
1582
1583/*
1584 *@@ Drive_Control_Record:
1585 * invariant for a disk drive.
1586 *
1587 *@@added V0.9.9 (2001-04-07) [umoeller]
1588 */
1589
1590typedef struct _Drive_Control_Record
1591{
1592 CARDINAL32 Drive_Number; // OS/2 Drive Number for this drive.
1593 CARDINAL32 Drive_Size; // The total number of sectors on the drive.
1594 DoubleWord Drive_Serial_Number; // The serial number assigned to this drive. For info. purposes only.
1595 ADDRESS Drive_Handle; // Handle used for operations on the disk that this record corresponds to.
1596 CARDINAL32 Cylinder_Count; // The number of cylinders on the drive.
1597 CARDINAL32 Heads_Per_Cylinder; // The number of heads per cylinder for this drive.
1598 CARDINAL32 Sectors_Per_Track; // The number of sectors per track for this drive.
1599 BOOLEAN Drive_Is_PRM; // Set to TRUE if this drive is a PRM.
1600 BYTE Reserved[3]; // Alignment.
1601} Drive_Control_Record;
1602
1603/*
1604 *@@ Drive_Control_Array:
1605 * returned by the Get_Drive_Control_Data function
1606 *
1607 *@@added V0.9.9 (2001-04-07) [umoeller]
1608 */
1609
1610typedef struct _Drive_Control_Array
1611{
1612 Drive_Control_Record * Drive_Control_Data; // An array of drive control records.
1613 CARDINAL32 Count; // The number of entries in the array of drive control records.
1614} Drive_Control_Array;
1615
1616/*
1617 *@@ Drive_Information_Record:
1618 * defines the information that can be changed for a specific disk drive.
1619 *
1620 *@@added V0.9.9 (2001-04-07) [umoeller]
1621 */
1622
1623typedef struct _Drive_Information_Record
1624{
1625 CARDINAL32 Total_Available_Sectors; // The number of sectors on the disk which are not currently assigned to a partition.
1626 CARDINAL32 Largest_Free_Block_Of_Sectors; // The number of sectors in the largest contiguous block of available sectors.
1627 BOOLEAN Corrupt_Partition_Table; // If TRUE, then the partitioning information found on the drive is incorrect!
1628 BOOLEAN Unusable; // If TRUE, the drive's MBR is not accessible and the drive can not be partitioned.
1629 BOOLEAN IO_Error; // If TRUE, then the last I/O operation on this drive failed!
1630 BOOLEAN Is_Big_Floppy; // If TRUE, then the drive is a PRM formatted as a big floppy (i.e. the old style removable media support).
1631 char Drive_Name[DISK_NAME_SIZE]; // User assigned name for this disk drive.
1632} Drive_Information_Record;
1633
1634/*
1635 *@@ Partition_Information_Record:
1636 *
1637 *@@added V0.9.9 (2001-04-07) [umoeller]
1638 */
1639
1640typedef struct _Partition_Information_Record
1641{
1642 ADDRESS Partition_Handle;
1643 // The handle used to perform operations on this partition.
1644 ADDRESS Volume_Handle;
1645 // If this partition is part of a volume, this will be the handle of
1646 // the volume. If this partition is NOT part of a volume, then this
1647 // handle will be 0.
1648 ADDRESS Drive_Handle;
1649 // The handle for the drive this partition resides on.
1650 DoubleWord Partition_Serial_Number;
1651 // The serial number assigned to this partition.
1652 CARDINAL32 Partition_Start;
1653 // The LBA of the first sector of the partition.
1654 CARDINAL32 True_Partition_Size;
1655 // The total number of sectors comprising the partition.
1656 CARDINAL32 Usable_Partition_Size;
1657 // The size of the partition as reported to the IFSM. This is the
1658 // size of the partition less any LVM overhead.
1659 CARDINAL32 Boot_Limit;
1660 // The maximum number of sectors from this block of free space that
1661 // can be used to create a bootable partition if you allocate from the
1662 // beginning of the block of free space.
1663 BOOLEAN Spanned_Volume;
1664 // TRUE if this partition is part of a multi-partition volume.
1665 BOOLEAN Primary_Partition;
1666 // True or False. Any non-zero value here indicates that this partition
1667 // is a primary partition. Zero here indicates that this partition is
1668 // a "logical drive" - i.e. it resides inside of an extended partition.
1669 BYTE Active_Flag;
1670 // 80 = Partition is marked as being active.
1671 // 0 = Partition is not active.
1672 BYTE OS_Flag;
1673 // This field is from the partition table. It is known as the OS flag,
1674 // the Partition Type Field, Filesystem Type, and various other names.
1675 // Values of interest
1676 // If this field is: (values are in hex)
1677 // 07 = The partition is a compatibility partition formatted for use
1678 // with an installable filesystem, such as HPFS or JFS.
1679 // 00 = Unformatted partition
1680 // 01 = FAT12 filesystem is in use on this partition.
1681 // 04 = FAT16 filesystem is in use on this partition.
1682 // 0A = OS/2 Boot Manager Partition
1683 // 35 = LVM partition
1684 // 84 = OS/2 FAT16 partition which has been relabeled by Boot Manager to "Hide" it.
1685 BYTE Partition_Type;
1686 // 0 = Free Space
1687 // 1 = LVM Partition (Part of an LVM Volume.)
1688 // 2 = Compatibility Partition
1689 // All other values are reserved for future use.
1690 BYTE Partition_Status;
1691 // 0 = Free Space
1692 // 1 = In Use - i.e. already assigned to a volume.
1693 // 2 = Available - i.e. not currently assigned to a volume.
1694 BOOLEAN On_Boot_Manager_Menu;
1695 // Set to TRUE if this partition is not part of a Volume yet is on the
1696 // Boot Manager Menu.
1697 BYTE Reserved;
1698 // Alignment.
1699 char Volume_Drive_Letter;
1700 // The drive letter assigned to the volume that this partition is a part of.
1701 char Drive_Name[DISK_NAME_SIZE];
1702 // User assigned name for this disk drive.
1703 char File_System_Name[FILESYSTEM_NAME_SIZE];
1704 // The name of the filesystem in use on this partition, if it is known.
1705 char Partition_Name[PARTITION_NAME_SIZE];
1706 // The user assigned name for this partition.
1707 char Volume_Name[VOLUME_NAME_SIZE];
1708 // If this partition is part of a volume, then this will be the
1709 // name of the volume that this partition is a part of. If this
1710 // record represents free space, then the Volume_Name will be
1711 // "FREE SPACE xx", where xx is a unique numeric ID generated by
1712 // LVM.DLL. Otherwise it will be an empty string.
1713} Partition_Information_Record;
1714
1715// The following defines are for use with the Partition_Type field in the
1716// Partition_Information_Record.
1717#define FREE_SPACE_PARTITION 0
1718#define LVM_PARTITION 1
1719#define COMPATIBILITY_PARTITION 2
1720
1721// The following defines are for use with the Partition_Status field in the
1722// Partition_Information_Record.
1723#define PARTITION_IS_IN_USE 1
1724#define PARTITION_IS_AVAILABLE 2
1725#define PARTITION_IS_FREE_SPACE 0
1726
1727/*
1728 *@@ Partition_Information_Array:
1729 * returned by various functions in the LVM Engine.
1730 *
1731 *@@added V0.9.9 (2001-04-07) [umoeller]
1732 */
1733
1734typedef struct _Partition_Information_Array
1735{
1736 Partition_Information_Record * Partition_Array; // An array of Partition_Information_Records.
1737 CARDINAL32 Count; // The number of entries in the Partition_Array.
1738} Partition_Information_Array;
1739
1740/*
1741 *@@ Volume_Information_Record:
1742 * variable information for a volume.
1743 *
1744 *@@added V0.9.9 (2001-04-07) [umoeller]
1745 */
1746
1747typedef struct _Volume_Information_Record
1748{
1749 CARDINAL32 Volume_Size;
1750 // The number of sectors comprising the volume.
1751 CARDINAL32 Partition_Count;
1752 // The number of partitions which comprise this volume.
1753 CARDINAL32 Drive_Letter_Conflict;
1754 // 0 indicates that the drive letter preference for this volume is unique.
1755 // 1 indicates that the drive letter preference for this volume
1756 // is not unique, but this volume got its preferred drive letter anyway.
1757 // 2 indicates that the drive letter preference for this volume
1758 // is not unique, and this volume did NOT get its preferred drive letter.
1759 // 4 indicates that this volume is currently "hidden" - i.e. it has
1760 // no drive letter preference at the current time.
1761 BOOLEAN Compatibility_Volume;
1762 // TRUE if this is for a compatibility volume, FALSE otherwise.
1763 BOOLEAN Bootable;
1764 // Set to TRUE if this volume appears on the Boot Manager menu, or if it is
1765 // a compatibility volume and its corresponding partition is the first active
1766 // primary partition on the first drive.
1767 char Drive_Letter_Preference;
1768 // The drive letter that this volume desires to be.
1769 char Current_Drive_Letter;
1770 // The drive letter currently used to access this volume.
1771 // May be different than Drive_Letter_Preference if there was a conflict ( i.e. Drive_Letter_Preference
1772 // is already in use by another volume ).
1773 char Initial_Drive_Letter;
1774 // The drive letter assigned to this volume by the operating system
1775 // when LVM was started. This may be different from the
1776 // Drive_Letter_Preference if there were conflicts, and
1777 // may be different from the Current_Drive_Letter. This
1778 // will be 0x0 if the Volume did not exist when the LVM Engine
1779 // was opened (i.e. it was created during this LVM session).
1780 BOOLEAN New_Volume;
1781 // Set to FALSE if this volume existed before the LVM Engine was
1782 // opened. Set to TRUE if this volume was created after the LVM
1783 // Engine was opened.
1784 BYTE Status;
1785 // 0 = None.
1786 // 1 = Bootable
1787 // 2 = Startable
1788 // 3 = Installable.
1789 BYTE Reserved_1;
1790 char Volume_Name[VOLUME_NAME_SIZE];
1791 // The user assigned name for this volume.
1792 char File_System_Name[FILESYSTEM_NAME_SIZE];
1793 // The name of the filesystem in use on this partition, if it
1794 // is known.
1795} Volume_Information_Record;
1796
1797#pragma pack()
1798
1799/********************************************************************
1800 *
1801 * Quick LVM Interface API
1802 *
1803 ********************************************************************/
1804
1805/*
1806 *@@ LVMINFOPRIVATE:
1807 * private structure used by doshQueryLVMInfo.
1808 * This is what the LVMINFO pointer really
1809 * points to.
1810 *
1811 *@@added V0.9.9 (2001-04-07) [umoeller]
1812 */
1813
1814typedef struct _LVMINFOPRIVATE
1815{
1816 LVMINFO LVMInfo; // public structure (dosh.h)
1817
1818 // function pointers resolved from LVM.DLL
1819
1820 void (* _System Open_LVM_Engine)(BOOLEAN Ignore_CHS,
1821 CARDINAL32 *Error_Code);
1822
1823 void (* _System Free_Engine_Memory)(ADDRESS Object);
1824
1825 void (* _System Close_LVM_Engine)(void);
1826
1827 Drive_Control_Array (* _System
1828 Get_Drive_Control_Data)(CARDINAL32 *Error_Code);
1829
1830 Drive_Information_Record (* _System
1831 Get_Drive_Status)(ADDRESS Drive_Handle,
1832 CARDINAL32 *Error_Code);
1833
1834 Partition_Information_Array (* _System
1835 Get_Partitions)(ADDRESS Handle,
1836 CARDINAL32 *Error_Code);
1837
1838 Volume_Information_Record (*_System
1839 Get_Volume_Information)(ADDRESS Volume_Handle,
1840 CARDINAL32 *Error_Code);
1841
1842} LVMINFOPRIVATE, *PLVMINFOPRIVATE;
1843
1844#define LVM_ERROR_FIRST 20000
1845
1846/*
1847 *@@ doshQueryLVMInfo:
1848 * creates an LVMINFO structure if LVM is installed.
1849 * Returns that structure (which the caller must free
1850 * using doshFreeLVMInfo) or NULL if LVM.DLL was not
1851 * found along the LIBPATH.
1852 *
1853 *@@added V0.9.9 (2001-04-07) [umoeller]
1854 */
1855
1856APIRET doshQueryLVMInfo(PLVMINFO *ppLVMInfo)
1857{
1858 APIRET arc = NO_ERROR;
1859 CHAR szError[100];
1860 PLVMINFOPRIVATE pLVMInfo = NULL;
1861 HMODULE hmodLVM = NULLHANDLE;
1862
1863 if (!(arc = DosLoadModule(szError,
1864 sizeof(szError),
1865 "LVM",
1866 &hmodLVM)))
1867 {
1868 // got LVM.DLL:
1869 pLVMInfo = NEW(LVMINFOPRIVATE);
1870 if (!pLVMInfo)
1871 arc = ERROR_NOT_ENOUGH_MEMORY;
1872 else
1873 {
1874 // array of function pointers to be resolved from LVM.DLL
1875 RESOLVEFUNCTION aFunctions[] =
1876 {
1877 "Open_LVM_Engine", (PFN*)&pLVMInfo->Open_LVM_Engine,
1878 "Free_Engine_Memory", (PFN*)&pLVMInfo->Free_Engine_Memory,
1879 "Close_LVM_Engine", (PFN*)&pLVMInfo->Close_LVM_Engine,
1880 "Get_Drive_Control_Data", (PFN*)&pLVMInfo->Get_Drive_Control_Data,
1881 "Get_Drive_Status", (PFN*)&pLVMInfo->Get_Drive_Status,
1882 "Get_Partitions", (PFN*)&pLVMInfo->Get_Partitions,
1883 "Get_Volume_Information", (PFN*)&pLVMInfo->Get_Volume_Information
1884 };
1885 ULONG ul;
1886
1887 ZERO(pLVMInfo);
1888
1889 pLVMInfo->LVMInfo.hmodLVM = hmodLVM;
1890
1891 // now resolve function pointers
1892 for (ul = 0;
1893 ul < ARRAYITEMCOUNT(aFunctions);
1894 ul++)
1895 {
1896 PRESOLVEFUNCTION pFuncThis = &aFunctions[ul];
1897 arc = DosQueryProcAddr(hmodLVM,
1898 0, // ordinal, ignored
1899 (PSZ)pFuncThis->pcszFunctionName,
1900 pFuncThis->ppFuncAddress);
1901 if (!pFuncThis->ppFuncAddress)
1902 arc = ERROR_INVALID_NAME;
1903
1904 if (arc)
1905 break;
1906 }
1907 }
1908 }
1909
1910 if (arc)
1911 doshFreeLVMInfo((PLVMINFO)pLVMInfo);
1912 else
1913 *ppLVMInfo = (PLVMINFO)pLVMInfo;
1914
1915 return arc;
1916}
1917
1918/*
1919 *@@ doshReadLVMPartitions:
1920 * using the LVMINFO parameter from doshQueryLVMInfo,
1921 * builds an array of PARTITIONINFO structures with
1922 * the data returned from LVM.DLL.
1923 *
1924 *@@added V0.9.9 (2001-04-07) [umoeller]
1925 */
1926
1927APIRET doshReadLVMPartitions(PLVMINFO pInfo, // in: LVM info
1928 PPARTITIONINFO *ppPartitionInfo, // out: partitions array
1929 PUSHORT pcPartitions) // out: partitions count
1930{
1931 APIRET arc = NO_ERROR;
1932 CARDINAL32 Error = 0;
1933
1934 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
1935 *ppiTemp = NULL;
1936 USHORT cPartitions = 0; // bootable partition count
1937 PLVMINFOPRIVATE pLVMInfo = (PLVMINFOPRIVATE)pInfo;
1938
1939 _Pmpf((__FUNCTION__ ": entering"));
1940
1941 if (!pLVMInfo)
1942 return (ERROR_INVALID_PARAMETER);
1943
1944 // initialize LVM engine
1945 pLVMInfo->Open_LVM_Engine(TRUE,
1946 &Error);
1947
1948 _Pmpf((" Open_LVM_Engine Error: %d"));
1949
1950 if (!Error)
1951 {
1952 Drive_Control_Array DCA = pLVMInfo->Get_Drive_Control_Data(&Error);
1953 // member records to be freed
1954
1955 _Pmpf((" Get_Drive_Control_Data Error: %d, drive count: %d", Error, DCA.Count));
1956
1957 if ( (!Error)
1958 && (DCA.Count)
1959 )
1960 {
1961 // DCA.Drive_Control_Data now contains drive information records;
1962 // this must be freed
1963 ULONG ulDisk;
1964
1965 for (ulDisk = 0;
1966 ulDisk < DCA.Count;
1967 ulDisk++)
1968 {
1969 Drive_Control_Record *pDriveControlRecord
1970 = &DCA.Drive_Control_Data[ulDisk];
1971 ADDRESS hDrive = pDriveControlRecord->Drive_Handle;
1972
1973 /* Drive_Information_Record pDriveInfoRecord
1974 = pLVMInfo->Get_Drive_Status(hDrive,
1975 &Error);
1976
1977 _Pmpf((" drive %d Get_Drive_Status Error: %d", ulDisk, Error));
1978
1979 if (!Error) */
1980 {
1981 Partition_Information_Array PIA
1982 = pLVMInfo->Get_Partitions(hDrive,
1983 &Error);
1984
1985 _Pmpf((" Get_Partitions Error: %d", Error));
1986
1987 if (!Error)
1988 {
1989 // PIA.Partition_Array now contains
1990 // Partition_Information_Record; must be freed
1991
1992 // now go thru partitions of this drive
1993 ULONG ulPart;
1994 for (ulPart = 0;
1995 ulPart < PIA.Count;
1996 ulPart++)
1997 {
1998 Partition_Information_Record *pPartition
1999 = &PIA.Partition_Array[ulPart];
2000 Volume_Information_Record VolumeInfo;
2001
2002 const char *pcszBootName = NULL; // for now
2003 BOOL fBootable = FALSE;
2004
2005 if (pPartition->Volume_Handle)
2006 {
2007 // this partition is part of a volume:
2008 // only then can it be bootable...
2009 // get the volume info
2010 VolumeInfo
2011 = pLVMInfo->Get_Volume_Information(pPartition->Volume_Handle,
2012 &Error);
2013 pcszBootName = VolumeInfo.Volume_Name;
2014
2015 fBootable = (VolumeInfo.Status == 1);
2016 }
2017
2018
2019 if (arc = AppendPartition(&pPartitionInfos,
2020 &ppiTemp,
2021 &cPartitions,
2022 ulDisk + 1,
2023 pcszBootName,
2024 pPartition->Volume_Drive_Letter,
2025 pPartition->OS_Flag, // FS type
2026 pPartition->Primary_Partition,
2027 fBootable,
2028 pPartition->True_Partition_Size))
2029 break;
2030 }
2031
2032 // clean up partitions
2033 pLVMInfo->Free_Engine_Memory(PIA.Partition_Array);
2034 }
2035 }
2036 /* else
2037 // error:
2038 break; */
2039 }
2040
2041 // clean up drive data
2042 pLVMInfo->Free_Engine_Memory(DCA.Drive_Control_Data);
2043 }
2044 }
2045
2046 // close LVM
2047 pLVMInfo->Close_LVM_Engine();
2048
2049 if (Error)
2050 {
2051 // if we got an error, return it with the
2052 // LVM error offset
2053 arc = LVM_ERROR_FIRST + Error;
2054
2055 CleanPartitionInfos(pPartitionInfos);
2056 }
2057
2058 if (!arc)
2059 {
2060 *ppPartitionInfo = pPartitionInfos;
2061 *pcPartitions = cPartitions;
2062 }
2063
2064 _Pmpf((__FUNCTION__ ": exiting, arg = %d", arc));
2065
2066 return arc;
2067}
2068
2069/*
2070 *@@ doshFreeLVMInfo:
2071 *
2072 *@@added V0.9.9 (2001-04-07) [umoeller]
2073 */
2074
2075VOID doshFreeLVMInfo(PLVMINFO pInfo)
2076{
2077 if (pInfo)
2078 {
2079 if (pInfo->hmodLVM)
2080 DosFreeModule(pInfo->hmodLVM);
2081
2082 free(pInfo);
2083 }
2084}
2085
2086/*
2087 *@@category: Helpers\Control program helpers\Wildcard matching
2088 * See doshMatch.
2089 */
2090
2091/* ******************************************************************
2092 *
2093 * Wildcard matching
2094 *
2095 ********************************************************************/
2096
2097/*
2098 * PerformMatch:
2099 * compares a single path component. The input strings must
2100 * not have slashes or backslashes in them.
2101 *
2102 * fHasDot must be true if pName contains at least one dot.
2103 *
2104 * Note that this function is recursive.
2105 */
2106
2107STATIC BOOL PerformMatch(PCSZ pMask,
2108 PCSZ pName,
2109 int fHasDot)
2110{
2111 while (TRUE)
2112 {
2113 // go thru the pMask char by char
2114 switch (*pMask)
2115 {
2116 case 0:
2117 // if we've reached the end of the mask,
2118 // we better have the end of the name too
2119 if (*pName == 0)
2120 return TRUE;
2121 return FALSE;
2122
2123 case '?':
2124 // a question mark matches one single character;
2125 // it does _not_ match a dot;
2126 // at the end of the component, it also matches
2127 // no characters
2128 if ( (*pName != '.')
2129 && (*pName != 0)
2130 )
2131 ++pName;
2132 ++pMask;
2133 break;
2134
2135 case '*':
2136 // asterisk matches zero or more characters
2137
2138 // skip extra asterisks
2139 /*
2140 do
2141 {
2142 ++pMask;
2143 } while (*pMask == '*');
2144 */
2145
2146 while (*(++pMask) == '*') // V0.9.20 (2002-07-25) [umoeller]
2147 ;
2148
2149 // pMask points to after '*';
2150 // pName is unchanged... so for each pName
2151 // that follows, check if it matches
2152 while (TRUE)
2153 {
2154 if (PerformMatch(pMask, pName, fHasDot))
2155 // the remainder matched:
2156 // then everything matches
2157 return TRUE;
2158
2159 if (*pName == 0)
2160 return FALSE;
2161
2162 // didn't match: try next pName
2163 ++pName;
2164 }
2165
2166 case '.':
2167 // a dot matches a dot only, even if the name doesn't
2168 // have one at the end
2169 ++pMask;
2170 if (*pName == '.')
2171 ++pName;
2172 else if ( (fHasDot)
2173 || (*pName != 0)
2174 )
2175 return FALSE;
2176 break;
2177
2178 default:
2179 if (*pMask++ != *pName++)
2180 return FALSE;
2181 break;
2182 }
2183 }
2184}
2185
2186/*
2187 *@@ doshMatchCase:
2188 * this matches '*' and '?' wildcards, similar to what
2189 * DosEditName does. However, this does not require a
2190 * file to be present, but works on strings only.
2191 *
2192 * Returns TRUE if the given name matches the given mask.
2193 *
2194 * This accepts both short and fully qualified masks and
2195 * names, but the following rules apply:
2196 *
2197 * -- Either both the mask and the name must be fully
2198 * qualified, or both must not. Otherwise the match fails.
2199 *
2200 * -- If fully qualified, only the last component may contain
2201 * wildcards.
2202 *
2203 * -- This compares WITH respect to case always. Upper-case
2204 * both the mask and the name before calling this, or
2205 * use doshMatch instead.
2206 *
2207 * -- As opposed to the WPS, this handles multiple dots in
2208 * filenames correctly. For example, the WPS will not
2209 * match "*.ZIP" against "whatever-0.9.3.zip", but this
2210 * one will.
2211 *
2212 * This replaces strhMatchOS2 which has been removed with
2213 * V0.9.16 and is a lot faster than the old code, which has
2214 * been completely rewritten.
2215 *
2216 *@@added V0.9.16 (2002-01-01) [umoeller]
2217 */
2218
2219BOOL doshMatchCase(const char *pcszMask, // in: mask (e.g. "*.TXT")
2220 const char *pcszName) // in: string to check (e.g. "TEST.TXT")
2221{
2222 BOOL brc = FALSE;
2223
2224 PCSZ pLastMaskComponent,
2225 pLastNameComponent;
2226
2227 ULONG cbMaskPath = 0,
2228 cbNamePath = 0;
2229
2230 if (pLastMaskComponent = strrchr(pcszMask, '\\'))
2231 {
2232 // length of path component
2233 cbMaskPath = pLastMaskComponent - pcszMask;
2234 pLastMaskComponent++;
2235 }
2236 else
2237 pLastMaskComponent = pcszMask;
2238
2239 if (pLastNameComponent = strrchr(pcszName, '\\'))
2240 {
2241 // length of path component
2242 cbNamePath = pLastNameComponent - pcszName;
2243 pLastNameComponent++;
2244 }
2245 else
2246 pLastNameComponent = pcszName;
2247
2248 // compare paths; if the lengths are different
2249 // or memcmp fails, we can't match
2250 if ( (cbMaskPath == cbNamePath) // can both be null
2251 && ( (cbMaskPath == 0)
2252 || (!memcmp(pcszMask, pcszName, cbMaskPath))
2253 )
2254 )
2255 {
2256 // alright, paths match:
2257 brc = PerformMatch(pLastMaskComponent,
2258 pLastNameComponent,
2259 // has dot?
2260 (strchr(pLastNameComponent, '.') != NULL));
2261
2262 }
2263
2264 return brc;
2265}
2266
2267/*
2268 *@@ doshMatchCaseNoPath:
2269 * like doshMatchCase, but is faster if you are sure that
2270 * neither pcszMask nor pcszName contain path separators
2271 * ("\" characters). In other words, this is for short
2272 * filenames.
2273 *
2274 *@@added V0.9.20 (2002-07-25) [umoeller]
2275 */
2276
2277BOOL doshMatchCaseNoPath(const char *pcszMask, // in: mask (e.g. "*.TXT")
2278 const char *pcszName) // in: string to check (e.g. "TEST.TXT")
2279{
2280 return PerformMatch(pcszMask,
2281 pcszName,
2282 // has dot?
2283 (strchr(pcszName, '.') != NULL));
2284}
2285
2286/*
2287 *@@ doshMatch:
2288 * like doshMatchCase, but compares without respect
2289 * to case.
2290 *
2291 *@@added V0.9.16 (2002-01-26) [umoeller]
2292 */
2293
2294BOOL doshMatch(const char *pcszMask, // in: mask (e.g. "*.TXT")
2295 const char *pcszName) // in: string to check (e.g. "TEST.TXT")
2296{
2297 ULONG cbMask = strlen(pcszMask),
2298 cbName = strlen(pcszName);
2299 PSZ pszMask = (PSZ)_alloca(cbMask + 1),
2300 pszName = (PSZ)_alloca(cbName + 1);
2301
2302 memcpy(pszMask, pcszMask, cbMask + 1);
2303 nlsUpper(pszMask);
2304 memcpy(pszName, pcszName, cbName + 1);
2305 nlsUpper(pszName);
2306
2307 return doshMatchCase(pszMask,
2308 pszName);
2309}
Note: See TracBrowser for help on using the repository browser.