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

Last change on this file since 231 was 229, checked in by umoeller, 23 years ago

Sources as of 1.0.0.

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