source: trunk/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.3 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
336 if ( (!pcszDir)
337 || (!(*pcszDir))
338 )
339 return ERROR_INVALID_PARAMETER;
340
341 if (pcszDir[1] == ':')
342 {
343 // drive given:
344 CHAR cDrive = toupper(*(pcszDir));
345 // change drive
346 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
347 // 1 = A:, 2 = B:, ...
348 }
349
350 if (!arc)
351 arc = DosSetCurrentDir((PSZ)pcszDir);
352
353 return arc; // V0.9.9 (2001-04-04) [umoeller]
354}
355
356/*
357 *@@ CopyToBuffer:
358 * little helper for copying a string to
359 * a target buffer with length checking.
360 *
361 * Returns:
362 *
363 * -- NO_ERROR
364 *
365 * -- ERROR_BUFFER_OVERFLOW if pszTarget does
366 * not have enough room to hold pcszSource
367 * (including the null terminator).
368 *
369 *@@added V0.9.16 (2001-10-08) [umoeller]
370 */
371
372STATIC APIRET CopyToBuffer(PSZ pszTarget, // out: target buffer
373 PCSZ pcszSource, // in: source string
374 ULONG cbTarget) // in: size of target buffer
375{
376 ULONG ulLength = strlen(pcszSource);
377 if (ulLength < cbTarget)
378 {
379 memcpy(pszTarget,
380 pcszSource,
381 ulLength + 1);
382 return NO_ERROR;
383 }
384
385 return ERROR_BUFFER_OVERFLOW;
386}
387
388/*
389 *@@ doshSearchPath:
390 * replacement for DosSearchPath.
391 *
392 * This looks along all directories which are
393 * specified in the value of the given environment
394 * variable if pcszFile is found.
395 *
396 * As opposed to the stupid DosSearchPath, this
397 * ignores subdirectories in the path particles.
398 * For example, DosSearchPath would usually not
399 * find an INSTALL file because \OS2 contains
400 * an INSTALL directory, or NETSCAPE because
401 * \OS2\INSTALL contains a NETSCAPE directory.
402 *
403 * Returns:
404 *
405 * -- NO_ERROR: pszExecutable has received the
406 * full path of pcszFile.
407 *
408 * -- ERROR_FILE_NOT_FOUND: pcszFile was not found
409 * in the specified path (or is a directory).
410 *
411 * -- ERROR_BUFFER_OVERFLOW: pcszFile was found, but
412 * the pszExecutable buffer is too small to hold
413 * the full path.
414 *
415 *@@added V0.9.16 (2001-10-08) [umoeller]
416 *@@changed V1.0.0 (2002-11-23) [umoeller]: allowing NULL pcszPath to search for "PATH"
417 */
418
419APIRET doshSearchPath(const char *pcszPath, // in: path variable name; if NULL, we use "PATH"
420 const char *pcszFile, // in: file to look for (e.g. "LVM.EXE")
421 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
422 ULONG cbExecutable) // in: sizeof (*pszExecutable)
423{
424 APIRET arc = NO_ERROR;
425
426 // get the PATH value
427 PCSZ pcszPathValue;
428
429 if (!pcszPath)
430 pcszPath = "PATH"; // V1.0.0 (2002-11-23) [umoeller]
431
432 if (!(arc = DosScanEnv((PSZ)pcszPath,
433#if __cplusplus
434 &pcszPathValue)))
435#else
436 (PSZ*)&pcszPathValue)))
437#endif
438 {
439 // run thru the path components
440 PSZ pszPathCopy;
441 if (pszPathCopy = strdup(pcszPathValue))
442 {
443 PSZ pszToken = strtok(pszPathCopy, ";");
444 while (pszToken)
445 {
446 CHAR szFileMask[2*CCHMAXPATH];
447 FILESTATUS3 fs3;
448
449 sprintf(szFileMask,
450 "%s\\%s",
451 pszToken, // path particle
452 pcszFile); // e.g. "netscape"
453
454 if ( (!(arc = DosQueryPathInfo(szFileMask,
455 FIL_STANDARD,
456 &fs3,
457 sizeof(fs3))))
458 // make sure it's not a directory
459 // and that it's not hidden
460 && (!(fs3.attrFile & (FILE_DIRECTORY | FILE_HIDDEN)))
461 )
462 {
463 // copy
464 arc = CopyToBuffer(pszExecutable,
465 szFileMask,
466 cbExecutable);
467 // and stop
468 break;
469 }
470 else
471 arc = ERROR_FILE_NOT_FOUND;
472 // and search on
473
474 pszToken = strtok(NULL, ";");
475 };
476
477 free(pszPathCopy);
478 }
479 else
480 arc = ERROR_NOT_ENOUGH_MEMORY;
481 }
482
483 return arc;
484}
485
486/*
487 *@@ doshSearchDirs:
488 * This looks along all directories which are
489 * specified in the passed directory list
490 * if pcszFile is found.
491 *
492 * As opposed to the stupid DosSearchPath, this
493 * ignores subdirectories in the path particles.
494 * For example, DosSearchPath would usually not
495 * find an INSTALL file because \OS2 contains
496 * an INSTALL directory, or NETSCAPE because
497 * \OS2\INSTALL contains a NETSCAPE directory.
498 *
499 * Returns:
500 *
501 * -- NO_ERROR: pszExecutable has received the
502 * full path of pcszFile.
503 *
504 * -- ERROR_FILE_NOT_FOUND: pcszFile was not found
505 * in the specified path (or is a directory).
506 *
507 * -- ERROR_BUFFER_OVERFLOW: pcszFile was found, but
508 * the pszExecutable buffer is too small to hold
509 * the full path.
510 *
511 *@@added V1.0.4 (2005-06-16) [chennecke]: blatantly stolen from doshSearchPath
512 */
513
514APIRET doshSearchDirs(const char *pcszDirList, // in: list of directories in PATH style
515 const char *pcszFile, // in: file to look for (e.g. "LVM.EXE")
516 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
517 ULONG cbExecutable) // in: sizeof (*pszExecutable)
518{
519 APIRET arc = NO_ERROR;
520
521 // run thru the path components
522 PSZ pszPathCopy;
523 if (pszPathCopy = strdup(pcszDirList))
524 {
525 PSZ pszToken = strtok(pszPathCopy, ";");
526 while (pszToken)
527 {
528 CHAR szFileMask[2*CCHMAXPATH];
529 FILESTATUS3 fs3;
530
531 sprintf(szFileMask,
532 "%s\\%s",
533 pszToken, // path particle
534 pcszFile); // e.g. "netscape"
535
536 if ( (!(arc = DosQueryPathInfo(szFileMask,
537 FIL_STANDARD,
538 &fs3,
539 sizeof(fs3))))
540 // make sure it's not a directory
541 // and that it's not hidden
542 && (!(fs3.attrFile & (FILE_DIRECTORY | FILE_HIDDEN)))
543 )
544 {
545 // copy
546 arc = CopyToBuffer(pszExecutable,
547 szFileMask,
548 cbExecutable);
549 // and stop
550 break;
551 }
552 else
553 arc = ERROR_FILE_NOT_FOUND;
554 // and search on
555
556 pszToken = strtok(NULL, ";");
557 };
558
559 free(pszPathCopy);
560 }
561 else
562 arc = ERROR_NOT_ENOUGH_MEMORY;
563
564 return arc;
565}
566
567/*
568 * FindFile:
569 * helper for doshFindExecutable.
570 *
571 *added V0.9.11 (2001-04-25) [umoeller]
572 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewrote second half for DosSearchPath replacement, which returns directories too
573 */
574
575STATIC APIRET FindFile(const char *pcszCommand, // in: command (e.g. "lvm")
576 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
577 ULONG cbExecutable) // in: sizeof (*pszExecutable)
578{
579 APIRET arc = NO_ERROR;
580 FILESTATUS3 fs3;
581
582 if ( (strchr(pcszCommand, '\\'))
583 || (strchr(pcszCommand, ':'))
584 )
585 {
586 // looks like this is qualified:
587 arc = DosQueryPathInfo((PSZ)pcszCommand,
588 FIL_STANDARD,
589 &fs3,
590 sizeof(fs3));
591 if (!arc)
592 if (!(fs3.attrFile & FILE_DIRECTORY))
593 arc = CopyToBuffer(pszExecutable,
594 pcszCommand,
595 cbExecutable);
596 else
597 // directory:
598 arc = ERROR_INVALID_EXE_SIGNATURE;
599 }
600 else
601 {
602 // non-qualified:
603 /* arc = DosSearchPath(SEARCH_IGNORENETERRS
604 | SEARCH_ENVIRONMENT
605 | SEARCH_CUR_DIRECTORY,
606 "PATH",
607 (PSZ)pcszCommand,
608 pszExecutable,
609 cbExecutable); */
610 // The above is not useable. It returns directories
611 // on the path... for example, it returns \OS2\INSTALL\NETSCAPE
612 // if netscape is looked for. So we search manually... sigh.
613 // V0.9.16 (2001-10-08) [umoeller]
614 arc = doshSearchPath("PATH",
615 pcszCommand,
616 pszExecutable,
617 cbExecutable);
618 }
619
620 return arc;
621}
622
623/*
624 *@@ doshFindExecutable:
625 * this attempts to find an executable by doing the
626 * following:
627 *
628 * 1) If pcszCommand appears to be qualified (i.e. contains
629 * a backslash), this checks for whether the file exists.
630 * If it is a directory, ERROR_INVALID_EXE_SIGNATURE is
631 * returned.
632 *
633 * 2) If pcszCommand contains no backslash, this searches
634 * all directories on the PATH in order to find the full
635 * path of the executable. Starting with V0.9.16, we
636 * use doshSearchPath for that.
637 *
638 * papcszExtensions determines if additional searches are to be
639 * performed if the file doesn't exist (case 1) or doshSearchPath
640 * returned ERROR_FILE_NOT_FOUND (case 2).
641 * This must point to an array of strings specifying the extra
642 * extensions to search for.
643 *
644 * If both papcszExtensions and cExtensions are null, no
645 * extra searches are performed.
646 *
647 * Returns:
648 *
649 * -- NO_ERROR: pszExecutable has received the full path of
650 * the executable found by DosSearchPath.
651 *
652 * -- ERROR_FILE_NOT_FOUND
653 *
654 * -- ERROR_BUFFER_OVERFLOW: pcszCommand was found, but
655 * the pszExecutable buffer is too small to hold
656 * the full path.
657 *
658 * Example:
659 *
660 + const char *aExtensions[] = { "EXE",
661 + "COM",
662 + "CMD"
663 + };
664 + CHAR szExecutable[CCHMAXPATH];
665 + APIRET arc = doshFindExecutable("lvm",
666 + szExecutable,
667 + sizeof(szExecutable),
668 + aExtensions,
669 + 3);
670 *
671 *@@added V0.9.9 (2001-03-07) [umoeller]
672 *@@changed V0.9.11 (2001-04-25) [umoeller]: this never worked for qualified pcszCommand's, fixed
673 */
674
675APIRET doshFindExecutable(const char *pcszCommand, // in: command (e.g. "lvm")
676 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
677 ULONG cbExecutable, // in: sizeof (*pszExecutable)
678 const char **papcszExtensions, // in: array of extensions (without dots)
679 ULONG cExtensions) // in: array item count
680{
681 APIRET arc = FindFile(pcszCommand,
682 pszExecutable,
683 cbExecutable);
684
685 if ( (arc == ERROR_FILE_NOT_FOUND) // not found?
686 && (cExtensions) // any extra searches wanted?
687 )
688 {
689 // try additional things then
690 PSZ psz2;
691 if (psz2 = (PSZ)malloc(strlen(pcszCommand) + 20))
692 {
693 ULONG ul;
694 for (ul = 0;
695 ul < cExtensions;
696 ul++)
697 {
698 const char *pcszExtThis = papcszExtensions[ul];
699 sprintf(psz2,
700 "%s.%s",
701 pcszCommand,
702 pcszExtThis);
703 arc = FindFile(psz2,
704 pszExecutable,
705 cbExecutable);
706 if (arc != ERROR_FILE_NOT_FOUND)
707 break;
708 }
709
710 free(psz2);
711 }
712 else
713 arc = ERROR_NOT_ENOUGH_MEMORY;
714 }
715
716 return arc;
717}
718
719/*
720 *@@category: Helpers\Control program helpers\Partitions info
721 * functions for retrieving partition information directly
722 * from the partition tables on the disk. See doshGetPartitionsList.
723 */
724
725/********************************************************************
726 *
727 * Partition functions
728 *
729 ********************************************************************/
730
731/*
732 *@@ doshQueryDiskCount:
733 * returns the no. of physical disks installed
734 * on the system.
735 *
736 *@@added V0.9.0 [umoeller]
737 */
738
739UINT doshQueryDiskCount(VOID)
740{
741 USHORT usCount = 0;
742 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &usCount, 2, 0, 0);
743 return usCount;
744}
745
746/*
747 *@@ doshType2FSName:
748 * this returns a static, zero-terminated string
749 * for the given FS type, or NULL if the type
750 * is unknown.
751 *
752 *@@added V0.9.0 [umoeller]
753 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewritten
754 */
755
756const char* doshType2FSName(unsigned char bFSType) // in: FS type
757{
758 switch (bFSType)
759 {
760 case 0x00: return "empty";
761 case 0x01: return "DOS 12-bit FAT < 10 Mb";
762 case 0x02: return "XENIX root file system";
763 case 0x03: return "XENIX /usr file system (obsolete)";
764 case 0x04: return "DOS 16-bit FAT < 32 Mb";
765 case 0x05: return "DOS 3.3+ extended partition";
766 case 0x06: return "DOS 3.31+ 16-bit FAT > 32 Mb";
767 case 0x07: return "HPFS/NTFS/QNX/Advanced Unix";
768 case 0x08: return "OS/2 1.0-1.3/AIX/Commodore/DELL";
769 case 0x09: return "AIX data/Coherent";
770 case 0x0A: return "OS/2 Boot Manager/OPUS/Coherent Swap";
771 case 0x0B: return "Windows95 with 32-bit FAT";
772 case 0x0C: return "Windows95 with 32-bit FAT (LBA)";
773 case 0x0E: return "Windows 95 VFAT (06h plus LBA)";
774 case 0x0F: return "Windows 95 VFAT (05h plus LBA)";
775 case 0x10: return "OPUS";
776 case 0x11: return "OS/2 Boot Manager hidden 12-bit FAT";
777 case 0x12: return "Compaq Diagnostics";
778 case 0x14: return "OS/2 Boot Manager hidden sub-32M 16-bit FAT";
779 case 0x16: return "OS/2 Boot Manager hidden over-32M 16-bit FAT";
780 case 0x17: return "OS/2 Boot Manager hidden HPFS";
781 case 0x18: return "AST special Windows swap file (\"Zero-Volt Suspend\")";
782 // case 0x21: reserved
783 // case 0x23: reserved
784 case 0x24: return "NEC MS-DOS 3.x";
785 // case 0x26: reserved
786 // case 0x31: reserved
787 // case 0x33: reserved
788 // case 0x34: reserved
789 // case 0x36: reserved
790 case 0x38: return "Theos";
791 case 0x3C: return "PowerQuest PartitionMagic recovery partition";
792 case 0x40: return "VENIX 80286";
793 case 0x41: return "Personal RISC Boot";
794 case 0x42: return "SFS (Secure File System) by Peter Gutmann";
795 case 0x50: return "OnTrack Disk Manager, read-only";
796 case 0x51: return "OnTrack Disk Manager, read/write";
797 case 0x52: return "CP/M or Microport System V/386";
798 case 0x53: return "OnTrack Disk Manager, write-only???";
799 case 0x54: return "OnTrack Disk Manager (DDO)";
800 case 0x56: return "GoldenBow VFeature";
801 case 0x61: return "SpeedStor";
802 case 0x63: return "Unix SysV/386, 386/ix or Mach, MtXinu BSD 4.3 on Mach or GNU HURD";
803 case 0x64: return "Novell NetWare 286";
804 case 0x65: return "Novell NetWare (3.11)";
805 case 0x67:
806 case 0x68:
807 case 0x69: return "Novell";
808 case 0x70: return "DiskSecure Multi-Boot";
809 // case 0x71: reserved
810 // case 0x73: reserved
811 // case 0x74: reserved
812 case 0x75: return "PC/IX";
813 // case 0x76: reserved
814 case 0x80: return "Minix v1.1 - 1.4a";
815 case 0x81: return "Minix v1.4b+ or Linux or Mitac Advanced Disk Manager";
816 case 0x82: return "Linux Swap or Prime";
817 case 0x83: return "Linux native file system (ext2fs/xiafs)";
818 case 0x84: return "OS/2-renumbered type 04h (hidden DOS C: drive)";
819 case 0x86: return "FAT16 volume/stripe set (Windows NT)";
820 case 0x87: return "HPFS Fault-Tolerant mirrored partition or NTFS volume/stripe set";
821 case 0x93: return "Amoeba file system";
822 case 0x94: return "Amoeba bad block table";
823 case 0xA0: return "Phoenix NoteBIOS Power Management \"Save-to-Disk\" partition";
824 // case 0xA1: reserved
825 // case 0xA3: reserved
826 // case 0xA4: reserved
827 case 0xA5: return "FreeBSD, BSD/386";
828 // case 0xA6: reserved
829 // case 0xB1: reserved
830 // case 0xB3: reserved
831 // case 0xB4: reserved
832 // case 0xB6: reserved
833 case 0xB7: return "BSDI file system (secondarily swap)";
834 case 0xB8: return "BSDI swap (secondarily file system)";
835 case 0xC1: return "DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT";
836 case 0xC4: return "DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT";
837 case 0xC6: return "DR DOS 6.0 LOGIN.EXE-secured Huge partition or NT corrupted FAT16 volume/stripe set";
838 case 0xC7: return "Syrinx Boot or corrupted NTFS volume/stripe set";
839 case 0xD8: return "CP/M-86";
840 case 0xDB: return "CP/M, Concurrent CP/M, Concurrent DOS, Convergent Technologies OS";
841 case 0xE1: return "SpeedStor 12-bit FAT extended partition";
842 case 0xE3: return "DOS read-only or Storage Dimensions";
843 case 0xE4: return "SpeedStor 16-bit FAT extended partition";
844 // case 0xE5: reserved
845 // case 0xE6: reserved
846 case 0xF1: return "Storage Dimensions";
847 case 0xF2: return "DOS 3.3+ secondary partition";
848 // case 0xF3: reserved
849 case 0xF4: return "SpeedStor or Storage Dimensions";
850 // case 0xF6: reserved
851 case 0xFE: return "LANstep or IBM PS/2 IML";
852 case 0xFF: return "Xenix bad block table";
853 }
854
855 return NULL;
856}
857
858/*
859 * AppendPartition:
860 * this appends the given partition information to
861 * the given partition list. To do this, a new
862 * PARTITIONINFO structure is created and appended
863 * in a list (managed thru the PARTITIONINFO.pNext
864 * items).
865 *
866 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
867 * With each call of this function, this pointer is advanced
868 * to point to the newly created PARTITIONINFO, so before
869 * calling this function for the first time,
870 *
871 *@@added V0.9.0 [umoeller]
872 *@@changed V0.9.20 (2002-08-10) [umoeller]: fixed truncated LVM names
873 */
874
875STATIC APIRET AppendPartition(PARTITIONINFO **pppiFirst,
876 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
877 PUSHORT posCount, // in/out: partition count
878 BYTE bDisk, // in: disk of partition
879 const char *pszBootName, // in: boot partition name
880 CHAR cLetter, // in/out: drive letter
881 BYTE bFsType, // in: file system type
882 BOOL fPrimary, // in: primary?
883 BOOL fBootable,
884 ULONG ulSectors) // in: no. of sectors
885{
886 APIRET arc = NO_ERROR;
887 PPARTITIONINFO ppiNew;
888 if (ppiNew = NEW(PARTITIONINFO))
889 {
890 ZERO(ppiNew);
891
892 // store data
893 ppiNew->bDisk = bDisk;
894 if ( (fBootable)
895 && (pszBootName)
896 )
897 {
898 // fixed truncated LVM names V0.9.20 (2002-08-10) [umoeller]
899 strhncpy0(ppiNew->szBootName,
900 pszBootName,
901 sizeof(ppiNew->szBootName));
902 }
903 else
904 ppiNew->szBootName[0] = 0;
905 ppiNew->cLetter = cLetter;
906 ppiNew->bFSType = bFsType;
907 ppiNew->pcszFSType = doshType2FSName(bFsType);
908 ppiNew->fPrimary = fPrimary;
909 ppiNew->fBootable = fBootable;
910 ppiNew->ulSize = ulSectors / 2048;
911
912 ppiNew->pNext = NULL;
913
914 (*posCount)++;
915
916 if (*pppiFirst == (PPARTITIONINFO)NULL)
917 {
918 // first call:
919 *pppiFirst = ppiNew;
920 *pppiThis = ppiNew;
921 }
922 else
923 {
924 // append to list
925 (**pppiThis).pNext = ppiNew;
926 *pppiThis = ppiNew;
927 }
928 }
929 else
930 arc = ERROR_NOT_ENOUGH_MEMORY;
931
932 return arc;
933}
934
935#ifndef __XWPLITE__
936
937/*
938 *@@ doshReadSector:
939 * reads a physical disk sector.
940 *
941 * If NO_ERROR is returned, the sector contents
942 * have been stored in *buff.
943 *
944 * Originally contributed by Dmitry A. Steklenev.
945 *
946 *@@added V0.9.0 [umoeller]
947 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
948 */
949
950APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
951 void *buff,
952 USHORT head,
953 USHORT cylinder,
954 USHORT sector)
955{
956 APIRET arc;
957 HFILE dh = 0;
958 char dn[256];
959
960 sprintf(dn, "%u:", disk);
961 if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
962 {
963 TRACKLAYOUT DiskIOParm;
964 ULONG IOCtlDataLength = sizeof(DiskIOParm);
965 ULONG IOCtlParmLength = 512;
966
967 DiskIOParm.bCommand = 0;
968 DiskIOParm.usHead = head;
969 DiskIOParm.usCylinder = cylinder;
970 DiskIOParm.usFirstSector = 0;
971 DiskIOParm.cSectors = 1;
972 DiskIOParm.TrackTable[0].usSectorNumber = sector;
973 DiskIOParm.TrackTable[0].usSectorSize = 512;
974
975 arc = DosDevIOCtl(dh,
976 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
977 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
978 buff , IOCtlDataLength, &IOCtlDataLength);
979
980 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
981 }
982
983 return arc;
984}
985
986// Sector and Cylinder values are actually 6 bits and 10 bits:
987//
988// 1 1 1 1 1 1
989// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
990// ³c c c c c c c c C c S s s s s s³
991// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
992//
993// The high two bits of the second byte are used as the high bits
994// of a 10-bit value. This allows for as many as 1024 cylinders
995// and 64 sectors per cylinder.
996
997/*
998 * GetCyl:
999 * get cylinder number.
1000 *
1001 * Originally contributed by Dmitry A. Steklenev.
1002 *
1003 *@@added V0.9.0 [umoeller]
1004 */
1005
1006STATIC USHORT GetCyl(USHORT rBeginSecCyl)
1007{
1008 return ((rBeginSecCyl & 0x00C0) << 2)
1009 + ((rBeginSecCyl & 0xFF00) >> 8);
1010}
1011
1012/*
1013 * GetSec:
1014 * get sector number.
1015 *
1016 * Originally contributed by Dmitry A. Steklenev.
1017 *
1018 *@@added V0.9.0 [umoeller]
1019 */
1020
1021STATIC USHORT GetSec(USHORT rBeginSecCyl)
1022{
1023 return rBeginSecCyl & 0x003F;
1024}
1025
1026/*
1027 *@@ doshGetBootManager:
1028 * this goes thru the master boot records on all
1029 * disks to find the boot manager partition.
1030 *
1031 * Returns:
1032 *
1033 * -- NO_ERROR: boot manager found; in that case,
1034 * information about the boot manager
1035 * is written into *pusDisk, *pusPart,
1036 * *BmInfo. Any of these pointers can
1037 * be NULL if you're not interested.
1038 *
1039 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
1040 *
1041 * Originally contributed by Dmitry A. Steklenev.
1042 *
1043 *@@added V0.9.0 [umoeller]
1044 */
1045
1046APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
1047 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
1048 PAR_INFO *pBmInfo) // out: if != NULL, boot manager partition info
1049{
1050 APIRET arc = NO_ERROR;
1051 USHORT count = doshQueryDiskCount(); // Physical disk number
1052 MBR_INFO MBoot; // Master Boot
1053 USHORT usDisk;
1054
1055 if (count > 8) // Not above 8 disks
1056 count = 8;
1057
1058 for (usDisk = 1; usDisk <= count; usDisk++)
1059 {
1060 USHORT usPrim = 0;
1061
1062 // for each disk, read the MBR, which has the
1063 // primary partitions
1064 if ((arc = doshReadSector(usDisk,
1065 &MBoot,
1066 0, // head
1067 0, // cylinder
1068 1))) // sector
1069 return arc;
1070
1071 // scan primary partitions for whether
1072 // BootManager partition exists
1073 for (usPrim = 0; usPrim < 4; usPrim++)
1074 {
1075 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
1076 {
1077 // this is boot manager:
1078 if (pBmInfo)
1079 *pBmInfo = MBoot.sPrtnInfo[usPrim];
1080 if (pusPart)
1081 *pusPart = usPrim;
1082 if (pusDisk)
1083 *pusDisk = usDisk;
1084 // stop scanning
1085 return NO_ERROR;
1086 }
1087 }
1088 }
1089
1090 return ERROR_NOT_SUPPORTED;
1091}
1092
1093/*
1094 * GetPrimaryPartitions:
1095 * this returns the primary partitions.
1096 *
1097 * This gets called from doshGetPartitionsList.
1098 *
1099 * Returns:
1100 *
1101 * -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
1102 *
1103 * Originally contributed by Dmitry A. Steklenev.
1104 *
1105 *@@added V0.9.0 [umoeller]
1106 */
1107
1108STATIC APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
1109 PARTITIONINFO **pppiThis,
1110 PUSHORT posCount, // in/out: partition count
1111 PCHAR pcLetter, // in/out: drive letter counter
1112 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
1113 PAR_INFO* pBmInfo, // in: info returned by doshGetBootManager or NULL
1114 UINT iDisk) // in: system's physical disk count
1115{
1116 APIRET arc = NO_ERROR;
1117
1118 if (!pBmInfo)
1119 arc = ERROR_INVALID_PARAMETER;
1120 else
1121 {
1122 SYS_INFO MName[32]; // Name Space from Boot Manager
1123 memset(&MName, 0, sizeof(MName));
1124
1125 // read boot manager name table;
1126 // this is in the boot manager primary partition
1127 // at sector offset 3 (?!?)
1128 if (!(arc = doshReadSector(BmDisk,
1129 &MName,
1130 // head, cylinder, sector of bmgr primary partition:
1131 pBmInfo->bBeginHead,
1132 GetCyl(pBmInfo->rBeginSecCyl),
1133 GetSec(pBmInfo->rBeginSecCyl) + 3)))
1134 {
1135 // got bmgr name table:
1136 MBR_INFO MBoot; // Master Boot
1137 USHORT i;
1138
1139 // read master boot record of this disk
1140 if (!(arc = doshReadSector(iDisk,
1141 &MBoot,
1142 0, // head
1143 0, // cylinder
1144 1))) // sector
1145 {
1146 for (i = 0;
1147 i < 4; // there can be only four primary partitions
1148 i++)
1149 {
1150 // skip unused partition, BootManager or Extended partition
1151 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
1152 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A) // skip boot manager
1153 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x05) // skip extended partition
1154 )
1155 {
1156 BOOL fBootable = ( (pBmInfo)
1157 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
1158 );
1159 // store this partition
1160 if ((arc = AppendPartition(pppiFirst,
1161 pppiThis,
1162 posCount,
1163 iDisk,
1164 (fBootable)
1165 ? (char*)&MName[(iDisk - 1) * 4 + i].name
1166 : "",
1167 *pcLetter,
1168 MBoot.sPrtnInfo[i].bFileSysCode,
1169 TRUE, // primary
1170 fBootable,
1171 MBoot.sPrtnInfo[i].lTotalSects)))
1172 return arc;
1173 }
1174 }
1175 }
1176 }
1177 }
1178
1179 return arc;
1180}
1181
1182/*
1183 * GetLogicalDrives:
1184 * this returns info for the logical drives
1185 * in the extended partition. This gets called
1186 * from GetExtendedPartition.
1187 *
1188 * This gets called from GetExtendedPartition.
1189 *
1190 * Originally contributed by Dmitry A. Steklenev.
1191 *
1192 *@@added V0.9.0 [umoeller]
1193 */
1194
1195STATIC APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
1196 PARTITIONINFO **pppiThis,
1197 PUSHORT posCount,
1198 PCHAR pcLetter,
1199 PAR_INFO* PrInfo, // in: MBR entry of extended partition
1200 UINT PrDisk,
1201 PAR_INFO* BmInfo)
1202{
1203 APIRET arc = NO_ERROR;
1204 EXT_INFO MBoot; // Master Boot
1205 USHORT i;
1206
1207 if ((arc = doshReadSector(PrDisk,
1208 &MBoot,
1209 PrInfo->bBeginHead,
1210 GetCyl(PrInfo->rBeginSecCyl),
1211 GetSec(PrInfo->rBeginSecCyl))))
1212 return arc;
1213
1214 for (i = 0; i < 4; i++)
1215 {
1216 // skip unused partition or BootManager partition
1217 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
1218 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A)
1219 )
1220 {
1221 BOOL fBootable = FALSE;
1222 BOOL fAssignLetter = FALSE;
1223
1224 // special work around extended partition
1225 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
1226 {
1227 if ((arc = GetLogicalDrives(pppiFirst,
1228 pppiThis,
1229 posCount,
1230 pcLetter,
1231 &MBoot.sPrtnInfo[i],
1232 PrDisk,
1233 BmInfo)))
1234 return arc;
1235
1236 continue;
1237 }
1238
1239 // raise driver letter if OS/2 would recognize this drive
1240 if ( (MBoot.sPrtnInfo[i].bFileSysCode < 0x75)
1241 )
1242 fAssignLetter = TRUE;
1243
1244 if (fAssignLetter)
1245 (*pcLetter)++;
1246
1247 fBootable = ( (BmInfo)
1248 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
1249 );
1250
1251 if ((arc = AppendPartition(pppiFirst,
1252 pppiThis,
1253 posCount,
1254 PrDisk,
1255 (fBootable)
1256 ? (char*)&MBoot.sBmNames[i].name
1257 : "",
1258 (fAssignLetter)
1259 ? *pcLetter
1260 : ' ',
1261 MBoot.sPrtnInfo[i].bFileSysCode,
1262 FALSE, // primary
1263 fBootable, // bootable
1264 MBoot.sPrtnInfo[i].lTotalSects)))
1265 return arc;
1266 }
1267 }
1268
1269 return NO_ERROR;
1270}
1271
1272/*
1273 * GetExtendedPartition:
1274 * this finds the extended partition on the given
1275 * drive and calls GetLogicalDrives in turn.
1276 *
1277 * This gets called from doshGetPartitionsList.
1278 *
1279 * Originally contributed by Dmitry A. Steklenev.
1280 *
1281 *@@added V0.9.0 [umoeller]
1282 */
1283
1284STATIC APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
1285 PARTITIONINFO **pppiThis,
1286 PUSHORT posCount,
1287 PCHAR pcLetter,
1288 PAR_INFO* BmInfo,
1289 UINT iDisk) // in: disk to query
1290{
1291 APIRET arc = NO_ERROR;
1292 MBR_INFO MBoot; // Master Boot
1293 USHORT i;
1294
1295 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
1296 return arc;
1297
1298 // go thru MBR entries to find extended partition
1299 for (i = 0;
1300 i < 4;
1301 i++)
1302 {
1303 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
1304 {
1305 if ((arc = GetLogicalDrives(pppiFirst,
1306 pppiThis,
1307 posCount,
1308 pcLetter,
1309 &MBoot.sPrtnInfo[i],
1310 iDisk,
1311 BmInfo)))
1312 return arc;
1313 }
1314 }
1315
1316 return NO_ERROR;
1317}
1318
1319/*
1320 *@@ ReadFDiskPartitions:
1321 * helper for doshGetPartitionsList for non-LVM
1322 * systems.
1323 *
1324 * Originally contributed by Dmitry A. Steklenev.
1325 *
1326 *@@added V0.9.16 (2001-10-08) [umoeller]
1327 */
1328
1329STATIC APIRET ReadFDiskPartitions(PARTITIONINFO **ppPartitionInfos,
1330 USHORT *pcPartitions,
1331 PUSHORT pusContext) // out: error context
1332{
1333 APIRET arc = NO_ERROR;
1334
1335 PAR_INFO BmInfo; // BootManager partition
1336 USHORT usBmDisk; // BootManager disk
1337 USHORT cDisks = doshQueryDiskCount(); // physical disks count
1338 USHORT i;
1339
1340 CHAR cLetter = 'C'; // first drive letter
1341
1342 PARTITIONINFO *ppiTemp = NULL;
1343
1344 if (cDisks > 8) // Not above 8 disks
1345 cDisks = 8;
1346
1347 // get boot manager disk and info
1348 if ((arc = doshGetBootManager(&usBmDisk,
1349 NULL,
1350 &BmInfo)) != NO_ERROR)
1351 {
1352 *pusContext = 1;
1353 }
1354 else
1355 {
1356 // on each disk, read primary partitions
1357 for (i = 1; i <= cDisks; i++)
1358 {
1359 if ((arc = GetPrimaryPartitions(ppPartitionInfos,
1360 &ppiTemp,
1361 pcPartitions,
1362 &cLetter,
1363 usBmDisk,
1364 usBmDisk ? &BmInfo : 0,
1365 i)))
1366 {
1367 *pusContext = 2;
1368 }
1369 }
1370
1371 if (!arc && usBmDisk)
1372 {
1373 // boot manager found:
1374 // on each disk, read extended partition
1375 // with logical drives
1376 for (i = 1; i <= cDisks; i++)
1377 {
1378 if ((arc = GetExtendedPartition(ppPartitionInfos,
1379 &ppiTemp,
1380 pcPartitions,
1381 &cLetter,
1382 &BmInfo,
1383 i)))
1384 {
1385 *pusContext = 3;
1386 }
1387 }
1388 }
1389 } // end else if ((arc = doshGetBootManager(&usBmDisk,
1390
1391 return arc;
1392}
1393
1394#endif
1395
1396/*
1397 *@@ CleanPartitionInfos:
1398 *
1399 *@@added V0.9.9 (2001-04-07) [umoeller]
1400 */
1401
1402STATIC VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
1403{
1404 while (ppiThis)
1405 {
1406 PPARTITIONINFO ppiNext = ppiThis->pNext;
1407 free(ppiThis);
1408 ppiThis = ppiNext;
1409 }
1410}
1411
1412/*
1413 *@@ doshGetPartitionsList:
1414 * this returns lots of information about the
1415 * partitions on all physical disks, which is
1416 * read directly from the MBRs and partition
1417 * tables.
1418 *
1419 * If NO_ERROR is returned by this function,
1420 * *ppPartitionInfo points to a linked list of
1421 * PARTITIONINFO structures, which has
1422 * *pusPartitionCount items.
1423 *
1424 * In that case, use doshFreePartitionsList to
1425 * free the resources allocated by this function.
1426 *
1427 * What this function returns depends on whether
1428 * LVM is installed.
1429 *
1430 * -- If LVM.DLL is found on the LIBPATH, this opens
1431 * the LVM engine and returns the info from the
1432 * LVM engine in the PARTITIONINFO structures.
1433 * The partitions are then sorted by disk in
1434 * ascending order.
1435 *
1436 * -- Otherwise, we parse the partition tables
1437 * manually. The linked list then starts out with
1438 * all the primary partitions, followed by the
1439 * logical drives in the extended partitions.
1440 * This function attempts to guess the correct drive
1441 * letters and stores these with the PARTITIONINFO
1442 * items, but there's no guarantee that this is
1443 * correct. We correctly ignore Linux partitions here
1444 * and give all primary partitions the C: letter, but
1445 * I have no idea what happens with NTFS partitions,
1446 * since I have none.
1447 *
1448 * If an error != NO_ERROR is returned, *pusContext
1449 * will be set to one of the following:
1450 *
1451 * -- 1: boot manager not found
1452 *
1453 * -- 2: primary partitions error
1454 *
1455 * -- 3: secondary partitions error
1456 *
1457 * -- 0: something else.
1458 *
1459 * Originally contributed by Dmitry A. Steklenev.
1460 *
1461 *@@added V0.9.0 [umoeller]
1462 *@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
1463 *@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
1464 */
1465
1466APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
1467 PUSHORT pusContext) // out: error context
1468{
1469 APIRET arc = NO_ERROR;
1470
1471 PLVMINFO pLVMInfo = NULL;
1472
1473 PARTITIONINFO *pPartitionInfos = NULL; // linked list of all partitions
1474 USHORT cPartitions = 0; // bootable partition count
1475
1476 if (!ppList)
1477 return ERROR_INVALID_PARAMETER;
1478
1479 if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
1480 {
1481 // LVM installed:
1482 arc = doshReadLVMPartitions(pLVMInfo, // in: LVM info
1483 &pPartitionInfos, // out: partitions array
1484 &cPartitions); // out: partitions count
1485 // copied to output below
1486
1487 if (arc)
1488 {
1489 // error: start over
1490 doshFreeLVMInfo(pLVMInfo);
1491 CleanPartitionInfos(pPartitionInfos);
1492 pPartitionInfos = NULL;
1493 cPartitions = 0;
1494 }
1495 }
1496
1497#ifndef __XWPLITE__
1498 if (arc)
1499 // LVM not installed, or failed:
1500 // parse partitions manually
1501 arc = ReadFDiskPartitions(&pPartitionInfos,
1502 &cPartitions,
1503 pusContext);
1504#endif
1505
1506 if (!arc)
1507 {
1508 // no error so far:
1509 *pusContext = 0;
1510
1511 *ppList = NEW(PARTITIONSLIST);
1512 if (!(*ppList))
1513 arc = ERROR_NOT_ENOUGH_MEMORY;
1514 else
1515 {
1516 ZERO(*ppList);
1517
1518 (*ppList)->pPartitionInfo = pPartitionInfos;
1519 (*ppList)->cPartitions = cPartitions;
1520
1521 _Pmpf((__FUNCTION__ ": returning %d partitions", cPartitions));
1522 }
1523 }
1524
1525 if (arc)
1526 CleanPartitionInfos(pPartitionInfos);
1527
1528 _Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
1529
1530 return arc;
1531}
1532
1533/*
1534 *@@ doshFreePartitionsList:
1535 * this frees the resources allocated by
1536 * doshGetPartitionsList.
1537 *
1538 *@@added V0.9.0 [umoeller]
1539 */
1540
1541APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
1542{
1543 if (!ppList)
1544 return ERROR_INVALID_PARAMETER;
1545
1546 CleanPartitionInfos(ppList->pPartitionInfo);
1547 doshFreeLVMInfo(ppList->pLVMInfo);
1548 free(ppList);
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.