source: trunk/src/helpers/dosh.c@ 223

Last change on this file since 223 was 222, checked in by umoeller, 23 years ago

Minor adjustments for new static handling.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 141.5 KB
Line 
1
2/*
3 *@@sourcefile dosh.c:
4 * dosh.c contains Control Program helper functions.
5 *
6 * This file has miscellaneous system functions,
7 * drive helpers, file helpers, and partition functions.
8 *
9 * Usage: All OS/2 programs.
10 *
11 * Function prefixes (new with V0.81):
12 * -- dosh* Dos (Control Program) helper functions
13 *
14 * These funcs are forward-declared in dosh.h, which
15 * must be #include'd first.
16 *
17 * The resulting dosh.obj object file can be linked
18 * against any application object file. As opposed to
19 * the code in dosh2.c, it does not require any other
20 * code from the helpers.
21 *
22 * dosh.obj can also be used with the VAC subsystem
23 * library (/rn compiler option).
24 *
25 * Note: Version numbering in this file relates to XWorkplace version
26 * numbering.
27 *
28 *@@header "helpers\dosh.h"
29 */
30
31/*
32 * This file Copyright (C) 1997-2000 Ulrich M”ller.
33 * This file is part of the "XWorkplace helpers" source package.
34 * This is free software; you can redistribute it and/or modify
35 * it under the terms of the GNU General Public License as published
36 * by the Free Software Foundation, in version 2 as it comes in the
37 * "COPYING" file of the XWorkplace main distribution.
38 * This program is distributed in the hope that it will be useful,
39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
41 * GNU General Public License for more details.
42 */
43
44#define OS2EMX_PLAIN_CHAR
45 // this is needed for "os2emx.h"; if this is defined,
46 // emx will define PSZ as _signed_ char, otherwise
47 // as unsigned char
48
49#define INCL_DOSMODULEMGR
50#define INCL_DOSPROCESS
51#define INCL_DOSEXCEPTIONS
52#define INCL_DOSSESMGR
53#define INCL_DOSQUEUES
54#define INCL_DOSSEMAPHORES
55#define INCL_DOSMISC
56#define INCL_DOSDEVICES
57#define INCL_DOSDEVIOCTL
58#define INCL_DOSERRORS
59
60#define INCL_KBD
61#include <os2.h>
62
63#include <stdlib.h>
64#include <string.h>
65#include <stdio.h>
66#include <stdarg.h>
67#include <ctype.h>
68
69#include "setup.h" // code generation and debugging options
70
71#include "helpers\dosh.h"
72#include "helpers\standards.h"
73
74#pragma hdrstop
75
76// static const CHAR G_acDriveLetters[28] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
77
78/*
79 *@@category: Helpers\Control program helpers\Wrappers
80 */
81
82/* ******************************************************************
83 *
84 * Wrappers
85 *
86 ********************************************************************/
87
88#ifdef DOSH_STANDARDWRAPPERS
89
90 /*
91 *@@ doshSleep:
92 *
93 *@@added V0.9.16 (2002-01-26) [umoeller]
94 */
95
96 APIRET doshSleep(ULONG msec)
97 {
98 // put the call in brackets so the macro won't apply here
99 return (DosSleep)(msec);
100 }
101
102 /*
103 *@@ doshCreateMutexSem:
104 *
105 *@@added V0.9.16 (2002-01-26) [umoeller]
106 */
107
108 APIRET doshCreateMutexSem(PSZ pszName,
109 PHMTX phmtx,
110 ULONG flAttr,
111 BOOL32 fState)
112 {
113 // put the call in brackets so the macro won't apply here
114 return (DosCreateMutexSem)(pszName, phmtx, flAttr, fState);
115 }
116
117 /*
118 *@@ doshRequestMutexSem:
119 *
120 *@@added V0.9.16 (2002-01-26) [umoeller]
121 */
122
123 APIRET doshRequestMutexSem(HMTX hmtx, ULONG ulTimeout)
124 {
125 return (DosRequestMutexSem)(hmtx, ulTimeout);
126 }
127
128 /*
129 *@@ doshReleaseMutexSem:
130 *
131 *@@added V0.9.16 (2002-01-26) [umoeller]
132 */
133
134 APIRET doshReleaseMutexSem(HMTX hmtx)
135 {
136 return (DosReleaseMutexSem)(hmtx);
137 }
138
139 /*
140 *@@ doshSetExceptionHandler:
141 *
142 *@@added V0.9.16 (2002-01-26) [umoeller]
143 */
144
145 APIRET doshSetExceptionHandler(PEXCEPTIONREGISTRATIONRECORD pERegRec)
146 {
147 // put the call in brackets so the macro won't apply here
148 return (DosSetExceptionHandler)(pERegRec);
149 }
150
151 /*
152 *@@ doshUnsetExceptionHandler:
153 *
154 *@@added V0.9.16 (2002-01-26) [umoeller]
155 */
156
157 APIRET doshUnsetExceptionHandler(PEXCEPTIONREGISTRATIONRECORD pERegRec)
158 {
159 // put the call in brackets so the macro won't apply here
160 return (DosUnsetExceptionHandler)(pERegRec);
161 }
162
163#endif
164
165/*
166 *@@category: Helpers\Control program helpers\Miscellaneous
167 * Miscellaneous helpers in dosh.c that didn't fit into any other
168 * category.
169 */
170
171/* ******************************************************************
172 *
173 * Miscellaneous
174 *
175 ********************************************************************/
176
177/*
178 *@@ doshGetChar:
179 * reads a single character from the keyboard.
180 * Useful for VIO sessions, since there's great
181 * confusion between the various C dialects about
182 * how to use getc(), and getc() doesn't work
183 * with the VAC subsystem library.
184 *
185 *@@added V0.9.4 (2000-07-27) [umoeller]
186 */
187
188CHAR doshGetChar(VOID)
189{
190 // CHAR c;
191 // ULONG ulRead = 0;
192
193 KBDKEYINFO kki;
194 KbdCharIn(&kki,
195 0, // wait
196 0);
197
198 return (kki.chChar);
199}
200
201/*
202 *@@ doshQueryShiftState:
203 * returns TRUE if any of the SHIFT keys are
204 * currently pressed. Useful for checks during
205 * WM_COMMAND messages from menus.
206 *
207 *@@changed V0.9.5 (2000-09-27) [umoeller]: added error checking
208 */
209
210BOOL doshQueryShiftState(VOID)
211{
212 BOOL brc = FALSE;
213 APIRET arc = NO_ERROR;
214 HFILE hfKbd;
215 ULONG ulAction;
216
217 if (!(arc = DosOpen("KBD$", &hfKbd, &ulAction, 0,
218 FILE_NORMAL,
219 FILE_OPEN,
220 OPEN_ACCESS_READONLY | OPEN_SHARE_DENYWRITE,
221 (PEAOP2)NULL)))
222 {
223 SHIFTSTATE ShiftState;
224 ULONG cbDataLen = sizeof(ShiftState);
225
226 if (!(arc = DosDevIOCtl(hfKbd, IOCTL_KEYBOARD, KBD_GETSHIFTSTATE,
227 NULL, 0, NULL, // no parameters
228 &ShiftState, cbDataLen, &cbDataLen)))
229 brc = ((ShiftState.fsState & 3) != 0);
230
231 DosClose(hfKbd);
232 }
233
234 return brc;
235}
236
237/*
238 *@@ doshIsWarp4:
239 * checks the OS/2 system version number.
240 *
241 * Returns:
242 *
243 * -- 0 (FALSE): OS/2 2.x or Warp 3 is running.
244 *
245 * -- 1: Warp 4.0 is running.
246 *
247 * -- 2: Warp 4.5 is running (WSeB or Warp 4 FP 13+ or eCS
248 * or ACP/MCP), or even something newer.
249 *
250 *@@changed V0.9.2 (2000-03-05) [umoeller]: reported TRUE on Warp 3 also; fixed
251 *@@changed V0.9.6 (2000-10-16) [umoeller]: patched for speed
252 *@@changed V0.9.9 (2001-04-04) [umoeller]: now returning 2 for Warp 4.5 and above
253 */
254
255ULONG doshIsWarp4(VOID)
256{
257 static BOOL s_fQueried = FALSE;
258 static ULONG s_ulrc = 0;
259
260 if (!s_fQueried)
261 {
262 // first call:
263 ULONG aulBuf[3];
264
265 DosQuerySysInfo(QSV_VERSION_MAJOR, // 11
266 QSV_VERSION_MINOR, // 12
267 &aulBuf, sizeof(aulBuf));
268 // Warp 3 is reported as 20.30
269 // Warp 4 is reported as 20.40
270 // Aurora is reported as 20.45 (regardless of convenience packs)
271
272 if ( (aulBuf[0] > 20) // major > 20; not the case with Warp 3, 4, 5
273 || ( (aulBuf[0] == 20) // major == 20 and minor >= 45
274 && (aulBuf[1] >= 45)
275 )
276 )
277 // Warp 4.5 or newer:
278 s_ulrc = 2;
279 else if ( (aulBuf[0] == 20) // major == 20 and minor == 40
280 && (aulBuf[1] == 40)
281 )
282 // Warp 4:
283 s_ulrc = 1;
284
285 s_fQueried = TRUE;
286 }
287
288 return (s_ulrc);
289}
290
291/*
292 *@@ doshQuerySysErrorMsg:
293 * this retrieves the error message for a system error
294 * (APIRET) from the system error message file (OSO001.MSG).
295 * This file better be on the DPATH (it normally is).
296 *
297 * This returns the string in the "SYSxxx: blahblah" style,
298 * which is normally displayed on the command line when
299 * errors occur.
300 *
301 * The error message is returned in a newly allocated
302 * buffer, which should be free()'d afterwards.
303 *
304 * Returns NULL upon errors.
305 */
306
307PSZ doshQuerySysErrorMsg(APIRET arc) // in: DOS error code
308{
309 PSZ pszReturn = 0;
310 CHAR szDosError[1000];
311 ULONG cbDosError = 0;
312 DosGetMessage(NULL, 0, // no string replacements
313 szDosError, sizeof(szDosError),
314 arc,
315 "OSO001.MSG", // default OS/2 message file
316 &cbDosError);
317 if (cbDosError > 2)
318 {
319 szDosError[cbDosError - 2] = 0;
320 pszReturn = strdup(szDosError);
321 }
322 return (pszReturn);
323}
324
325/*
326 *@@ doshQuerySysUptime:
327 * returns the system uptime in milliseconds.
328 * This can be used for time comparisons.
329 *
330 *@@added V0.9.12 (2001-05-18) [umoeller]
331 */
332
333ULONG doshQuerySysUptime(VOID)
334{
335 ULONG ulms;
336 DosQuerySysInfo(QSV_MS_COUNT,
337 QSV_MS_COUNT,
338 &ulms,
339 sizeof(ulms));
340 return (ulms);
341}
342
343/*
344 *@@ doshDevIOCtl:
345 *
346 * Works with those IOCtls where the buffer
347 * size parameters are always the same anyway,
348 * which applies to all IOCtls I have seen
349 * so far.
350 *
351 *@@added V0.9.13 (2001-06-14) [umoeller]
352 */
353
354APIRET doshDevIOCtl(HFILE hf,
355 ULONG ulCategory,
356 ULONG ulFunction,
357 PVOID pvParams,
358 ULONG cbParams,
359 PVOID pvData,
360 ULONG cbData)
361{
362 return (DosDevIOCtl(hf,
363 ulCategory,
364 ulFunction,
365 pvParams, cbParams, &cbParams,
366 pvData, cbData, &cbData));
367}
368
369/*
370 *@@category: Helpers\Control program helpers\Shared memory management
371 * helpers for allocating and requesting shared memory.
372 */
373
374/* ******************************************************************
375 *
376 * Memory helpers
377 *
378 ********************************************************************/
379
380/*
381 *@@ doshMalloc:
382 * wrapper around malloc() which automatically
383 * sets ERROR_NOT_ENOUGH_MEMORY.
384 *
385 *@@added V0.9.16 (2001-10-19) [umoeller]
386 */
387
388PVOID doshMalloc(ULONG cb,
389 APIRET *parc)
390{
391 PVOID pv;
392 *parc = NO_ERROR;
393 if (!(pv = malloc(cb)))
394 *parc = ERROR_NOT_ENOUGH_MEMORY;
395
396 return (pv);
397}
398
399/*
400 *@@ doshAllocArray:
401 * allocates c * cbArrayItem bytes.
402 * Similar to calloc(), but returns
403 * error codes:
404 *
405 * -- NO_ERROR: *ppv and *pcbAllocated were set.
406 *
407 * -- ERROR_NO_DATA: either c or cbArrayItem are
408 * zero.
409 *
410 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
411 *
412 *@@added V0.9.16 (2001-12-08) [umoeller]
413 */
414
415APIRET doshAllocArray(ULONG c, // in: array item count
416 ULONG cbArrayItem, // in: size of one array item
417 PBYTE *ppv, // out: memory ptr if NO_ERROR is returned
418 PULONG pcbAllocated) // out: # of bytes allocated
419{
420 if (!c || !cbArrayItem)
421 return ERROR_NO_DATA;
422
423 *pcbAllocated = c * cbArrayItem;
424 if (!(*ppv = (PBYTE)malloc(*pcbAllocated)))
425 return ERROR_NOT_ENOUGH_MEMORY;
426
427 return NO_ERROR;
428}
429
430/*
431 *@@ doshAllocSharedMem:
432 * wrapper for DosAllocSharedMem which has
433 * a malloc()-like syntax. Just due to my
434 * lazyness.
435 *
436 * Note that ulSize is always rounded up to the
437 * next 4KB value, so don't use this hundreds of times.
438 *
439 * Returns NULL upon errors. Possible errors include
440 * that a memory block calle pcszName has already been
441 * allocated.
442 *
443 * Use DosFreeMem(pvrc) to free the memory. The memory
444 * will only be freed if no other process has requested
445 * access.
446 *
447 *@@added V0.9.3 (2000-04-18) [umoeller]
448 */
449
450PVOID doshAllocSharedMem(ULONG ulSize, // in: requested mem block size (rounded up to 4KB)
451 const char* pcszName) // in: name of block ("\\SHAREMEM\\xxx") or NULL
452{
453 PVOID pvrc = NULL;
454 APIRET arc = DosAllocSharedMem((PVOID*)&pvrc,
455 (PSZ)pcszName,
456 ulSize,
457 PAG_COMMIT | PAG_READ | PAG_WRITE);
458 if (arc == NO_ERROR)
459 return (pvrc);
460
461 return NULL;
462}
463
464/*
465 *@@ doshRequestSharedMem:
466 * requests access to a block of named shared memory
467 * allocated by doshAllocSharedMem.
468 *
469 * Returns NULL upon errors.
470 *
471 * Use DosFreeMem(pvrc) to free the memory. The memory
472 * will only be freed if no other process has requested
473 * access.
474 *
475 *@@added V0.9.3 (2000-04-19) [umoeller]
476 */
477
478PVOID doshRequestSharedMem(PCSZ pcszName)
479{
480 PVOID pvrc = NULL;
481 APIRET arc = DosGetNamedSharedMem((PVOID*)pvrc,
482 (PSZ)pcszName,
483 PAG_READ | PAG_WRITE);
484 if (arc == NO_ERROR)
485 return (pvrc);
486
487 return NULL;
488}
489
490/*
491 *@@category: Helpers\Control program helpers\Drive management
492 * functions for managing drives... enumerating, testing,
493 * querying etc.
494 */
495
496/* ******************************************************************
497 *
498 * Drive helpers
499 *
500 ********************************************************************/
501
502/*
503 *@@ doshIsFixedDisk:
504 * checks whether a disk is fixed or removeable.
505 * ulLogicalDrive must be 1 for drive A:, 2 for B:, ...
506 * The result is stored in *pfFixed.
507 * Returns DOS error code.
508 *
509 * From my testing, this function does _not_ provoke
510 * "drive not ready" popups, even if the disk is not
511 * ready.
512 *
513 * Warning: This uses DosDevIOCtl, which has proved
514 * to cause problems with some device drivers for
515 * removeable disks.
516 *
517 * Returns:
518 *
519 * -- NO_ERROR: *pfFixed was set.
520 *
521 * -- ERROR_INVALID_DRIVE: drive letter invalid
522 *
523 * -- ERROR_NOT_SUPPORTED (50): for network drives.
524 *
525 *@@changed V0.9.14 (2001-08-03) [umoeller]: added extra fix for A: and B:
526 */
527
528APIRET doshIsFixedDisk(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
529 PBOOL pfFixed) // out: TRUE for fixed disks
530{
531 APIRET arc = ERROR_INVALID_DRIVE;
532
533 if ( (ulLogicalDrive == 1)
534 || (ulLogicalDrive == 2)
535 )
536 {
537 // drive A: and B: can never be fixed V0.9.14 (2001-08-03) [umoeller]
538 *pfFixed = FALSE;
539 return NO_ERROR;
540 }
541
542 if (ulLogicalDrive)
543 {
544 // parameter packet
545 #pragma pack(1)
546 struct {
547 UCHAR command,
548 drive;
549 } parms;
550 #pragma pack()
551
552 // data packet
553 UCHAR ucNonRemoveable;
554
555 parms.drive = (UCHAR)(ulLogicalDrive - 1);
556 if (!(arc = doshDevIOCtl((HFILE)-1,
557 IOCTL_DISK, // 0x08
558 DSK_BLOCKREMOVABLE, // 0x20
559 &parms, sizeof(parms),
560 &ucNonRemoveable, sizeof(ucNonRemoveable))))
561 *pfFixed = (BOOL)ucNonRemoveable;
562 }
563
564 return arc;
565}
566
567/*
568 *@@ doshQueryDiskParams:
569 * this retrieves more information about a given drive,
570 * which is stored in the specified BIOSPARAMETERBLOCK
571 * structure.
572 *
573 * BIOSPARAMETERBLOCK is defined in the Toolkit headers,
574 * and from my testing, it's the same with the Toolkits
575 * 3 and 4.5.
576 *
577 * If NO_ERROR is returned, the bDeviceType field can
578 * be one of the following (according to CPREF):
579 *
580 * -- 0: 48 TPI low-density diskette drive
581 * -- 1: 96 TPI high-density diskette drive
582 * -- 2: 3.5-inch 720KB diskette drive
583 * -- 3: 8-Inch single-density diskette drive
584 * -- 4: 8-Inch double-density diskette drive
585 * -- 5: Fixed disk
586 * -- 6: Tape drive
587 * -- 7: Other (includes 1.44MB 3.5-inch diskette drive)
588 * -- 8: R/W optical disk
589 * -- 9: 3.5-inch 4.0MB diskette drive (2.88MB formatted)
590 *
591 * From my testing, this function does _not_ provoke
592 * "drive not ready" popups, even if the disk is not
593 * ready.
594 *
595 * Warning: This uses DosDevIOCtl, which has proved
596 * to cause problems with some device drivers for
597 * removeable disks.
598 *
599 * This returns the DOS error code of DosDevIOCtl.
600 * This will be:
601 *
602 * -- NO_ERROR for all local disks;
603 *
604 * -- ERROR_NOT_SUPPORTED (50) for network drives.
605 *
606 *@@added V0.9.0 [umoeller]
607 *@@changed V0.9.13 (2001-06-14) [umoeller]: changed prototype to use BIOSPARAMETERBLOCK directly
608 *@@changed V0.9.13 (2001-06-14) [umoeller]: now querying standard media, no redetermine
609 */
610
611APIRET doshQueryDiskParams(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
612 PBIOSPARAMETERBLOCK pdp) // out: drive parameters
613{
614 APIRET arc = ERROR_INVALID_DRIVE;
615
616 if (ulLogicalDrive)
617 {
618 #pragma pack(1)
619 // parameter packet
620 struct {
621 UCHAR ucCommand,
622 ucDrive;
623 } parms;
624 #pragma pack()
625
626 parms.ucCommand = 0; // 0 = return standard media,
627 // 1 = read currently inserted media
628 // (1 doesn't work any more, returns arc 87
629 // V0.9.13 (2001-06-14) [umoeller])
630 parms.ucDrive=(UCHAR)(ulLogicalDrive-1);
631
632 // zero the structure V0.9.13 (2001-06-14) [umoeller]
633 memset(pdp, 0, sizeof(BIOSPARAMETERBLOCK));
634
635 arc = doshDevIOCtl((HFILE)-1,
636 IOCTL_DISK, // 0x08
637 DSK_GETDEVICEPARAMS, // 0x63
638 &parms, sizeof(parms),
639 pdp, sizeof(BIOSPARAMETERBLOCK));
640
641 /* if (!arc)
642 {
643 _Pmpf((" bDeviceType: %d", pdp->bDeviceType));
644 _Pmpf((" bytes per sector: %d", pdp->usBytesPerSector));
645 _Pmpf((" sectors per track: %d", pdp->usSectorsPerTrack));
646 } */
647 }
648
649 return arc;
650}
651
652/*
653 *@@ doshQueryDriveType:
654 * tests the specified BIOSPARAMETERBLOCK
655 * for whether it represents a CD-ROM or
656 * some other removeable drive type.
657 *
658 * Returns one of:
659 *
660 * -- DRVTYPE_HARDDISK (0)
661 *
662 * -- DRVTYPE_PARTITIONABLEREMOVEABLE
663 *
664 * -- DRVTYPE_CDROM
665 *
666 * -- DRVTYPE_TAPE
667 *
668 * -- DRVTYPE_VDISK
669 *
670 * -- DRVTYPE_FLOPPY
671 *
672 * -- DRVTYPE_UNKNOWN (255)
673 *
674 * The BIOSPARAMETERBLOCK must be filled
675 * first using doshQueryDiskParams.
676 *
677 *@@added V0.9.16 (2002-01-13) [umoeller]
678 */
679
680BYTE doshQueryDriveType(ULONG ulLogicalDrive,
681 PBIOSPARAMETERBLOCK pdp,
682 BOOL fFixed)
683{
684 if (pdp)
685 {
686 if (pdp->fsDeviceAttr & DEVATTR_PARTITIONALREMOVEABLE) // 0x08
687 return DRVTYPE_PRT;
688
689 if (fFixed)
690 return DRVTYPE_HARDDISK;
691
692 if ( (pdp->bDeviceType == 7) // "other"
693 && (pdp->usBytesPerSector == 2048)
694 && (pdp->usSectorsPerTrack == (USHORT)-1)
695 )
696 return DRVTYPE_CDROM;
697
698 switch (pdp->bDeviceType)
699 {
700 case DEVTYPE_TAPE: // 6
701 return DRVTYPE_TAPE;
702
703 case DEVTYPE_48TPI: // 0, 360k 5.25" floppy
704 case DEVTYPE_96TPI: // 1, 1.2M 5.25" floppy
705 case DEVTYPE_35: // 2, 720k 3.5" floppy
706 case DEVTYPE_OTHER: // 7, 1.44 3.5" floppy
707 // 1.84M 3.5" floppy
708 case DEVTYPE_35_288MB:
709 if ( (ulLogicalDrive == 1)
710 || (ulLogicalDrive == 2)
711 )
712 return DRVTYPE_FLOPPY;
713
714 return DRVTYPE_VDISK;
715
716 case DEVTYPE_RWOPTICAL: // 8, what is this?!?
717 return DRVTYPE_FLOPPY;
718 }
719 }
720
721 return DRVTYPE_UNKNOWN;
722}
723
724/*
725 *@@ doshQueryCDDrives:
726 * returns the no. of CD-ROM drives on the system
727 * as well as the drive letter of the first
728 * CD-ROM drive.
729 *
730 *@@added V0.9.21 (2002-08-31) [umoeller]
731 */
732
733APIRET doshQueryCDDrives(PBYTE pcCDs, // out: CD-ROM drives count
734 PCHAR pcFirstCD) // out: drive letter of first CD
735{
736 APIRET arc;
737 HFILE hfCDROM;
738 ULONG ulAction;
739
740 if (!(arc = DosOpen("\\DEV\\CD-ROM2$",
741 &hfCDROM,
742 &ulAction,
743 0,
744 FILE_NORMAL,
745 OPEN_ACTION_OPEN_IF_EXISTS,
746 OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY,
747 NULL)))
748 {
749 struct
750 {
751 USHORT cCDs;
752 USHORT usFirstCD; // 0 == A:, 1 == B:, ...
753 } cdinfo;
754
755 ULONG cb = sizeof(cdinfo);
756
757 if (!(arc = DosDevIOCtl(hfCDROM,
758 0x82,
759 0x60,
760 NULL,
761 0,
762 NULL,
763 &cdinfo,
764 cb,
765 &cb)))
766 {
767 *pcCDs = cdinfo.cCDs;
768 *pcFirstCD = cdinfo.usFirstCD + 'A';
769 }
770
771 DosClose(hfCDROM);
772 }
773
774 return arc;
775}
776
777/*
778 *@@ doshOpenDrive:
779 * opens the given logical drive using
780 * DosOpen with OPEN_FLAGS_DASD. Use
781 * the file handle returned from here
782 * for doshHasAudioCD and doshQueryCDStatus.
783 *
784 * If NO_ERROR is returned, use DosClose
785 * to close the device again.
786 *
787 *@@added V0.9.21 (2002-08-31) [umoeller]
788 */
789
790APIRET doshOpenDrive(ULONG ulLogicalDrive,
791 HFILE *phf) // out: open drive's file handle
792{
793 ULONG dummy;
794
795 CHAR szDrive[] = "C:";
796 szDrive[0] = 'A' + ulLogicalDrive - 1;
797
798 return DosOpen(szDrive, // "C:", "D:", ...
799 phf,
800 &dummy,
801 0,
802 FILE_NORMAL,
803 // OPEN_ACTION_FAIL_IF_NEW
804 OPEN_ACTION_OPEN_IF_EXISTS,
805 OPEN_FLAGS_DASD
806 | OPEN_FLAGS_FAIL_ON_ERROR
807 // ^^^ if this flag is not set, we get the white
808 // hard-error box
809 | OPEN_FLAGS_NOINHERIT // V0.9.6 (2000-11-25) [pr]
810 // | OPEN_ACCESS_READONLY // V0.9.13 (2001-06-14) [umoeller]
811 | OPEN_SHARE_DENYNONE,
812 NULL);
813}
814
815/*
816 *@@ doshHasAudioCD:
817 * sets *pfAudio to whether ulLogicalDrive
818 * currently has an audio CD inserted.
819 *
820 * Better call this only if you're sure that
821 * ulLogicalDrive is a CD-ROM drive. Use
822 * doshQueryRemoveableType to check.
823 *
824 *@@added V0.9.14 (2001-08-01) [umoeller]
825 *@@changed V0.9.21 (2002-08-31) [umoeller]: removed ulLogicalDrive which was not needed
826 */
827
828APIRET doshHasAudioCD(HFILE hfDrive, // in: DASD open
829 BOOL fMixedModeCD,
830 PBOOL pfAudio)
831{
832 APIRET arc = NO_ERROR;
833
834 ULONG ulAudioTracks = 0,
835 ulDataTracks = 0;
836
837 CHAR cds1[4] = { 'C', 'D', '0', '1' };
838 CHAR cds2[4];
839
840 *pfAudio = FALSE;
841
842 // check for proper driver signature
843 if (!(arc = doshDevIOCtl(hfDrive,
844 IOCTL_CDROMDISK,
845 CDROMDISK_GETDRIVER,
846 &cds1, sizeof(cds1),
847 &cds2, sizeof(cds2))))
848 {
849 if (memcmp(&cds1, &cds2, 4))
850 // this is not a CD-ROM then:
851 arc = NO_ERROR;
852 else
853 {
854 #pragma pack(1) // V0.9.21 (2002-08-31) [umoeller]
855
856 struct {
857 UCHAR ucFirstTrack,
858 ucLastTrack;
859 ULONG ulLeadOut;
860 } cdat;
861
862 struct {
863 ULONG ulTrackAddress;
864 BYTE bFlags;
865 } trackdata;
866
867 #pragma pack()
868
869 // get track count
870 if (!(arc = doshDevIOCtl(hfDrive,
871 IOCTL_CDROMAUDIO,
872 CDROMAUDIO_GETAUDIODISK,
873 &cds1, sizeof(cds1),
874 &cdat, sizeof(cdat))))
875 {
876 // still no error: build the audio TOC
877 ULONG i;
878 for (i = cdat.ucFirstTrack;
879 i <= cdat.ucLastTrack;
880 i++)
881 {
882 BYTE cdtp[5] =
883 { 'C', 'D', '0', '1', (UCHAR)i };
884
885 if (!(arc = doshDevIOCtl(hfDrive,
886 IOCTL_CDROMAUDIO,
887 CDROMAUDIO_GETAUDIOTRACK,
888 &cdtp, sizeof(cdtp),
889 &trackdata, sizeof(trackdata))))
890 {
891 if (trackdata.bFlags & 64)
892 ulDataTracks++;
893 else
894 {
895 ulAudioTracks++;
896
897 if (!fMixedModeCD)
898 {
899 // caller doesn't want mixed mode:
900 // stop here
901 ulDataTracks = 0;
902 break;
903 }
904 }
905 }
906 }
907
908 // _Pmpf((" got %d audio, %d data tracks",
909 // ulAudioTracks, ulDataTracks));
910
911 if (!ulDataTracks)
912 *pfAudio = TRUE;
913 }
914 else
915 {
916 // not audio disk:
917 // go on then
918 // _Pmpf((" CDROMAUDIO_GETAUDIODISK returned %d", arc));
919 arc = NO_ERROR;
920 }
921 }
922 }
923 else
924 {
925 // not CD-ROM: go on then
926 // _Pmpf((" CDROMDISK_GETDRIVER returned %d", arc));
927 arc = NO_ERROR;
928 }
929
930 return arc;
931}
932
933/*
934 *@@ doshQueryCDStatus:
935 * returns the status bits of a CD-ROM drive.
936 * This calls the CDROMDISK_DEVICESTATUS
937 * ioctl.
938 *
939 * If NO_ERROR is returned, *pflStatus has
940 * received the following flags:
941 *
942 * -- CDFL_DOOROPEN (bit 0)
943 *
944 * -- CDFL_DOORLOCKED (bit 1)
945 *
946 * and many more (see dosh.h).
947 *
948 * Actually I wrote this function to have a way to
949 * find out whether the drive door is already open.
950 * But thanks to IBM's thoughtful design, this ioctl
951 * is 99% useless for that purpose since it requires
952 * a DASD disk handle to be passed in, which cannot
953 * be obtained if there's no media in the drive.
954 *
955 * In other words, it is absolutely impossible to
956 * ever get the CDFL_DOOROPEN flag, because if the
957 * door is open, DosOpen already fails on the drive.
958 * As a consequence, it is seems to be impossible
959 * to find out if the door is open with OS/2.
960 *
961 *@@added V0.9.21 (2002-08-31) [umoeller]
962 */
963
964APIRET doshQueryCDStatus(HFILE hfDrive, // in: DASD open
965 PULONG pflStatus) // out: CD-ROM status bits
966{
967 APIRET arc;
968
969 CHAR cds1[4] = { 'C', 'D', '0', '1' };
970 ULONG fl;
971
972 *pflStatus = 0;
973
974 if (!(arc = doshDevIOCtl(hfDrive,
975 IOCTL_CDROMDISK,
976 CDROMDISK_DEVICESTATUS,
977 &cds1, sizeof(cds1),
978 &fl, sizeof(fl))))
979 {
980 *pflStatus = fl;
981 }
982
983 return arc;
984}
985
986/*
987 *@@ doshEnumDrives:
988 * this function enumerates all valid drive letters on
989 * the system by composing a string of drive letters
990 * in the buffer pointed to by pszBuffer, which should
991 * be 27 characters in size to hold information for
992 * all drives. The buffer will be null-terminated.
993 *
994 * If (pcszFileSystem != NULL), only drives matching
995 * the specified file system type (e.g. "HPFS") will
996 * be enumerated. If (pcszFileSystem == NULL), all
997 * drives will be enumerated.
998 *
999 * If (fSkipRemovables == TRUE), removeable drives will
1000 * be skipped. This applies to floppy, CD-ROM, and
1001 * virtual floppy drives. This will start the search
1002 * at drive letter C: so that drives A: and B: will
1003 * never be checked (to avoid the hardware bumps).
1004 *
1005 * Otherwise, the search starts at drive A:. Still,
1006 * removeable drives will only be added if valid media
1007 * is inserted.
1008 *
1009 *@@changed V0.9.4 (2000-07-03) [umoeller]: this stopped at the first invalid drive letter; fixed
1010 *@@changed V0.9.4 (2000-07-03) [umoeller]: added fSkipRemoveables
1011 */
1012
1013VOID doshEnumDrives(PSZ pszBuffer, // out: drive letters
1014 PCSZ pcszFileSystem, // in: FS's to match or NULL
1015 BOOL fSkipRemoveables) // in: if TRUE, only non-removeable disks will be returned
1016{
1017 CHAR szName[5] = "";
1018 ULONG ulLogicalDrive = 1, // start with drive A:
1019 ulFound = 0; // found drives count
1020 APIRET arc = NO_ERROR; // return code
1021
1022 if (fSkipRemoveables)
1023 // start with drive C:
1024 ulLogicalDrive = 3;
1025
1026 // go thru the drives, start with C: (== 3), stop after Z: (== 26)
1027 while (ulLogicalDrive <= 26)
1028 {
1029 #pragma pack(1)
1030 struct
1031 {
1032 UCHAR dummy,drive;
1033 } parms;
1034 #pragma pack()
1035
1036 // data packet
1037 UCHAR nonRemovable=0;
1038
1039 parms.drive=(UCHAR)(ulLogicalDrive-1);
1040 arc = doshDevIOCtl((HFILE)-1,
1041 IOCTL_DISK,
1042 DSK_BLOCKREMOVABLE,
1043 &parms, sizeof(parms),
1044 &nonRemovable, sizeof(nonRemovable));
1045
1046 if ( // fixed disk and non-removeable
1047 ((arc == NO_ERROR) && (nonRemovable))
1048 // or network drive:
1049 || (arc == ERROR_NOT_SUPPORTED)
1050 )
1051 {
1052 ULONG ulOrdinal = 0; // ordinal of entry in name list
1053 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
1054 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
1055 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
1056
1057 szName[0] = ulLogicalDrive + 'A' - 1;
1058 szName[1] = ':';
1059 szName[2] = '\0';
1060
1061 arc = DosQueryFSAttach(szName, // logical drive of attached FS
1062 ulOrdinal, // ignored for FSAIL_QUERYNAME
1063 FSAIL_QUERYNAME, // return data for a Drive or Device
1064 pfsqBuffer, // returned data
1065 &cbBuffer); // returned data length
1066
1067 if (arc == NO_ERROR)
1068 {
1069 // The data for the last three fields in the FSQBUFFER2
1070 // structure are stored at the offset of fsqBuffer.szName.
1071 // Each data field following fsqBuffer.szName begins
1072 // immediately after the previous item.
1073 CHAR* pszFSDName = (PSZ)&(pfsqBuffer->szName) + (pfsqBuffer->cbName) + 1;
1074 if (pcszFileSystem == NULL)
1075 {
1076 // enum-all mode: always copy
1077 pszBuffer[ulFound] = szName[0]; // drive letter
1078 ulFound++;
1079 }
1080 else if (strcmp(pszFSDName, pcszFileSystem) == 0)
1081 {
1082 pszBuffer[ulFound] = szName[0]; // drive letter
1083 ulFound++;
1084 }
1085 }
1086 }
1087
1088 ulLogicalDrive++;
1089 } // end while (G_acDriveLetters[ulLogicalDrive] <= 'Z')
1090
1091 pszBuffer[ulFound] = '\0';
1092}
1093
1094/*
1095 *@@ doshQueryBootDrive:
1096 * returns the letter of the boot drive as a
1097 * single (capital) character, which is useful for
1098 * constructing file names using sprintf and such.
1099 *
1100 *@@changed V0.9.16 (2002-01-13) [umoeller]: optimized
1101 */
1102
1103CHAR doshQueryBootDrive(VOID)
1104{
1105 // this can never change, so query this only once
1106 // V0.9.16 (2002-01-13) [umoeller]
1107 static CHAR cBootDrive = '\0';
1108
1109 if (!cBootDrive)
1110 {
1111 ULONG ulBootDrive;
1112 DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
1113 &ulBootDrive,
1114 sizeof(ulBootDrive));
1115 cBootDrive = (CHAR)ulBootDrive + 'A' - 1;
1116 }
1117
1118 return cBootDrive;
1119}
1120
1121/*
1122 *@@ doshQueryMedia:
1123 * determines whether the given drive currently
1124 * has media inserted.
1125 *
1126 * Call this only for non-fixed (removable) disks.
1127 * Use doshIsFixedDisk to find out.
1128 *
1129 * Returns:
1130 *
1131 * -- NO_ERROR: media is present.
1132 *
1133 * -- ERROR_AUDIO_CD_ROM (10000): audio CD-ROM is present.
1134 *
1135 * -- ERROR_NOT_READY (21) or other: drive has no media.
1136 *
1137 *@@added V0.9.16 (2002-01-13) [umoeller]
1138 */
1139
1140APIRET doshQueryMedia(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1141 BOOL fCDROM, // in: is drive CD-ROM?
1142 ULONG fl) // in: DRVFL_* flags
1143{
1144 APIRET arc;
1145
1146 HFILE hf;
1147
1148 // exported this code to doshOpenDrive V0.9.21 (2002-08-31) [umoeller]
1149 arc = doshOpenDrive(ulLogicalDrive,
1150 &hf);
1151
1152 // this still returns NO_ERROR for audio CDs in a
1153 // CD-ROM drive...
1154 // however, the WPS then attempts to read in the
1155 // root directory for audio CDs, which produces
1156 // a "sector not found" error box...
1157
1158 if ( (!arc)
1159 && (hf)
1160 && (fCDROM)
1161 )
1162 {
1163 BOOL fAudio;
1164 if ( (!(arc = doshHasAudioCD(hf,
1165 ((fl & DRVFL_MIXEDMODECD) != 0),
1166 &fAudio)))
1167 && (fAudio)
1168 )
1169 arc = ERROR_AUDIO_CD_ROM; // special private error code (10000)
1170 }
1171
1172 if (hf)
1173 DosClose(hf);
1174
1175 return arc;
1176}
1177
1178/*
1179 *@@ doshAssertDrive:
1180 * this checks for whether the given drive
1181 * is currently available without provoking
1182 * those ugly white "Drive not ready" popups.
1183 *
1184 * "fl" can specify additional flags for testing
1185 * and can be any combination of:
1186 *
1187 * -- DRVFL_MIXEDMODECD: whether to allow
1188 * mixed-mode CD-ROMs. See error codes below.
1189 *
1190 * This returns (from my testing):
1191 *
1192 * -- NO_ERROR: drive is available.
1193 *
1194 * -- ERROR_INVALID_DRIVE (15): drive letter does not exist.
1195 *
1196 * -- ERROR_NOT_READY (21): drive exists, but is not ready.
1197 * This is produced by floppies and CD-ROM drives
1198 * which do not have valid media inserted.
1199 *
1200 * -- ERROR_AUDIO_CD_ROM (10000): special error code returned
1201 * only by this function if a CD-ROM drive has audio
1202 * media inserted.
1203 *
1204 * If DRVFL_MIXEDMODECD was specified, ERROR_AUDIO_CD_ROM
1205 * is returned _only_ if _no_ data tracks are
1206 * present on a CD-ROM. Since OS/2 is not very
1207 * good at handling mixed-mode CDs, this might not
1208 * be desireable.
1209 *
1210 * If DRVFL_MIXEDMODECD was not set, ERROR_AUDIO_CD_ROM
1211 * will be returned already if _one_ audio track is present.
1212 *
1213 *@@changed V0.9.1 (99-12-13) [umoeller]: rewritten, prototype changed. Now using DosOpen on the drive instead of DosError.
1214 *@@changed V0.9.1 (2000-01-08) [umoeller]: DosClose was called even if DosOpen failed, which messed up OS/2 error handling.
1215 *@@changed V0.9.1 (2000-02-09) [umoeller]: this didn't work for network drives, including RAMFS; fixed.
1216 *@@changed V0.9.3 (2000-03-28) [umoeller]: added check for network drives, which weren't working
1217 *@@changed V0.9.4 (2000-08-03) [umoeller]: more network fixes
1218 *@@changed V0.9.9 (2001-03-19) [pr]: validate drive number
1219 *@@changed V0.9.11 (2001-04-23) [umoeller]: added an extra check for floppies
1220 *@@changed V0.9.13 (2001-06-14) [umoeller]: added "fl" parameter and lots of CD-ROM checks
1221 */
1222
1223APIRET doshAssertDrive(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1224 ULONG fl) // in: DRVFL_* flags
1225{
1226 APIRET arc = NO_ERROR;
1227 BOOL fFixed = FALSE,
1228 fCDROM = FALSE;
1229
1230 if ((ulLogicalDrive < 1) || (ulLogicalDrive > 26))
1231 return(ERROR_PATH_NOT_FOUND);
1232
1233 arc = doshIsFixedDisk(ulLogicalDrive,
1234 &fFixed); // V0.9.13 (2001-06-14) [umoeller]
1235
1236 // _Pmpf((__FUNCTION__ ": doshIsFixedDisk returned %d for disk %d", arc, ulLogicalDrive));
1237 // _Pmpf((" fFixed is %d", fFixed));
1238
1239 if (!arc)
1240 if (!fFixed)
1241 {
1242 // removeable disk:
1243 // check if it's a CD-ROM
1244 BIOSPARAMETERBLOCK bpb;
1245 arc = doshQueryDiskParams(ulLogicalDrive,
1246 &bpb);
1247 // _Pmpf((" doshQueryDiskParams returned %d", arc));
1248
1249 if ( (!arc)
1250 && (DRVTYPE_CDROM == doshQueryDriveType(ulLogicalDrive,
1251 &bpb,
1252 fFixed))
1253 )
1254 {
1255 // _Pmpf((" --> is CD-ROM"));
1256 fCDROM = TRUE;
1257 }
1258 }
1259
1260 if (!arc)
1261 arc = doshQueryMedia(ulLogicalDrive,
1262 fCDROM,
1263 fl);
1264
1265 switch (arc)
1266 {
1267 case ERROR_NETWORK_ACCESS_DENIED: // 65
1268 // added V0.9.3 (2000-03-27) [umoeller];
1269 // according to user reports, this is returned
1270 // by all network drives, which apparently don't
1271 // support DASD DosOpen
1272 case ERROR_ACCESS_DENIED: // 5
1273 // added V0.9.4 (2000-07-10) [umoeller]
1274 // LAN drives still didn't work... apparently
1275 // the above only works for NFS drives
1276 case ERROR_PATH_NOT_FOUND: // 3
1277 // added V0.9.4 (2000-08-03) [umoeller]:
1278 // this is returned by some other network types...
1279 // sigh...
1280 case ERROR_NOT_SUPPORTED: // 50
1281 // this is returned by file systems which don't
1282 // support DASD DosOpen;
1283 // use some other method then, this isn't likely
1284 // to fail -- V0.9.1 (2000-02-09) [umoeller]
1285
1286 // but don't do this for floppies
1287 // V0.9.11 (2001-04-23) [umoeller]
1288 if (ulLogicalDrive > 2)
1289 {
1290 FSALLOCATE fsa;
1291 arc = DosQueryFSInfo(ulLogicalDrive,
1292 FSIL_ALLOC,
1293 &fsa,
1294 sizeof(fsa));
1295 // _Pmpf((" re-checked, DosQueryFSInfo returned %d", arc));
1296 }
1297 break;
1298 }
1299
1300 return arc;
1301}
1302
1303/*
1304 *@@ doshGetDriveInfo:
1305 * fills the given XDISKINFO buffer with
1306 * information about the given logical drive.
1307 *
1308 * This function will not provoke "Drive not
1309 * ready" popups, hopefully.
1310 *
1311 * fl can be any combination of the following:
1312 *
1313 * -- DRVFL_MIXEDMODECD: see doshAssertDrive.
1314 *
1315 * -- DRVFL_TOUCHFLOPPIES: drive A: and B: should
1316 * be touched for media checks (click, click);
1317 * otherwise they will be left alone and
1318 * default values will be returned.
1319 *
1320 * -- DRVFL_CHECKEAS: drive should always be
1321 * checked for EA support. If this is set,
1322 * we will call DosFSCtl for the non-well-known
1323 * file systems so we will always have a
1324 * value for the DFL_SUPPORTS_EAS flags.
1325 * The EA support returned by DosFSCtl
1326 * might not be correct for remote file
1327 * systems since not all of them support
1328 * that query.
1329 *
1330 * If not set, we set DFL_SUPPORTS_EAS only
1331 * for file systems such as HPFS and JFS
1332 * that are known to support EAs.
1333 *
1334 * -- DRVFL_CHECKLONGNAMES: drive should be
1335 * tested for longname support. If this is
1336 * set, we will try a DosOpen("\\long.name.file")
1337 * on the drive to see if it supports long
1338 * filenames (unless it's a "well-known"
1339 * file-system and we know it does). If enabled,
1340 * the DFL_SUPPORTS_LONGNAMES flag is reliable.
1341 * Note that this does not check for what special
1342 * characters are supported in file names.
1343 *
1344 * This should return only one of the following:
1345 *
1346 * -- NO_ERROR: disk info was filled, but not
1347 * necessarily all info was available (e.g.
1348 * if no media was present in CD-ROM drive).
1349 * See remarks below.
1350 *
1351 * -- ERROR_INVALID_DRIVE 15): ulLogicalDrive
1352 * is not used at all (invalid drive letter)
1353 *
1354 * -- ERROR_BAD_UNIT (20): if drive was renamed for
1355 * some reason (according to user reports
1356 *
1357 * -- ERROR_NOT_READY (21): for ZIP disks where
1358 * no media is inserted, depending on the
1359 * driver apparently... normally ZIP drive
1360 * letters should disappear when no media
1361 * is present
1362 *
1363 * -- ERROR_DRIVE_LOCKED (108)
1364 *
1365 * So in order to check whether a drive is present
1366 * and available, use this function as follows:
1367 *
1368 * 1) Call this function and check whether it
1369 * returns NO_ERROR for the given drive.
1370 * This will rule out invalid drive letters
1371 * and drives that are presently locked by
1372 * CHKDSK or something.
1373 *
1374 * 2) If so, check whether XDISKINFO.flDevice
1375 * has the DFL_MEDIA_PRESENT flag set.
1376 * This will rule out removeable drives without
1377 * media and unformatted hard disks.
1378 *
1379 * 3) If so, you can test the other fields if
1380 * you need more information. For example,
1381 * it would not be a good idea to create
1382 * a new file if the bType field is
1383 * DRVTYPE_CDROM.
1384 *
1385 * If you want to exclude removeable disks,
1386 * instead of checking bType, you should
1387 * rather check flDevice for the DFL_FIXED
1388 * flag, which will be set for ZIP drives also.
1389 *
1390 * Remarks for special drive types:
1391 *
1392 * -- Hard disks always have bType == DRVTYPE_HARDDISK.
1393 * For them, we always check the file system.
1394 * If this is reported as "UNKNOWN", this means
1395 * that the drive is unformatted or formatted
1396 * with a file system that OS/2 does not understand
1397 * (e.g. NTFS). Only in that case, flDevice
1398 * has the DFL_MEDIA_PRESENT bit clear.
1399 *
1400 * DFL_FIXED is always set.
1401 *
1402 * -- Remote (LAN) drives always have bType == DRVTYPE_LAN.
1403 * flDevice will always have the DFL_REMOTE and
1404 * DFL_MEDIA_PRESENT bits set.
1405 *
1406 * -- ZIP disks will have bType == DRVTYPE_PARTITIONABLEREMOVEABLE.
1407 * For them, flDevice will have both the
1408 * and DFL_PARTITIONABLEREMOVEABLE and DFL_FIXED
1409 * bits set.
1410 *
1411 * ZIP disks are a bit special because they are
1412 * dynamically mounted and unmounted when media
1413 * is inserted and removed. In other words, if
1414 * no media is present, the drive letter becomes
1415 * invalid.
1416 *
1417 * -- CD-ROM and DVD drives and CD writers will always
1418 * be reported as DRVTYPE_CDROM. The DFL_FIXED bit
1419 * will be clear always. For them, always check the
1420 * DFL_MEDIA_PRESENT present bit to avoid "Drive not
1421 * ready" popups.
1422 *
1423 * As a special goody, we can also determine if the
1424 * drive currently has audio media inserted (which
1425 * would provoke errors also), by setting the
1426 * DFL_AUDIO_CD bit.
1427 *
1428 *@@added V0.9.16 (2002-01-13) [umoeller]
1429 *@@changed V0.9.19 (2002-04-25) [umoeller]: added CDWFS (RSJ CD-Writer)
1430 */
1431
1432APIRET doshGetDriveInfo(ULONG ulLogicalDrive,
1433 ULONG fl, // in: DRVFL_* flags
1434 PXDISKINFO pdi)
1435{
1436 APIRET arc = NO_ERROR;
1437
1438 HFILE hf;
1439 ULONG dummy;
1440 BOOL fCheck = TRUE,
1441 fCheckFS = FALSE,
1442 fCheckLongnames = FALSE,
1443 fCheckEAs = FALSE;
1444
1445 memset(pdi, 0, sizeof(XDISKINFO));
1446
1447 pdi->cDriveLetter = 'A' + ulLogicalDrive - 1;
1448 pdi->cLogicalDrive = ulLogicalDrive;
1449
1450 pdi->bType = DRVTYPE_UNKNOWN;
1451 pdi->fPresent = TRUE; // for now
1452
1453 if ( (ulLogicalDrive == 1)
1454 || (ulLogicalDrive == 2)
1455 )
1456 {
1457 // drive A: and B: are special cases,
1458 // we don't even want to touch them (click, click)
1459 pdi->bType = DRVTYPE_FLOPPY;
1460
1461 if (0 == (fl & DRVFL_TOUCHFLOPPIES))
1462 {
1463 fCheck = FALSE;
1464 // these support EAs too
1465 pdi->flDevice = DFL_MEDIA_PRESENT | DFL_SUPPORTS_EAS;
1466 strcpy(pdi->szFileSystem, "FAT");
1467 pdi->lFileSystem = FSYS_FAT;
1468 }
1469 }
1470
1471 if (fCheck)
1472 {
1473 // any other drive:
1474 // check if it's removeable first
1475 BOOL fFixed = FALSE;
1476 arc = doshIsFixedDisk(ulLogicalDrive,
1477 &fFixed);
1478
1479 switch (arc)
1480 {
1481 case ERROR_INVALID_DRIVE:
1482 // drive letter doesn't exist at all:
1483 pdi->fPresent = FALSE;
1484 // return this APIRET
1485 break;
1486
1487 case ERROR_NOT_SUPPORTED: // 50 for network drives
1488 // we get this for remote drives added
1489 // via "net use", so set these flags
1490 pdi->bType = DRVTYPE_LAN;
1491 pdi->lFileSystem = FSYS_REMOTE;
1492 pdi->flDevice |= DFL_REMOTE | DFL_MEDIA_PRESENT;
1493 // but still check what file-system we
1494 // have and whether longnames are supported
1495 fCheckFS = TRUE;
1496 fCheckLongnames = TRUE;
1497 fCheckEAs = TRUE;
1498 break;
1499
1500 case NO_ERROR:
1501 {
1502 if (fFixed)
1503 {
1504 // fixed drive:
1505 pdi->flDevice |= DFL_FIXED | DFL_MEDIA_PRESENT;
1506
1507 fCheckFS = TRUE;
1508 fCheckLongnames = TRUE;
1509 fCheckEAs = TRUE;
1510 }
1511
1512 if (!(arc = doshQueryDiskParams(ulLogicalDrive,
1513 &pdi->bpb)))
1514 {
1515 BYTE bTemp = doshQueryDriveType(ulLogicalDrive,
1516 &pdi->bpb,
1517 fFixed);
1518 if (bTemp != DRVTYPE_UNKNOWN)
1519 {
1520 // recognized: store it then
1521 pdi->bType = bTemp;
1522
1523 if (bTemp == DRVTYPE_PRT)
1524 pdi->flDevice |= DFL_FIXED
1525 | DFL_PARTITIONABLEREMOVEABLE;
1526 }
1527
1528 if (!fFixed)
1529 {
1530 // removeable:
1531
1532 // before checking the drive, try if we have media
1533 if (!(arc = doshQueryMedia(ulLogicalDrive,
1534 (pdi->bType == DRVTYPE_CDROM),
1535 fl)))
1536 {
1537 pdi->flDevice |= DFL_MEDIA_PRESENT;
1538 fCheckFS = TRUE;
1539 fCheckLongnames = TRUE;
1540 // but never EAs
1541 }
1542 else if (arc == ERROR_AUDIO_CD_ROM)
1543 {
1544 pdi->flDevice |= DFL_AUDIO_CD;
1545 // do not check longnames and file-system
1546 }
1547 else
1548 pdi->arcQueryMedia = arc;
1549
1550 arc = NO_ERROR;
1551 }
1552 }
1553 else
1554 pdi->arcQueryDiskParams = arc;
1555 }
1556 break;
1557
1558 default:
1559 pdi->arcIsFixedDisk = arc;
1560 // and return this
1561 break;
1562
1563 } // end swich arc = doshIsFixedDisk(ulLogicalDrive, &fFixed);
1564 }
1565
1566 if (fCheckFS)
1567 {
1568 // TRUE only for local fixed disks or
1569 // remote drives or if media was present above
1570 if (!(arc = doshQueryDiskFSType(ulLogicalDrive,
1571 pdi->szFileSystem,
1572 sizeof(pdi->szFileSystem))))
1573 {
1574 if (!stricmp(pdi->szFileSystem, "UNKNOWN"))
1575 {
1576 // this is returned by the stupid DosQueryFSAttach
1577 // if the file system is not recognized by OS/2,
1578 // or if the drive is unformatted
1579 pdi->lFileSystem = FSYS_UNKNOWN;
1580 pdi->flDevice &= ~DFL_MEDIA_PRESENT;
1581 fCheckLongnames = FALSE;
1582 fCheckEAs = FALSE;
1583 // should we return ERROR_NOT_DOS_DISK (26)
1584 // in this case?
1585 }
1586 else if (!stricmp(pdi->szFileSystem, "FAT"))
1587 {
1588 pdi->lFileSystem = FSYS_FAT;
1589 pdi->flDevice |= DFL_SUPPORTS_EAS;
1590 fCheckLongnames = FALSE;
1591 fCheckEAs = FALSE;
1592 }
1593 else if ( (!stricmp(pdi->szFileSystem, "HPFS"))
1594 || (!stricmp(pdi->szFileSystem, "JFS"))
1595 )
1596 {
1597 pdi->lFileSystem = FSYS_HPFS_JFS;
1598 pdi->flDevice |= DFL_SUPPORTS_EAS | DFL_SUPPORTS_LONGNAMES;
1599 fCheckLongnames = FALSE;
1600 fCheckEAs = FALSE;
1601 }
1602 else if (!stricmp(pdi->szFileSystem, "CDFS"))
1603 pdi->lFileSystem = FSYS_CDFS;
1604 else if ( (!stricmp(pdi->szFileSystem, "FAT32"))
1605 || (!stricmp(pdi->szFileSystem, "ext2"))
1606 )
1607 {
1608 pdi->lFileSystem = FSYS_FAT32_EXT2;
1609 fCheckLongnames = TRUE;
1610 fCheckEAs = TRUE;
1611 }
1612 else if (!stricmp(pdi->szFileSystem, "RAMFS"))
1613 {
1614 pdi->lFileSystem = FSYS_RAMFS;
1615 pdi->flDevice |= DFL_SUPPORTS_EAS | DFL_SUPPORTS_LONGNAMES;
1616 fCheckLongnames = FALSE;
1617 fCheckEAs = FALSE;
1618 }
1619 else if (!stricmp(pdi->szFileSystem, "TVFS"))
1620 {
1621 pdi->lFileSystem = FSYS_TVFS;
1622 fCheckLongnames = TRUE;
1623 fCheckEAs = TRUE;
1624 }
1625 else if (!stricmp(pdi->szFileSystem, "CDWFS"))
1626 // V0.9.19 (2002-04-25) [umoeller]
1627 {
1628 pdi->lFileSystem = FSYS_CDWFS;
1629 pdi->flDevice |= DFL_SUPPORTS_LONGNAMES;
1630 fCheckLongnames = FALSE;
1631 fCheckEAs = FALSE;
1632 }
1633 }
1634 else
1635 // store negative error code
1636 pdi->lFileSystem = -(LONG)arc;
1637 }
1638
1639 if ( (!arc)
1640 && (fCheckLongnames)
1641 && (fl & DRVFL_CHECKLONGNAMES)
1642 )
1643 {
1644 CHAR szTemp[30] = "?:\\long.name.file";
1645 szTemp[0] = ulLogicalDrive + 'A' - 1;
1646 if (!(arc = DosOpen(szTemp,
1647 &hf,
1648 &dummy,
1649 0,
1650 0,
1651 FILE_READONLY,
1652 OPEN_SHARE_DENYNONE | OPEN_FLAGS_NOINHERIT,
1653 0)))
1654 {
1655 DosClose(hf);
1656 }
1657
1658 switch (arc)
1659 {
1660 case NO_ERROR:
1661 case ERROR_OPEN_FAILED:
1662 case ERROR_FILE_NOT_FOUND: // returned by TVFS
1663 pdi->flDevice |= DFL_SUPPORTS_LONGNAMES;
1664 break;
1665
1666 // if longnames are not supported,
1667 // we get ERROR_INVALID_NAME
1668 default:
1669 pdi->arcOpenLongnames = arc;
1670 break;
1671
1672 // default:
1673 // printf(" drive %d returned %d\n", ulLogicalDrive, arc);
1674 }
1675
1676 arc = NO_ERROR;
1677 }
1678
1679 if ( (!arc)
1680 && (fCheckEAs)
1681 && (fl & DRVFL_CHECKEAS)
1682 )
1683 {
1684 EASIZEBUF easb = {0};
1685 ULONG cbData = sizeof(easb),
1686 cbParams = 0;
1687 CHAR szDrive[] = "?:\\";
1688 szDrive[0] = pdi->cDriveLetter;
1689 if (!(arc = DosFSCtl(&easb,
1690 cbData,
1691 &cbData,
1692 NULL, // params,
1693 cbParams,
1694 &cbParams,
1695 FSCTL_MAX_EASIZE,
1696 szDrive,
1697 -1, // HFILE
1698 FSCTL_PATHNAME)))
1699 if (easb.cbMaxEASize != 0)
1700 // the other field (cbMaxEAListSize) is 0 always, I think
1701 pdi->flDevice |= DFL_SUPPORTS_EAS;
1702 }
1703
1704 if (doshQueryBootDrive() == pdi->cDriveLetter)
1705 pdi->flDevice |= DFL_BOOTDRIVE;
1706
1707 return arc;
1708}
1709
1710/*
1711 *@@ doshSetLogicalMap:
1712 * sets the mapping of logical floppy drives onto a single
1713 * physical floppy drive.
1714 * This means selecting either drive A: or drive B: to refer
1715 * to the physical drive.
1716 *
1717 * Paul explained this to me as follows:
1718 *
1719 * "It is really very simple - in a single physical floppy
1720 * drive system, you still have 2 logical floppy drives
1721 * A: and B:. This was primarily to support disk copying
1722 * e.g. diskcopy a: b: on a single floppy system without
1723 * having to rewrite applications. Whenever the application
1724 * accessed the other logical drive, the user would get a
1725 * prompt from the OS to swap disks.
1726 *
1727 * "These calls allow applications to bypass the prompt by
1728 * doing the mapping themselves. They just get/set which
1729 * logical drive is currently mapped to the physical drive.
1730 * This concept existed in DOS as well although the specifics
1731 * of how it was done escape me now.... actually I just
1732 * looked it up - the byte at 0:504h in low memory on
1733 * DOS controlled this (it doesn't work in VDMs)."
1734 *
1735 *@@added V0.9.6 (2000-11-24) [pr]
1736 */
1737
1738APIRET doshSetLogicalMap(ULONG ulLogicalDrive)
1739{
1740 CHAR name[3] = "?:";
1741 ULONG fd = 0,
1742 action = 0;
1743// paramsize = 0;
1744// datasize = 0;
1745 APIRET rc = NO_ERROR;
1746 USHORT data,
1747 param;
1748
1749 name[0] = doshQueryBootDrive();
1750 rc = DosOpen(name,
1751 &fd,
1752 &action,
1753 0,
1754 0,
1755 OPEN_ACTION_FAIL_IF_NEW
1756 | OPEN_ACTION_OPEN_IF_EXISTS,
1757 OPEN_FLAGS_DASD
1758 | OPEN_FLAGS_FAIL_ON_ERROR
1759 | OPEN_FLAGS_NOINHERIT
1760 | OPEN_ACCESS_READONLY
1761 | OPEN_SHARE_DENYNONE,
1762 0);
1763
1764 if (rc == NO_ERROR)
1765 {
1766 param = 0;
1767 data = (USHORT)ulLogicalDrive;
1768 // paramsize = sizeof(param);
1769 // datasize = sizeof(data);
1770 rc = doshDevIOCtl(fd,
1771 IOCTL_DISK, DSK_SETLOGICALMAP,
1772 &param, sizeof(param),
1773 &data, sizeof(data));
1774 DosClose(fd);
1775 }
1776
1777 return(rc);
1778}
1779
1780/*
1781 *@@ doshQueryDiskSize:
1782 * returns the size of the specified disk in bytes.
1783 *
1784 * Note: This returns a "double" value, because a ULONG
1785 * can only hold values of some 4 billion, which would
1786 * lead to funny results for drives > 4 GB.
1787 *
1788 *@@added V0.9.11 (2001-04-18) [umoeller]
1789 */
1790
1791APIRET doshQueryDiskSize(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1792 double *pdSize)
1793{
1794 APIRET arc = NO_ERROR;
1795 FSALLOCATE fsa;
1796 // double dbl = -1;
1797
1798 if (!(arc = DosQueryFSInfo(ulLogicalDrive, FSIL_ALLOC, &fsa, sizeof(fsa))))
1799 *pdSize = ((double)fsa.cSectorUnit * fsa.cbSector * fsa.cUnit);
1800
1801 return arc;
1802}
1803
1804/*
1805 *@@ doshQueryDiskFree:
1806 * returns the number of bytes remaining on the disk
1807 * specified by the given logical drive.
1808 *
1809 * Note: This returns a "double" value, because a ULONG
1810 * can only hold values of some 4 billion, which would
1811 * lead to funny results for drives > 4 GB.
1812 *
1813 *@@changed V0.9.0 [umoeller]: fixed another > 4 GB bug (thanks to Rdiger Ihle)
1814 *@@changed V0.9.7 (2000-12-01) [umoeller]: changed prototype
1815 */
1816
1817APIRET doshQueryDiskFree(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1818 double *pdFree)
1819{
1820 APIRET arc = NO_ERROR;
1821 FSALLOCATE fsa;
1822 // double dbl = -1;
1823
1824 if (!(arc = DosQueryFSInfo(ulLogicalDrive, FSIL_ALLOC, &fsa, sizeof(fsa))))
1825 *pdFree = ((double)fsa.cSectorUnit * fsa.cbSector * fsa.cUnitAvail);
1826 // ^ fixed V0.9.0
1827
1828 return arc;
1829}
1830
1831/*
1832 *@@ doshQueryDiskFSType:
1833 * copies the file-system type of the given disk object
1834 * (HPFS, FAT, CDFS etc.) to pszBuf.
1835 * Returns the DOS error code.
1836 *
1837 *@@changed V0.9.1 (99-12-12) [umoeller]: added cbBuf to prototype
1838 *@@changed V0.9.14 (2001-08-01) [umoeller]: fixed, this never respected cbBuf
1839 *@@changed V0.9.16 (2001-10-02) [umoeller]: added check for valid logical disk no
1840 */
1841
1842APIRET doshQueryDiskFSType(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1843 PSZ pszBuf, // out: buffer for FS type
1844 ULONG cbBuf) // in: size of that buffer
1845{
1846 APIRET arc = NO_ERROR;
1847 CHAR szName[5];
1848
1849 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
1850 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
1851 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
1852
1853 // compose "D:"-type string from logical drive letter
1854 if (ulLogicalDrive > 0 && ulLogicalDrive < 27)
1855 {
1856 szName[0] = ulLogicalDrive + 'A' - 1;
1857 szName[1] = ':';
1858 szName[2] = '\0';
1859
1860 arc = DosQueryFSAttach(szName, // logical drive of attached FS ("D:"-style)
1861 0, // ulOrdinal, ignored for FSAIL_QUERYNAME
1862 FSAIL_QUERYNAME, // return name for a drive or device
1863 pfsqBuffer, // buffer for returned data
1864 &cbBuffer); // sizeof(*pfsqBuffer)
1865
1866 if (arc == NO_ERROR)
1867 {
1868 if (pszBuf)
1869 {
1870 // The data for the last three fields in the FSQBUFFER2
1871 // structure are stored at the offset of fsqBuffer.szName.
1872 // Each data field following fsqBuffer.szName begins
1873 // immediately after the previous item.
1874 strncpy(pszBuf,
1875 (CHAR*)(&pfsqBuffer->szName) + pfsqBuffer->cbName + 1,
1876 cbBuf); // V0.9.14 (2001-08-01) [umoeller]
1877 *(pszBuf + cbBuf) = '\0';
1878 }
1879 }
1880 }
1881 else
1882 arc = ERROR_INVALID_PARAMETER; // V0.9.16 (2001-10-02) [umoeller]
1883
1884 return arc;
1885}
1886
1887/*
1888 *@@ doshQueryDiskLabel:
1889 * this returns the label of a disk into
1890 * *pszVolumeLabel, which must be 12 bytes
1891 * in size.
1892 *
1893 * This function was added because the Toolkit
1894 * information for DosQueryFSInfo is only partly
1895 * correct. On OS/2 2.x, that function does not
1896 * take an FSINFO structure as input, but a VOLUMELABEL.
1897 * On Warp, this does take an FSINFO.
1898 *
1899 * DosSetFSInfo is even worse. See doshSetDiskLabel.
1900 *
1901 * See http://zebra.asta.fh-weingarten.de/os2/Snippets/Bugi6787.HTML
1902 * for details.
1903 *
1904 *@@added V0.9.0 [umoeller]
1905 *@@changed V0.9.11 (2001-04-22) [umoeller]: this copied even with errors, fixed
1906 */
1907
1908APIRET doshQueryDiskLabel(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1909 PSZ pszVolumeLabel) // out: volume label (must be 12 chars in size)
1910{
1911 APIRET arc;
1912
1913 #ifdef __OS2V2X__
1914 VOLUMELABEL FSInfoBuf;
1915 #else
1916 FSINFO FSInfoBuf;
1917 #endif
1918
1919 arc = DosQueryFSInfo(ulLogicalDrive,
1920 FSIL_VOLSER,
1921 &FSInfoBuf,
1922 sizeof(FSInfoBuf)); // depends
1923
1924 if (!arc) // V0.9.11 (2001-04-22) [umoeller]
1925 {
1926 #ifdef __OS2V2X__
1927 strcpy(pszVolumeLabel, FSInfoBuf.szVolLabel);
1928 #else
1929 strcpy(pszVolumeLabel, FSInfoBuf.vol.szVolLabel);
1930 #endif
1931 }
1932
1933 return arc;
1934}
1935
1936/*
1937 *@@ doshSetDiskLabel:
1938 * this sets the label of a disk.
1939 *
1940 * This function was added because the Toolkit
1941 * information for DosSetFSInfo is flat out wrong.
1942 * That function does not take an FSINFO structure
1943 * as input, but a VOLUMELABEL. As a result, using
1944 * that function with the Toolkit's calling specs
1945 * results in ERROR_LABEL_TOO_LONG always.
1946 *
1947 * See http://zebra.asta.fh-weingarten.de/os2/Snippets/Bugi6787.HTML
1948 * for details.
1949 *
1950 *@@added V0.9.0 [umoeller]
1951 */
1952
1953APIRET doshSetDiskLabel(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1954 PSZ pszNewLabel)
1955{
1956 VOLUMELABEL FSInfoBuf;
1957
1958 // check length; 11 chars plus null byte allowed
1959 FSInfoBuf.cch = (BYTE)strlen(pszNewLabel);
1960 if (FSInfoBuf.cch < sizeof(FSInfoBuf.szVolLabel))
1961 {
1962 strcpy(FSInfoBuf.szVolLabel, pszNewLabel);
1963
1964 return (DosSetFSInfo(ulLogicalDrive,
1965 FSIL_VOLSER,
1966 &FSInfoBuf,
1967 sizeof(FSInfoBuf)));
1968 }
1969 else
1970 return (ERROR_LABEL_TOO_LONG);
1971}
1972
1973/*
1974 *@@category: Helpers\Control program helpers\File name parsing
1975 */
1976
1977/* ******************************************************************
1978 *
1979 * File name parsing
1980 *
1981 ********************************************************************/
1982
1983/*
1984 *@@ doshGetDriveSpec:
1985 * returns the drive specification in pcszFullFile,
1986 * if any is present. This is useful for UNC support.
1987 *
1988 * This returns:
1989 *
1990 * -- NO_ERROR: drive spec was given, and the output
1991 * fields have been set.
1992 *
1993 * -- ERROR_INVALID_NAME: incorrect UNC syntax.
1994 *
1995 * -- ERROR_INVALID_DRIVE: second char is ':', but
1996 * drive letter is not in the range [A-Z].
1997 *
1998 * -- ERROR_INVALID_PARAMETER: no drive spec given
1999 * at all; apparently pcszFullFile is not fully
2000 * qualified in the first place, or it is NULL,
2001 * or its length is <= 2.
2002 *
2003 *@@added V0.9.16 (2001-10-25) [umoeller]
2004 */
2005
2006APIRET doshGetDriveSpec(PCSZ pcszFullFile, // in: fully q'fied file spec
2007 PSZ pszDrive, // out: drive spec ("C:" or "\\SERVER\RESOURCE"; ptr can be NULL)
2008 PULONG pulDriveLen, // out: length of drive spec (2 if local drive; ptr can be NULL)
2009 PBOOL pfIsUNC) // out: set to TRUE if UNC name, FALSE otherwise (ptr can be NULL)
2010{
2011 APIRET arc = NO_ERROR;
2012 ULONG ulFileSpecLength;
2013
2014 if ( (pcszFullFile)
2015 && (ulFileSpecLength = strlen(pcszFullFile))
2016 && (ulFileSpecLength >= 2)
2017 )
2018 {
2019 // upper-case the drive letter
2020 if (pcszFullFile[1] == ':')
2021 {
2022 CHAR cDrive = toupper(*pcszFullFile);
2023 // local drive specified:
2024 if ( (cDrive >= 'A')
2025 && (cDrive <= 'Z')
2026 )
2027 {
2028 if (pszDrive)
2029 {
2030 pszDrive[0] = cDrive;
2031 pszDrive[1] = ':';
2032 pszDrive[2] = '\0';
2033 }
2034
2035 if (pulDriveLen)
2036 *pulDriveLen = 2;
2037 if (pfIsUNC)
2038 *pfIsUNC = FALSE;
2039 }
2040 else
2041 // this is not a valid drive:
2042 arc = ERROR_INVALID_DRIVE;
2043 }
2044 else if ( (pcszFullFile[0] == '\\')
2045 && (pcszFullFile[1] == '\\')
2046 )
2047 {
2048 // UNC drive specified:
2049 // this better be a full \\SERVER\RESOURCE string
2050 PCSZ pResource;
2051 if (pResource = strchr(pcszFullFile + 3, '\\'))
2052 {
2053 // we got at least \\SERVER\:
2054 ULONG ulLength;
2055 PCSZ p;
2056
2057 // check if more stuff is coming
2058 if (p = strchr(pResource + 1, '\\'))
2059 {
2060 // yes: copy server and resource excluding that backslash
2061 if (p == pResource + 1)
2062 // "\\SERVER\\" is invalid
2063 arc = ERROR_INVALID_NAME;
2064 else
2065 // we got "\\SERVER\something\":
2066 // drop the last backslash
2067 ulLength = p - pcszFullFile;
2068 }
2069 else
2070 // "\\SERVER\something" only:
2071 ulLength = ulFileSpecLength;
2072
2073 if (!arc)
2074 {
2075 if (pszDrive)
2076 {
2077 memcpy(pszDrive,
2078 pcszFullFile,
2079 ulLength);
2080 pszDrive[ulLength] = '\0';
2081 }
2082
2083 if (pulDriveLen)
2084 *pulDriveLen = ulLength;
2085 if (pfIsUNC)
2086 *pfIsUNC = TRUE;
2087 }
2088 }
2089 else
2090 // invalid UNC name:
2091 arc = ERROR_INVALID_NAME;
2092 }
2093 else
2094 // neither local, nor UNC:
2095 arc = ERROR_INVALID_PARAMETER;
2096 }
2097 else
2098 arc = ERROR_INVALID_PARAMETER;
2099
2100 return arc;
2101}
2102
2103/*
2104 *@@ doshGetExtension:
2105 * finds the file name extension of pszFilename,
2106 * which can be a file name only or a fully
2107 * qualified filename.
2108 *
2109 * This returns a pointer into pszFilename to
2110 * the character after the last dot.
2111 *
2112 * Returns NULL if not found (e.g. if the filename
2113 * has no dot in it).
2114 *
2115 * In the pathological case of a dot in the path
2116 * but not in the filename itself (e.g.
2117 * "C:\files.new\readme"), this correctly returns
2118 * NULL.
2119 *
2120 *@@added V0.9.6 (2000-10-16) [umoeller]
2121 *@@changed V0.9.7 (2000-12-10) [umoeller]: fixed "F:filename.ext" case
2122 */
2123
2124PSZ doshGetExtension(PCSZ pcszFilename)
2125{
2126 PSZ pReturn = NULL;
2127
2128 if (pcszFilename)
2129 {
2130 // find filename
2131 PCSZ p2,
2132 pStartOfName = NULL,
2133 pExtension = NULL;
2134
2135 if (p2 = strrchr(pcszFilename + 2, '\\'))
2136 // works on "C:\blah" or "\\unc\blah"
2137 pStartOfName = p2 + 1;
2138 else
2139 {
2140 // no backslash found:
2141 // maybe only a drive letter was specified:
2142 if (pcszFilename[1] == ':')
2143 // yes:
2144 pStartOfName = pcszFilename + 2;
2145 else
2146 // then this is not qualified at all...
2147 // use start of filename
2148 pStartOfName = (PSZ)pcszFilename;
2149 }
2150
2151 // find last dot in filename
2152 if (pExtension = strrchr(pStartOfName, '.'))
2153 pReturn = (PSZ)pExtension + 1;
2154 }
2155
2156 return (pReturn);
2157}
2158
2159/*
2160 *@@category: Helpers\Control program helpers\File management
2161 */
2162
2163/* ******************************************************************
2164 *
2165 * File helpers
2166 *
2167 ********************************************************************/
2168
2169/*
2170 *@@ doshIsFileOnFAT:
2171 * returns TRUE if pszFileName resides on
2172 * a FAT drive. Note that pszFileName must
2173 * be fully qualified (i.e. the drive letter
2174 * must be the first character), or this will
2175 * return garbage.
2176 */
2177
2178BOOL doshIsFileOnFAT(const char* pcszFileName)
2179{
2180 BOOL brc = FALSE;
2181 CHAR szName[5];
2182
2183 APIRET arc;
2184 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
2185 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
2186 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
2187
2188 szName[0] = pcszFileName[0]; // copy drive letter
2189 szName[1] = ':';
2190 szName[2] = '\0';
2191
2192 if (!(arc = DosQueryFSAttach(szName, // logical drive of attached FS
2193 0, // ulOrdinal, ignored for FSAIL_QUERYNAME
2194 FSAIL_QUERYNAME, // return data for a Drive or Device
2195 pfsqBuffer, // returned data
2196 &cbBuffer))) // returned data length
2197 {
2198 // The data for the last three fields in the FSQBUFFER2
2199 // structure are stored at the offset of fsqBuffer.szName.
2200 // Each data field following fsqBuffer.szName begins
2201 // immediately after the previous item.
2202 if (!strncmp((PSZ)&(pfsqBuffer->szName) + pfsqBuffer->cbName + 1,
2203 "FAT",
2204 3))
2205 brc = TRUE;
2206 }
2207
2208 return brc;
2209}
2210
2211/*
2212 *@@ doshQueryFileSize:
2213 * returns the size of an already opened file
2214 * or 0 upon errors.
2215 * Use doshQueryPathSize to query the size of
2216 * any file.
2217 *
2218 *@@changed V0.9.16 (2001-10-19) [umoeller]: now returning APIRET
2219 */
2220
2221APIRET doshQueryFileSize(HFILE hFile, // in: file handle
2222 PULONG pulSize) // out: file size (ptr can be NULL)
2223{
2224 APIRET arc;
2225 FILESTATUS3 fs3;
2226 if (!(arc = DosQueryFileInfo(hFile, FIL_STANDARD, &fs3, sizeof(fs3))))
2227 if (pulSize)
2228 *pulSize = fs3.cbFile;
2229 return arc;
2230}
2231
2232/*
2233 *@@ doshQueryPathSize:
2234 * returns the size of any file,
2235 * or 0 if the file could not be
2236 * found.
2237 *
2238 * Use doshQueryFileSize instead to query the
2239 * size if you have a HFILE.
2240 *
2241 * Otherwise this returns:
2242 *
2243 * -- ERROR_FILE_NOT_FOUND
2244 * -- ERROR_PATH_NOT_FOUND
2245 * -- ERROR_SHARING_VIOLATION
2246 * -- ERROR_FILENAME_EXCED_RANGE
2247 * -- ERROR_INVALID_PARAMETER
2248 *
2249 *@@changed V0.9.16 (2001-10-19) [umoeller]: now returning APIRET
2250 */
2251
2252APIRET doshQueryPathSize(PCSZ pcszFile, // in: filename
2253 PULONG pulSize) // out: file size (ptr can be NULL)
2254{
2255 APIRET arc;
2256
2257 if (pcszFile) // V0.9.16 (2001-12-08) [umoeller]
2258 {
2259 FILESTATUS3 fs3;
2260 if (!(arc = DosQueryPathInfo((PSZ)pcszFile, FIL_STANDARD, &fs3, sizeof(fs3))))
2261 if (pulSize)
2262 *pulSize = fs3.cbFile;
2263 }
2264 else
2265 arc = ERROR_INVALID_PARAMETER;
2266
2267 return arc;
2268}
2269
2270/*
2271 *@@ doshQueryPathAttr:
2272 * returns the file attributes of pszFile,
2273 * which can be fully qualified. The
2274 * attributes will be stored in *pulAttr.
2275 * pszFile can also specify a directory,
2276 * although not all attributes make sense
2277 * for directories.
2278 *
2279 * fAttr can be:
2280 * -- FILE_ARCHIVED
2281 * -- FILE_READONLY
2282 * -- FILE_SYSTEM
2283 * -- FILE_HIDDEN
2284 *
2285 * This returns the APIRET of DosQueryPathInfo.
2286 * *pulAttr is only valid if NO_ERROR is
2287 * returned.
2288 *
2289 * Otherwise this returns:
2290 *
2291 * -- ERROR_FILE_NOT_FOUND
2292 * -- ERROR_PATH_NOT_FOUND
2293 * -- ERROR_SHARING_VIOLATION
2294 * -- ERROR_FILENAME_EXCED_RANGE
2295 *
2296 *@@added V0.9.0 [umoeller]
2297 */
2298
2299APIRET doshQueryPathAttr(const char* pcszFile, // in: file or directory name
2300 PULONG pulAttr) // out: attributes (ptr can be NULL)
2301{
2302 FILESTATUS3 fs3;
2303 APIRET arc;
2304
2305 if (!(arc = DosQueryPathInfo((PSZ)pcszFile,
2306 FIL_STANDARD,
2307 &fs3,
2308 sizeof(fs3))))
2309 {
2310 if (pulAttr)
2311 *pulAttr = fs3.attrFile;
2312 }
2313
2314 return arc;
2315}
2316
2317/*
2318 *@@ doshSetPathAttr:
2319 * sets the file attributes of pszFile,
2320 * which can be fully qualified.
2321 * pszFile can also specify a directory,
2322 * although not all attributes make sense
2323 * for directories.
2324 *
2325 * fAttr can be:
2326 * -- FILE_ARCHIVED
2327 * -- FILE_READONLY
2328 * -- FILE_SYSTEM
2329 * -- FILE_HIDDEN
2330 *
2331 * Note that this simply sets all the given
2332 * attributes; the existing attributes
2333 * are lost.
2334 *
2335 * This returns the APIRET of DosQueryPathInfo.
2336 */
2337
2338APIRET doshSetPathAttr(const char* pcszFile, // in: file or directory name
2339 ULONG ulAttr) // in: new attributes
2340{
2341 APIRET arc;
2342
2343 if (pcszFile)
2344 {
2345 FILESTATUS3 fs3;
2346 if (!(arc = DosQueryPathInfo((PSZ)pcszFile,
2347 FIL_STANDARD,
2348 &fs3,
2349 sizeof(fs3))))
2350 {
2351 fs3.attrFile = ulAttr;
2352 arc = DosSetPathInfo((PSZ)pcszFile,
2353 FIL_STANDARD,
2354 &fs3,
2355 sizeof(fs3),
2356 DSPI_WRTTHRU);
2357 }
2358 }
2359 else
2360 arc = ERROR_INVALID_PARAMETER;
2361
2362 return arc;
2363}
2364
2365/*
2366 *@@category: Helpers\Control program helpers\File management\XFILEs
2367 */
2368
2369/* ******************************************************************
2370 *
2371 * XFILEs
2372 *
2373 ********************************************************************/
2374
2375/*
2376 * doshOpen:
2377 * wrapper around DosOpen for simpler opening
2378 * of files.
2379 *
2380 * ulOpenMode determines the mode to open the
2381 * file in (fptr specifies the position after
2382 * the open):
2383 *
2384 + +-------------------------+------+------------+-----------+
2385 + | ulOpenMode | mode | if exists | if new |
2386 + +-------------------------+------+------------+-----------+
2387 + | XOPEN_READ_EXISTING | read | opens | fails |
2388 + | | | fptr = 0 | |
2389 + +-------------------------+------+------------+-----------+
2390 + | XOPEN_READWRITE_EXISTING r/w | opens | fails |
2391 + | | | fptr = 0 | |
2392 + +-------------------------+------+------------+-----------+
2393 + | XOPEN_READWRITE_APPEND | r/w | opens, | creates |
2394 + | | | appends | |
2395 + | | | fptr = end | fptr = 0 |
2396 + +-------------------------+------+------------+-----------+
2397 + | XOPEN_READWRITE_NEW | r/w | replaces | creates |
2398 + | | | fptr = 0 | fptr = 0 |
2399 + +-------------------------+------+------------+-----------+
2400 *
2401 * In addition, you can OR one of the above values with
2402 * the XOPEN_BINARY flag:
2403 *
2404 * -- If XOPEN_BINARY is set, no conversion is performed
2405 * on read and write.
2406 *
2407 * -- If XOPEN_BINARY is _not_ set, all \n chars are
2408 * converted to \r\n on write.
2409 *
2410 * *ppFile receives a new XFILE structure describing
2411 * the open file, if NO_ERROR is returned.
2412 *
2413 * The file pointer is then set to the beginning of the
2414 * file _unless_ XOPEN_READWRITE_APPEND was specified;
2415 * in that case only, the file pointer is set to the
2416 * end of the file so data can be appended (see above).
2417 *
2418 * Otherwise this returns:
2419 *
2420 * -- ERROR_FILE_NOT_FOUND
2421 * -- ERROR_PATH_NOT_FOUND
2422 * -- ERROR_SHARING_VIOLATION
2423 * -- ERROR_FILENAME_EXCED_RANGE
2424 *
2425 * -- ERROR_NOT_ENOUGH_MEMORY
2426 * -- ERROR_INVALID_PARAMETER
2427 *
2428 *@@added V0.9.16 (2001-10-19) [umoeller]
2429 *@@changed V0.9.16 (2001-12-18) [umoeller]: fixed error codes
2430 */
2431
2432APIRET doshOpen(PCSZ pcszFilename, // in: filename to open
2433 ULONG flOpenMode, // in: XOPEN_* mode
2434 PULONG pcbFile, // in: new file size (if new file is created)
2435 // out: file size
2436 PXFILE *ppFile)
2437{
2438 APIRET arc = NO_ERROR;
2439
2440 ULONG fsOpenFlags = 0,
2441 fsOpenMode = OPEN_FLAGS_FAIL_ON_ERROR
2442 | OPEN_FLAGS_NO_LOCALITY
2443 | OPEN_FLAGS_NOINHERIT;
2444
2445 switch (flOpenMode & XOPEN_ACCESS_MASK)
2446 {
2447 case XOPEN_READ_EXISTING:
2448 fsOpenFlags = OPEN_ACTION_FAIL_IF_NEW
2449 | OPEN_ACTION_OPEN_IF_EXISTS;
2450 fsOpenMode |= OPEN_SHARE_DENYWRITE
2451 | OPEN_ACCESS_READONLY;
2452
2453 // run this first, because if the file doesn't
2454 // exists, DosOpen only returns ERROR_OPEN_FAILED,
2455 // which isn't that meaningful
2456 // V0.9.16 (2001-12-08) [umoeller]
2457 arc = doshQueryPathSize(pcszFilename,
2458 pcbFile);
2459 break;
2460
2461 case XOPEN_READWRITE_EXISTING:
2462 fsOpenFlags = OPEN_ACTION_FAIL_IF_NEW
2463 | OPEN_ACTION_OPEN_IF_EXISTS;
2464 fsOpenMode |= OPEN_SHARE_DENYWRITE
2465 | OPEN_ACCESS_READWRITE;
2466
2467 arc = doshQueryPathSize(pcszFilename,
2468 pcbFile);
2469 break;
2470
2471 case XOPEN_READWRITE_APPEND:
2472 fsOpenFlags = OPEN_ACTION_CREATE_IF_NEW
2473 | OPEN_ACTION_OPEN_IF_EXISTS;
2474 fsOpenMode |= OPEN_SHARE_DENYREADWRITE
2475 | OPEN_ACCESS_READWRITE;
2476 // _Pmpf((__FUNCTION__ ": opening XOPEN_READWRITE_APPEND"));
2477 break;
2478
2479 case XOPEN_READWRITE_NEW:
2480 fsOpenFlags = OPEN_ACTION_CREATE_IF_NEW
2481 | OPEN_ACTION_REPLACE_IF_EXISTS;
2482 fsOpenMode |= OPEN_SHARE_DENYREADWRITE
2483 | OPEN_ACCESS_READWRITE;
2484 // _Pmpf((__FUNCTION__ ": opening XOPEN_READWRITE_NEW"));
2485 break;
2486 }
2487
2488 if ((!arc) && fsOpenFlags && pcbFile && ppFile)
2489 {
2490 PXFILE pFile;
2491 if (pFile = NEW(XFILE))
2492 {
2493 ULONG ulAction;
2494
2495 ZERO(pFile);
2496
2497 // copy open flags
2498 pFile->flOpenMode = flOpenMode;
2499
2500 if (!(arc = DosOpen((PSZ)pcszFilename,
2501 &pFile->hf,
2502 &ulAction,
2503 *pcbFile,
2504 FILE_ARCHIVED,
2505 fsOpenFlags,
2506 fsOpenMode,
2507 NULL))) // EAs
2508 {
2509 // alright, got the file:
2510
2511 if ( (ulAction == FILE_EXISTED)
2512 && ((flOpenMode & XOPEN_ACCESS_MASK) == XOPEN_READWRITE_APPEND)
2513 )
2514 // get its size and set ptr to end for append
2515 arc = DosSetFilePtr(pFile->hf,
2516 0,
2517 FILE_END,
2518 pcbFile);
2519 else
2520 arc = doshQueryFileSize(pFile->hf,
2521 pcbFile);
2522 // file ptr is at beginning
2523
2524 #ifdef DEBUG_DOSOPEN
2525 if (arc)
2526 _Pmpf((__FUNCTION__ ": DosSetFilePtr/queryfilesize returned %d for %s",
2527 arc, pcszFilename));
2528 #endif
2529
2530 // store file size
2531 pFile->cbInitial
2532 = pFile->cbCurrent
2533 = *pcbFile;
2534
2535 pFile->pszFilename = strdup(pcszFilename);
2536 }
2537 #ifdef DEBUG_DOSOPEN
2538 else
2539 _Pmpf((__FUNCTION__ ": DosOpen returned %d for %s",
2540 arc, pcszFilename));
2541 #endif
2542
2543 if (arc)
2544 doshClose(&pFile);
2545 else
2546 *ppFile = pFile;
2547 }
2548 else
2549 arc = ERROR_NOT_ENOUGH_MEMORY;
2550 }
2551 else
2552 if (!arc) // V0.9.19 (2002-04-02) [umoeller]
2553 arc = ERROR_INVALID_PARAMETER;
2554
2555 return arc;
2556}
2557
2558/*
2559 *@@ doshReadAt:
2560 * reads cb bytes from the position specified by
2561 * lOffset into the buffer pointed to by pbData,
2562 * which should be cb bytes in size.
2563 *
2564 * Note that lOffset is always considered to
2565 * be from the beginning of the file (FILE_BEGIN
2566 * method).
2567 *
2568 * This implements a small cache so that several
2569 * calls with a near offset will not touch the
2570 * disk always. The cache has been optimized for
2571 * the exeh* functions and works quite nicely
2572 * there.
2573 *
2574 * Note that the position of the file pointer is
2575 * undefined after calling this function because
2576 * the data might have been returned from the
2577 * cache.
2578 *
2579 * fl may be any combination of the following:
2580 *
2581 * -- DRFL_NOCACHE: do not fill the cache with
2582 * new data if the data is not in the cache
2583 * currently.
2584 *
2585 * -- DRFL_FAILIFLESS: return ERROR_NO_DATA
2586 * if the data returned by DosRead is less
2587 * than what was specified. This might
2588 * simplify error handling.
2589 *
2590 *@@added V0.9.13 (2001-06-14) [umoeller]
2591 *@@changed V0.9.16 (2001-12-18) [umoeller]: now with XFILE, and always using FILE_BEGIN
2592 *@@chaanged V0.9.19 (2002-04-02) [umoeller]: added params checking
2593 */
2594
2595APIRET doshReadAt(PXFILE pFile,
2596 ULONG ulOffset, // in: offset to read from (from beginning of file)
2597 PULONG pcb, // in: bytes to read, out: bytes read (req.)
2598 PBYTE pbData, // out: read buffer (must be cb bytes)
2599 ULONG fl) // in: DRFL_* flags
2600{
2601 APIRET arc = NO_ERROR;
2602 ULONG cb;
2603 ULONG ulDummy;
2604
2605 if (!pFile || !pcb)
2606 // V0.9.19 (2002-04-02) [umoeller]
2607 return ERROR_INVALID_PARAMETER;
2608
2609 cb = *pcb;
2610 *pcb = 0;
2611
2612 // check if we have the data in the cache already;
2613
2614 if ( (pFile->pbCache)
2615 // first byte must be in cache
2616 && (ulOffset >= pFile->ulReadFrom)
2617 // last byte must be in cache
2618 && ( ulOffset + cb
2619 <= pFile->ulReadFrom + pFile->cbCache
2620 )
2621 )
2622 {
2623 // alright, return data from cache simply
2624 ULONG ulOfsInCache = ulOffset - pFile->ulReadFrom;
2625
2626 memcpy(pbData,
2627 pFile->pbCache + ulOfsInCache,
2628 cb);
2629 *pcb = cb;
2630
2631 #ifdef DEBUG_DOSOPEN
2632 _Pmpf((__FUNCTION__ " %s: data is fully in cache",
2633 pFile->pszFilename));
2634 _Pmpf((" caller wants %d bytes from %d",
2635 cb, ulOffset));
2636 _Pmpf((" we got %d bytes from %d",
2637 pFile->cbCache, pFile->ulReadFrom));
2638 _Pmpf((" so copied %d bytes from cache ofs %d",
2639 cb, ulOfsInCache));
2640 #endif
2641 }
2642 else
2643 {
2644 // data is not in cache:
2645 // check how much it is... for small amounts,
2646 // we load the cache first
2647 if ( (cb <= 4096 - 512)
2648 && (!(fl & DRFL_NOCACHE))
2649 )
2650 {
2651 #ifdef DEBUG_DOSOPEN
2652 _Pmpf((__FUNCTION__ " %s: filling cache anew",
2653 pFile->pszFilename));
2654 _Pmpf((" caller wants %d bytes from %d",
2655 cb, ulOffset));
2656 #endif
2657
2658 // OK, then fix the offset to read from
2659 // to a multiple of 512 to get a full sector
2660 pFile->ulReadFrom = ulOffset / 512L * 512L;
2661 // and read 4096 bytes always plus the
2662 // value we cut off above
2663 pFile->cbCache = 4096;
2664
2665 #ifdef DEBUG_DOSOPEN
2666 _Pmpf((" getting %d bytes from %d",
2667 pFile->cbCache, pFile->ulReadFrom));
2668 #endif
2669
2670 // free old cache
2671 if (pFile->pbCache)
2672 free(pFile->pbCache);
2673
2674 // allocate new cache
2675 if (!(pFile->pbCache = (PBYTE)malloc(pFile->cbCache)))
2676 arc = ERROR_NOT_ENOUGH_MEMORY;
2677 else
2678 {
2679 ULONG ulOfsInCache = 0;
2680
2681 if (!(arc = DosSetFilePtr(pFile->hf,
2682 (LONG)pFile->ulReadFrom,
2683 FILE_BEGIN,
2684 &ulDummy)))
2685 {
2686 if (!(arc = DosRead(pFile->hf,
2687 pFile->pbCache,
2688 pFile->cbCache,
2689 &ulDummy)))
2690 {
2691 // got data:
2692 #ifdef DEBUG_DOSOPEN
2693 _Pmpf((" %d bytes read", ulDummy));
2694 #endif
2695
2696 pFile->cbCache = ulDummy;
2697
2698 // check bounds
2699 ulOfsInCache = ulOffset - pFile->ulReadFrom;
2700
2701 /*
2702 if (ulOfsInCache + cb > pFile->cbCache)
2703 {
2704 cb = pFile->cbCache - ulOfsInCache;
2705 if (fl & DRFL_FAILIFLESS)
2706 arc = ERROR_NO_DATA;
2707 }
2708 */
2709 }
2710 }
2711
2712 if (!arc)
2713 {
2714 // copy to caller
2715 memcpy(pbData,
2716 pFile->pbCache + ulOfsInCache,
2717 cb);
2718 *pcb = cb;
2719
2720 #ifdef DEBUG_DOSOPEN
2721 _Pmpf((" so copied %d bytes from cache ofs %d",
2722 cb, ulOfsInCache));
2723 #endif
2724 }
2725 else
2726 {
2727 free(pFile->pbCache);
2728 pFile->pbCache = NULL;
2729 }
2730 } // end else if (!(pFile->pbCache = (PBYTE)malloc(pFile->cbCache)))
2731 }
2732 else
2733 {
2734 // read uncached:
2735 #ifdef DEBUG_DOSOPEN
2736 _Pmpf((" " __FUNCTION__ " %s: reading uncached",
2737 pFile->pszFilename));
2738 _Pmpf((" caller wants %d bytes from %d",
2739 cb, ulOffset));
2740 #endif
2741
2742 if (!(arc = DosSetFilePtr(pFile->hf,
2743 (LONG)ulOffset,
2744 FILE_BEGIN,
2745 &ulDummy)))
2746 {
2747 if (!(arc = DosRead(pFile->hf,
2748 pbData,
2749 cb,
2750 &ulDummy)))
2751 {
2752 if ( (fl & DRFL_FAILIFLESS)
2753 && (ulDummy != cb)
2754 )
2755 arc = ERROR_NO_DATA;
2756 else
2757 *pcb = ulDummy; // bytes read
2758 }
2759 }
2760 }
2761 }
2762
2763 return arc;
2764}
2765
2766/*
2767 *@@ doshWrite:
2768 * writes the specified data to the file.
2769 * If (cb == 0), this runs strlen on pcsz
2770 * to find out the length.
2771 *
2772 * If the file is not in binary mode, all
2773 * \n chars are converted to \r\n before
2774 * writing.
2775 *
2776 * Note that this expects that the file
2777 * pointer is at the end of the file, or
2778 * you will get garbage.
2779 *
2780 *@@added V0.9.16 (2001-10-19) [umoeller]
2781 *@@changed V0.9.16 (2001-12-02) [umoeller]: added XOPEN_BINARY \r\n support
2782 *@@changed V0.9.16 (2001-12-06) [umoeller]: added check for pFile != NULL
2783 */
2784
2785APIRET doshWrite(PXFILE pFile,
2786 ULONG cb,
2787 PCSZ pbData)
2788{
2789 APIRET arc = NO_ERROR;
2790 if ((!pFile) || (!pbData))
2791 arc = ERROR_INVALID_PARAMETER;
2792 else
2793 {
2794 if (!cb)
2795 cb = strlen(pbData);
2796
2797 if (!cb)
2798 arc = ERROR_INVALID_PARAMETER;
2799 else
2800 {
2801 PSZ pszNew = NULL;
2802
2803 if (!(pFile->flOpenMode & XOPEN_BINARY))
2804 {
2805 // convert all \n to \r\n:
2806 // V0.9.16 (2001-12-02) [umoeller]
2807
2808 // count all \n first
2809 ULONG cNewLines = 0;
2810 PCSZ pSource = pbData;
2811 ULONG ul;
2812 for (ul = 0;
2813 ul < cb;
2814 ul++)
2815 {
2816 if (*pSource++ == '\n')
2817 cNewLines++;
2818 }
2819
2820 if (cNewLines)
2821 {
2822 // we have '\n' chars:
2823 // then we need just as many \r chars inserted
2824 ULONG cbNew = cb + cNewLines;
2825 if (!(pszNew = (PSZ)malloc(cbNew)))
2826 arc = ERROR_NOT_ENOUGH_MEMORY;
2827 else
2828 {
2829 PSZ pTarget = pszNew;
2830 pSource = pbData;
2831 for (ul = 0;
2832 ul < cb;
2833 ul++)
2834 {
2835 CHAR c = *pSource++;
2836 if (c == '\n')
2837 *pTarget++ = '\r';
2838 *pTarget++ = c;
2839 }
2840
2841 cb = cbNew;
2842 }
2843 }
2844 }
2845
2846 if (!arc)
2847 {
2848 ULONG cbWritten;
2849 if (!(arc = DosWrite(pFile->hf,
2850 (pszNew)
2851 ? pszNew
2852 : (PSZ)pbData,
2853 cb,
2854 &cbWritten)))
2855 {
2856 pFile->cbCurrent += cbWritten;
2857 // invalidate the cache
2858 FREE(pFile->pbCache);
2859 }
2860 }
2861
2862 if (pszNew)
2863 free(pszNew);
2864 }
2865 }
2866
2867 return arc;
2868}
2869
2870/*
2871 *@@ doshWriteAt:
2872 * writes cb bytes (pointed to by pbData) to the
2873 * specified file at the position lOffset (from
2874 * the beginning of the file).
2875 *
2876 *@@added V0.9.13 (2001-06-14) [umoeller]
2877 */
2878
2879APIRET doshWriteAt(PXFILE pFile,
2880 ULONG ulOffset, // in: offset to write at
2881 ULONG cb, // in: bytes to write
2882 PCSZ pbData) // in: ptr to bytes to write (must be cb bytes)
2883{
2884 APIRET arc = NO_ERROR;
2885 ULONG cbWritten;
2886 if (!(arc = DosSetFilePtr(pFile->hf,
2887 (LONG)ulOffset,
2888 FILE_BEGIN,
2889 &cbWritten)))
2890 {
2891 if (!(arc = DosWrite(pFile->hf,
2892 (PSZ)pbData,
2893 cb,
2894 &cbWritten)))
2895 {
2896 if (ulOffset + cbWritten > pFile->cbCurrent)
2897 pFile->cbCurrent = ulOffset + cbWritten;
2898 // invalidate the cache V0.9.19 (2002-04-02) [umoeller]
2899 FREE(pFile->pbCache);
2900 }
2901 }
2902
2903 return arc;
2904}
2905
2906/*
2907 *@@ doshWriteLogEntry
2908 * writes a log string to an XFILE, adding a
2909 * leading timestamp before the line.
2910 *
2911 * The internal string buffer is limited to 2000
2912 * characters. Length checking is _not_ performed.
2913 *
2914 *@@added V0.9.16 (2001-10-19) [umoeller]
2915 *@@changed V0.9.16 (2001-12-06) [umoeller]: added check for pFile != NULL
2916 */
2917
2918APIRET doshWriteLogEntry(PXFILE pFile,
2919 const char* pcszFormat,
2920 ...)
2921{
2922 APIRET arc = NO_ERROR;
2923
2924 if ((!pFile) || (!pcszFormat))
2925 arc = ERROR_INVALID_PARAMETER;
2926 else
2927 {
2928 DATETIME dt;
2929 CHAR szTemp[2000];
2930 ULONG ulLength;
2931
2932 DosGetDateTime(&dt);
2933 if (ulLength = sprintf(szTemp,
2934 "%04d-%02d-%02d %02d:%02d:%02d:%02d ",
2935 dt.year, dt.month, dt.day,
2936 dt.hours, dt.minutes, dt.seconds, dt.hundredths))
2937 {
2938 if (!(arc = doshWrite(pFile,
2939 ulLength,
2940 szTemp)))
2941 {
2942 va_list arg_ptr;
2943 va_start(arg_ptr, pcszFormat);
2944 ulLength = vsprintf(szTemp, pcszFormat, arg_ptr);
2945 va_end(arg_ptr);
2946
2947 if (pFile->flOpenMode & XOPEN_BINARY)
2948 // if we're in binary mode, we need to add \r too
2949 szTemp[ulLength++] = '\r';
2950 szTemp[ulLength++] = '\n';
2951
2952 arc = doshWrite(pFile,
2953 ulLength,
2954 szTemp);
2955 }
2956 }
2957 }
2958
2959 return arc;
2960}
2961
2962/*
2963 * doshClose:
2964 * closes an XFILE opened by doshOpen and
2965 * sets *ppFile to NULL.
2966 *
2967 *@@added V0.9.16 (2001-10-19) [umoeller]
2968 */
2969
2970APIRET doshClose(PXFILE *ppFile)
2971{
2972 APIRET arc = NO_ERROR;
2973 PXFILE pFile;
2974
2975 if ( (ppFile)
2976 && (pFile = *ppFile)
2977 )
2978 {
2979 // set the ptr to NULL
2980 *ppFile = NULL;
2981
2982 FREE(pFile->pbCache);
2983 FREE(pFile->pszFilename);
2984
2985 if (pFile->hf)
2986 {
2987 DosSetFileSize(pFile->hf, pFile->cbCurrent);
2988 DosClose(pFile->hf);
2989 pFile->hf = NULLHANDLE;
2990 }
2991
2992 free(pFile);
2993 }
2994 else
2995 arc = ERROR_INVALID_PARAMETER;
2996
2997 return arc;
2998}
2999
3000/*
3001 *@@ doshReadText:
3002 * reads all the contents of the given XFILE into
3003 * a newly allocated buffer. Handles Ctrl-Z properly.
3004 *
3005 * Implementation for doshLoadTextFile, but can
3006 * be called separately now.
3007 *
3008 *@@added V0.9.20 (2002-07-19) [umoeller]
3009 */
3010
3011APIRET doshReadText(PXFILE pFile,
3012 PSZ* ppszContent, // out: newly allocated buffer with file's content
3013 PULONG pcbRead) // out: size of that buffer including null byte (ptr can be NULL)
3014{
3015 APIRET arc;
3016 PSZ pszContent;
3017
3018 if (!(pszContent = (PSZ)malloc(pFile->cbCurrent + 1)))
3019 arc = ERROR_NOT_ENOUGH_MEMORY;
3020 else
3021 {
3022 ULONG cbRead = 0;
3023 if (!(arc = DosRead(pFile->hf,
3024 pszContent,
3025 pFile->cbCurrent,
3026 &cbRead)))
3027 {
3028 if (cbRead != pFile->cbCurrent)
3029 arc = ERROR_NO_DATA;
3030 else
3031 {
3032 PSZ p;
3033 pszContent[cbRead] = '\0';
3034
3035 // check if we have a ctrl-z (EOF) marker
3036 // this is present, for example, in config.sys
3037 // after install, and stupid E.EXE writes this
3038 // all the time when saving a file
3039 // V0.9.18 (2002-03-08) [umoeller]
3040 if (p = strchr(pszContent, '\26'))
3041 {
3042 *p = '\0';
3043 cbRead = p - pszContent;
3044 }
3045
3046 *ppszContent = pszContent;
3047 if (pcbRead)
3048 *pcbRead = cbRead + 1;
3049 }
3050 }
3051
3052 if (arc)
3053 free(pszContent);
3054 }
3055
3056 return arc;
3057}
3058
3059/*
3060 *@@ doshLoadTextFile:
3061 * reads a text file from disk, allocates memory
3062 * via malloc() and sets a pointer to this
3063 * buffer (or NULL upon errors).
3064 *
3065 * This allocates one extra byte to make the
3066 * buffer null-terminated always. The buffer
3067 * is _not_ converted WRT the line format.
3068 *
3069 * If CTRL-Z (ASCII 26) is encountered in the
3070 * content, it is set to the null character
3071 * instead (V0.9.18).
3072 *
3073 * This returns the APIRET of DosOpen and DosRead.
3074 * If any error occured, no buffer was allocated.
3075 * Otherwise, you should free() the buffer when
3076 * no longer needed.
3077 *
3078 *@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from doshReadTextFile
3079 *@@changed V0.9.16 (2002-01-05) [umoeller]: added pcbRead
3080 *@@changed V0.9.16 (2002-01-05) [umoeller]: rewritten using doshOpen
3081 *@@changed V0.9.18 (2002-03-08) [umoeller]: fixed ctrl-z (EOF) bug
3082 */
3083
3084APIRET doshLoadTextFile(PCSZ pcszFile, // in: file name to read
3085 PSZ* ppszContent, // out: newly allocated buffer with file's content
3086 PULONG pcbRead) // out: size of that buffer including null byte (ptr can be NULL)
3087{
3088 APIRET arc;
3089
3090 ULONG cbFile = 0;
3091 PXFILE pFile = NULL;
3092
3093 if (!(arc = doshOpen(pcszFile,
3094 XOPEN_READ_EXISTING,
3095 &cbFile,
3096 &pFile)))
3097 {
3098 doshReadText(pFile,
3099 ppszContent,
3100 pcbRead);
3101 doshClose(&pFile);
3102 }
3103
3104 return arc;
3105}
3106
3107/*
3108 *@@ doshCreateBackupFileName:
3109 * creates a valid backup filename of pszExisting
3110 * with a numerical file name extension which does
3111 * not exist in the directory where pszExisting
3112 * resides.
3113 * Returns a PSZ to a new buffer which was allocated
3114 * using malloc().
3115 *
3116 * <B>Example:</B> returns "C:\CONFIG.002" for input
3117 * "C:\CONFIG.SYS" if "C:\CONFIG.001" already exists.
3118 *
3119 *@@changed V0.9.1 (99-12-13) [umoeller]: this crashed if pszExisting had no file extension
3120 */
3121
3122PSZ doshCreateBackupFileName(const char* pszExisting)
3123{
3124 CHAR szFilename[CCHMAXPATH];
3125 PSZ pszLastDot;
3126 ULONG ulCount = 1;
3127 CHAR szCount[5];
3128 ULONG ulDummy;
3129
3130 strcpy(szFilename, pszExisting);
3131
3132 if (!(pszLastDot = strrchr(szFilename, '.')))
3133 // no dot in filename:
3134 pszLastDot = szFilename + strlen(szFilename);
3135
3136 do
3137 {
3138 sprintf(szCount, ".%03lu", ulCount);
3139 strcpy(pszLastDot, szCount);
3140 ulCount++;
3141 } while (!doshQueryPathSize(szFilename, &ulDummy));
3142
3143 return (strdup(szFilename));
3144}
3145
3146/*
3147 *@@ doshCreateTempFileName:
3148 * produces a file name in the the specified directory
3149 * or $(TEMP) which presently doesn't exist. This
3150 * checks the directory for existing files, but does
3151 * not open the temp file.
3152 *
3153 * If (pcszDir != NULL), we look into that directory.
3154 * Otherwise we look into the directory specified
3155 * by the $(TEMP) environment variable.
3156 * If $(TEMP) is not set, $(TMP) is tried next.
3157 *
3158 * If the directory thus specified does not exist, the
3159 * root directory of the boot drive is used instead.
3160 * As a result, this function should be fairly bomb-proof.
3161 *
3162 * If (pcszExt != NULL), the temp file receives
3163 * that extension, or no extension otherwise.
3164 * Do not specify the dot in pcszExt.
3165 *
3166 * pszTempFileName receives the fully qualified
3167 * file name of the temp file in that directory
3168 * and must point to a buffer CCHMAXPATH in size.
3169 * The file name is 8+3 compliant if pcszExt does
3170 * not exceed three characters.
3171 *
3172 * If (pcszPrefix != NULL), the temp file name
3173 * is prefixed with pcszPrefix. Since the temp
3174 * file name must not exceed 8+3 letters, we
3175 * can only use ( 8 - strlen(pcszPrefix ) digits
3176 * for a random number to make the temp file name
3177 * unique. You must therefore use a maximum of
3178 * four characters for the prefix. Otherwise
3179 * ERROR_INVALID_PARAMETER is returned.
3180 *
3181 * Example: Assuming TEMP is set to C:\TEMP,
3182 +
3183 + doshCreateTempFileName(szBuffer,
3184 + NULL, // use $(TEMP)
3185 + "pre", // prefix
3186 + "tmp") // extension
3187 +
3188 * would produce something like "C:\TEMP\pre07FG2.tmp".
3189 *
3190 *@@added V0.9.9 (2001-04-04) [umoeller]
3191 */
3192
3193APIRET doshCreateTempFileName(PSZ pszTempFileName, // out: fully q'fied temp file name
3194 PCSZ pcszDir, // in: dir or NULL for %TEMP%
3195 PCSZ pcszPrefix, // in: prefix for temp file or NULL
3196 PCSZ pcszExt) // in: extension (without dot) or NULL
3197{
3198 APIRET arc = NO_ERROR;
3199
3200 ULONG ulPrefixLen = (pcszPrefix)
3201 ? strlen(pcszPrefix)
3202 : 0;
3203
3204 if ( (!pszTempFileName)
3205 || (ulPrefixLen > 4)
3206 )
3207 arc = ERROR_INVALID_PARAMETER;
3208 else
3209 {
3210 CHAR szDir[CCHMAXPATH] = "";
3211 FILESTATUS3 fs3;
3212
3213 const char *pcszTemp = pcszDir;
3214
3215 if (!pcszTemp)
3216 {
3217 if (!(pcszTemp = getenv("TEMP")))
3218 pcszTemp = getenv("TMP");
3219 }
3220
3221 if (pcszTemp) // either pcszDir or $(TEMP) or $(TMP) now
3222 if (DosQueryPathInfo((PSZ)pcszTemp,
3223 FIL_STANDARD,
3224 &fs3,
3225 sizeof(fs3)))
3226 // TEMP doesn't exist:
3227 pcszTemp = NULL;
3228
3229 if (!pcszTemp)
3230 // not set, or doesn't exist:
3231 // use root directory on boot drive
3232 sprintf(szDir,
3233 "%c:\\",
3234 doshQueryBootDrive());
3235 else
3236 {
3237 strcpy(szDir, pcszTemp);
3238 if (szDir[strlen(szDir) - 1] != '\\')
3239 strcat(szDir, "\\");
3240 }
3241
3242 if (!szDir[0])
3243 arc = ERROR_PATH_NOT_FOUND; // shouldn't happen
3244 else
3245 {
3246 ULONG ulRandom = 0;
3247 ULONG cAttempts = 0;
3248
3249 // produce random number
3250 DosQuerySysInfo(QSV_MS_COUNT,
3251 QSV_MS_COUNT,
3252 &ulRandom,
3253 sizeof(ulRandom));
3254
3255 do
3256 {
3257 CHAR szFile[20] = "",
3258 szFullTryThis[CCHMAXPATH];
3259
3260 // use the lower eight hex digits of the
3261 // system uptime as the temp dir name
3262 sprintf(szFile,
3263 "%08lX",
3264 ulRandom & 0xFFFFFFFF);
3265
3266 // if prefix is specified, overwrite the
3267 // first characters in the random number
3268 if (pcszPrefix)
3269 memcpy(szFile, pcszPrefix, ulPrefixLen);
3270
3271 if (pcszExt)
3272 {
3273 szFile[8] = '.';
3274 strcpy(szFile + 9, pcszExt);
3275 }
3276
3277 // now compose full temp file name
3278 strcpy(szFullTryThis, szDir);
3279 strcat(szFullTryThis, szFile);
3280 // now we have: "C:\temp\wpiXXXXX"
3281 if (DosQueryPathInfo(szFullTryThis,
3282 FIL_STANDARD,
3283 &fs3,
3284 sizeof(fs3))
3285 == ERROR_FILE_NOT_FOUND)
3286 {
3287 // file or dir doesn't exist:
3288 // cool, we're done
3289 strcpy(pszTempFileName, szFullTryThis);
3290 return NO_ERROR;
3291 }
3292
3293 // if this didn't work, raise ulRandom and try again
3294 ulRandom += 123;
3295
3296 // try only 100 times, just to be sure
3297 cAttempts++;
3298 } while (cAttempts < 100);
3299
3300 // 100 loops elapsed:
3301 arc = ERROR_BAD_FORMAT;
3302 }
3303 }
3304
3305 return arc;
3306}
3307
3308/*
3309 *@@ doshWriteTextFile:
3310 * writes a text file to disk; pszFile must contain the
3311 * whole path and filename.
3312 *
3313 * An existing file will be backed up if (pszBackup != NULL),
3314 * using doshCreateBackupFileName. In that case, pszBackup
3315 * receives the name of the backup created, so that buffer
3316 * should be CCHMAXPATH in size.
3317 *
3318 * If (pszbackup == NULL), an existing file will be overwritten.
3319 *
3320 * On success (NO_ERROR returned), *pulWritten receives
3321 * the no. of bytes written.
3322 *
3323 *@@changed V0.9.3 (2000-05-01) [umoeller]: optimized DosOpen; added error checking; changed prototype
3324 *@@changed V0.9.3 (2000-05-12) [umoeller]: added pszBackup
3325 */
3326
3327APIRET doshWriteTextFile(const char* pszFile, // in: file name
3328 const char* pszContent, // in: text to write
3329 PULONG pulWritten, // out: bytes written (ptr can be NULL)
3330 PSZ pszBackup) // in/out: create-backup?
3331{
3332 APIRET arc = NO_ERROR;
3333 ULONG ulWritten = 0;
3334
3335 if ((!pszFile) || (!pszContent))
3336 arc = ERROR_INVALID_PARAMETER;
3337 else
3338 {
3339 ULONG ulAction = 0,
3340 ulLocal = 0;
3341 HFILE hFile = 0;
3342
3343 ULONG ulSize = strlen(pszContent); // exclude 0 byte
3344
3345 if (pszBackup)
3346 {
3347 PSZ pszBackup2 = doshCreateBackupFileName(pszFile);
3348 DosCopy((PSZ)pszFile,
3349 pszBackup2,
3350 DCPY_EXISTING); // delete existing
3351 strcpy(pszBackup, pszBackup2);
3352 free(pszBackup2);
3353 }
3354
3355 if (!(arc = DosOpen((PSZ)pszFile,
3356 &hFile,
3357 &ulAction, // action taken
3358 ulSize, // primary allocation size
3359 FILE_ARCHIVED | FILE_NORMAL, // file attribute
3360 OPEN_ACTION_CREATE_IF_NEW
3361 | OPEN_ACTION_REPLACE_IF_EXISTS, // open flags
3362 OPEN_FLAGS_NOINHERIT
3363 | OPEN_FLAGS_SEQUENTIAL // sequential, not random access
3364 | OPEN_SHARE_DENYWRITE // deny write mode
3365 | OPEN_ACCESS_WRITEONLY, // write mode
3366 NULL))) // no EAs
3367 {
3368 if (!(arc = DosSetFilePtr(hFile,
3369 0L,
3370 FILE_BEGIN,
3371 &ulLocal)))
3372 if (!(arc = DosWrite(hFile,
3373 (PVOID)pszContent,
3374 ulSize,
3375 &ulWritten)))
3376 arc = DosSetFileSize(hFile, ulSize);
3377
3378 DosClose(hFile);
3379 }
3380 } // end if ((pszFile) && (pszContent))
3381
3382 if (pulWritten)
3383 *pulWritten = ulWritten;
3384
3385 return arc;
3386}
3387
3388/*
3389 *@@category: Helpers\Control program helpers\Directory management
3390 * directory helpers (querying, creating, deleting etc.).
3391 */
3392
3393/* ******************************************************************
3394 *
3395 * Directory helpers
3396 *
3397 ********************************************************************/
3398
3399/*
3400 *@@ doshQueryDirExist:
3401 * returns TRUE if the given directory
3402 * exists and really is a directory.
3403 */
3404
3405BOOL doshQueryDirExist(PCSZ pcszDir)
3406{
3407 FILESTATUS3 fs3;
3408 APIRET arc;
3409
3410 if (!(arc = DosQueryPathInfo((PSZ)pcszDir,
3411 FIL_STANDARD,
3412 &fs3,
3413 sizeof(fs3))))
3414 // file found:
3415 return ((fs3.attrFile & FILE_DIRECTORY) != 0);
3416 else
3417 return FALSE;
3418}
3419
3420/*
3421 *@@ doshCreatePath:
3422 * this creates the specified directory.
3423 * As opposed to DosCreateDir, this
3424 * function can create several directories
3425 * at the same time, if the parent
3426 * directories do not exist yet.
3427 */
3428
3429APIRET doshCreatePath(PCSZ pcszPath,
3430 BOOL fHidden) // in: if TRUE, the new directories will get FILE_HIDDEN
3431{
3432 APIRET arc0 = NO_ERROR;
3433 CHAR path[CCHMAXPATH];
3434 CHAR *cp, c;
3435 ULONG cbPath;
3436
3437 strcpy(path, pcszPath);
3438 cbPath = strlen(path);
3439
3440 if (path[cbPath] != '\\')
3441 {
3442 path[cbPath] = '\\';
3443 path[cbPath + 1] = 0;
3444 }
3445
3446 cp = path;
3447 // advance past the drive letter only if we have one
3448 if (*(cp+1) == ':')
3449 cp += 3;
3450
3451 // go!
3452 while (*cp != 0)
3453 {
3454 if (*cp == '\\')
3455 {
3456 c = *cp;
3457 *cp = 0;
3458 if (!doshQueryDirExist(path))
3459 {
3460 APIRET arc = DosCreateDir(path,
3461 0); // no EAs
3462 if (arc != NO_ERROR)
3463 {
3464 arc0 = arc;
3465 break;
3466 }
3467 else
3468 if (fHidden)
3469 {
3470 // hide the directory we just created
3471 doshSetPathAttr(path, FILE_HIDDEN);
3472 }
3473 }
3474 *cp = c;
3475 }
3476 cp++;
3477 }
3478
3479 return (arc0);
3480}
3481
3482/*
3483 *@@ doshQueryCurrentDir:
3484 * writes the current directory into
3485 * the specified buffer, which should be
3486 * CCHMAXPATH in size.
3487 *
3488 * As opposed to DosQueryCurrentDir, this
3489 * includes the drive letter.
3490 *
3491 *@@added V0.9.4 (2000-07-22) [umoeller]
3492 */
3493
3494APIRET doshQueryCurrentDir(PSZ pszBuf)
3495{
3496 APIRET arc = NO_ERROR;
3497 ULONG ulCurDisk = 0;
3498 ULONG ulMap = 0;
3499 if (!(arc = DosQueryCurrentDisk(&ulCurDisk, &ulMap)))
3500 {
3501 ULONG cbBuf = CCHMAXPATH - 3;
3502 pszBuf[0] = ulCurDisk + 'A' - 1;
3503 pszBuf[1] = ':';
3504 pszBuf[2] = '\\';
3505 pszBuf[3] = '\0';
3506 arc = DosQueryCurrentDir(0, pszBuf + 3, &cbBuf);
3507 }
3508
3509 return arc;
3510}
3511
3512/*
3513 *@@ doshDeleteDir:
3514 * deletes a directory. As opposed to DosDeleteDir,
3515 * this removes empty subdirectories and/or files
3516 * as well.
3517 *
3518 * flFlags can be any combination of the following:
3519 *
3520 * -- DOSHDELDIR_RECURSE: recurse into subdirectories.
3521 *
3522 * -- DOSHDELDIR_DELETEFILES: delete all regular files
3523 * which are found on the way.
3524 *
3525 * THIS IS NOT IMPLEMENTED YET.
3526 *
3527 * If 0 is specified, this effectively behaves just as
3528 * DosDeleteDir.
3529 *
3530 * If you specify DOSHDELDIR_RECURSE only, only empty
3531 * subdirectories are deleted as well.
3532 *
3533 * If you specify DOSHDELDIR_RECURSE | DOSHDELDIR_DELETEFILES,
3534 * this removes an entire directory tree, including all
3535 * subdirectories and files.
3536 *
3537 *@@added V0.9.4 (2000-07-01) [umoeller]
3538 */
3539
3540APIRET doshDeleteDir(PCSZ pcszDir,
3541 ULONG flFlags,
3542 PULONG pulDirs, // out: directories found
3543 PULONG pulFiles) // out: files found
3544{
3545 APIRET arc = NO_ERROR,
3546 arcReturn = NO_ERROR;
3547
3548 HDIR hdirFindHandle = HDIR_CREATE;
3549 FILEFINDBUF3 ffb3 = {0}; // returned from FindFirst/Next
3550 ULONG ulResultBufLen = sizeof(FILEFINDBUF3);
3551 ULONG ulFindCount = 1; // look for 1 file at a time
3552
3553 CHAR szFileMask[2*CCHMAXPATH];
3554 sprintf(szFileMask, "%s\\*", pcszDir);
3555
3556 // find files
3557 arc = DosFindFirst(szFileMask,
3558 &hdirFindHandle, // directory search handle
3559 FILE_ARCHIVED | FILE_DIRECTORY | FILE_SYSTEM
3560 | FILE_HIDDEN | FILE_READONLY,
3561 // search attributes; include all, even dirs
3562 &ffb3, // result buffer
3563 ulResultBufLen, // result buffer length
3564 &ulFindCount, // number of entries to find
3565 FIL_STANDARD); // return level 1 file info
3566
3567 if (arc == NO_ERROR)
3568 {
3569 // keep finding the next file until there are no more files
3570 while (arc == NO_ERROR) // != ERROR_NO_MORE_FILES
3571 {
3572 if (ffb3.attrFile & FILE_DIRECTORY)
3573 {
3574 // we found a directory:
3575
3576 // ignore the pseudo-directories
3577 if ( (strcmp(ffb3.achName, ".") != 0)
3578 && (strcmp(ffb3.achName, "..") != 0)
3579 )
3580 {
3581 // real directory:
3582 if (flFlags & DOSHDELDIR_RECURSE)
3583 {
3584 // recurse!
3585 CHAR szSubDir[2*CCHMAXPATH];
3586 sprintf(szSubDir, "%s\\%s", pcszDir, ffb3.achName);
3587 arcReturn = doshDeleteDir(szSubDir,
3588 flFlags,
3589 pulDirs,
3590 pulFiles);
3591 // this removes ffb3.achName as well
3592 }
3593 else
3594 {
3595 // directory, but no recursion:
3596 // report "access denied"
3597 // (this is what OS/2 reports with DosDeleteDir as well)
3598 arcReturn = ERROR_ACCESS_DENIED; // 5
3599 (*pulDirs)++;
3600 }
3601 }
3602 }
3603 else
3604 {
3605 // it's a file:
3606 arcReturn = ERROR_ACCESS_DENIED;
3607 (*pulFiles)++;
3608 }
3609
3610 if (arc == NO_ERROR)
3611 {
3612 ulFindCount = 1; // reset find count
3613 arc = DosFindNext(hdirFindHandle, // directory handle
3614 &ffb3, // result buffer
3615 ulResultBufLen, // result buffer length
3616 &ulFindCount); // number of entries to find
3617 }
3618 } // endwhile
3619
3620 DosFindClose(hdirFindHandle); // close our find handle
3621 }
3622
3623 if (arcReturn == NO_ERROR)
3624 // success so far:
3625 // delete our directory now
3626 arcReturn = DosDeleteDir((PSZ)pcszDir);
3627
3628 return (arcReturn);
3629}
3630
3631/*
3632 *@@ doshCanonicalize:
3633 * simplifies path specifications to remove '.'
3634 * and '..' entries and generates a fully
3635 * qualified path name where possible.
3636 * File specifications are left unchanged.
3637 *
3638 * This returns:
3639 *
3640 * -- NO_ERROR: the buffers were valid.
3641 *
3642 * -- ERROR_INVALID_PARAMETER: the buffers
3643 * were invalid.
3644 *
3645 *@@added V0.9.19 (2002-04-22) [pr]
3646 */
3647
3648APIRET doshCanonicalize(PCSZ pcszFileIn, // in: path to canonicalize
3649 PSZ pszFileOut, // out: canonicalized path if NO_ERROR
3650 ULONG cbFileOut) // in: size of pszFileOut buffer
3651{
3652 APIRET ulrc = NO_ERROR;
3653 CHAR szFileTemp[CCHMAXPATH];
3654
3655 if (pcszFileIn && pszFileOut && cbFileOut)
3656 {
3657 strncpy(szFileTemp, pcszFileIn, sizeof(szFileTemp) - 1);
3658 szFileTemp[sizeof(szFileTemp) - 1] = 0;
3659 if ( strchr(szFileTemp, '\\')
3660 || strchr(szFileTemp, ':')
3661 )
3662 {
3663 ULONG cbFileTemp = strlen(szFileTemp);
3664
3665 if ( (cbFileTemp > 3)
3666 && (szFileTemp[cbFileTemp - 1] == '\\')
3667 )
3668 {
3669 szFileTemp[cbFileTemp - 1] = 0;
3670 }
3671
3672 if (DosQueryPathInfo(szFileTemp,
3673 FIL_QUERYFULLNAME,
3674 pszFileOut,
3675 cbFileOut))
3676 {
3677 pszFileOut[0] = 0;
3678 }
3679 }
3680 else
3681 {
3682 strncpy(pszFileOut, pcszFileIn, cbFileOut - 1);
3683 pszFileOut[cbFileOut - 1] = 0;
3684 }
3685 }
3686 else
3687 ulrc = ERROR_INVALID_PARAMETER;
3688
3689 return(ulrc);
3690}
3691
3692/*
3693 *@@category: Helpers\Control program helpers\Module handling
3694 * helpers for importing functions from a module (DLL).
3695 */
3696
3697/* ******************************************************************
3698 *
3699 * Module handling helpers
3700 *
3701 ********************************************************************/
3702
3703/*
3704 *@@ doshQueryProcAddr:
3705 * attempts to resolve the given procedure from
3706 * the given module name. Saves you from querying
3707 * the module handle and all that.
3708 *
3709 * This is intended for resolving undocumented
3710 * APIs from OS/2 system DLls such as PMMERGE
3711 * and DOSCALLS. It is assumed that the specified
3712 * module is already loaded.
3713 *
3714 * Returns:
3715 *
3716 * -- NO_ERROR
3717 *
3718 * -- ERROR_INVALID_NAME
3719 *
3720 * -- ERROR_INVALID_ORDINAL
3721 *
3722 * plus the error codes of DosLoadModule.
3723 *
3724 *@@added V0.9.16 (2002-01-13) [umoeller]
3725 */
3726
3727APIRET doshQueryProcAddr(PCSZ pcszModuleName, // in: module name (e.g. "PMMERGE")
3728 ULONG ulOrdinal, // in: proc ordinal
3729 PFN *ppfn) // out: proc address
3730{
3731 HMODULE hmod;
3732 APIRET arc;
3733 if (!(arc = DosQueryModuleHandle((PSZ)pcszModuleName,
3734 &hmod)))
3735 {
3736 if ((arc = DosQueryProcAddr(hmod,
3737 ulOrdinal,
3738 NULL,
3739 ppfn)))
3740 {
3741 // the CP programming guide and reference says use
3742 // DosLoadModule if DosQueryProcAddr fails with this error
3743 if (arc == ERROR_INVALID_HANDLE)
3744 {
3745 if (!(arc = DosLoadModule(NULL,
3746 0,
3747 (PSZ)pcszModuleName,
3748 &hmod)))
3749 {
3750 arc = DosQueryProcAddr(hmod,
3751 ulOrdinal,
3752 NULL,
3753 ppfn);
3754 }
3755 }
3756 }
3757 }
3758
3759 return arc;
3760}
3761
3762/*
3763 *@@ doshResolveImports:
3764 * this function loads the module called pszModuleName
3765 * and resolves imports dynamically using DosQueryProcAddress.
3766 *
3767 * To specify the functions to be imported, a RESOLVEFUNCTION
3768 * array is used. In each of the array items, specify the
3769 * name of the function and a pointer to a function pointer
3770 * where to store the resolved address.
3771 *
3772 *@@added V0.9.3 (2000-04-29) [umoeller]
3773 */
3774
3775APIRET doshResolveImports(PCSZ pcszModuleName, // in: DLL to load
3776 HMODULE *phmod, // out: module handle
3777 PCRESOLVEFUNCTION paResolves, // in/out: function resolves
3778 ULONG cResolves) // in: array item count (not array size!)
3779{
3780 CHAR szName[CCHMAXPATH];
3781 APIRET arc;
3782
3783 if (!(arc = DosLoadModule(szName,
3784 sizeof(szName),
3785 (PSZ)pcszModuleName,
3786 phmod)))
3787 {
3788 ULONG ul;
3789 for (ul = 0;
3790 ul < cResolves;
3791 ul++)
3792 {
3793 if (arc = DosQueryProcAddr(*phmod,
3794 0, // ordinal, ignored
3795 (PSZ)paResolves[ul].pcszFunctionName,
3796 paResolves[ul].ppFuncAddress))
3797 // error:
3798 break;
3799 }
3800
3801 if (arc)
3802 // V0.9.16 (2001-12-08) [umoeller]
3803 DosFreeModule(*phmod);
3804 }
3805
3806 return arc;
3807}
3808
3809/*
3810 *@@category: Helpers\Control program helpers\Performance (CPU load) helpers
3811 * helpers around DosPerfSysCall.
3812 */
3813
3814/* ******************************************************************
3815 *
3816 * Performance Counters (CPU Load)
3817 *
3818 ********************************************************************/
3819
3820/*
3821 *@@ doshPerfOpen:
3822 * initializes the OS/2 DosPerfSysCall API for
3823 * the calling thread.
3824 *
3825 * Note: This API is not supported on all OS/2
3826 * versions. I believe it came up with some Warp 4
3827 * fixpak. The API is resolved dynamically by
3828 * this function (using DosQueryProcAddr).
3829 *
3830 * This properly initializes the internal counters
3831 * which the OS/2 kernel uses for this API. Apparently,
3832 * with newer kernels (FP13/14), IBM has chosen to no
3833 * longer do this automatically, which is the reason
3834 * why many "pulse" utilities display garbage with these
3835 * fixpaks.
3836 *
3837 * After NO_ERROR is returned, DOSHPERFSYS.cProcessors
3838 * contains the no. of processors found on the system.
3839 * All pointers in DOSHPERFSYS then point to arrays
3840 * which have exactly cProcessors array items.
3841 *
3842 * So after NO_ERROR was returned here, you can keep
3843 * calling doshPerfGet to get a current snapshot of the
3844 * IRQ and user loads for each CPU. Note that interrupts
3845 * are only processed on CPU 0 on SMP systems.
3846 *
3847 * Call doshPerfClose to clean up resources allocated
3848 * by this function.
3849 *
3850 * For more sample code, take a look at the "Pulse" widget
3851 * in the XWorkplace sources (src\xcenter\w_pulse.c).
3852 *
3853 * Example code:
3854 *
3855 + PDOSHPERFSYS pPerf = NULL;
3856 + APIRET arc;
3857 + if (!(arc = arc = doshPerfOpen(&pPerf)))
3858 + {
3859 + // this should really be in a timer,
3860 + // e.g. once per second
3861 + ULONG ulCPU;
3862 + if (!(arc = doshPerfGet(&pPerf)))
3863 + {
3864 + // go thru all CPUs
3865 + for (ulCPU = 0; ulCPU < pPerf->cProcessors; ulCPU++)
3866 + {
3867 + LONG lUserLoadThis = pPerf->palLoads[ulCPU];
3868 + ...
3869 + }
3870 + }
3871 +
3872 + // clean up
3873 + doshPerfClose(&pPerf);
3874 + }
3875 +
3876 *
3877 *@@added V0.9.7 (2000-12-02) [umoeller]
3878 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
3879 */
3880
3881APIRET doshPerfOpen(PDOSHPERFSYS *ppPerfSys) // out: new DOSHPERFSYS structure
3882{
3883 APIRET arc = NO_ERROR;
3884
3885 // allocate DOSHPERFSYS structure
3886 *ppPerfSys = (PDOSHPERFSYS)malloc(sizeof(DOSHPERFSYS));
3887 if (!*ppPerfSys)
3888 arc = ERROR_NOT_ENOUGH_MEMORY;
3889 else
3890 {
3891 // initialize structure
3892 PDOSHPERFSYS pPerfSys = *ppPerfSys;
3893 memset(pPerfSys, 0, sizeof(*pPerfSys));
3894
3895 // resolve DosPerfSysCall API entry
3896 if (!(arc = DosLoadModule(NULL,
3897 0,
3898 "DOSCALLS",
3899 &pPerfSys->hmod)))
3900 {
3901 if (!(arc = DosQueryProcAddr(pPerfSys->hmod,
3902 976,
3903 "DosPerfSysCall",
3904 (PFN*)&pPerfSys->pDosPerfSysCall)))
3905 {
3906 // OK, we got the API: initialize!
3907 if (!(arc = pPerfSys->pDosPerfSysCall(CMD_KI_ENABLE,
3908 0,
3909 0,
3910 0)))
3911 {
3912 pPerfSys->fInitialized = TRUE;
3913 // call CMD_KI_DISABLE later
3914
3915 if (!(arc = pPerfSys->pDosPerfSysCall(CMD_PERF_INFO,
3916 0,
3917 (ULONG)&pPerfSys->cProcessors,
3918 0)))
3919 {
3920 ULONG ul = 0,
3921 cProcs = pPerfSys->cProcessors,
3922 cbDouble = cProcs * sizeof(double),
3923 cbLong = cProcs * sizeof(LONG);
3924
3925 // allocate arrays
3926 if ( (!(pPerfSys->paCPUUtils = (PCPUUTIL)calloc(cProcs,
3927 sizeof(CPUUTIL))))
3928 || (!(pPerfSys->padBusyPrev
3929 = (double*)malloc(cbDouble)))
3930 || (!(pPerfSys->padTimePrev
3931 = (double*)malloc(cbDouble)))
3932 || (!(pPerfSys->padIntrPrev
3933 = (double*)malloc(cbDouble)))
3934 || (!(pPerfSys->palLoads
3935 = (PLONG)malloc(cbLong)))
3936 || (!(pPerfSys->palIntrs
3937 = (PLONG)malloc(cbLong)))
3938 )
3939 arc = ERROR_NOT_ENOUGH_MEMORY;
3940 else
3941 {
3942 for (ul = 0; ul < cProcs; ul++)
3943 {
3944 pPerfSys->padBusyPrev[ul] = 0.0;
3945 pPerfSys->padTimePrev[ul] = 0.0;
3946 pPerfSys->padIntrPrev[ul] = 0.0;
3947 pPerfSys->palLoads[ul] = 0;
3948 pPerfSys->palIntrs[ul] = 0;
3949 }
3950 }
3951 }
3952 }
3953 } // end if (arc == NO_ERROR)
3954 } // end if (arc == NO_ERROR)
3955
3956 if (arc)
3957 // error: clean up
3958 doshPerfClose(ppPerfSys);
3959
3960 } // end else if (!*ppPerfSys)
3961
3962 return arc;
3963}
3964
3965/*
3966 *@@ doshPerfGet:
3967 * calculates a current snapshot of the system load,
3968 * compared with the load which was calculated on
3969 * the previous call.
3970 *
3971 * If you want to continually measure the system CPU
3972 * load, this is the function you will want to call
3973 * regularly -- e.g. with a timer once per second.
3974 *
3975 * Call this ONLY if doshPerfOpen returned NO_ERROR,
3976 * or you'll get crashes.
3977 *
3978 * If this call returns NO_ERROR, you get LONG load
3979 * values for each CPU in the system in the arrays
3980 * in DOSHPERFSYS (in per-mille, 0-1000).
3981 *
3982 * There are two arrays:
3983 *
3984 * -- DOSHPERFSYS.palLoads contains the "user" load
3985 * for each CPU.
3986 *
3987 * -- DOSHPERFSYS.palIntrs contains the "IRQ" load
3988 * for each CPU.
3989 *
3990 * Sum up the two values to get the total load for
3991 * each CPU.
3992 *
3993 * For example, if there are two CPUs, after this call,
3994 *
3995 * -- DOSHPERFSYS.palLoads[0] contains the "user" load
3996 * of the first CPU,
3997 *
3998 * -- DOSHPERFSYS.palLoads[0] contains the "user" load
3999 * of the second CPU.
4000 *
4001 * See doshPerfOpen for example code.
4002 *
4003 *@@added V0.9.7 (2000-12-02) [umoeller]
4004 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
4005 */
4006
4007APIRET doshPerfGet(PDOSHPERFSYS pPerfSys)
4008{
4009 APIRET arc = NO_ERROR;
4010 if (!pPerfSys->pDosPerfSysCall)
4011 arc = ERROR_INVALID_PARAMETER;
4012 else
4013 {
4014 if (!(arc = pPerfSys->pDosPerfSysCall(CMD_KI_RDCNT,
4015 (ULONG)pPerfSys->paCPUUtils,
4016 0, 0)))
4017 {
4018 // go thru all processors
4019 ULONG ul = 0;
4020 for (; ul < pPerfSys->cProcessors; ul++)
4021 {
4022 PCPUUTIL pCPUUtilThis = &pPerfSys->paCPUUtils[ul];
4023
4024 double dTime = LL2F(pCPUUtilThis->ulTimeHigh,
4025 pCPUUtilThis->ulTimeLow);
4026 double dBusy = LL2F(pCPUUtilThis->ulBusyHigh,
4027 pCPUUtilThis->ulBusyLow);
4028 double dIntr = LL2F(pCPUUtilThis->ulIntrHigh,
4029 pCPUUtilThis->ulIntrLow);
4030
4031 double *pdBusyPrevThis = &pPerfSys->padBusyPrev[ul];
4032 double *pdTimePrevThis = &pPerfSys->padTimePrev[ul];
4033 double *pdIntrPrevThis = &pPerfSys->padIntrPrev[ul];
4034
4035 // avoid division by zero
4036 double dTimeDelta;
4037 if (dTimeDelta = (dTime - *pdTimePrevThis))
4038 {
4039 pPerfSys->palLoads[ul]
4040 = (LONG)( (double)( (dBusy - *pdBusyPrevThis)
4041 / dTimeDelta
4042 * 1000.0
4043 )
4044 );
4045 pPerfSys->palIntrs[ul]
4046 = (LONG)( (double)( (dIntr - *pdIntrPrevThis)
4047 / dTimeDelta
4048 * 1000.0
4049 )
4050 );
4051 }
4052 else
4053 {
4054 // no clear readings are available
4055 pPerfSys->palLoads[ul] = 0;
4056 pPerfSys->palIntrs[ul] = 0;
4057 }
4058
4059 *pdTimePrevThis = dTime;
4060 *pdBusyPrevThis = dBusy;
4061 *pdIntrPrevThis = dIntr;
4062 }
4063 }
4064 }
4065
4066 return arc;
4067}
4068
4069/*
4070 *@@ doshPerfClose:
4071 * frees all resources allocated by doshPerfOpen.
4072 *
4073 *@@added V0.9.7 (2000-12-02) [umoeller]
4074 *@@changed V0.9.9 (2001-02-06) [umoeller]: removed disable; this broke the WarpCenter
4075 *@@changed V0.9.9 (2001-03-14) [umoeller]: fixed memory leak
4076 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
4077 */
4078
4079APIRET doshPerfClose(PDOSHPERFSYS *ppPerfSys)
4080{
4081 APIRET arc = NO_ERROR;
4082 PDOSHPERFSYS pPerfSys;
4083 if ( (!ppPerfSys)
4084 || (!(pPerfSys = *ppPerfSys))
4085 )
4086 arc = ERROR_INVALID_PARAMETER;
4087 else
4088 {
4089 // do not call this, this messes up the WarpCenter V0.9.9 (2001-02-06) [umoeller]
4090 // if (pPerfSys->fInitialized) pPerfSys->pDosPerfSysCall(CMD_KI_DISABLE, 0, 0, 0);
4091
4092 if (pPerfSys->paCPUUtils)
4093 free(pPerfSys->paCPUUtils);
4094 if (pPerfSys->padBusyPrev)
4095 free(pPerfSys->padBusyPrev);
4096 if (pPerfSys->padTimePrev)
4097 free(pPerfSys->padTimePrev);
4098 if (pPerfSys->padIntrPrev)
4099 free(pPerfSys->padIntrPrev);
4100 if (pPerfSys->palLoads) // was missing V0.9.9 (2001-03-14) [umoeller]
4101 free(pPerfSys->palLoads);
4102 if (pPerfSys->palIntrs)
4103 free(pPerfSys->palIntrs);
4104
4105 if (pPerfSys->hmod)
4106 DosFreeModule(pPerfSys->hmod);
4107 free(pPerfSys);
4108 *ppPerfSys = NULL;
4109 }
4110
4111 return arc;
4112}
4113
4114/*
4115 *@@category: Helpers\Control program helpers\Process management
4116 * helpers for starting subprocesses.
4117 */
4118
4119/* ******************************************************************
4120 *
4121 * Process helpers
4122 *
4123 ********************************************************************/
4124
4125STATIC PVOID // G_pvGlobalInfoSeg = NULL,
4126 G_pvLocalInfoSeg = NULL;
4127
4128USHORT _Far16 _Pascal Dos16GetInfoSeg(PSEL pselGlobal,
4129 PSEL pselLocal);
4130
4131/*
4132 * GetInfoSegs:
4133 *
4134 */
4135
4136STATIC VOID GetInfoSegs(VOID)
4137{
4138 SEL GlobalInfoSegSelector,
4139 LocalInfoSegSelector;
4140
4141 // get selectors (old 16-bit API; this gets called only once)
4142 Dos16GetInfoSeg(&GlobalInfoSegSelector,
4143 &LocalInfoSegSelector);
4144 // thunk
4145 /* G_pvGlobalInfoSeg = (PVOID)( (GlobalInfoSegSelector << 0x000D)
4146 & 0x01fff0000); */
4147 G_pvLocalInfoSeg = (PVOID)( (LocalInfoSegSelector << 0x000D)
4148 & 0x01fff0000);
4149}
4150
4151/*
4152 *@@ doshMyPID:
4153 * returns the PID of the current process.
4154 *
4155 * This uses an interesting hack which is way
4156 * faster than DosGetInfoBlocks.
4157 *
4158 *@@added V0.9.9 (2001-04-04) [umoeller]
4159 */
4160
4161ULONG doshMyPID(VOID)
4162{
4163 if (!G_pvLocalInfoSeg)
4164 // first call:
4165 GetInfoSegs();
4166
4167 // PID is at offset 0 in the local info seg
4168 return (*(PUSHORT)G_pvLocalInfoSeg);
4169}
4170
4171/*
4172 *@@ doshMyTID:
4173 * returns the TID of the current thread.
4174 *
4175 * This uses an interesting hack which is way
4176 * faster than DosGetInfoBlocks.
4177 *
4178 *@@added V0.9.9 (2001-04-04) [umoeller]
4179 */
4180
4181ULONG doshMyTID(VOID)
4182{
4183 if (!G_pvLocalInfoSeg)
4184 // first call:
4185 GetInfoSegs();
4186
4187 // TID is at offset 6 in the local info seg
4188 return (*(PUSHORT)((PBYTE)G_pvLocalInfoSeg + 6));
4189}
4190
4191/*
4192 *@@ doshExecVIO:
4193 * executes cmd.exe with the /c parameter
4194 * and pcszExecWithArgs. This is equivalent
4195 * to the C library system() function,
4196 * except that an OS/2 error code is returned
4197 * and that this works with the VAC subsystem
4198 * library.
4199 *
4200 * This uses DosExecPgm and handles the sick
4201 * argument passing.
4202 *
4203 * If NO_ERROR is returned, *plExitCode receives
4204 * the exit code of the process. If the process
4205 * was terminated abnormally, *plExitCode receives:
4206 *
4207 * -- -1: hard error halt
4208 * -- -2: 16-bit trap
4209 * -- -3: DosKillProcess
4210 * -- -4: 32-bit exception
4211 *
4212 *@@added V0.9.4 (2000-07-27) [umoeller]
4213 */
4214
4215APIRET doshExecVIO(PCSZ pcszExecWithArgs,
4216 PLONG plExitCode) // out: exit code (ptr can be NULL)
4217{
4218 APIRET arc = NO_ERROR;
4219
4220 if (strlen(pcszExecWithArgs) > 255)
4221 arc = ERROR_INSUFFICIENT_BUFFER;
4222 else
4223 {
4224 CHAR szObj[CCHMAXPATH];
4225 RESULTCODES res = {0};
4226 CHAR szBuffer[300];
4227
4228 // DosExecPgm expects two args in szBuffer:
4229 // -- cmd.exe\0
4230 // -- then the actual argument, terminated by two 0 bytes.
4231 memset(szBuffer, 0, sizeof(szBuffer));
4232 strcpy(szBuffer, "cmd.exe\0");
4233 sprintf(szBuffer + 8, "/c %s",
4234 pcszExecWithArgs);
4235
4236 arc = DosExecPgm(szObj, sizeof(szObj),
4237 EXEC_SYNC,
4238 szBuffer,
4239 0,
4240 &res,
4241 "cmd.exe");
4242 if ((arc == NO_ERROR) && (plExitCode))
4243 {
4244 if (res.codeTerminate == 0)
4245 // normal exit:
4246 *plExitCode = res.codeResult;
4247 else
4248 *plExitCode = -((LONG)res.codeTerminate);
4249 }
4250 }
4251
4252 return arc;
4253}
4254
4255/*
4256 *@@ doshQuickStartSession:
4257 * this is a shortcut to DosStartSession w/out having to
4258 * deal with all the messy parameters.
4259 *
4260 * This one starts pszPath as a child session and passes
4261 * pszParams to it.
4262 *
4263 * In usPgmCtl, OR any or none of the following (V0.9.0):
4264 * -- SSF_CONTROL_NOAUTOCLOSE (0x0008): do not close automatically.
4265 * This bit is used only for VIO Windowable apps and ignored
4266 * for all other applications.
4267 * -- SSF_CONTROL_MINIMIZE (0x0004)
4268 * -- SSF_CONTROL_MAXIMIZE (0x0002)
4269 * -- SSF_CONTROL_INVISIBLE (0x0001)
4270 * -- SSF_CONTROL_VISIBLE (0x0000)
4271 *
4272 * Specifying 0 will therefore auto-close the session and make it
4273 * visible.
4274 *
4275 * If (fWait), this function will create a termination queue
4276 * and not return until the child session has ended. Be warned,
4277 * this blocks the calling thread.
4278 *
4279 * Otherwise the function will return immediately.
4280 *
4281 * The session and process IDs of the child session will be
4282 * written to *pulSID and *ppid. Of course, in "wait" mode,
4283 * these are no longer valid after this function returns.
4284 *
4285 * Returns the error code of DosStartSession.
4286 *
4287 * Note: According to CPREF, calling DosStartSession calls
4288 * DosExecPgm in turn for all full-screen, VIO, and PM sessions.
4289 *
4290 *@@changed V0.9.0 [umoeller]: prototype changed to include usPgmCtl
4291 *@@changed V0.9.1 (99-12-30) [umoeller]: queue was sometimes not closed. Fixed.
4292 *@@changed V0.9.3 (2000-05-03) [umoeller]: added fForeground
4293 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed potential queue leak
4294 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed memory leak in wait mode; added pusReturn to prototype
4295 */
4296
4297APIRET doshQuickStartSession(PCSZ pcszPath, // in: program to start
4298 PCSZ pcszParams, // in: parameters for program
4299 BOOL fForeground, // in: if TRUE, session will be in foreground
4300 USHORT usPgmCtl, // in: STARTDATA.PgmControl
4301 BOOL fWait, // in: wait for termination?
4302 PULONG pulSID, // out: session ID (req.)
4303 PPID ppid, // out: process ID (req.)
4304 PUSHORT pusReturn) // out: in wait mode, session's return code (ptr can be NULL)
4305{
4306 APIRET arc = NO_ERROR;
4307 // queue stuff
4308 const char *pcszQueueName = "\\queues\\xwphlpsw.que";
4309 HQUEUE hq = 0;
4310 PID qpid = 0;
4311
4312 if (fWait)
4313 {
4314 if (!(arc = DosCreateQueue(&hq,
4315 QUE_FIFO | QUE_CONVERT_ADDRESS,
4316 (PSZ)pcszQueueName)))
4317 arc = DosOpenQueue(&qpid, &hq, (PSZ)pcszQueueName);
4318 }
4319
4320 if (!arc) // V0.9.14 (2001-08-03) [umoeller]
4321 {
4322 STARTDATA SData;
4323 CHAR szObjBuf[CCHMAXPATH];
4324
4325 SData.Length = sizeof(STARTDATA);
4326 SData.Related = SSF_RELATED_CHILD; //INDEPENDENT;
4327 SData.FgBg = (fForeground) ? SSF_FGBG_FORE : SSF_FGBG_BACK;
4328 // V0.9.3 (2000-05-03) [umoeller]
4329 SData.TraceOpt = SSF_TRACEOPT_NONE;
4330
4331 SData.PgmTitle = (PSZ)pcszPath; // title for window
4332 SData.PgmName = (PSZ)pcszPath;
4333 SData.PgmInputs = (PSZ)pcszParams;
4334
4335 SData.TermQ = (fWait) ? (PSZ)pcszQueueName : NULL;
4336 SData.Environment = 0;
4337 SData.InheritOpt = SSF_INHERTOPT_PARENT;
4338 SData.SessionType = SSF_TYPE_DEFAULT;
4339 SData.IconFile = 0;
4340 SData.PgmHandle = 0;
4341
4342 SData.PgmControl = usPgmCtl;
4343
4344 SData.InitXPos = 30;
4345 SData.InitYPos = 40;
4346 SData.InitXSize = 200;
4347 SData.InitYSize = 140;
4348 SData.Reserved = 0;
4349 SData.ObjectBuffer = szObjBuf;
4350 SData.ObjectBuffLen = (ULONG)sizeof(szObjBuf);
4351
4352 if ( (!(arc = DosStartSession(&SData, pulSID, ppid)))
4353 && (fWait)
4354 )
4355 {
4356 // block on the termination queue, which is written
4357 // to when the subprocess ends
4358 REQUESTDATA rqdata;
4359 ULONG cbData = 0;
4360 PULONG pulData = NULL;
4361 BYTE elpri;
4362
4363 rqdata.pid = qpid;
4364 if (!(arc = DosReadQueue(hq, // in: queue handle
4365 &rqdata, // out: pid and ulData
4366 &cbData, // out: size of data returned
4367 (PVOID*)&pulData, // out: data returned
4368 0, // in: remove first element in queue
4369 0, // in: wait for queue data (block thread)
4370 &elpri, // out: element's priority
4371 0))) // in: event semaphore to be posted
4372 {
4373 if (!rqdata.ulData)
4374 {
4375 // child session ended:
4376 // V0.9.14 (2001-08-03) [umoeller]
4377
4378 // *pulSID = (*pulData) & 0xffff;
4379 if (pusReturn)
4380 *pusReturn = ((*pulData) >> 16) & 0xffff;
4381
4382 }
4383 // else: continue looping
4384
4385 if (pulData)
4386 DosFreeMem(pulData);
4387 }
4388 }
4389 }
4390
4391 if (hq)
4392 DosCloseQueue(hq);
4393
4394 return arc;
4395}
4396
4397
Note: See TracBrowser for help on using the repository browser.