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

Last change on this file since 265 was 265, checked in by pr, 21 years ago

Fixed spelling errors.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 76.6 KB
Line 
1
2/*
3 *@@sourcefile dosh2.c:
4 * dosh.c contains more Control Program helper functions.
5 *
6 * This file is new with V0.9.4 (2000-07-26) [umoeller].
7 *
8 * As opposed to the functions in dosh.c, these require
9 * linking against other helpers. As a result, these have
10 * been separated from dosh.c to allow linking against
11 * dosh.obj only.
12 *
13 * Function prefixes:
14 * -- dosh* Dos (Control Program) helper functions
15 *
16 * This has the same header as dosh.c, dosh.h.
17 *
18 * The partition functions in this file are based on
19 * code which has kindly been provided by Dmitry A. Steklenev.
20 * See doshGetPartitionsList for how to use these.
21 *
22 * Note: Version numbering in this file relates to XWorkplace version
23 * numbering.
24 *
25 *@@header "helpers\dosh.h"
26 *@@added V0.9.4 (2000-07-27) [umoeller]
27 */
28
29/*
30 * This file Copyright (C) 1997-2000 Ulrich M”ller,
31 * Dmitry A. Steklenev.
32 * This file is part of the "XWorkplace helpers" source package.
33 * This is free software; you can redistribute it and/or modify
34 * it under the terms of the GNU General Public License as published
35 * by the Free Software Foundation, in version 2 as it comes in the
36 * "COPYING" file of the XWorkplace main distribution.
37 * This program is distributed in the hope that it will be useful,
38 * but WITHOUT ANY WARRANTY; without even the implied warranty of
39 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40 * GNU General Public License for more details.
41 */
42
43#define OS2EMX_PLAIN_CHAR
44 // this is needed for "os2emx.h"; if this is defined,
45 // emx will define PSZ as _signed_ char, otherwise
46 // as unsigned char
47
48#define INCL_DOSMODULEMGR
49#define INCL_DOSPROCESS
50#define INCL_DOSSESMGR
51#define INCL_DOSQUEUES
52#define INCL_DOSMISC
53#define INCL_DOSDEVICES
54#define INCL_DOSDEVIOCTL
55#define INCL_DOSERRORS
56#include <os2.h>
57
58#include <stdlib.h>
59#include <string.h>
60#include <stdio.h>
61#include <ctype.h>
62
63#include "setup.h" // code generation and debugging options
64
65#include "helpers\dosh.h"
66#include "helpers\ensure.h"
67#include "helpers\nls.h"
68#include "helpers\standards.h"
69#include "helpers\stringh.h"
70
71#pragma hdrstop
72
73/*
74 *@@category: Helpers\Control program helpers\Miscellaneous
75 */
76
77/* ******************************************************************
78 *
79 * Miscellaneous
80 *
81 ********************************************************************/
82
83/*
84 *@@ doshIsValidFileName:
85 * this returns NO_ERROR only if pszFile is a valid file name.
86 * This may include a full path.
87 *
88 * If a drive letter is specified, this checks for whether
89 * that drive is a FAT drive and adjust the checks accordingly,
90 * i.e. 8+3 syntax (per path component).
91 *
92 * If no drive letter is specified, this check is performed
93 * for the current drive.
94 *
95 * This also checks if pszFileNames contains characters which
96 * are invalid for the current drive.
97 *
98 * Note: this performs syntactic checks only. This does not
99 * check for whether the specified path components exist.
100 * However, it _is_ checked for whether the given drive
101 * exists.
102 *
103 * This func is especially useful to check filenames that
104 * have been entered by the user in a "Save as" dialog.
105 *
106 * If an error is found, the corresponding DOS error code
107 * is returned:
108 * -- ERROR_INVALID_DRIVE
109 * -- ERROR_FILENAME_EXCED_RANGE (on FAT: no 8+3 filename)
110 * -- ERROR_INVALID_NAME (invalid character)
111 * -- ERROR_CURRENT_DIRECTORY (if fFullyQualified: no full path specified)
112 *
113 *@@changed V0.9.2 (2000-03-11) [umoeller]: added fFullyQualified
114 */
115
116APIRET doshIsValidFileName(const char* pcszFile,
117 BOOL fFullyQualified) // in: if TRUE, pcszFile must be fully q'fied
118{
119 APIRET arc = NO_ERROR;
120 CHAR szPath[CCHMAXPATH+4] = " :";
121 CHAR szComponent[CCHMAXPATH];
122 PSZ p1, p2;
123 BOOL fIsFAT = FALSE;
124 PSZ pszInvalid;
125
126 if (fFullyQualified) // V0.9.2 (2000-03-11) [umoeller]
127 {
128 if ( (*(pcszFile + 1) != ':')
129 || (*(pcszFile + 2) != '\\')
130 )
131 arc = ERROR_CURRENT_DIRECTORY;
132 }
133
134 // check drive first
135 if (*(pcszFile + 1) == ':')
136 {
137 CHAR cDrive = toupper(*pcszFile);
138 double d;
139 // drive specified:
140 strcpy(szPath, pcszFile);
141 szPath[0] = toupper(*pcszFile);
142 arc = doshQueryDiskFree(cDrive - 'A' + 1, &d);
143 }
144 else
145 {
146 // no drive specified: take current
147 ULONG ulDriveNum = 0,
148 ulDriveMap = 0;
149 arc = DosQueryCurrentDisk(&ulDriveNum, &ulDriveMap);
150 szPath[0] = ((UCHAR)ulDriveNum) + 'A' - 1;
151 szPath[1] = ':';
152 strcpy(&szPath[2], pcszFile);
153 }
154
155 if (arc == NO_ERROR)
156 {
157 fIsFAT = doshIsFileOnFAT(szPath);
158
159 pszInvalid = (fIsFAT)
160 ? "<>|+=:;,\"/[] " // invalid characters in FAT
161 : "<>|:\"/"; // invalid characters in IFS's
162
163 // now separate path components
164 p1 = &szPath[2]; // advance past ':'
165
166 do {
167
168 if (*p1 == '\\')
169 p1++;
170
171 p2 = strchr(p1, '\\');
172 if (p2 == NULL)
173 p2 = p1 + strlen(p1);
174
175 if (p1 != p2)
176 {
177 LONG lDotOfs = -1,
178 lAfterDot = -1;
179 ULONG cbFile,
180 ul;
181 PSZ pSource = szComponent;
182
183 strncpy(szComponent, p1, p2-p1);
184 szComponent[p2-p1] = 0;
185 cbFile = strlen(szComponent);
186
187 // now check each path component
188 for (ul = 0; ul < cbFile; ul++)
189 {
190 if (fIsFAT)
191 {
192 // on FAT: only 8 characters allowed before dot
193 if (*pSource == '.')
194 {
195 lDotOfs = ul;
196 lAfterDot = 0;
197 if (ul > 7)
198 return ERROR_FILENAME_EXCED_RANGE;
199 }
200 }
201 // and check for invalid characters
202 if (strchr(pszInvalid, *pSource) != NULL)
203 return ERROR_INVALID_NAME;
204
205 pSource++;
206
207 // on FAT, allow only three chars after dot
208 if (fIsFAT)
209 if (lAfterDot != -1)
210 {
211 lAfterDot++;
212 if (lAfterDot > 3)
213 return ERROR_FILENAME_EXCED_RANGE;
214 }
215 }
216
217 // we are still missing the case of a FAT file
218 // name without extension; if so, check whether
219 // the file stem is <= 8 chars
220 if (fIsFAT)
221 if (lDotOfs == -1) // dot not found:
222 if (cbFile > 8)
223 return ERROR_FILENAME_EXCED_RANGE;
224 }
225
226 // go for next component
227 p1 = p2+1;
228 } while (*p2);
229 }
230
231 return arc;
232}
233
234/*
235 *@@ doshMakeRealName:
236 * this copies pszSource to pszTarget, replacing
237 * all characters which are not supported by file
238 * systems with cReplace.
239 *
240 * pszTarget must be at least the same size as pszSource.
241 * If (fIsFAT), the file name will be made FAT-compliant (8+3).
242 *
243 * Returns TRUE if characters were replaced.
244 *
245 *@@changed V0.9.0 (99-11-06) [umoeller]: now replacing "*" too
246 */
247
248BOOL doshMakeRealName(PSZ pszTarget, // out: new real name
249 PSZ pszSource, // in: filename to translate
250 CHAR cReplace, // in: replacement char for invalid
251 // characters (e.g. '!')
252 BOOL fIsFAT) // in: make-FAT-compatible flag
253{
254 ULONG ul,
255 cbSource = strlen(pszSource);
256 LONG lDotOfs = -1,
257 lAfterDot = -1;
258 BOOL brc = FALSE;
259 PSZ pSource = pszSource,
260 pTarget = pszTarget;
261
262 const char *pcszInvalid = (fIsFAT)
263 ? "*<>|+=:;,\"/\\[] " // invalid characters in FAT
264 : "*<>|:\"/\\"; // invalid characters in IFS's
265
266 for (ul = 0; ul < cbSource; ul++)
267 {
268 if (fIsFAT)
269 {
270 // on FAT: truncate filename if neccessary
271 if (*pSource == '.')
272 {
273 lDotOfs = ul;
274 lAfterDot = 0;
275 if (ul > 7) {
276 // only 8 characters allowed before dot,
277 // so set target ptr to dot pos
278 pTarget = pszTarget+8;
279 }
280 }
281 }
282 // and replace invalid characters
283 if (strchr(pcszInvalid, *pSource) == NULL)
284 *pTarget = *pSource;
285 else
286 {
287 *pTarget = cReplace;
288 brc = TRUE;
289 }
290 pTarget++;
291 pSource++;
292
293 // on FAT, allow only three chars after dot
294 if (fIsFAT)
295 if (lAfterDot != -1)
296 {
297 lAfterDot++;
298 if (lAfterDot > 3)
299 break;
300 }
301 }
302 *pTarget = '\0';
303
304 if (fIsFAT)
305 {
306 // we are still missing the case of a FAT file
307 // name without extension; if so, check whether
308 // the file stem is <= 8 chars
309 if (lDotOfs == -1) // dot not found:
310 if (cbSource > 8)
311 *(pszTarget+8) = 0; // truncate
312
313 // convert to upper case
314 strupr(pszTarget);
315 }
316
317 return brc;
318}
319
320/*
321 *@@ doshSetCurrentDir:
322 * sets the current working directory
323 * to the given path.
324 *
325 * As opposed to DosSetCurrentDir, this
326 * one will change the current drive
327 * also, if one is specified.
328 *
329 *@@changed V0.9.9 (2001-04-04) [umoeller]: this returned an error even if none occurred, fixed
330 */
331
332APIRET doshSetCurrentDir(const char *pcszDir)
333{
334 APIRET arc = NO_ERROR;
335
336 if ( (!pcszDir)
337 || (!(*pcszDir))
338 )
339 return ERROR_INVALID_PARAMETER;
340
341 if (pcszDir[1] == ':')
342 {
343 // drive given:
344 CHAR cDrive = toupper(*(pcszDir));
345 // change drive
346 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
347 // 1 = A:, 2 = B:, ...
348 }
349
350 if (!arc)
351 arc = DosSetCurrentDir((PSZ)pcszDir);
352
353 return arc; // V0.9.9 (2001-04-04) [umoeller]
354}
355
356/*
357 *@@ CopyToBuffer:
358 * little helper for copying a string to
359 * a target buffer with length checking.
360 *
361 * Returns:
362 *
363 * -- NO_ERROR
364 *
365 * -- ERROR_BUFFER_OVERFLOW if pszTarget does
366 * not have enough room to hold pcszSource
367 * (including the null terminator).
368 *
369 *@@added V0.9.16 (2001-10-08) [umoeller]
370 */
371
372STATIC APIRET CopyToBuffer(PSZ pszTarget, // out: target buffer
373 PCSZ pcszSource, // in: source string
374 ULONG cbTarget) // in: size of target buffer
375{
376 ULONG ulLength = strlen(pcszSource);
377 if (ulLength < cbTarget)
378 {
379 memcpy(pszTarget,
380 pcszSource,
381 ulLength + 1);
382 return NO_ERROR;
383 }
384
385 return ERROR_BUFFER_OVERFLOW;
386}
387
388/*
389 *@@ doshSearchPath:
390 * replacement for DosSearchPath.
391 *
392 * This looks along all directories which are
393 * specified in the value of the given environment
394 * variable if pcszFile is found.
395 *
396 * As opposed to the stupid DosSearchPath, this
397 * ignores subdirectories in the path particles.
398 * For example, DosSearchPath would usually not
399 * find an INSTALL file because \OS2 contains
400 * an INSTALL directory, or NETSCAPE because
401 * \OS2\INSTALL contains a NETSCAPE directory.
402 *
403 * Returns:
404 *
405 * -- NO_ERROR: pszExecutable has received the
406 * full path of pcszFile.
407 *
408 * -- ERROR_FILE_NOT_FOUND: pcszFile was not found
409 * in the specified path (or is a directory).
410 *
411 * -- ERROR_BUFFER_OVERFLOW: pcszFile was found, but
412 * the pszExecutable buffer is too small to hold
413 * the full path.
414 *
415 *@@added V0.9.16 (2001-10-08) [umoeller]
416 *@@changed V1.0.0 (2002-11-23) [umoeller]: allowing NULL pcszPath to search for "PATH"
417 */
418
419APIRET doshSearchPath(const char *pcszPath, // in: path variable name; if NULL, we use "PATH"
420 const char *pcszFile, // in: file to look for (e.g. "LVM.EXE")
421 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
422 ULONG cbExecutable) // in: sizeof (*pszExecutable)
423{
424 APIRET arc = NO_ERROR;
425
426 // get the PATH value
427 PCSZ pcszPathValue;
428
429 if (!pcszPath)
430 pcszPath = "PATH"; // V1.0.0 (2002-11-23) [umoeller]
431
432 if (!(arc = DosScanEnv((PSZ)pcszPath,
433#if __cplusplus
434 &pcszPathValue)))
435#else
436 (PSZ*)&pcszPathValue)))
437#endif
438 {
439 // run thru the path components
440 PSZ pszPathCopy;
441 if (pszPathCopy = strdup(pcszPathValue))
442 {
443 PSZ pszToken = strtok(pszPathCopy, ";");
444 while (pszToken)
445 {
446 CHAR szFileMask[2*CCHMAXPATH];
447 FILESTATUS3 fs3;
448
449 sprintf(szFileMask,
450 "%s\\%s",
451 pszToken, // path particle
452 pcszFile); // e.g. "netscape"
453
454 if ( (!(arc = DosQueryPathInfo(szFileMask,
455 FIL_STANDARD,
456 &fs3,
457 sizeof(fs3))))
458 // make sure it's not a directory
459 // and that it's not hidden
460 && (!(fs3.attrFile & (FILE_DIRECTORY | FILE_HIDDEN)))
461 )
462 {
463 // copy
464 arc = CopyToBuffer(pszExecutable,
465 szFileMask,
466 cbExecutable);
467 // and stop
468 break;
469 }
470 else
471 arc = ERROR_FILE_NOT_FOUND;
472 // and search on
473
474 pszToken = strtok(NULL, ";");
475 };
476
477 free(pszPathCopy);
478 }
479 else
480 arc = ERROR_NOT_ENOUGH_MEMORY;
481 }
482
483 return arc;
484}
485
486/*
487 * FindFile:
488 * helper for doshFindExecutable.
489 *
490 *added V0.9.11 (2001-04-25) [umoeller]
491 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewrote second half for DosSearchPath replacement, which returns directories too
492 */
493
494STATIC APIRET FindFile(const char *pcszCommand, // in: command (e.g. "lvm")
495 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
496 ULONG cbExecutable) // in: sizeof (*pszExecutable)
497{
498 APIRET arc = NO_ERROR;
499 FILESTATUS3 fs3;
500
501 if ( (strchr(pcszCommand, '\\'))
502 || (strchr(pcszCommand, ':'))
503 )
504 {
505 // looks like this is qualified:
506 arc = DosQueryPathInfo((PSZ)pcszCommand,
507 FIL_STANDARD,
508 &fs3,
509 sizeof(fs3));
510 if (!arc)
511 if (!(fs3.attrFile & FILE_DIRECTORY))
512 arc = CopyToBuffer(pszExecutable,
513 pcszCommand,
514 cbExecutable);
515 else
516 // directory:
517 arc = ERROR_INVALID_EXE_SIGNATURE;
518 }
519 else
520 {
521 // non-qualified:
522 /* arc = DosSearchPath(SEARCH_IGNORENETERRS
523 | SEARCH_ENVIRONMENT
524 | SEARCH_CUR_DIRECTORY,
525 "PATH",
526 (PSZ)pcszCommand,
527 pszExecutable,
528 cbExecutable); */
529 // The above is not useable. It returns directories
530 // on the path... for example, it returns \OS2\INSTALL\NETSCAPE
531 // if netscape is looked for. So we search manually... sigh.
532 // V0.9.16 (2001-10-08) [umoeller]
533 arc = doshSearchPath("PATH",
534 pcszCommand,
535 pszExecutable,
536 cbExecutable);
537 }
538
539 return arc;
540}
541
542/*
543 *@@ doshFindExecutable:
544 * this attempts to find an executable by doing the
545 * following:
546 *
547 * 1) If pcszCommand appears to be qualified (i.e. contains
548 * a backslash), this checks for whether the file exists.
549 * If it is a directory, ERROR_INVALID_EXE_SIGNATURE is
550 * returned.
551 *
552 * 2) If pcszCommand contains no backslash, this searches
553 * all directories on the PATH in order to find the full
554 * path of the executable. Starting with V0.9.16, we
555 * use doshSearchPath for that.
556 *
557 * papcszExtensions determines if additional searches are to be
558 * performed if the file doesn't exist (case 1) or doshSearchPath
559 * returned ERROR_FILE_NOT_FOUND (case 2).
560 * This must point to an array of strings specifying the extra
561 * extensions to search for.
562 *
563 * If both papcszExtensions and cExtensions are null, no
564 * extra searches are performed.
565 *
566 * Returns:
567 *
568 * -- NO_ERROR: pszExecutable has received the full path of
569 * the executable found by DosSearchPath.
570 *
571 * -- ERROR_FILE_NOT_FOUND
572 *
573 * -- ERROR_BUFFER_OVERFLOW: pcszCommand was found, but
574 * the pszExecutable buffer is too small to hold
575 * the full path.
576 *
577 * Example:
578 *
579 + const char *aExtensions[] = { "EXE",
580 + "COM",
581 + "CMD"
582 + };
583 + CHAR szExecutable[CCHMAXPATH];
584 + APIRET arc = doshFindExecutable("lvm",
585 + szExecutable,
586 + sizeof(szExecutable),
587 + aExtensions,
588 + 3);
589 *
590 *@@added V0.9.9 (2001-03-07) [umoeller]
591 *@@changed V0.9.11 (2001-04-25) [umoeller]: this never worked for qualified pcszCommand's, fixed
592 */
593
594APIRET doshFindExecutable(const char *pcszCommand, // in: command (e.g. "lvm")
595 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
596 ULONG cbExecutable, // in: sizeof (*pszExecutable)
597 const char **papcszExtensions, // in: array of extensions (without dots)
598 ULONG cExtensions) // in: array item count
599{
600 APIRET arc = FindFile(pcszCommand,
601 pszExecutable,
602 cbExecutable);
603
604 if ( (arc == ERROR_FILE_NOT_FOUND) // not found?
605 && (cExtensions) // any extra searches wanted?
606 )
607 {
608 // try additional things then
609 PSZ psz2;
610 if (psz2 = (PSZ)malloc(strlen(pcszCommand) + 20))
611 {
612 ULONG ul;
613 for (ul = 0;
614 ul < cExtensions;
615 ul++)
616 {
617 const char *pcszExtThis = papcszExtensions[ul];
618 sprintf(psz2,
619 "%s.%s",
620 pcszCommand,
621 pcszExtThis);
622 arc = FindFile(psz2,
623 pszExecutable,
624 cbExecutable);
625 if (arc != ERROR_FILE_NOT_FOUND)
626 break;
627 }
628
629 free(psz2);
630 }
631 else
632 arc = ERROR_NOT_ENOUGH_MEMORY;
633 }
634
635 return arc;
636}
637
638/*
639 *@@category: Helpers\Control program helpers\Partitions info
640 * functions for retrieving partition information directly
641 * from the partition tables on the disk. See doshGetPartitionsList.
642 */
643
644/********************************************************************
645 *
646 * Partition functions
647 *
648 ********************************************************************/
649
650/*
651 *@@ doshQueryDiskCount:
652 * returns the no. of physical disks installed
653 * on the system.
654 *
655 *@@added V0.9.0 [umoeller]
656 */
657
658UINT doshQueryDiskCount(VOID)
659{
660 USHORT usCount = 0;
661 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &usCount, 2, 0, 0);
662 return usCount;
663}
664
665/*
666 *@@ doshType2FSName:
667 * this returns a static, zero-terminated string
668 * for the given FS type, or NULL if the type
669 * is unknown.
670 *
671 *@@added V0.9.0 [umoeller]
672 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewritten
673 */
674
675const char* doshType2FSName(unsigned char bFSType) // in: FS type
676{
677 switch (bFSType)
678 {
679 case 0x00: return "empty";
680 case 0x01: return "DOS 12-bit FAT < 10 Mb";
681 case 0x02: return "XENIX root file system";
682 case 0x03: return "XENIX /usr file system (obsolete)";
683 case 0x04: return "DOS 16-bit FAT < 32 Mb";
684 case 0x05: return "DOS 3.3+ extended partition";
685 case 0x06: return "DOS 3.31+ 16-bit FAT > 32 Mb";
686 case 0x07: return "HPFS/NTFS/QNX/Advanced Unix";
687 case 0x08: return "OS/2 1.0-1.3/AIX/Commodore/DELL";
688 case 0x09: return "AIX data/Coherent";
689 case 0x0A: return "OS/2 Boot Manager/OPUS/Coherent Swap";
690 case 0x0B: return "Windows95 with 32-bit FAT";
691 case 0x0C: return "Windows95 with 32-bit FAT (LBA)";
692 case 0x0E: return "Windows 95 VFAT (06h plus LBA)";
693 case 0x0F: return "Windows 95 VFAT (05h plus LBA)";
694 case 0x10: return "OPUS";
695 case 0x11: return "OS/2 Boot Manager hidden 12-bit FAT";
696 case 0x12: return "Compaq Diagnostics";
697 case 0x14: return "OS/2 Boot Manager hidden sub-32M 16-bit FAT";
698 case 0x16: return "OS/2 Boot Manager hidden over-32M 16-bit FAT";
699 case 0x17: return "OS/2 Boot Manager hidden HPFS";
700 case 0x18: return "AST special Windows swap file (\"Zero-Volt Suspend\")";
701 // case 0x21: reserved
702 // case 0x23: reserved
703 case 0x24: return "NEC MS-DOS 3.x";
704 // case 0x26: reserved
705 // case 0x31: reserved
706 // case 0x33: reserved
707 // case 0x34: reserved
708 // case 0x36: reserved
709 case 0x38: return "Theos";
710 case 0x3C: return "PowerQuest PartitionMagic recovery partition";
711 case 0x40: return "VENIX 80286";
712 case 0x41: return "Personal RISC Boot";
713 case 0x42: return "SFS (Secure File System) by Peter Gutmann";
714 case 0x50: return "OnTrack Disk Manager, read-only";
715 case 0x51: return "OnTrack Disk Manager, read/write";
716 case 0x52: return "CP/M or Microport System V/386";
717 case 0x53: return "OnTrack Disk Manager, write-only???";
718 case 0x54: return "OnTrack Disk Manager (DDO)";
719 case 0x56: return "GoldenBow VFeature";
720 case 0x61: return "SpeedStor";
721 case 0x63: return "Unix SysV/386, 386/ix or Mach, MtXinu BSD 4.3 on Mach or GNU HURD";
722 case 0x64: return "Novell NetWare 286";
723 case 0x65: return "Novell NetWare (3.11)";
724 case 0x67:
725 case 0x68:
726 case 0x69: return "Novell";
727 case 0x70: return "DiskSecure Multi-Boot";
728 // case 0x71: reserved
729 // case 0x73: reserved
730 // case 0x74: reserved
731 case 0x75: return "PC/IX";
732 // case 0x76: reserved
733 case 0x80: return "Minix v1.1 - 1.4a";
734 case 0x81: return "Minix v1.4b+ or Linux or Mitac Advanced Disk Manager";
735 case 0x82: return "Linux Swap or Prime";
736 case 0x83: return "Linux native file system (ext2fs/xiafs)";
737 case 0x84: return "OS/2-renumbered type 04h (hidden DOS C: drive)";
738 case 0x86: return "FAT16 volume/stripe set (Windows NT)";
739 case 0x87: return "HPFS Fault-Tolerant mirrored partition or NTFS volume/stripe set";
740 case 0x93: return "Amoeba file system";
741 case 0x94: return "Amoeba bad block table";
742 case 0xA0: return "Phoenix NoteBIOS Power Management \"Save-to-Disk\" partition";
743 // case 0xA1: reserved
744 // case 0xA3: reserved
745 // case 0xA4: reserved
746 case 0xA5: return "FreeBSD, BSD/386";
747 // case 0xA6: reserved
748 // case 0xB1: reserved
749 // case 0xB3: reserved
750 // case 0xB4: reserved
751 // case 0xB6: reserved
752 case 0xB7: return "BSDI file system (secondarily swap)";
753 case 0xB8: return "BSDI swap (secondarily file system)";
754 case 0xC1: return "DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT";
755 case 0xC4: return "DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT";
756 case 0xC6: return "DR DOS 6.0 LOGIN.EXE-secured Huge partition or NT corrupted FAT16 volume/stripe set";
757 case 0xC7: return "Syrinx Boot or corrupted NTFS volume/stripe set";
758 case 0xD8: return "CP/M-86";
759 case 0xDB: return "CP/M, Concurrent CP/M, Concurrent DOS, Convergent Technologies OS";
760 case 0xE1: return "SpeedStor 12-bit FAT extended partition";
761 case 0xE3: return "DOS read-only or Storage Dimensions";
762 case 0xE4: return "SpeedStor 16-bit FAT extended partition";
763 // case 0xE5: reserved
764 // case 0xE6: reserved
765 case 0xF1: return "Storage Dimensions";
766 case 0xF2: return "DOS 3.3+ secondary partition";
767 // case 0xF3: reserved
768 case 0xF4: return "SpeedStor or Storage Dimensions";
769 // case 0xF6: reserved
770 case 0xFE: return "LANstep or IBM PS/2 IML";
771 case 0xFF: return "Xenix bad block table";
772 }
773
774 return NULL;
775}
776
777/*
778 * AppendPartition:
779 * this appends the given partition information to
780 * the given partition list. To do this, a new
781 * PARTITIONINFO structure is created and appended
782 * in a list (managed thru the PARTITIONINFO.pNext
783 * items).
784 *
785 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
786 * With each call of this function, this pointer is advanced
787 * to point to the newly created PARTITIONINFO, so before
788 * calling this function for the first time,
789 *
790 *@@added V0.9.0 [umoeller]
791 *@@changed V0.9.20 (2002-08-10) [umoeller]: fixed truncated LVM names
792 */
793
794STATIC APIRET AppendPartition(PARTITIONINFO **pppiFirst,
795 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
796 PUSHORT posCount, // in/out: partition count
797 BYTE bDisk, // in: disk of partition
798 const char *pszBootName, // in: boot partition name
799 CHAR cLetter, // in/out: drive letter
800 BYTE bFsType, // in: file system type
801 BOOL fPrimary, // in: primary?
802 BOOL fBootable,
803 ULONG ulSectors) // in: no. of sectors
804{
805 APIRET arc = NO_ERROR;
806 PPARTITIONINFO ppiNew;
807 if (ppiNew = NEW(PARTITIONINFO))
808 {
809 ZERO(ppiNew);
810
811 // store data
812 ppiNew->bDisk = bDisk;
813 if ( (fBootable)
814 && (pszBootName)
815 )
816 {
817 // fixed truncated LVM names V0.9.20 (2002-08-10) [umoeller]
818 strhncpy0(ppiNew->szBootName,
819 pszBootName,
820 sizeof(ppiNew->szBootName));
821 }
822 else
823 ppiNew->szBootName[0] = 0;
824 ppiNew->cLetter = cLetter;
825 ppiNew->bFSType = bFsType;
826 ppiNew->pcszFSType = doshType2FSName(bFsType);
827 ppiNew->fPrimary = fPrimary;
828 ppiNew->fBootable = fBootable;
829 ppiNew->ulSize = ulSectors / 2048;
830
831 ppiNew->pNext = NULL;
832
833 (*posCount)++;
834
835 if (*pppiFirst == (PPARTITIONINFO)NULL)
836 {
837 // first call:
838 *pppiFirst = ppiNew;
839 *pppiThis = ppiNew;
840 }
841 else
842 {
843 // append to list
844 (**pppiThis).pNext = ppiNew;
845 *pppiThis = ppiNew;
846 }
847 }
848 else
849 arc = ERROR_NOT_ENOUGH_MEMORY;
850
851 return arc;
852}
853
854#ifndef __XWPLITE__
855
856/*
857 *@@ doshReadSector:
858 * reads a physical disk sector.
859 *
860 * If NO_ERROR is returned, the sector contents
861 * have been stored in *buff.
862 *
863 * Originally contributed by Dmitry A. Steklenev.
864 *
865 *@@added V0.9.0 [umoeller]
866 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
867 */
868
869APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
870 void *buff,
871 USHORT head,
872 USHORT cylinder,
873 USHORT sector)
874{
875 APIRET arc;
876 HFILE dh = 0;
877 char dn[256];
878
879 sprintf(dn, "%u:", disk);
880 if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
881 {
882 TRACKLAYOUT DiskIOParm;
883 ULONG IOCtlDataLength = sizeof(DiskIOParm);
884 ULONG IOCtlParmLength = 512;
885
886 DiskIOParm.bCommand = 0;
887 DiskIOParm.usHead = head;
888 DiskIOParm.usCylinder = cylinder;
889 DiskIOParm.usFirstSector = 0;
890 DiskIOParm.cSectors = 1;
891 DiskIOParm.TrackTable[0].usSectorNumber = sector;
892 DiskIOParm.TrackTable[0].usSectorSize = 512;
893
894 arc = DosDevIOCtl(dh,
895 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
896 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
897 buff , IOCtlDataLength, &IOCtlDataLength);
898
899 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
900 }
901
902 return arc;
903}
904
905// Sector and Cylinder values are actually 6 bits and 10 bits:
906//
907// 1 1 1 1 1 1
908// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
909// ³c c c c c c c c C c S s s s s s³
910// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
911//
912// The high two bits of the second byte are used as the high bits
913// of a 10-bit value. This allows for as many as 1024 cylinders
914// and 64 sectors per cylinder.
915
916/*
917 * GetCyl:
918 * get cylinder number.
919 *
920 * Originally contributed by Dmitry A. Steklenev.
921 *
922 *@@added V0.9.0 [umoeller]
923 */
924
925STATIC USHORT GetCyl(USHORT rBeginSecCyl)
926{
927 return ((rBeginSecCyl & 0x00C0) << 2)
928 + ((rBeginSecCyl & 0xFF00) >> 8);
929}
930
931/*
932 * GetSec:
933 * get sector number.
934 *
935 * Originally contributed by Dmitry A. Steklenev.
936 *
937 *@@added V0.9.0 [umoeller]
938 */
939
940STATIC USHORT GetSec(USHORT rBeginSecCyl)
941{
942 return rBeginSecCyl & 0x003F;
943}
944
945/*
946 *@@ doshGetBootManager:
947 * this goes thru the master boot records on all
948 * disks to find the boot manager partition.
949 *
950 * Returns:
951 *
952 * -- NO_ERROR: boot manager found; in that case,
953 * information about the boot manager
954 * is written into *pusDisk, *pusPart,
955 * *BmInfo. Any of these pointers can
956 * be NULL if you're not interested.
957 *
958 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
959 *
960 * Originally contributed by Dmitry A. Steklenev.
961 *
962 *@@added V0.9.0 [umoeller]
963 */
964
965APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
966 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
967 PAR_INFO *pBmInfo) // out: if != NULL, boot manager partition info
968{
969 APIRET arc = NO_ERROR;
970 USHORT count = doshQueryDiskCount(); // Physical disk number
971 MBR_INFO MBoot; // Master Boot
972 USHORT usDisk;
973
974 if (count > 8) // Not above 8 disks
975 count = 8;
976
977 for (usDisk = 1; usDisk <= count; usDisk++)
978 {
979 USHORT usPrim = 0;
980
981 // for each disk, read the MBR, which has the
982 // primary partitions
983 if ((arc = doshReadSector(usDisk,
984 &MBoot,
985 0, // head
986 0, // cylinder
987 1))) // sector
988 return arc;
989
990 // scan primary partitions for whether
991 // BootManager partition exists
992 for (usPrim = 0; usPrim < 4; usPrim++)
993 {
994 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
995 {
996 // this is boot manager:
997 if (pBmInfo)
998 *pBmInfo = MBoot.sPrtnInfo[usPrim];
999 if (pusPart)
1000 *pusPart = usPrim;
1001 if (pusDisk)
1002 *pusDisk = usDisk;
1003 // stop scanning
1004 return NO_ERROR;
1005 }
1006 }
1007 }
1008
1009 return ERROR_NOT_SUPPORTED;
1010}
1011
1012/*
1013 * GetPrimaryPartitions:
1014 * this returns the primary partitions.
1015 *
1016 * This gets called from doshGetPartitionsList.
1017 *
1018 * Returns:
1019 *
1020 * -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
1021 *
1022 * Originally contributed by Dmitry A. Steklenev.
1023 *
1024 *@@added V0.9.0 [umoeller]
1025 */
1026
1027STATIC APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
1028 PARTITIONINFO **pppiThis,
1029 PUSHORT posCount, // in/out: partition count
1030 PCHAR pcLetter, // in/out: drive letter counter
1031 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
1032 PAR_INFO* pBmInfo, // in: info returned by doshGetBootManager or NULL
1033 UINT iDisk) // in: system's physical disk count
1034{
1035 APIRET arc = NO_ERROR;
1036
1037 if (!pBmInfo)
1038 arc = ERROR_INVALID_PARAMETER;
1039 else
1040 {
1041 SYS_INFO MName[32]; // Name Space from Boot Manager
1042 memset(&MName, 0, sizeof(MName));
1043
1044 // read boot manager name table;
1045 // this is in the boot manager primary partition
1046 // at sector offset 3 (?!?)
1047 if (!(arc = doshReadSector(BmDisk,
1048 &MName,
1049 // head, cylinder, sector of bmgr primary partition:
1050 pBmInfo->bBeginHead,
1051 GetCyl(pBmInfo->rBeginSecCyl),
1052 GetSec(pBmInfo->rBeginSecCyl) + 3)))
1053 {
1054 // got bmgr name table:
1055 MBR_INFO MBoot; // Master Boot
1056 USHORT i;
1057
1058 // read master boot record of this disk
1059 if (!(arc = doshReadSector(iDisk,
1060 &MBoot,
1061 0, // head
1062 0, // cylinder
1063 1))) // sector
1064 {
1065 for (i = 0;
1066 i < 4; // there can be only four primary partitions
1067 i++)
1068 {
1069 // skip unused partition, BootManager or Extended partition
1070 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
1071 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A) // skip boot manager
1072 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x05) // skip extended partition
1073 )
1074 {
1075 BOOL fBootable = ( (pBmInfo)
1076 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
1077 );
1078 // store this partition
1079 if ((arc = AppendPartition(pppiFirst,
1080 pppiThis,
1081 posCount,
1082 iDisk,
1083 (fBootable)
1084 ? (char*)&MName[(iDisk - 1) * 4 + i].name
1085 : "",
1086 *pcLetter,
1087 MBoot.sPrtnInfo[i].bFileSysCode,
1088 TRUE, // primary
1089 fBootable,
1090 MBoot.sPrtnInfo[i].lTotalSects)))
1091 return arc;
1092 }
1093 }
1094 }
1095 }
1096 }
1097
1098 return arc;
1099}
1100
1101/*
1102 * GetLogicalDrives:
1103 * this returns info for the logical drives
1104 * in the extended partition. This gets called
1105 * from GetExtendedPartition.
1106 *
1107 * This gets called from GetExtendedPartition.
1108 *
1109 * Originally contributed by Dmitry A. Steklenev.
1110 *
1111 *@@added V0.9.0 [umoeller]
1112 */
1113
1114STATIC APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
1115 PARTITIONINFO **pppiThis,
1116 PUSHORT posCount,
1117 PCHAR pcLetter,
1118 PAR_INFO* PrInfo, // in: MBR entry of extended partition
1119 UINT PrDisk,
1120 PAR_INFO* BmInfo)
1121{
1122 APIRET arc = NO_ERROR;
1123 EXT_INFO MBoot; // Master Boot
1124 USHORT i;
1125
1126 if ((arc = doshReadSector(PrDisk,
1127 &MBoot,
1128 PrInfo->bBeginHead,
1129 GetCyl(PrInfo->rBeginSecCyl),
1130 GetSec(PrInfo->rBeginSecCyl))))
1131 return arc;
1132
1133 for (i = 0; i < 4; i++)
1134 {
1135 // skip unused partition or BootManager partition
1136 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
1137 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A)
1138 )
1139 {
1140 BOOL fBootable = FALSE;
1141 BOOL fAssignLetter = FALSE;
1142
1143 // special work around extended partition
1144 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
1145 {
1146 if ((arc = GetLogicalDrives(pppiFirst,
1147 pppiThis,
1148 posCount,
1149 pcLetter,
1150 &MBoot.sPrtnInfo[i],
1151 PrDisk,
1152 BmInfo)))
1153 return arc;
1154
1155 continue;
1156 }
1157
1158 // raise driver letter if OS/2 would recognize this drive
1159 if ( (MBoot.sPrtnInfo[i].bFileSysCode < 0x75)
1160 )
1161 fAssignLetter = TRUE;
1162
1163 if (fAssignLetter)
1164 (*pcLetter)++;
1165
1166 fBootable = ( (BmInfo)
1167 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
1168 );
1169
1170 if ((arc = AppendPartition(pppiFirst,
1171 pppiThis,
1172 posCount,
1173 PrDisk,
1174 (fBootable)
1175 ? (char*)&MBoot.sBmNames[i].name
1176 : "",
1177 (fAssignLetter)
1178 ? *pcLetter
1179 : ' ',
1180 MBoot.sPrtnInfo[i].bFileSysCode,
1181 FALSE, // primary
1182 fBootable, // bootable
1183 MBoot.sPrtnInfo[i].lTotalSects)))
1184 return arc;
1185 }
1186 }
1187
1188 return NO_ERROR;
1189}
1190
1191/*
1192 * GetExtendedPartition:
1193 * this finds the extended partition on the given
1194 * drive and calls GetLogicalDrives in turn.
1195 *
1196 * This gets called from doshGetPartitionsList.
1197 *
1198 * Originally contributed by Dmitry A. Steklenev.
1199 *
1200 *@@added V0.9.0 [umoeller]
1201 */
1202
1203STATIC APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
1204 PARTITIONINFO **pppiThis,
1205 PUSHORT posCount,
1206 PCHAR pcLetter,
1207 PAR_INFO* BmInfo,
1208 UINT iDisk) // in: disk to query
1209{
1210 APIRET arc = NO_ERROR;
1211 MBR_INFO MBoot; // Master Boot
1212 USHORT i;
1213
1214 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
1215 return arc;
1216
1217 // go thru MBR entries to find extended partition
1218 for (i = 0;
1219 i < 4;
1220 i++)
1221 {
1222 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
1223 {
1224 if ((arc = GetLogicalDrives(pppiFirst,
1225 pppiThis,
1226 posCount,
1227 pcLetter,
1228 &MBoot.sPrtnInfo[i],
1229 iDisk,
1230 BmInfo)))
1231 return arc;
1232 }
1233 }
1234
1235 return NO_ERROR;
1236}
1237
1238/*
1239 *@@ ReadFDiskPartitions:
1240 * helper for doshGetPartitionsList for non-LVM
1241 * systems.
1242 *
1243 * Originally contributed by Dmitry A. Steklenev.
1244 *
1245 *@@added V0.9.16 (2001-10-08) [umoeller]
1246 */
1247
1248STATIC APIRET ReadFDiskPartitions(PARTITIONINFO **ppPartitionInfos,
1249 USHORT *pcPartitions,
1250 PUSHORT pusContext) // out: error context
1251{
1252 APIRET arc = NO_ERROR;
1253
1254 PAR_INFO BmInfo; // BootManager partition
1255 USHORT usBmDisk; // BootManager disk
1256 USHORT cDisks = doshQueryDiskCount(); // physical disks count
1257 USHORT i;
1258
1259 CHAR cLetter = 'C'; // first drive letter
1260
1261 PARTITIONINFO *ppiTemp = NULL;
1262
1263 if (cDisks > 8) // Not above 8 disks
1264 cDisks = 8;
1265
1266 // get boot manager disk and info
1267 if ((arc = doshGetBootManager(&usBmDisk,
1268 NULL,
1269 &BmInfo)) != NO_ERROR)
1270 {
1271 *pusContext = 1;
1272 }
1273 else
1274 {
1275 // on each disk, read primary partitions
1276 for (i = 1; i <= cDisks; i++)
1277 {
1278 if ((arc = GetPrimaryPartitions(ppPartitionInfos,
1279 &ppiTemp,
1280 pcPartitions,
1281 &cLetter,
1282 usBmDisk,
1283 usBmDisk ? &BmInfo : 0,
1284 i)))
1285 {
1286 *pusContext = 2;
1287 }
1288 }
1289
1290 if (!arc && usBmDisk)
1291 {
1292 // boot manager found:
1293 // on each disk, read extended partition
1294 // with logical drives
1295 for (i = 1; i <= cDisks; i++)
1296 {
1297 if ((arc = GetExtendedPartition(ppPartitionInfos,
1298 &ppiTemp,
1299 pcPartitions,
1300 &cLetter,
1301 &BmInfo,
1302 i)))
1303 {
1304 *pusContext = 3;
1305 }
1306 }
1307 }
1308 } // end else if ((arc = doshGetBootManager(&usBmDisk,
1309
1310 return arc;
1311}
1312
1313#endif
1314
1315/*
1316 *@@ CleanPartitionInfos:
1317 *
1318 *@@added V0.9.9 (2001-04-07) [umoeller]
1319 */
1320
1321STATIC VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
1322{
1323 while (ppiThis)
1324 {
1325 PPARTITIONINFO ppiNext = ppiThis->pNext;
1326 free(ppiThis);
1327 ppiThis = ppiNext;
1328 }
1329}
1330
1331/*
1332 *@@ doshGetPartitionsList:
1333 * this returns lots of information about the
1334 * partitions on all physical disks, which is
1335 * read directly from the MBRs and partition
1336 * tables.
1337 *
1338 * If NO_ERROR is returned by this function,
1339 * *ppPartitionInfo points to a linked list of
1340 * PARTITIONINFO structures, which has
1341 * *pusPartitionCount items.
1342 *
1343 * In that case, use doshFreePartitionsList to
1344 * free the resources allocated by this function.
1345 *
1346 * What this function returns depends on whether
1347 * LVM is installed.
1348 *
1349 * -- If LVM.DLL is found on the LIBPATH, this opens
1350 * the LVM engine and returns the info from the
1351 * LVM engine in the PARTITIONINFO structures.
1352 * The partitions are then sorted by disk in
1353 * ascending order.
1354 *
1355 * -- Otherwise, we parse the partition tables
1356 * manually. The linked list then starts out with
1357 * all the primary partitions, followed by the
1358 * logical drives in the extended partitions.
1359 * This function attempts to guess the correct drive
1360 * letters and stores these with the PARTITIONINFO
1361 * items, but there's no guarantee that this is
1362 * correct. We correctly ignore Linux partitions here
1363 * and give all primary partitions the C: letter, but
1364 * I have no idea what happens with NTFS partitions,
1365 * since I have none.
1366 *
1367 * If an error != NO_ERROR is returned, *pusContext
1368 * will be set to one of the following:
1369 *
1370 * -- 1: boot manager not found
1371 *
1372 * -- 2: primary partitions error
1373 *
1374 * -- 3: secondary partitions error
1375 *
1376 * -- 0: something else.
1377 *
1378 * Originally contributed by Dmitry A. Steklenev.
1379 *
1380 *@@added V0.9.0 [umoeller]
1381 *@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
1382 *@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
1383 */
1384
1385APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
1386 PUSHORT pusContext) // out: error context
1387{
1388 APIRET arc = NO_ERROR;
1389
1390 PLVMINFO pLVMInfo = NULL;
1391
1392 PARTITIONINFO *pPartitionInfos = NULL; // linked list of all partitions
1393 USHORT cPartitions = 0; // bootable partition count
1394
1395 if (!ppList)
1396 return ERROR_INVALID_PARAMETER;
1397
1398 if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
1399 {
1400 // LVM installed:
1401 arc = doshReadLVMPartitions(pLVMInfo, // in: LVM info
1402 &pPartitionInfos, // out: partitions array
1403 &cPartitions); // out: partitions count
1404 // copied to output below
1405
1406 if (arc)
1407 {
1408 // error: start over
1409 doshFreeLVMInfo(pLVMInfo);
1410 CleanPartitionInfos(pPartitionInfos);
1411 pPartitionInfos = NULL;
1412 cPartitions = 0;
1413 }
1414 }
1415
1416#ifndef __XWPLITE__
1417 if (arc)
1418 // LVM not installed, or failed:
1419 // parse partitions manually
1420 arc = ReadFDiskPartitions(&pPartitionInfos,
1421 &cPartitions,
1422 pusContext);
1423#endif
1424
1425 if (!arc)
1426 {
1427 // no error so far:
1428 *pusContext = 0;
1429
1430 *ppList = NEW(PARTITIONSLIST);
1431 if (!(*ppList))
1432 arc = ERROR_NOT_ENOUGH_MEMORY;
1433 else
1434 {
1435 ZERO(*ppList);
1436
1437 (*ppList)->pPartitionInfo = pPartitionInfos;
1438 (*ppList)->cPartitions = cPartitions;
1439
1440 _Pmpf((__FUNCTION__ ": returning %d partitions", cPartitions));
1441 }
1442 }
1443
1444 if (arc)
1445 CleanPartitionInfos(pPartitionInfos);
1446
1447 _Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
1448
1449 return arc;
1450}
1451
1452/*
1453 *@@ doshFreePartitionsList:
1454 * this frees the resources allocated by
1455 * doshGetPartitionsList.
1456 *
1457 *@@added V0.9.0 [umoeller]
1458 */
1459
1460APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
1461{
1462 if (!ppList)
1463 return ERROR_INVALID_PARAMETER;
1464
1465 CleanPartitionInfos(ppList->pPartitionInfo);
1466 doshFreeLVMInfo(ppList->pLVMInfo);
1467 free(ppList);
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.