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

Last change on this file since 212 was 200, checked in by umoeller, 23 years ago

More fixes.

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