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

Last change on this file since 132 was 132, checked in by umoeller, 24 years ago

Misc changes.

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