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

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

Lots of updates from the last week for conditional compiles and other stuff.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 89.7 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_DOSMISC
54#define INCL_DOSDEVICES
55#define INCL_DOSDEVIOCTL
56#define INCL_DOSERRORS
57
58#define INCL_KBD
59#include <os2.h>
60
61#include <stdlib.h>
62#include <string.h>
63#include <stdio.h>
64
65#include "setup.h" // code generation and debugging options
66
67#include "helpers\dosh.h"
68
69#pragma hdrstop
70
71// static const CHAR G_acDriveLetters[28] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
72
73/*
74 *@@category: Helpers\Control program helpers\Miscellaneous
75 * Miscellaneous helpers in dosh.c that didn't fit into any other
76 * category.
77 */
78
79/* ******************************************************************
80 *
81 * Miscellaneous
82 *
83 ********************************************************************/
84
85/*
86 *@@ doshGetChar:
87 * reads a single character from the keyboard.
88 * Useful for VIO sessions, since there's great
89 * confusion between the various C dialects about
90 * how to use getc(), and getc() doesn't work
91 * with the VAC subsystem library.
92 *
93 *@@added V0.9.4 (2000-07-27) [umoeller]
94 */
95
96CHAR doshGetChar(VOID)
97{
98 // CHAR c;
99 // ULONG ulRead = 0;
100
101 KBDKEYINFO kki;
102 KbdCharIn(&kki,
103 0, // wait
104 0);
105
106 return (kki.chChar);
107}
108
109/*
110 *@@ doshQueryShiftState:
111 * returns TRUE if any of the SHIFT keys are
112 * currently pressed. Useful for checks during
113 * WM_COMMAND messages from menus.
114 *
115 *@@changed V0.9.5 (2000-09-27) [umoeller]: added error checking
116 */
117
118BOOL doshQueryShiftState(VOID)
119{
120 BOOL brc = FALSE;
121 APIRET arc = NO_ERROR;
122 HFILE hfKbd;
123 ULONG ulAction;
124
125 arc = DosOpen("KBD$", &hfKbd, &ulAction, 0,
126 FILE_NORMAL, FILE_OPEN,
127 OPEN_ACCESS_READONLY | OPEN_SHARE_DENYWRITE,
128 (PEAOP2)NULL);
129 if (arc == NO_ERROR)
130 {
131 SHIFTSTATE ShiftState;
132 ULONG cbDataLen = sizeof(ShiftState);
133
134 arc = DosDevIOCtl(hfKbd, IOCTL_KEYBOARD, KBD_GETSHIFTSTATE,
135 NULL, 0, NULL, // no parameters
136 &ShiftState, cbDataLen, &cbDataLen);
137 if (arc == NO_ERROR)
138 brc = ((ShiftState.fsState & 3) != 0);
139
140 DosClose(hfKbd);
141 }
142
143 return brc;
144}
145
146/*
147 *@@ doshIsWarp4:
148 * checks the OS/2 system version number.
149 *
150 * Returns:
151 *
152 * -- 0 (FALSE): OS/2 2.x or Warp 3 is running.
153 *
154 * -- 1: Warp 4.0 is running.
155 *
156 * -- 2: Warp 4.5 is running (WSeB or Warp 4 FP 13+ or eCS
157 * or ACP/MCP), or even something newer.
158 *
159 *@@changed V0.9.2 (2000-03-05) [umoeller]: reported TRUE on Warp 3 also; fixed
160 *@@changed V0.9.6 (2000-10-16) [umoeller]: patched for speed
161 *@@changed V0.9.9 (2001-04-04) [umoeller]: now returning 2 for Warp 4.5 and above
162 */
163
164ULONG doshIsWarp4(VOID)
165{
166 static BOOL s_fQueried = FALSE;
167 static ULONG s_ulrc = 0;
168
169 if (!s_fQueried)
170 {
171 // first call:
172 ULONG aulBuf[3];
173
174 DosQuerySysInfo(QSV_VERSION_MAJOR, // 11
175 QSV_VERSION_MINOR, // 12
176 &aulBuf, sizeof(aulBuf));
177 // Warp 3 is reported as 20.30
178 // Warp 4 is reported as 20.40
179 // Aurora is reported as 20.45
180
181 if ( (aulBuf[0] > 20) // major > 20; not the case with Warp 3, 4, 5
182 || ( (aulBuf[0] == 20) // major == 20 and minor >= 45
183 && (aulBuf[1] >= 45)
184 )
185 )
186 // Warp 4.5 or newer:
187 s_ulrc = 2;
188 else if ( (aulBuf[0] == 20) // major == 20 and minor == 40
189 && (aulBuf[1] == 40)
190 )
191 // Warp 4:
192 s_ulrc = 1;
193
194 s_fQueried = TRUE;
195 }
196
197 return (s_ulrc);
198}
199
200/*
201 *@@ doshQuerySysErrorMsg:
202 * this retrieves the error message for a system error
203 * (APIRET) from the system error message file (OSO001.MSG).
204 * This file better be on the DPATH (it normally is).
205 *
206 * This returns the string in the "SYSxxx: blahblah" style,
207 * which is normally displayed on the command line when
208 * errors occur.
209 *
210 * The error message is returned in a newly allocated
211 * buffer, which should be free()'d afterwards.
212 *
213 * Returns NULL upon errors.
214 */
215
216PSZ doshQuerySysErrorMsg(APIRET arc) // in: DOS error code
217{
218 PSZ pszReturn = 0;
219 CHAR szDosError[1000];
220 ULONG cbDosError = 0;
221 DosGetMessage(NULL, 0, // no string replacements
222 szDosError, sizeof(szDosError),
223 arc,
224 "OSO001.MSG", // default OS/2 message file
225 &cbDosError);
226 if (cbDosError > 2)
227 {
228 szDosError[cbDosError - 2] = 0;
229 pszReturn = strdup(szDosError);
230 }
231 return (pszReturn);
232}
233
234/*
235 *@@ doshQuerySysUptime:
236 * returns the system uptime in milliseconds.
237 * This can be used for time comparisons.
238 *
239 *@@added V0.9.12 (2001-05-18) [umoeller]
240 */
241
242ULONG doshQuerySysUptime(VOID)
243{
244 ULONG ulms;
245 DosQuerySysInfo(QSV_MS_COUNT,
246 QSV_MS_COUNT,
247 &ulms,
248 sizeof(ulms));
249 return (ulms);
250}
251
252/*
253 *@@ doshDevIOCtl:
254 *
255 * Works with those IOCtls where the buffer
256 * size parameters are always the same anyway,
257 * which applies to all IOCtls I have seen
258 * so far.
259 *
260 *@@added V0.9.13 (2001-06-14) [umoeller]
261 */
262
263APIRET doshDevIOCtl(HFILE hf,
264 ULONG ulCategory,
265 ULONG ulFunction,
266 PVOID pvParams,
267 ULONG cbParams,
268 PVOID pvData,
269 ULONG cbData)
270{
271 return (DosDevIOCtl(hf,
272 ulCategory,
273 ulFunction,
274 pvParams, cbParams, &cbParams,
275 pvData, cbData, &cbData));
276}
277
278/*
279 *@@category: Helpers\Control program helpers\Shared memory management
280 * helpers for allocating and requesting shared memory.
281 */
282
283/* ******************************************************************
284 *
285 * Memory helpers
286 *
287 ********************************************************************/
288
289/*
290 *@@ doshAllocSharedMem:
291 * wrapper for DosAllocSharedMem which has
292 * a malloc()-like syntax. Just due to my
293 * lazyness.
294 *
295 * Note that ulSize is always rounded up to the
296 * next 4KB value, so don't use this hundreds of times.
297 *
298 * Returns NULL upon errors. Possible errors include
299 * that a memory block calle pcszName has already been
300 * allocated.
301 *
302 * Use DosFreeMem(pvrc) to free the memory. The memory
303 * will only be freed if no other process has requested
304 * access.
305 *
306 *@@added V0.9.3 (2000-04-18) [umoeller]
307 */
308
309PVOID doshAllocSharedMem(ULONG ulSize, // in: requested mem block size (rounded up to 4KB)
310 const char* pcszName) // in: name of block ("\\SHAREMEM\\xxx") or NULL
311{
312 PVOID pvrc = NULL;
313 APIRET arc = DosAllocSharedMem((PVOID*)&pvrc,
314 (PSZ)pcszName,
315 ulSize,
316 PAG_COMMIT | PAG_READ | PAG_WRITE);
317 if (arc == NO_ERROR)
318 return (pvrc);
319
320 return (NULL);
321}
322
323/*
324 *@@ doshRequestSharedMem:
325 * requests access to a block of named shared memory
326 * allocated by doshAllocSharedMem.
327 *
328 * Returns NULL upon errors.
329 *
330 * Use DosFreeMem(pvrc) to free the memory. The memory
331 * will only be freed if no other process has requested
332 * access.
333 *
334 *@@added V0.9.3 (2000-04-19) [umoeller]
335 */
336
337PVOID doshRequestSharedMem(const char *pcszName)
338{
339 PVOID pvrc = NULL;
340 APIRET arc = DosGetNamedSharedMem((PVOID*)pvrc,
341 (PSZ)pcszName,
342 PAG_READ | PAG_WRITE);
343 if (arc == NO_ERROR)
344 return (pvrc);
345
346 return (NULL);
347}
348
349/*
350 *@@category: Helpers\Control program helpers\Drive management
351 * functions for managing drives... enumerating, testing,
352 * querying etc.
353 */
354
355/* ******************************************************************
356 *
357 * Drive helpers
358 *
359 ********************************************************************/
360
361/*
362 *@@ doshIsFixedDisk:
363 * checks whether a disk is fixed or removeable.
364 * ulLogicalDrive must be 1 for drive A:, 2 for B:, ...
365 * The result is stored in *pfFixed.
366 * Returns DOS error code.
367 *
368 * From my testing, this function does _not_ provoke
369 * "drive not ready" popups, even if the disk is not
370 * ready.
371 *
372 * Warning: This uses DosDevIOCtl, which has proved
373 * to cause problems with some device drivers for
374 * removeable disks.
375 *
376 *@@changed V0.9.14 (2001-08-03) [umoeller]: added extra fix for A: and B:
377 */
378
379APIRET doshIsFixedDisk(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
380 PBOOL pfFixed) // out: TRUE for fixed disks
381{
382 APIRET arc = ERROR_INVALID_DRIVE;
383
384 if ( (ulLogicalDrive == 1)
385 || (ulLogicalDrive == 2)
386 )
387 {
388 // drive A: and B: can never be fixed V0.9.14 (2001-08-03) [umoeller]
389 *pfFixed = FALSE;
390 return NO_ERROR;
391 }
392
393 if (ulLogicalDrive)
394 {
395 // parameter packet
396 #pragma pack(1)
397 struct {
398 UCHAR command,
399 drive;
400 } parms;
401 #pragma pack()
402
403 // data packet
404 UCHAR ucNonRemoveable;
405
406 parms.drive = (UCHAR)(ulLogicalDrive-1);
407 arc = doshDevIOCtl((HFILE)-1,
408 IOCTL_DISK, // 0x08
409 DSK_BLOCKREMOVABLE, // 0x20
410 &parms, sizeof(parms),
411 &ucNonRemoveable, sizeof(ucNonRemoveable));
412
413 if (arc == NO_ERROR)
414 *pfFixed = (BOOL)ucNonRemoveable;
415 }
416
417 return (arc);
418}
419
420/*
421 *@@ doshQueryDiskParams:
422 * this retrieves more information about a given drive,
423 * which is stored in the specified BIOSPARAMETERBLOCK
424 * structure.
425 *
426 * BIOSPARAMETERBLOCK is defined in the Toolkit headers,
427 * and from my testing, it's the same with the Toolkits
428 * 3 and 4.5.
429 *
430 * If NO_ERROR is returned, the bDeviceType field can
431 * be one of the following (according to CPREF):
432 *
433 * -- 0: 48 TPI low-density diskette drive
434 * -- 1: 96 TPI high-density diskette drive
435 * -- 2: 3.5-inch 720KB diskette drive
436 * -- 3: 8-Inch single-density diskette drive
437 * -- 4: 8-Inch double-density diskette drive
438 * -- 5: Fixed disk
439 * -- 6: Tape drive
440 * -- 7: Other (includes 1.44MB 3.5-inch diskette drive)
441 * -- 8: R/W optical disk
442 * -- 9: 3.5-inch 4.0MB diskette drive (2.88MB formatted)
443 *
444 * From my testing, this function does _not_ provoke
445 * "drive not ready" popups, even if the disk is not
446 * ready.
447 *
448 * Warning: This uses DosDevIOCtl, which has proved
449 * to cause problems with some device drivers for
450 * removeable disks.
451 *
452 * This returns the DOS error code of DosDevIOCtl.
453 *
454 *@@added V0.9.0 [umoeller]
455 *@@changed V0.9.13 (2001-06-14) [umoeller]: changed prototype to use BIOSPARAMETERBLOCK directly
456 *@@changed V0.9.13 (2001-06-14) [umoeller]: now querying standard media, no redetermine
457 */
458
459APIRET doshQueryDiskParams(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
460 PBIOSPARAMETERBLOCK pdp) // out: drive parameters
461{
462 APIRET arc = ERROR_INVALID_DRIVE;
463
464 if (ulLogicalDrive)
465 {
466 #pragma pack(1)
467 // parameter packet
468 struct {
469 UCHAR ucCommand,
470 ucDrive;
471 } parms;
472 #pragma pack()
473
474 parms.ucCommand = 0; // 0 = return standard media,
475 // 1 = read currently inserted media
476 // (1 doesn't work any more, returns arc 87
477 // V0.9.13 (2001-06-14) [umoeller])
478 parms.ucDrive=(UCHAR)(ulLogicalDrive-1);
479
480 // zero the structure V0.9.13 (2001-06-14) [umoeller]
481 memset(pdp, 0, sizeof(BIOSPARAMETERBLOCK));
482
483 arc = doshDevIOCtl((HFILE)-1,
484 IOCTL_DISK, // 0x08
485 DSK_GETDEVICEPARAMS, // 0x63
486 &parms, sizeof(parms),
487 pdp, sizeof(BIOSPARAMETERBLOCK));
488
489 /* if (!arc)
490 {
491 _Pmpf((" bDeviceType: %d", pdp->bDeviceType));
492 _Pmpf((" bytes per sector: %d", pdp->usBytesPerSector));
493 _Pmpf((" sectors per track: %d", pdp->usSectorsPerTrack));
494 } */
495 }
496
497 return (arc);
498}
499
500/*
501 *@@ doshIsCDROM:
502 * tests the specified BIOSPARAMETERBLOCK
503 * for whether it represents a CD-ROM drive.
504 *
505 * The BIOSPARAMETERBLOCK must be filled
506 * first using doshQueryDiskParams.
507 *
508 *@@added V0.9.13 (2001-06-14) [umoeller]
509 */
510
511BOOL doshIsCDROM(PBIOSPARAMETERBLOCK pdp)
512{
513 return ( (pdp)
514 && (pdp->bDeviceType == 7) // "other"
515 && (pdp->usBytesPerSector == 2048)
516 && (pdp->usSectorsPerTrack == (USHORT)-1)
517 );
518}
519
520/*
521 *@@ doshHasAudioCD:
522 * sets *pfAudio to whether ulLogicalDrive
523 * currently has an audio CD inserted.
524 *
525 * Better call this only if you're sure that
526 * ulLogicalDrive is a CD-ROM drive. Use
527 * doshIsCDROM to check.
528 *
529 *@@added V0.9.14 (2001-08-01) [umoeller]
530 */
531
532APIRET doshHasAudioCD(ULONG ulLogicalDrive,
533 HFILE hfDrive, // in: DASD open
534 BOOL fMixedModeCD,
535 PBOOL pfAudio)
536{
537 APIRET arc = NO_ERROR;
538
539 ULONG ulAudioTracks = 0,
540 ulDataTracks = 0;
541
542 CHAR cds1[4] = { 'C', 'D', '0', '1' };
543 CHAR cds2[4];
544
545 *pfAudio = FALSE;
546
547 // check for proper driver signature
548 if (!(arc = doshDevIOCtl(hfDrive,
549 IOCTL_CDROMDISK,
550 CDROMDISK_GETDRIVER,
551 &cds1, sizeof(cds1),
552 &cds2, sizeof(cds2))))
553 {
554 if (memcmp(&cds1, &cds2, 4))
555 // this is not a CD-ROM then:
556 arc = NO_ERROR;
557 else
558 {
559 struct {
560 UCHAR ucFirstTrack,
561 ucLastTrack;
562 ULONG ulLeadOut;
563 } cdat;
564
565 // get track count
566 if (!(arc = doshDevIOCtl(hfDrive,
567 IOCTL_CDROMAUDIO,
568 CDROMAUDIO_GETAUDIODISK,
569 &cds1, sizeof(cds1),
570 &cdat, sizeof(cdat))))
571 {
572 // still no error: build the audio TOC
573 ULONG i;
574 for (i = cdat.ucFirstTrack;
575 i <= cdat.ucLastTrack;
576 i++)
577 {
578 BYTE cdtp[5] =
579 { 'C', 'D', '0', '1', (UCHAR)i };
580
581 struct {
582 ULONG ulTrackAddress;
583 BYTE bFlags;
584 } trackdata;
585
586 if (!(arc = doshDevIOCtl(hfDrive,
587 IOCTL_CDROMAUDIO,
588 CDROMAUDIO_GETAUDIOTRACK,
589 &cdtp, sizeof(cdtp),
590 &trackdata, sizeof(trackdata))))
591 {
592 if (trackdata.bFlags & 64)
593 ulDataTracks++;
594 else
595 {
596 ulAudioTracks++;
597
598 if (!fMixedModeCD)
599 {
600 // caller doesn't want mixed mode:
601 // stop here
602 ulDataTracks = 0;
603 break;
604 }
605 }
606 }
607 }
608
609 // _Pmpf((" got %d audio, %d data tracks",
610 // ulAudioTracks, ulDataTracks));
611
612 if (!ulDataTracks)
613 *pfAudio = TRUE;
614 }
615 else
616 {
617 // not audio disk:
618 // go on then
619 // _Pmpf((" CDROMAUDIO_GETAUDIODISK returned %d", arc));
620 arc = NO_ERROR;
621 }
622 }
623 }
624 else
625 {
626 // not CD-ROM: go on then
627 // _Pmpf((" CDROMDISK_GETDRIVER returned %d", arc));
628 arc = NO_ERROR;
629 }
630
631 return (arc);
632}
633
634/*
635 *@@ doshEnumDrives:
636 * this function enumerates all valid drive letters on
637 * the system by composing a string of drive letters
638 * in the buffer pointed to by pszBuffer, which should
639 * be 27 characters in size to hold information for
640 * all drives. The buffer will be null-terminated.
641 *
642 * If (pcszFileSystem != NULL), only drives matching
643 * the specified file system type (e.g. "HPFS") will
644 * be enumerated. If (pcszFileSystem == NULL), all
645 * drives will be enumerated.
646 *
647 * If (fSkipRemovables == TRUE), removeable drives will
648 * be skipped. This applies to floppy, CD-ROM, and
649 * virtual floppy drives. This will start the search
650 * at drive letter C: so that drives A: and B: will
651 * never be checked (to avoid the hardware bumps).
652 *
653 * Otherwise, the search starts at drive A:. Still,
654 * removeable drives will only be added if valid media
655 * is inserted.
656 *
657 *@@changed V0.9.4 (2000-07-03) [umoeller]: this stopped at the first invalid drive letter; fixed
658 *@@changed V0.9.4 (2000-07-03) [umoeller]: added fSkipRemoveables
659 */
660
661VOID doshEnumDrives(PSZ pszBuffer, // out: drive letters
662 const char *pcszFileSystem, // in: FS's to match or NULL
663 BOOL fSkipRemoveables) // in: if TRUE, only non-removeable disks will be returned
664{
665 CHAR szName[5] = "";
666 ULONG ulLogicalDrive = 1, // start with drive A:
667 ulFound = 0; // found drives count
668 APIRET arc = NO_ERROR; // return code
669
670 if (fSkipRemoveables)
671 // start with drive C:
672 ulLogicalDrive = 3;
673
674 // go thru the drives, start with C: (== 3), stop after Z: (== 26)
675 while (ulLogicalDrive <= 26)
676 {
677 #pragma pack(1)
678 struct
679 {
680 UCHAR dummy,drive;
681 } parms;
682 #pragma pack()
683
684 // data packet
685 UCHAR nonRemovable=0;
686
687 parms.drive=(UCHAR)(ulLogicalDrive-1);
688 arc = doshDevIOCtl((HFILE)-1,
689 IOCTL_DISK,
690 DSK_BLOCKREMOVABLE,
691 &parms, sizeof(parms),
692 &nonRemovable, sizeof(nonRemovable));
693
694 if ( // fixed disk and non-removeable
695 ((arc == NO_ERROR) && (nonRemovable))
696 // or network drive:
697 || (arc == ERROR_NOT_SUPPORTED)
698 )
699 {
700 ULONG ulOrdinal = 0; // ordinal of entry in name list
701 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
702 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
703 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
704
705 szName[0] = ulLogicalDrive + 'A' - 1;
706 szName[1] = ':';
707 szName[2] = '\0';
708
709 arc = DosQueryFSAttach(szName, // logical drive of attached FS
710 ulOrdinal, // ignored for FSAIL_QUERYNAME
711 FSAIL_QUERYNAME, // return data for a Drive or Device
712 pfsqBuffer, // returned data
713 &cbBuffer); // returned data length
714
715 if (arc == NO_ERROR)
716 {
717 // The data for the last three fields in the FSQBUFFER2
718 // structure are stored at the offset of fsqBuffer.szName.
719 // Each data field following fsqBuffer.szName begins
720 // immediately after the previous item.
721 CHAR* pszFSDName = (PSZ)&(pfsqBuffer->szName) + (pfsqBuffer->cbName) + 1;
722 if (pcszFileSystem == NULL)
723 {
724 // enum-all mode: always copy
725 pszBuffer[ulFound] = szName[0]; // drive letter
726 ulFound++;
727 }
728 else if (strcmp(pszFSDName, pcszFileSystem) == 0)
729 {
730 pszBuffer[ulFound] = szName[0]; // drive letter
731 ulFound++;
732 }
733 }
734 }
735
736 ulLogicalDrive++;
737 } // end while (G_acDriveLetters[ulLogicalDrive] <= 'Z')
738
739 pszBuffer[ulFound] = '\0';
740}
741
742/*
743 *@@ doshQueryBootDrive:
744 * returns the letter of the boot drive as a
745 * single (capital) character, which is useful for
746 * constructing file names using sprintf and such.
747 */
748
749CHAR doshQueryBootDrive(VOID)
750{
751 ULONG ulBootDrive;
752 DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
753 &ulBootDrive,
754 sizeof(ulBootDrive));
755 return (ulBootDrive + 'A' - 1);
756}
757
758/*
759 *@@ doshAssertDrive:
760 * this checks for whether the given drive
761 * is currently available without provoking
762 * those ugly white "Drive not ready" popups.
763 *
764 * "fl" can specify additional flags for testing
765 * and can be any combination of:
766 *
767 * -- ASSERTFL_MIXEDMODECD: whether to allow
768 * mixed-mode CD-ROMs. See error codes below.
769 *
770 * This returns (from my testing):
771 *
772 * -- NO_ERROR: drive is available.
773 *
774 * -- ERROR_INVALID_DRIVE (15): drive letter does not exist.
775 *
776 * -- ERROR_NOT_READY (21): drive exists, but is not ready.
777 * This is produced by floppies and CD-ROM drives
778 * which do not have valid media inserted.
779 *
780 * -- ERROR_AUDIO_CD_ROM (10000): special error code returned
781 * only by this function if a CD-ROM drive has audio
782 * media inserted.
783 *
784 * If ASSERTFL_MIXEDMODECD was specified, ERROR_AUDIO_CD_ROM
785 * is returned _only_ if _no_ data tracks are
786 * present on a CD-ROM. Since OS/2 is not very
787 * good at handling mixed-mode CDs, this might not
788 * be desireable.
789 *
790 * If ASSERTFL_MIXEDMODECD was not set, ERROR_AUDIO_CD_ROM
791 * will be returned already if _one_ audio track is present.
792 *
793 *@@changed V0.9.1 (99-12-13) [umoeller]: rewritten, prototype changed. Now using DosOpen on the drive instead of DosError.
794 *@@changed V0.9.1 (2000-01-08) [umoeller]: DosClose was called even if DosOpen failed, which messed up OS/2 error handling.
795 *@@changed V0.9.1 (2000-02-09) [umoeller]: this didn't work for network drives, including RAMFS; fixed.
796 *@@changed V0.9.3 (2000-03-28) [umoeller]: added check for network drives, which weren't working
797 *@@changed V0.9.4 (2000-08-03) [umoeller]: more network fixes
798 *@@changed V0.9.9 (2001-03-19) [pr]: validate drive number
799 *@@changed V0.9.11 (2001-04-23) [umoeller]: added an extra check for floppies
800 *@@changed V0.9.13 (2001-06-14) [umoeller]: added "fl" parameter and lots of CD-ROM checks
801 */
802
803APIRET doshAssertDrive(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
804 ULONG fl) // in: ASSERTFL_* flags
805{
806 APIRET arc = NO_ERROR;
807 BOOL fFixed = FALSE,
808 fCDROM = FALSE;
809
810 if ((ulLogicalDrive < 1) || (ulLogicalDrive > 26))
811 return(ERROR_PATH_NOT_FOUND);
812
813 arc = doshIsFixedDisk(ulLogicalDrive,
814 &fFixed); // V0.9.13 (2001-06-14) [umoeller]
815
816 // _Pmpf((__FUNCTION__ ": doshIsFixedDisk returned %d for disk %d", arc, ulLogicalDrive));
817 // _Pmpf((" fFixed is %d", fFixed));
818
819 if (!arc)
820 if (!fFixed)
821 {
822 // removeable disk:
823 // check if it's a CD-ROM
824 BIOSPARAMETERBLOCK bpb;
825 arc = doshQueryDiskParams(ulLogicalDrive,
826 &bpb);
827 // _Pmpf((" doshQueryDiskParams returned %d", arc));
828
829 if ( (!arc)
830 && (doshIsCDROM(&bpb))
831 )
832 {
833 // _Pmpf((" --> is CD-ROM"));
834 fCDROM = TRUE;
835 }
836 }
837
838 if (!arc)
839 {
840 HFILE hfDrive = NULLHANDLE;
841
842 ULONG ulTemp = 0;
843 CHAR szDrive[3] = "C:";
844 szDrive[0] = 'A' + ulLogicalDrive - 1;
845
846 arc = DosOpen(szDrive, // "C:", "D:", ...
847 &hfDrive,
848 &ulTemp,
849 0,
850 FILE_NORMAL,
851 OPEN_ACTION_FAIL_IF_NEW
852 | OPEN_ACTION_OPEN_IF_EXISTS,
853 OPEN_FLAGS_DASD
854 | OPEN_FLAGS_FAIL_ON_ERROR
855 | OPEN_FLAGS_NOINHERIT // V0.9.6 (2000-11-25) [pr]
856 // | OPEN_ACCESS_READONLY // V0.9.13 (2001-06-14) [umoeller]
857 | OPEN_SHARE_DENYNONE,
858 NULL);
859
860 // _Pmpf((" DosOpen(OPEN_FLAGS_DASD) returned %d", arc));
861
862 // this still returns NO_ERROR for audio CDs in a
863 // CD-ROM drive...
864 // however, the WPS then attempts to read in the
865 // root directory for audio CDs, which produces
866 // a "sector not found" error box...
867
868 if ( (!arc)
869 && (hfDrive)
870 && (fCDROM)
871 )
872 {
873 BOOL fAudio;
874 if ( (!(arc = doshHasAudioCD(ulLogicalDrive,
875 hfDrive,
876 ((fl & ASSERTFL_MIXEDMODECD) != 0),
877 &fAudio)))
878 && (fAudio)
879 )
880 arc = ERROR_AUDIO_CD_ROM; // special private error code (10000)
881 }
882
883 if (hfDrive)
884 DosClose(hfDrive);
885 }
886
887 switch (arc)
888 {
889 case ERROR_NETWORK_ACCESS_DENIED: // 65
890 // added V0.9.3 (2000-03-27) [umoeller];
891 // according to user reports, this is returned
892 // by all network drives, which apparently don't
893 // support DASD DosOpen
894 case ERROR_ACCESS_DENIED: // 5
895 // added V0.9.4 (2000-07-10) [umoeller]
896 // LAN drives still didn't work... apparently
897 // the above only works for NFS drives
898 case ERROR_PATH_NOT_FOUND: // 3
899 // added V0.9.4 (2000-08-03) [umoeller]:
900 // this is returned by some other network types...
901 // sigh...
902 case ERROR_NOT_SUPPORTED: // 50
903 // this is returned by file systems which don't
904 // support DASD DosOpen;
905 // use some other method then, this isn't likely
906 // to fail -- V0.9.1 (2000-02-09) [umoeller]
907
908 // but don't do this for floppies
909 // V0.9.11 (2001-04-23) [umoeller]
910 if (ulLogicalDrive > 2)
911 {
912 FSALLOCATE fsa;
913 arc = DosQueryFSInfo(ulLogicalDrive,
914 FSIL_ALLOC,
915 &fsa,
916 sizeof(fsa));
917 // _Pmpf((" re-checked, DosQueryFSInfo returned %d", arc));
918 }
919 break;
920 }
921
922 return (arc);
923}
924
925/*
926 *@@ doshSetLogicalMap:
927 * sets the mapping of logical floppy drives onto a single
928 * physical floppy drive.
929 * This means selecting either drive A: or drive B: to refer
930 * to the physical drive.
931 *
932 *@@added V0.9.6 (2000-11-24) [pr]
933 */
934
935APIRET doshSetLogicalMap(ULONG ulLogicalDrive)
936{
937 CHAR name[3] = "?:";
938 ULONG fd = 0,
939 action = 0;
940// paramsize = 0;
941// datasize = 0;
942 APIRET rc = NO_ERROR;
943 USHORT data,
944 param;
945
946 name[0] = doshQueryBootDrive();
947 rc = DosOpen(name,
948 &fd,
949 &action,
950 0,
951 0,
952 OPEN_ACTION_FAIL_IF_NEW
953 | OPEN_ACTION_OPEN_IF_EXISTS,
954 OPEN_FLAGS_DASD
955 | OPEN_FLAGS_FAIL_ON_ERROR
956 | OPEN_FLAGS_NOINHERIT
957 | OPEN_ACCESS_READONLY
958 | OPEN_SHARE_DENYNONE,
959 0);
960
961 if (rc == NO_ERROR)
962 {
963 param = 0;
964 data = (USHORT)ulLogicalDrive;
965 // paramsize = sizeof(param);
966 // datasize = sizeof(data);
967 rc = doshDevIOCtl(fd,
968 IOCTL_DISK, DSK_SETLOGICALMAP,
969 &param, sizeof(param),
970 &data, sizeof(data));
971 DosClose(fd);
972 }
973
974 return(rc);
975}
976
977/*
978 *@@ doshQueryDiskSize:
979 * returns the size of the specified disk in bytes.
980 *
981 * Note: This returns a "double" value, because a ULONG
982 * can only hold values of some 4 billion, which would
983 * lead to funny results for drives > 4 GB.
984 *
985 *@@added V0.9.11 (2001-04-18) [umoeller]
986 */
987
988APIRET doshQueryDiskSize(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
989 double *pdSize)
990{
991 APIRET arc = NO_ERROR;
992 FSALLOCATE fsa;
993 // double dbl = -1;
994
995 if (!(arc = DosQueryFSInfo(ulLogicalDrive, FSIL_ALLOC, &fsa, sizeof(fsa))))
996 *pdSize = ((double)fsa.cSectorUnit * fsa.cbSector * fsa.cUnit);
997
998 return (arc);
999}
1000
1001/*
1002 *@@ doshQueryDiskFree:
1003 * returns the number of bytes remaining on the disk
1004 * specified by the given logical drive.
1005 *
1006 * Note: This returns a "double" value, because a ULONG
1007 * can only hold values of some 4 billion, which would
1008 * lead to funny results for drives > 4 GB.
1009 *
1010 *@@changed V0.9.0 [umoeller]: fixed another > 4 GB bug (thanks to Rdiger Ihle)
1011 *@@changed V0.9.7 (2000-12-01) [umoeller]: changed prototype
1012 */
1013
1014APIRET doshQueryDiskFree(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1015 double *pdFree)
1016{
1017 APIRET arc = NO_ERROR;
1018 FSALLOCATE fsa;
1019 // double dbl = -1;
1020
1021 if (!(arc = DosQueryFSInfo(ulLogicalDrive, FSIL_ALLOC, &fsa, sizeof(fsa))))
1022 *pdFree = ((double)fsa.cSectorUnit * fsa.cbSector * fsa.cUnitAvail);
1023 // ^ fixed V0.9.0
1024
1025 return (arc);
1026}
1027
1028/*
1029 *@@ doshQueryDiskFSType:
1030 * copies the file-system type of the given disk object
1031 * (HPFS, FAT, CDFS etc.) to pszBuf.
1032 * Returns the DOS error code.
1033 *
1034 *@@changed V0.9.1 (99-12-12) [umoeller]: added cbBuf to prototype
1035 *@@changed V0.9.14 (2001-08-01) [umoeller]: fixed, this never respected cbBuf
1036 *@@changed V0.9.16 (2001-10-02) [umoeller]: added check for valid logical disk no
1037 */
1038
1039APIRET doshQueryDiskFSType(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1040 PSZ pszBuf, // out: buffer for FS type
1041 ULONG cbBuf) // in: size of that buffer
1042{
1043 APIRET arc = NO_ERROR;
1044 CHAR szName[5];
1045
1046 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
1047 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
1048 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
1049
1050 // compose "D:"-type string from logical drive letter
1051 if (ulLogicalDrive > 0 && ulLogicalDrive < 27)
1052 {
1053 szName[0] = ulLogicalDrive + 'A' - 1;
1054 szName[1] = ':';
1055 szName[2] = '\0';
1056
1057 arc = DosQueryFSAttach(szName, // logical drive of attached FS ("D:"-style)
1058 0, // ulOrdinal, ignored for FSAIL_QUERYNAME
1059 FSAIL_QUERYNAME, // return name for a drive or device
1060 pfsqBuffer, // buffer for returned data
1061 &cbBuffer); // sizeof(*pfsqBuffer)
1062
1063 if (arc == NO_ERROR)
1064 {
1065 if (pszBuf)
1066 {
1067 // The data for the last three fields in the FSQBUFFER2
1068 // structure are stored at the offset of fsqBuffer.szName.
1069 // Each data field following fsqBuffer.szName begins
1070 // immediately after the previous item.
1071 strncpy(pszBuf,
1072 (CHAR*)(&pfsqBuffer->szName) + pfsqBuffer->cbName + 1,
1073 cbBuf); // V0.9.14 (2001-08-01) [umoeller]
1074 *(pszBuf + cbBuf) = '\0';
1075 }
1076 }
1077 }
1078 else
1079 arc = ERROR_INVALID_PARAMETER; // V0.9.16 (2001-10-02) [umoeller]
1080
1081 return (arc);
1082}
1083
1084/*
1085 *@@ doshQueryDiskLabel:
1086 * this returns the label of a disk into
1087 * *pszVolumeLabel, which must be 12 bytes
1088 * in size.
1089 *
1090 * This function was added because the Toolkit
1091 * information for DosQueryFSInfo is only partly
1092 * correct. On OS/2 2.x, that function does not
1093 * take an FSINFO structure as input, but a VOLUMELABEL.
1094 * On Warp, this does take an FSINFO.
1095 *
1096 * DosSetFSInfo is even worse. See doshSetDiskLabel.
1097 *
1098 * See http://zebra.asta.fh-weingarten.de/os2/Snippets/Bugi6787.HTML
1099 * for details.
1100 *
1101 *@@added V0.9.0 [umoeller]
1102 *@@changed V0.9.11 (2001-04-22) [umoeller]: this copied even with errors, fixed
1103 */
1104
1105APIRET doshQueryDiskLabel(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1106 PSZ pszVolumeLabel) // out: volume label (must be 12 chars in size)
1107{
1108 APIRET arc;
1109
1110 #ifdef __OS2V2X__
1111 VOLUMELABEL FSInfoBuf;
1112 #else
1113 FSINFO FSInfoBuf;
1114 #endif
1115
1116 arc = DosQueryFSInfo(ulLogicalDrive,
1117 FSIL_VOLSER,
1118 &FSInfoBuf,
1119 sizeof(FSInfoBuf)); // depends
1120
1121 if (!arc) // V0.9.11 (2001-04-22) [umoeller]
1122 {
1123 #ifdef __OS2V2X__
1124 strcpy(pszVolumeLabel, FSInfoBuf.szVolLabel);
1125 #else
1126 strcpy(pszVolumeLabel, FSInfoBuf.vol.szVolLabel);
1127 #endif
1128 }
1129
1130 return (arc);
1131}
1132
1133/*
1134 *@@ doshSetDiskLabel:
1135 * this sets the label of a disk.
1136 *
1137 * This function was added because the Toolkit
1138 * information for DosSetFSInfo is flat out wrong.
1139 * That function does not take an FSINFO structure
1140 * as input, but a VOLUMELABEL. As a result, using
1141 * that function with the Toolkit's calling specs
1142 * results in ERROR_LABEL_TOO_LONG always.
1143 *
1144 * See http://zebra.asta.fh-weingarten.de/os2/Snippets/Bugi6787.HTML
1145 * for details.
1146 *
1147 *@@added V0.9.0 [umoeller]
1148 */
1149
1150APIRET doshSetDiskLabel(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1151 PSZ pszNewLabel)
1152{
1153 VOLUMELABEL FSInfoBuf;
1154
1155 // check length; 11 chars plus null byte allowed
1156 FSInfoBuf.cch = (BYTE)strlen(pszNewLabel);
1157 if (FSInfoBuf.cch < sizeof(FSInfoBuf.szVolLabel))
1158 {
1159 strcpy(FSInfoBuf.szVolLabel, pszNewLabel);
1160
1161 return (DosSetFSInfo(ulLogicalDrive,
1162 FSIL_VOLSER,
1163 &FSInfoBuf,
1164 sizeof(FSInfoBuf)));
1165 }
1166 else
1167 return (ERROR_LABEL_TOO_LONG);
1168}
1169
1170/*
1171 *@@category: Helpers\Control program helpers\File management
1172 */
1173
1174/* ******************************************************************
1175 *
1176 * File helpers
1177 *
1178 ********************************************************************/
1179
1180/*
1181 *@@ doshGetExtension:
1182 * finds the file name extension of pszFilename,
1183 * which can be a file name only or a fully
1184 * qualified filename.
1185 *
1186 * This returns a pointer into pszFilename to
1187 * the character after the last dot.
1188 *
1189 * Returns NULL if not found (e.g. if the filename
1190 * has no dot in it).
1191 *
1192 *@@added V0.9.6 (2000-10-16) [umoeller]
1193 *@@changed V0.9.7 (2000-12-10) [umoeller]: fixed "F:filename.ext" case
1194 */
1195
1196PSZ doshGetExtension(const char *pcszFilename)
1197{
1198 PSZ pReturn = NULL;
1199
1200 if (pcszFilename)
1201 {
1202 // find filename
1203 const char *p2 = strrchr(pcszFilename + 2, '\\'),
1204 // works on "C:\blah" or "\\unc\blah"
1205 *pStartOfName = NULL,
1206 *pExtension = NULL;
1207
1208 if (p2)
1209 pStartOfName = p2 + 1;
1210 else
1211 {
1212 // no backslash found:
1213 // maybe only a drive letter was specified:
1214 if (*(pcszFilename + 1) == ':')
1215 // yes:
1216 pStartOfName = pcszFilename + 2;
1217 else
1218 // then this is not qualified at all...
1219 // use start of filename
1220 pStartOfName = (PSZ)pcszFilename;
1221 }
1222
1223 // find last dot in filename
1224 pExtension = strrchr(pStartOfName, '.');
1225 if (pExtension)
1226 pReturn = (PSZ)pExtension + 1;
1227 }
1228
1229 return (pReturn);
1230}
1231
1232/*
1233 *@@ doshIsFileOnFAT:
1234 * returns TRUE if pszFileName resides on
1235 * a FAT drive. Note that pszFileName must
1236 * be fully qualified (i.e. the drive letter
1237 * must be the first character), or this will
1238 * return garbage.
1239 */
1240
1241BOOL doshIsFileOnFAT(const char* pcszFileName)
1242{
1243 BOOL brc = FALSE;
1244 CHAR szName[5];
1245
1246 APIRET rc = NO_ERROR; // return code
1247 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
1248 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
1249 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
1250
1251 szName[0] = pcszFileName[0]; // copy drive letter
1252 szName[1] = ':';
1253 szName[2] = '\0';
1254
1255 rc = DosQueryFSAttach(szName, // logical drive of attached FS
1256 0, // ulOrdinal, ignored for FSAIL_QUERYNAME
1257 FSAIL_QUERYNAME, // return data for a Drive or Device
1258 pfsqBuffer, // returned data
1259 &cbBuffer); // returned data length
1260
1261 if (rc == NO_ERROR)
1262 {
1263 // The data for the last three fields in the FSQBUFFER2
1264 // structure are stored at the offset of fsqBuffer.szName.
1265 // Each data field following fsqBuffer.szName begins
1266 // immediately after the previous item.
1267 if (strncmp((PSZ)&(pfsqBuffer->szName) + pfsqBuffer->cbName + 1,
1268 "FAT",
1269 3)
1270 == 0)
1271 brc = TRUE;
1272 }
1273
1274 return (brc);
1275}
1276
1277/*
1278 *@@ doshQueryFileSize:
1279 * returns the size of an already opened file
1280 * or 0 upon errors.
1281 * Use doshQueryPathSize to query the size of
1282 * any file.
1283 */
1284
1285ULONG doshQueryFileSize(HFILE hFile)
1286{
1287 FILESTATUS3 fs3;
1288 if (DosQueryFileInfo(hFile, FIL_STANDARD, &fs3, sizeof(fs3)))
1289 return (0);
1290 else
1291 return (fs3.cbFile);
1292}
1293
1294/*
1295 *@@ doshQueryPathSize:
1296 * returns the size of any file,
1297 * or 0 if the file could not be
1298 * found.
1299 * Use doshQueryFileSize instead to query the
1300 * size if you have a HFILE.
1301 */
1302
1303ULONG doshQueryPathSize(PSZ pszFile)
1304{
1305 FILESTATUS3 fs3;
1306 if (DosQueryPathInfo(pszFile, FIL_STANDARD, &fs3, sizeof(fs3)))
1307 return (0);
1308 else
1309 return (fs3.cbFile);
1310}
1311
1312/*
1313 *@@ doshQueryPathAttr:
1314 * returns the file attributes of pszFile,
1315 * which can be fully qualified. The
1316 * attributes will be stored in *pulAttr.
1317 * pszFile can also specify a directory,
1318 * although not all attributes make sense
1319 * for directories.
1320 *
1321 * fAttr can be:
1322 * -- FILE_ARCHIVED
1323 * -- FILE_READONLY
1324 * -- FILE_SYSTEM
1325 * -- FILE_HIDDEN
1326 *
1327 * This returns the APIRET of DosQueryPathInfo.
1328 * *pulAttr is only valid if NO_ERROR is
1329 * returned.
1330 *
1331 *@@added V0.9.0 [umoeller]
1332 */
1333
1334APIRET doshQueryPathAttr(const char* pcszFile, // in: file or directory name
1335 PULONG pulAttr) // out: attributes
1336{
1337 FILESTATUS3 fs3;
1338 APIRET arc = DosQueryPathInfo((PSZ)pcszFile,
1339 FIL_STANDARD,
1340 &fs3,
1341 sizeof(fs3));
1342 if (arc == NO_ERROR)
1343 {
1344 if (pulAttr)
1345 *pulAttr = fs3.attrFile;
1346 }
1347
1348 return (arc);
1349}
1350
1351/*
1352 *@@ doshSetPathAttr:
1353 * sets the file attributes of pszFile,
1354 * which can be fully qualified.
1355 * pszFile can also specify a directory,
1356 * although not all attributes make sense
1357 * for directories.
1358 *
1359 * fAttr can be:
1360 * -- FILE_ARCHIVED
1361 * -- FILE_READONLY
1362 * -- FILE_SYSTEM
1363 * -- FILE_HIDDEN
1364 *
1365 * Note that this simply sets all the given
1366 * attributes; the existing attributes
1367 * are lost.
1368 *
1369 * This returns the APIRET of DosQueryPathInfo.
1370 */
1371
1372APIRET doshSetPathAttr(const char* pcszFile, // in: file or directory name
1373 ULONG ulAttr) // in: new attributes
1374{
1375 FILESTATUS3 fs3;
1376 APIRET rc = DosQueryPathInfo((PSZ)pcszFile,
1377 FIL_STANDARD,
1378 &fs3,
1379 sizeof(fs3));
1380
1381 if (rc == NO_ERROR)
1382 {
1383 fs3.attrFile = ulAttr;
1384 rc = DosSetPathInfo((PSZ)pcszFile,
1385 FIL_STANDARD,
1386 &fs3,
1387 sizeof(fs3),
1388 DSPI_WRTTHRU);
1389 }
1390 return (rc);
1391}
1392
1393/*
1394 *@@ doshOpenExisting:
1395 * opens an existing file for read-write access. Does
1396 * not create a new file if the file doesn't exist.
1397 *
1398 * This is just a simple wrapper around DosOpen.
1399 *
1400 * ulOpenFlags is passed to DosOpen. Should be one
1401 * of:
1402 *
1403 * -- for read-only access:
1404 *
1405 + OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY
1406 *
1407 * -- for read-write access:
1408 *
1409 + OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE
1410 *
1411 * In addition, you can specify
1412 *
1413 + OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_RANDOM
1414 + | OPEN_FLAGS_NOINHERIT
1415 *
1416 *@@added V0.9.13 (2001-06-14) [umoeller]
1417 */
1418
1419APIRET doshOpenExisting(const char *pcszFilename, // in: file name
1420 ULONG ulOpenFlags, // in: open flags
1421 HFILE *phf) // out: OS/2 file handle
1422{
1423 ULONG ulAction;
1424 return (DosOpen((PSZ)pcszFilename,
1425 phf,
1426 &ulAction,
1427 0, // cbFile
1428 0, // attributes
1429 OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
1430 ulOpenFlags,
1431 NULL)); // EAs
1432}
1433
1434/*
1435 *@@ doshWriteAt:
1436 * writes cb bytes (pointed to by pbData) to the
1437 * position specified by ulMethod and lOffset into
1438 * the file specified by hf.
1439 *
1440 * If ulMethod is FILE_BEGIN, lOffset specifies the
1441 * offset from the beginning of the file. With
1442 * FILE_CURRENT, lOffset is considered from the
1443 * current file pointer, and with FILE_END, it is
1444 * considered from the end of the file.
1445 *
1446 *@@added V0.9.13 (2001-06-14) [umoeller]
1447 */
1448
1449APIRET doshWriteAt(HFILE hf, // in: OS/2 file handle
1450 LONG lOffset, // in: offset to write at (depends on ulMethod)
1451 ULONG ulMethod, // in: one of FILE_BEGIN, FILE_CURRENT, FILE_END
1452 ULONG cb, // in: bytes to write
1453 PBYTE pbData) // in: ptr to bytes to write (must be cb bytes)
1454{
1455 APIRET arc;
1456 ULONG ulDummy;
1457 if (!(arc = DosSetFilePtr(hf,
1458 lOffset,
1459 ulMethod,
1460 &ulDummy)))
1461 arc = DosWrite(hf,
1462 pbData,
1463 cb,
1464 &ulDummy);
1465
1466 return (arc);
1467}
1468
1469/*
1470 *@@ doshReadAt:
1471 * reads cb bytes from the position specified by
1472 * ulMethod and lOffset into the buffer pointed to
1473 * by pbData, which should be cb bytes in size.
1474 *
1475 * Use lOffset and ulMethod as with doshWriteAt.
1476 *
1477 *@@added V0.9.13 (2001-06-14) [umoeller]
1478 */
1479
1480APIRET doshReadAt(HFILE hf, // in: OS/2 file handle
1481 LONG lOffset, // in: offset to write at (depends on ulMethod)
1482 ULONG ulMethod, // in: one of FILE_BEGIN, FILE_CURRENT, FILE_END
1483 ULONG cb, // in: bytes to write
1484 PBYTE pbData) // out: read buffer (must be cb bytes)
1485{
1486 APIRET arc;
1487 ULONG ulDummy;
1488 if (!(arc = DosSetFilePtr(hf,
1489 lOffset,
1490 ulMethod,
1491 &ulDummy)))
1492 arc = DosRead(hf,
1493 pbData,
1494 cb,
1495 &ulDummy);
1496
1497 return (arc);
1498}
1499
1500/*
1501 *@@ doshLoadTextFile:
1502 * reads a text file from disk, allocates memory
1503 * via malloc() and sets a pointer to this
1504 * buffer (or NULL upon errors).
1505 *
1506 * This returns the APIRET of DosOpen and DosRead.
1507 * If any error occured, no buffer was allocated.
1508 * Otherwise, you should free() the buffer when
1509 * no longer needed.
1510 *
1511 *@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from doshReadTextFile
1512 */
1513
1514APIRET doshLoadTextFile(const char *pcszFile, // in: file name to read
1515 PSZ* ppszContent) // out: newly allocated buffer with file's content
1516{
1517 ULONG ulSize,
1518 ulBytesRead = 0,
1519 ulAction, ulLocal;
1520 HFILE hFile;
1521 PSZ pszContent = NULL;
1522
1523 APIRET arc = DosOpen((PSZ)pcszFile,
1524 &hFile,
1525 &ulAction, // action taken
1526 5000L, // primary allocation size
1527 FILE_ARCHIVED | FILE_NORMAL, // file attribute
1528 OPEN_ACTION_OPEN_IF_EXISTS, // open flags
1529 OPEN_FLAGS_NOINHERIT
1530 | OPEN_SHARE_DENYNONE
1531 | OPEN_ACCESS_READONLY, // read-only mode
1532 NULL); // no EAs
1533
1534 if (arc == NO_ERROR)
1535 {
1536 ulSize = doshQueryFileSize(hFile);
1537 pszContent = (PSZ)malloc(ulSize+1);
1538 arc = DosSetFilePtr(hFile,
1539 0L,
1540 FILE_BEGIN,
1541 &ulLocal);
1542 arc = DosRead(hFile,
1543 pszContent,
1544 ulSize,
1545 &ulBytesRead);
1546 DosClose(hFile);
1547 *(pszContent+ulBytesRead) = 0;
1548
1549 // set output buffer pointer
1550 *ppszContent = pszContent;
1551 }
1552 else
1553 *ppszContent = 0;
1554
1555 return (arc);
1556}
1557
1558/*
1559 *@@ doshCreateBackupFileName:
1560 * creates a valid backup filename of pszExisting
1561 * with a numerical file name extension which does
1562 * not exist in the directory where pszExisting
1563 * resides.
1564 * Returns a PSZ to a new buffer which was allocated
1565 * using malloc().
1566 *
1567 * <B>Example:</B> returns "C:\CONFIG.002" for input
1568 * "C:\CONFIG.SYS" if "C:\CONFIG.001" already exists.
1569 *
1570 *@@changed V0.9.1 (99-12-13) [umoeller]: this crashed if pszExisting had no file extension
1571 */
1572
1573PSZ doshCreateBackupFileName(const char* pszExisting)
1574{
1575 CHAR szFilename[CCHMAXPATH];
1576 PSZ pszLastDot;
1577 ULONG ulCount = 1;
1578 CHAR szCount[5];
1579
1580 strcpy(szFilename, pszExisting);
1581 pszLastDot = strrchr(szFilename, '.');
1582 if (!pszLastDot)
1583 // no dot in filename:
1584 pszLastDot = szFilename + strlen(szFilename);
1585 do
1586 {
1587 sprintf(szCount, ".%03lu", ulCount);
1588 strcpy(pszLastDot, szCount);
1589 ulCount++;
1590 } while (doshQueryPathSize(szFilename) != 0);
1591
1592 return (strdup(szFilename));
1593}
1594
1595/*
1596 *@@ doshCreateTempFileName:
1597 * produces a file name in the the specified directory
1598 * or $(TEMP) which presently doesn't exist. This
1599 * checks the directory for existing files, but does
1600 * not open the temp file.
1601 *
1602 * If (pcszDir != NULL), we look into that directory.
1603 * Otherwise we look into the directory specified
1604 * by the $(TEMP) environment variable.
1605 * If $(TEMP) is not set, $(TMP) is tried next.
1606 *
1607 * If the directory thus specified does not exist, the
1608 * root directory of the boot drive is used instead.
1609 * As a result, this function should be fairly bomb-proof.
1610 *
1611 * If (pcszExt != NULL), the temp file receives
1612 * that extension, or no extension otherwise.
1613 * Do not specify the dot in pcszExt.
1614 *
1615 * pszTempFileName receives the fully qualified
1616 * file name of the temp file in that directory
1617 * and must point to a buffer CCHMAXPATH in size.
1618 * The file name is 8+3 compliant if pcszExt does
1619 * not exceed three characters.
1620 *
1621 * If (pcszPrefix != NULL), the temp file name
1622 * is prefixed with pcszPrefix. Since the temp
1623 * file name must not exceed 8+3 letters, we
1624 * can only use ( 8 - strlen(pcszPrefix ) digits
1625 * for a random number to make the temp file name
1626 * unique. You must therefore use a maximum of
1627 * four characters for the prefix. Otherwise
1628 * ERROR_INVALID_PARAMETER is returned.
1629 *
1630 * Example: Assuming TEMP is set to C:\TEMP,
1631 +
1632 + dosCreateTempFileName(szBuffer,
1633 + NULL, // use $(TEMP)
1634 + "pre", // prefix
1635 + "tmp") // extension
1636 +
1637 * would produce something like "C:\TEMP\pre07FG2.tmp".
1638 *
1639 *@@added V0.9.9 (2001-04-04) [umoeller]
1640 */
1641
1642APIRET doshCreateTempFileName(PSZ pszTempFileName, // out: fully q'fied temp file name
1643 const char *pcszDir, // in: dir or NULL for %TEMP%
1644 const char *pcszPrefix, // in: prefix for temp file or NULL
1645 const char *pcszExt) // in: extension (without dot) or NULL
1646{
1647 APIRET arc = NO_ERROR;
1648
1649 ULONG ulPrefixLen = (pcszPrefix)
1650 ? strlen(pcszPrefix)
1651 : 0;
1652
1653 if ( (!pszTempFileName)
1654 || (ulPrefixLen > 4)
1655 )
1656 arc = ERROR_INVALID_PARAMETER;
1657 else
1658 {
1659 CHAR szDir[CCHMAXPATH] = "";
1660 FILESTATUS3 fs3;
1661
1662 const char *pcszTemp = pcszDir;
1663
1664 if (!pcszTemp)
1665 {
1666 pcszTemp = getenv("TEMP");
1667 if (!pcszTemp)
1668 pcszTemp = getenv("TMP");
1669 }
1670
1671 if (pcszTemp) // either pcszDir or $(TEMP) or $(TMP) now
1672 if (DosQueryPathInfo((PSZ)pcszTemp,
1673 FIL_STANDARD,
1674 &fs3,
1675 sizeof(fs3)))
1676 // TEMP doesn't exist:
1677 pcszTemp = NULL;
1678
1679 if (!pcszTemp)
1680 // not set, or doesn't exist:
1681 // use root directory on boot drive
1682 sprintf(szDir,
1683 "%c:\\",
1684 doshQueryBootDrive());
1685 else
1686 {
1687 strcpy(szDir, pcszTemp);
1688 if (szDir[strlen(szDir) - 1] != '\\')
1689 strcat(szDir, "\\");
1690 }
1691
1692 if (!szDir[0])
1693 arc = ERROR_PATH_NOT_FOUND; // shouldn't happen
1694 else
1695 {
1696 ULONG ulRandom = 0;
1697 ULONG cAttempts = 0;
1698
1699 // produce random number
1700 DosQuerySysInfo(QSV_MS_COUNT,
1701 QSV_MS_COUNT,
1702 &ulRandom,
1703 sizeof(ulRandom));
1704
1705 do
1706 {
1707 CHAR szFile[20] = "",
1708 szFullTryThis[CCHMAXPATH];
1709
1710 // use the lower eight hex digits of the
1711 // system uptime as the temp dir name
1712 sprintf(szFile,
1713 "%08lX",
1714 ulRandom & 0xFFFFFFFF);
1715
1716 // if prefix is specified, overwrite the
1717 // first characters in the random number
1718 if (pcszPrefix)
1719 memcpy(szFile, pcszPrefix, ulPrefixLen);
1720
1721 if (pcszExt)
1722 {
1723 szFile[8] = '.';
1724 strcpy(szFile + 9, pcszExt);
1725 }
1726
1727 // now compose full temp file name
1728 strcpy(szFullTryThis, szDir);
1729 strcat(szFullTryThis, szFile);
1730 // now we have: "C:\temp\wpiXXXXX"
1731 if (DosQueryPathInfo(szFullTryThis,
1732 FIL_STANDARD,
1733 &fs3,
1734 sizeof(fs3))
1735 == ERROR_FILE_NOT_FOUND)
1736 {
1737 // file or dir doesn't exist:
1738 // cool, we're done
1739 strcpy(pszTempFileName, szFullTryThis);
1740 return (NO_ERROR);
1741 }
1742
1743 // if this didn't work, raise ulRandom and try again
1744 ulRandom += 123;
1745
1746 // try only 100 times, just to be sure
1747 cAttempts++;
1748 } while (cAttempts < 100);
1749
1750 // 100 loops elapsed:
1751 arc = ERROR_BAD_FORMAT;
1752 }
1753 }
1754
1755 return (arc);
1756}
1757
1758/*
1759 *@@ doshWriteTextFile:
1760 * writes a text file to disk; pszFile must contain the
1761 * whole path and filename.
1762 *
1763 * An existing file will be backed up if (pszBackup != NULL),
1764 * using doshCreateBackupFileName. In that case, pszBackup
1765 * receives the name of the backup created, so that buffer
1766 * should be CCHMAXPATH in size.
1767 *
1768 * If (pszbackup == NULL), an existing file will be overwritten.
1769 *
1770 * On success (NO_ERROR returned), *pulWritten receives
1771 * the no. of bytes written.
1772 *
1773 *@@changed V0.9.3 (2000-05-01) [umoeller]: optimized DosOpen; added error checking; changed prototype
1774 *@@changed V0.9.3 (2000-05-12) [umoeller]: added pszBackup
1775 */
1776
1777APIRET doshWriteTextFile(const char* pszFile, // in: file name
1778 const char* pszContent, // in: text to write
1779 PULONG pulWritten, // out: bytes written (ptr can be NULL)
1780 PSZ pszBackup) // in/out: create-backup?
1781{
1782 APIRET arc = NO_ERROR;
1783 ULONG ulWritten = 0;
1784
1785 if ((!pszFile) || (!pszContent))
1786 arc = ERROR_INVALID_PARAMETER;
1787 else
1788 {
1789 ULONG ulAction = 0,
1790 ulLocal = 0;
1791 HFILE hFile = 0;
1792
1793 ULONG ulSize = strlen(pszContent); // exclude 0 byte
1794
1795 if (pszBackup)
1796 {
1797 PSZ pszBackup2 = doshCreateBackupFileName(pszFile);
1798 DosCopy((PSZ)pszFile,
1799 pszBackup2,
1800 DCPY_EXISTING); // delete existing
1801 strcpy(pszBackup, pszBackup2);
1802 free(pszBackup2);
1803 }
1804
1805 arc = DosOpen((PSZ)pszFile,
1806 &hFile,
1807 &ulAction, // action taken
1808 ulSize, // primary allocation size
1809 FILE_ARCHIVED | FILE_NORMAL, // file attribute
1810 OPEN_ACTION_CREATE_IF_NEW
1811 | OPEN_ACTION_REPLACE_IF_EXISTS, // open flags
1812 OPEN_FLAGS_NOINHERIT
1813 | OPEN_FLAGS_SEQUENTIAL // sequential, not random access
1814 | OPEN_SHARE_DENYWRITE // deny write mode
1815 | OPEN_ACCESS_WRITEONLY, // write mode
1816 NULL); // no EAs
1817
1818 if (arc == NO_ERROR)
1819 {
1820 arc = DosSetFilePtr(hFile,
1821 0L,
1822 FILE_BEGIN,
1823 &ulLocal);
1824 if (arc == NO_ERROR)
1825 {
1826 arc = DosWrite(hFile,
1827 (PVOID)pszContent,
1828 ulSize,
1829 &ulWritten);
1830 if (arc == NO_ERROR)
1831 arc = DosSetFileSize(hFile, ulSize);
1832 }
1833
1834 DosClose(hFile);
1835 }
1836 } // end if ((pszFile) && (pszContent))
1837
1838 if (pulWritten)
1839 *pulWritten = ulWritten;
1840
1841 return (arc);
1842}
1843
1844/*
1845 *@@ doshOpenLogFile:
1846 * this opens a log file in the root directory of
1847 * the boot drive; it is titled pszFilename, and
1848 * the file handle is returned.
1849 */
1850
1851HFILE doshOpenLogFile(const char* pcszFilename)
1852{
1853 APIRET rc;
1854 CHAR szFileName[CCHMAXPATH];
1855 HFILE hfLog;
1856 ULONG ulAction;
1857 ULONG ibActual;
1858
1859 sprintf(szFileName, "%c:\\%s", doshQueryBootDrive(), pcszFilename);
1860 rc = DosOpen(szFileName,
1861 &hfLog,
1862 &ulAction,
1863 0, // file size
1864 FILE_NORMAL,
1865 OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
1866 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYWRITE,
1867 (PEAOP2)NULL);
1868 if (rc == NO_ERROR)
1869 {
1870 DosSetFilePtr(hfLog, 0, FILE_END, &ibActual);
1871 return (hfLog);
1872 }
1873 else
1874 return (0);
1875}
1876
1877/*
1878 * doshWriteToLogFile
1879 * writes a string to a log file, adding a
1880 * leading timestamp.
1881 */
1882
1883APIRET doshWriteToLogFile(HFILE hfLog, const char* pcsz)
1884{
1885 if (hfLog)
1886 {
1887 DATETIME dt;
1888 CHAR szTemp[2000];
1889 ULONG cbWritten;
1890 DosGetDateTime(&dt);
1891 sprintf(szTemp,
1892 "Time: %02d:%02d:%02d %s",
1893 dt.hours, dt.minutes, dt.seconds,
1894 pcsz);
1895 return (DosWrite(hfLog, (PVOID)szTemp, strlen(szTemp), &cbWritten));
1896 }
1897 else return (ERROR_INVALID_HANDLE);
1898}
1899
1900/*
1901 *@@category: Helpers\Control program helpers\Directory management
1902 * directory helpers (querying, creating, deleting etc.).
1903 */
1904
1905/* ******************************************************************
1906 *
1907 * Directory helpers
1908 *
1909 ********************************************************************/
1910
1911/*
1912 *@@ doshQueryDirExist:
1913 * returns TRUE if the given directory
1914 * exists.
1915 */
1916
1917BOOL doshQueryDirExist(const char *pcszDir)
1918{
1919 FILESTATUS3 fs3;
1920 APIRET arc = DosQueryPathInfo((PSZ)pcszDir,
1921 FIL_STANDARD,
1922 &fs3,
1923 sizeof(fs3));
1924 if (arc == NO_ERROR)
1925 // file found:
1926 return ((fs3.attrFile & FILE_DIRECTORY) != 0);
1927 else
1928 return FALSE;
1929}
1930
1931/*
1932 *@@ doshCreatePath:
1933 * this creates the specified directory.
1934 * As opposed to DosCreateDir, this
1935 * function can create several directories
1936 * at the same time, if the parent
1937 * directories do not exist yet.
1938 */
1939
1940APIRET doshCreatePath(const char *pcszPath,
1941 BOOL fHidden) // in: if TRUE, the new directories will get FILE_HIDDEN
1942{
1943 APIRET arc0 = NO_ERROR;
1944 CHAR path[CCHMAXPATH];
1945 CHAR *cp, c;
1946 ULONG cbPath;
1947
1948 strcpy(path, pcszPath);
1949 cbPath = strlen(path);
1950
1951 if (path[cbPath] != '\\')
1952 {
1953 path[cbPath] = '\\';
1954 path[cbPath+1] = 0;
1955 }
1956
1957 cp = path;
1958 // advance past the drive letter only if we have one
1959 if (*(cp+1) == ':')
1960 cp += 3;
1961
1962 // go!
1963 while (*cp != 0)
1964 {
1965 if (*cp == '\\')
1966 {
1967 c = *cp;
1968 *cp = 0;
1969 if (!doshQueryDirExist(path))
1970 {
1971 APIRET arc = DosCreateDir(path,
1972 0); // no EAs
1973 if (arc != NO_ERROR)
1974 {
1975 arc0 = arc;
1976 break;
1977 }
1978 else
1979 if (fHidden)
1980 {
1981 // hide the directory we just created
1982 doshSetPathAttr(path, FILE_HIDDEN);
1983 }
1984 }
1985 *cp = c;
1986 }
1987 cp++;
1988 }
1989 return (arc0);
1990}
1991
1992/*
1993 *@@ doshQueryCurrentDir:
1994 * writes the current directory into
1995 * the specified buffer, which should be
1996 * CCHMAXPATH in size.
1997 *
1998 * As opposed to DosQueryCurrentDir, this
1999 * includes the drive letter.
2000 *
2001 *@@added V0.9.4 (2000-07-22) [umoeller]
2002 */
2003
2004APIRET doshQueryCurrentDir(PSZ pszBuf)
2005{
2006 APIRET arc = NO_ERROR;
2007 ULONG ulCurDisk = 0;
2008 ULONG ulMap = 0;
2009 if (!(arc = DosQueryCurrentDisk(&ulCurDisk, &ulMap)))
2010 {
2011 ULONG cbBuf = CCHMAXPATH - 3;
2012 *pszBuf = ulCurDisk + 'A' - 1;
2013 *(pszBuf + 1) = ':';
2014 *(pszBuf + 2) = '\\';
2015 arc = DosQueryCurrentDir(0, pszBuf + 3, &cbBuf);
2016 }
2017
2018 return (arc);
2019}
2020
2021/*
2022 *@@ doshDeleteDir:
2023 * deletes a directory. As opposed to DosDeleteDir,
2024 * this removes empty subdirectories and/or files
2025 * as well.
2026 *
2027 * flFlags can be any combination of the following:
2028 *
2029 * -- DOSHDELDIR_RECURSE: recurse into subdirectories.
2030 *
2031 * -- DOSHDELDIR_DELETEFILES: delete all regular files
2032 * which are found on the way.
2033 *
2034 * THIS IS NOT IMPLEMENTED YET.
2035 *
2036 * If 0 is specified, this effectively behaves just as
2037 * DosDeleteDir.
2038 *
2039 * If you specify DOSHDELDIR_RECURSE only, only empty
2040 * subdirectories are deleted as well.
2041 *
2042 * If you specify DOSHDELDIR_RECURSE | DOSHDELDIR_DELETEFILES,
2043 * this removes an entire directory tree, including all
2044 * subdirectories and files.
2045 *
2046 *@@added V0.9.4 (2000-07-01) [umoeller]
2047 */
2048
2049APIRET doshDeleteDir(const char *pcszDir,
2050 ULONG flFlags,
2051 PULONG pulDirs, // out: directories found
2052 PULONG pulFiles) // out: files found
2053{
2054 APIRET arc = NO_ERROR,
2055 arcReturn = NO_ERROR;
2056
2057 HDIR hdirFindHandle = HDIR_CREATE;
2058 FILEFINDBUF3 ffb3 = {0}; // returned from FindFirst/Next
2059 ULONG ulResultBufLen = sizeof(FILEFINDBUF3);
2060 ULONG ulFindCount = 1; // look for 1 file at a time
2061
2062 CHAR szFileMask[2*CCHMAXPATH];
2063 sprintf(szFileMask, "%s\\*", pcszDir);
2064
2065 // find files
2066 arc = DosFindFirst(szFileMask,
2067 &hdirFindHandle, // directory search handle
2068 FILE_ARCHIVED | FILE_DIRECTORY | FILE_SYSTEM
2069 | FILE_HIDDEN | FILE_READONLY,
2070 // search attributes; include all, even dirs
2071 &ffb3, // result buffer
2072 ulResultBufLen, // result buffer length
2073 &ulFindCount, // number of entries to find
2074 FIL_STANDARD); // return level 1 file info
2075
2076 if (arc == NO_ERROR)
2077 {
2078 // keep finding the next file until there are no more files
2079 while (arc == NO_ERROR) // != ERROR_NO_MORE_FILES
2080 {
2081 if (ffb3.attrFile & FILE_DIRECTORY)
2082 {
2083 // we found a directory:
2084
2085 // ignore the pseudo-directories
2086 if ( (strcmp(ffb3.achName, ".") != 0)
2087 && (strcmp(ffb3.achName, "..") != 0)
2088 )
2089 {
2090 // real directory:
2091 if (flFlags & DOSHDELDIR_RECURSE)
2092 {
2093 // recurse!
2094 CHAR szSubDir[2*CCHMAXPATH];
2095 sprintf(szSubDir, "%s\\%s", pcszDir, ffb3.achName);
2096 arcReturn = doshDeleteDir(szSubDir,
2097 flFlags,
2098 pulDirs,
2099 pulFiles);
2100 // this removes ffb3.achName as well
2101 }
2102 else
2103 {
2104 // directory, but no recursion:
2105 // report "access denied"
2106 // (this is what OS/2 reports with DosDeleteDir as well)
2107 arcReturn = ERROR_ACCESS_DENIED; // 5
2108 (*pulDirs)++;
2109 }
2110 }
2111 }
2112 else
2113 {
2114 // it's a file:
2115 arcReturn = ERROR_ACCESS_DENIED;
2116 (*pulFiles)++;
2117 }
2118
2119 if (arc == NO_ERROR)
2120 {
2121 ulFindCount = 1; // reset find count
2122 arc = DosFindNext(hdirFindHandle, // directory handle
2123 &ffb3, // result buffer
2124 ulResultBufLen, // result buffer length
2125 &ulFindCount); // number of entries to find
2126 }
2127 } // endwhile
2128
2129 DosFindClose(hdirFindHandle); // close our find handle
2130 }
2131
2132 if (arcReturn == NO_ERROR)
2133 // success so far:
2134 // delete our directory now
2135 arc = DosDeleteDir((PSZ)pcszDir);
2136
2137 return (arcReturn);
2138}
2139
2140/*
2141 *@@category: Helpers\Control program helpers\Module handling
2142 * helpers for importing functions from a module (DLL).
2143 */
2144
2145/* ******************************************************************
2146 *
2147 * Module handling helpers
2148 *
2149 ********************************************************************/
2150
2151/*
2152 *@@ doshResolveImports:
2153 * this function loads the module called pszModuleName
2154 * and resolves imports dynamically using DosQueryProcAddress.
2155 *
2156 * To specify the functions to be imported, a RESOLVEFUNCTION
2157 * array is used. In each of the array items, specify the
2158 * name of the function and a pointer to a function pointer
2159 * where to store the resolved address.
2160 *
2161 *@@added V0.9.3 (2000-04-29) [umoeller]
2162 */
2163
2164APIRET doshResolveImports(PSZ pszModuleName, // in: DLL to load
2165 HMODULE *phmod, // out: module handle
2166 PRESOLVEFUNCTION paResolves, // in/out: function resolves
2167 ULONG cResolves) // in: array item count (not array size!)
2168{
2169 CHAR szName[CCHMAXPATH];
2170 APIRET arc = DosLoadModule(szName,
2171 sizeof(szName),
2172 pszModuleName,
2173 phmod);
2174 if (arc == NO_ERROR)
2175 {
2176 ULONG ul;
2177 for (ul = 0;
2178 ul < cResolves;
2179 ul++)
2180 {
2181 arc = DosQueryProcAddr(*phmod,
2182 0, // ordinal, ignored
2183 (PSZ)paResolves[ul].pcszFunctionName,
2184 paResolves[ul].ppFuncAddress);
2185
2186 /* _Pmpf(("Resolved %s to 0x%lX, rc: %d",
2187 paResolves[ul].pcszFunctionName,
2188 *paResolves[ul].ppFuncAddress,
2189 arc)); */
2190 if (arc != NO_ERROR)
2191 break;
2192 }
2193 }
2194
2195 return (arc);
2196}
2197
2198/*
2199 *@@category: Helpers\Control program helpers\Performance (CPU load) helpers
2200 * helpers around DosPerfSysCall.
2201 */
2202
2203/* ******************************************************************
2204 *
2205 * Performance Counters (CPU Load)
2206 *
2207 ********************************************************************/
2208
2209/*
2210 *@@ doshPerfOpen:
2211 * initializes the OS/2 DosPerfSysCall API for
2212 * the calling thread.
2213 *
2214 * Note: This API is not supported on all OS/2
2215 * versions. I believe it came up with some Warp 4
2216 * fixpak. The API is resolved dynamically by
2217 * this function (using DosQueryProcAddr). Only
2218 * if NO_ERROR is returned, you may call doshPerfGet
2219 * afterwards.
2220 *
2221 * This properly initializes the internal counters
2222 * which the OS/2 kernel uses for this API. Apparently,
2223 * with newer kernels (FP13/14), IBM has chosen to no
2224 * longer do this automatically, which is the reason
2225 * why many "pulse" utilities display garbage with these
2226 * fixpaks.
2227 *
2228 * After NO_ERROR is returned, DOSHPERFSYS.cProcessors
2229 * contains the no. of processors found on the system.
2230 * All pointers in DOSHPERFSYS then point to arrays
2231 * which have exactly cProcessors array items.
2232 *
2233 * Call doshPerfClose to clean up resources allocated
2234 * by this function.
2235 *
2236 * Example code:
2237 *
2238 + PDOSHPERFSYS pPerf = NULL;
2239 + APIRET arc = doshPerfOpen(&pPerf);
2240 + if (arc == NO_ERROR)
2241 + {
2242 + // this should really be in a timer
2243 + ULONG ulCPU;
2244 + arc = doshPerfGet(&pPerf);
2245 + // go thru all CPUs
2246 + for (ulCPU = 0; ulCPU < pPerf->cProcessors; ulCPU++)
2247 + {
2248 + LONG lLoadThis = pPerf->palLoads[ulCPU];
2249 + ...
2250 + }
2251 +
2252 + ...
2253 +
2254 + // clean up
2255 + doshPerfClose(&pPerf);
2256 + }
2257 +
2258 *
2259 *@@added V0.9.7 (2000-12-02) [umoeller]
2260 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
2261 */
2262
2263APIRET doshPerfOpen(PDOSHPERFSYS *ppPerfSys) // out: new DOSHPERFSYS structure
2264{
2265 APIRET arc = NO_ERROR;
2266
2267 // allocate DOSHPERFSYS structure
2268 *ppPerfSys = (PDOSHPERFSYS)malloc(sizeof(DOSHPERFSYS));
2269 if (!*ppPerfSys)
2270 arc = ERROR_NOT_ENOUGH_MEMORY;
2271 else
2272 {
2273 // initialize structure
2274 PDOSHPERFSYS pPerfSys = *ppPerfSys;
2275 memset(pPerfSys, 0, sizeof(*pPerfSys));
2276
2277 // resolve DosPerfSysCall API entry
2278 if (!(arc = DosLoadModule(NULL, 0, "DOSCALLS", &pPerfSys->hmod)))
2279 {
2280 if (!(arc = DosQueryProcAddr(pPerfSys->hmod,
2281 976,
2282 "DosPerfSysCall",
2283 (PFN*)(&pPerfSys->pDosPerfSysCall))))
2284 {
2285 // OK, we got the API: initialize!
2286 if (!(arc = pPerfSys->pDosPerfSysCall(CMD_KI_ENABLE, 0, 0, 0)))
2287 {
2288 pPerfSys->fInitialized = TRUE;
2289 // call CMD_KI_DISABLE later
2290
2291 if (!(arc = pPerfSys->pDosPerfSysCall(CMD_PERF_INFO,
2292 0,
2293 (ULONG)(&pPerfSys->cProcessors),
2294 0)))
2295 {
2296 ULONG ul = 0,
2297 cProcs = pPerfSys->cProcessors,
2298 cbDouble = cProcs * sizeof(double),
2299 cbLong = cProcs * sizeof(LONG);
2300
2301 // allocate arrays
2302 if ( (!(pPerfSys->paCPUUtils = (PCPUUTIL)calloc(cProcs,
2303 sizeof(CPUUTIL))))
2304 || (!(pPerfSys->padBusyPrev
2305 = (double*)malloc(cbDouble)))
2306 || (!(pPerfSys->padTimePrev
2307 = (double*)malloc(cbDouble)))
2308 || (!(pPerfSys->padIntrPrev
2309 = (double*)malloc(cbDouble)))
2310 || (!(pPerfSys->palLoads
2311 = (PLONG)malloc(cbLong)))
2312 || (!(pPerfSys->palIntrs
2313 = (PLONG)malloc(cbLong)))
2314 )
2315 arc = ERROR_NOT_ENOUGH_MEMORY;
2316 else
2317 {
2318 for (ul = 0; ul < cProcs; ul++)
2319 {
2320 pPerfSys->padBusyPrev[ul] = 0.0;
2321 pPerfSys->padTimePrev[ul] = 0.0;
2322 pPerfSys->padIntrPrev[ul] = 0.0;
2323 pPerfSys->palLoads[ul] = 0;
2324 pPerfSys->palIntrs[ul] = 0;
2325 }
2326 }
2327 }
2328 }
2329 } // end if (arc == NO_ERROR)
2330 } // end if (arc == NO_ERROR)
2331
2332 if (arc)
2333 // error: clean up
2334 doshPerfClose(ppPerfSys);
2335
2336 } // end else if (!*ppPerfSys)
2337
2338 return (arc);
2339}
2340
2341/*
2342 *@@ doshPerfGet:
2343 * calculates a current snapshot of the system load,
2344 * compared with the load which was calculated on
2345 * the previous call.
2346 *
2347 * If you want to continually measure the system CPU
2348 * load, this is the function you will want to call
2349 * regularly -- e.g. with a timer once per second.
2350 *
2351 * Call this ONLY if doshPerfOpen returned NO_ERROR,
2352 * or you'll get crashes.
2353 *
2354 * If this call returns NO_ERROR, you get LONG load
2355 * values for each CPU in the system in the arrays
2356 * in DOSHPERFSYS (in per-mille, 0-1000).
2357 *
2358 * There are two arrays:
2359 *
2360 * -- DOSHPERFSYS.palLoads contains the "user" load
2361 * for each CPU.
2362 *
2363 * -- DOSHPERFSYS.palIntrs contains the "IRQ" load
2364 * for each CPU.
2365 *
2366 * Sum up the two values to get the total load for
2367 * each CPU.
2368 *
2369 * For example, if there are two CPUs, after this call,
2370 *
2371 * -- DOSHPERFSYS.palLoads[0] contains the "user" load
2372 * of the first CPU,
2373 *
2374 * -- DOSHPERFSYS.palLoads[0] contains the "user" load
2375 * of the second CPU.
2376 *
2377 * See doshPerfOpen for example code.
2378 *
2379 *@@added V0.9.7 (2000-12-02) [umoeller]
2380 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
2381 */
2382
2383APIRET doshPerfGet(PDOSHPERFSYS pPerfSys)
2384{
2385 APIRET arc = NO_ERROR;
2386 if (!pPerfSys->pDosPerfSysCall)
2387 arc = ERROR_INVALID_PARAMETER;
2388 else
2389 {
2390 if (!(arc = pPerfSys->pDosPerfSysCall(CMD_KI_RDCNT,
2391 (ULONG)pPerfSys->paCPUUtils,
2392 0, 0)))
2393 {
2394 // go thru all processors
2395 ULONG ul = 0;
2396 for (; ul < pPerfSys->cProcessors; ul++)
2397 {
2398 PCPUUTIL pCPUUtilThis = &pPerfSys->paCPUUtils[ul];
2399
2400 double dTime = LL2F(pCPUUtilThis->ulTimeHigh,
2401 pCPUUtilThis->ulTimeLow);
2402 double dBusy = LL2F(pCPUUtilThis->ulBusyHigh,
2403 pCPUUtilThis->ulBusyLow);
2404 double dIntr = LL2F(pCPUUtilThis->ulIntrHigh,
2405 pCPUUtilThis->ulIntrLow);
2406
2407 double *pdBusyPrevThis = &pPerfSys->padBusyPrev[ul];
2408 double *pdTimePrevThis = &pPerfSys->padTimePrev[ul];
2409 double *pdIntrPrevThis = &pPerfSys->padIntrPrev[ul];
2410
2411 // avoid division by zero
2412 double dTimeDelta = (dTime - *pdTimePrevThis);
2413 if (dTimeDelta)
2414 {
2415 pPerfSys->palLoads[ul]
2416 = (LONG)( (double)( (dBusy - *pdBusyPrevThis)
2417 / dTimeDelta
2418 * 1000.0
2419 )
2420 );
2421 pPerfSys->palIntrs[ul]
2422 = (LONG)( (double)( (dIntr - *pdIntrPrevThis)
2423 / dTimeDelta
2424 * 1000.0
2425 )
2426 );
2427 }
2428 else
2429 {
2430 // no clear readings are available
2431 pPerfSys->palLoads[ul] = 0;
2432 pPerfSys->palIntrs[ul] = 0;
2433 }
2434
2435 *pdTimePrevThis = dTime;
2436 *pdBusyPrevThis = dBusy;
2437 *pdIntrPrevThis = dIntr;
2438 }
2439 }
2440 }
2441
2442 return (arc);
2443}
2444
2445/*
2446 *@@ doshPerfClose:
2447 * frees all resources allocated by doshPerfOpen.
2448 *
2449 *@@added V0.9.7 (2000-12-02) [umoeller]
2450 *@@changed V0.9.9 (2001-02-06) [umoeller]: removed disable; this broke the WarpCenter
2451 *@@changed V0.9.9 (2001-03-14) [umoeller]: fixed memory leak
2452 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
2453 */
2454
2455APIRET doshPerfClose(PDOSHPERFSYS *ppPerfSys)
2456{
2457 APIRET arc = NO_ERROR;
2458 PDOSHPERFSYS pPerfSys = *ppPerfSys;
2459 if (!pPerfSys)
2460 arc = ERROR_INVALID_PARAMETER;
2461 else
2462 {
2463 // do not call this, this messes up the WarpCenter V0.9.9 (2001-02-06) [umoeller]
2464 // if (pPerfSys->fInitialized) pPerfSys->pDosPerfSysCall(CMD_KI_DISABLE, 0, 0, 0);
2465
2466 if (pPerfSys->paCPUUtils)
2467 free(pPerfSys->paCPUUtils);
2468 if (pPerfSys->padBusyPrev)
2469 free(pPerfSys->padBusyPrev);
2470 if (pPerfSys->padTimePrev)
2471 free(pPerfSys->padTimePrev);
2472 if (pPerfSys->padIntrPrev)
2473 free(pPerfSys->padIntrPrev);
2474 if (pPerfSys->palLoads) // was missing V0.9.9 (2001-03-14) [umoeller]
2475 free(pPerfSys->palLoads);
2476 if (pPerfSys->palIntrs)
2477 free(pPerfSys->palIntrs);
2478
2479 if (pPerfSys->hmod)
2480 DosFreeModule(pPerfSys->hmod);
2481 free(pPerfSys);
2482 *ppPerfSys = NULL;
2483 }
2484
2485 return (arc);
2486}
2487
2488/*
2489 *@@category: Helpers\Control program helpers\Process management
2490 * helpers for starting subprocesses.
2491 */
2492
2493/* ******************************************************************
2494 *
2495 * Process helpers
2496 *
2497 ********************************************************************/
2498
2499static PVOID // G_pvGlobalInfoSeg = NULL,
2500 G_pvLocalInfoSeg = NULL;
2501
2502USHORT _Far16 _Pascal Dos16GetInfoSeg(PSEL pselGlobal,
2503 PSEL pselLocal);
2504
2505/*
2506 * GetInfoSegs:
2507 *
2508 */
2509
2510VOID GetInfoSegs(VOID)
2511{
2512 SEL GlobalInfoSegSelector,
2513 LocalInfoSegSelector;
2514
2515 // get selectors (old 16-bit API; this gets called only once)
2516 Dos16GetInfoSeg(&GlobalInfoSegSelector,
2517 &LocalInfoSegSelector);
2518 // thunk
2519 /* G_pvGlobalInfoSeg = (PVOID)( (GlobalInfoSegSelector << 0x000D)
2520 & 0x01fff0000); */
2521 G_pvLocalInfoSeg = (PVOID)( (LocalInfoSegSelector << 0x000D)
2522 & 0x01fff0000);
2523}
2524
2525/*
2526 *@@ doshMyPID:
2527 * returns the PID of the current process.
2528 *
2529 * This uses an interesting hack which is way
2530 * faster than DosGetInfoBlocks.
2531 *
2532 *@@added V0.9.9 (2001-04-04) [umoeller]
2533 */
2534
2535ULONG doshMyPID(VOID)
2536{
2537 if (!G_pvLocalInfoSeg)
2538 // first call:
2539 GetInfoSegs();
2540
2541 // PID is at offset 0 in the local info seg
2542 return (*(PUSHORT)G_pvLocalInfoSeg);
2543}
2544
2545/*
2546 *@@ doshMyTID:
2547 * returns the TID of the current thread.
2548 *
2549 * This uses an interesting hack which is way
2550 * faster than DosGetInfoBlocks.
2551 *
2552 *@@added V0.9.9 (2001-04-04) [umoeller]
2553 */
2554
2555ULONG doshMyTID(VOID)
2556{
2557 if (!G_pvLocalInfoSeg)
2558 // first call:
2559 GetInfoSegs();
2560
2561 // TID is at offset 6 in the local info seg
2562 return (*(PUSHORT)((PBYTE)G_pvLocalInfoSeg + 6));
2563}
2564
2565/*
2566 *@@ doshExecVIO:
2567 * executes cmd.exe with the /c parameter
2568 * and pcszExecWithArgs. This is equivalent
2569 * to the C library system() function,
2570 * except that an OS/2 error code is returned
2571 * and that this works with the VAC subsystem
2572 * library.
2573 *
2574 * This uses DosExecPgm and handles the sick
2575 * argument passing.
2576 *
2577 * If NO_ERROR is returned, *plExitCode receives
2578 * the exit code of the process. If the process
2579 * was terminated abnormally, *plExitCode receives:
2580 *
2581 * -- -1: hard error halt
2582 * -- -2: 16-bit trap
2583 * -- -3: DosKillProcess
2584 * -- -4: 32-bit exception
2585 *
2586 *@@added V0.9.4 (2000-07-27) [umoeller]
2587 */
2588
2589APIRET doshExecVIO(const char *pcszExecWithArgs,
2590 PLONG plExitCode) // out: exit code (ptr can be NULL)
2591{
2592 APIRET arc = NO_ERROR;
2593
2594 if (strlen(pcszExecWithArgs) > 255)
2595 arc = ERROR_INSUFFICIENT_BUFFER;
2596 else
2597 {
2598 CHAR szObj[CCHMAXPATH];
2599 RESULTCODES res = {0};
2600 CHAR szBuffer[300];
2601
2602 // DosExecPgm expects two args in szBuffer:
2603 // -- cmd.exe\0
2604 // -- then the actual argument, terminated by two 0 bytes.
2605 memset(szBuffer, 0, sizeof(szBuffer));
2606 strcpy(szBuffer, "cmd.exe\0");
2607 sprintf(szBuffer + 8, "/c %s",
2608 pcszExecWithArgs);
2609
2610 arc = DosExecPgm(szObj, sizeof(szObj),
2611 EXEC_SYNC,
2612 szBuffer,
2613 0,
2614 &res,
2615 "cmd.exe");
2616 if ((arc == NO_ERROR) && (plExitCode))
2617 {
2618 if (res.codeTerminate == 0)
2619 // normal exit:
2620 *plExitCode = res.codeResult;
2621 else
2622 *plExitCode = -((LONG)res.codeTerminate);
2623 }
2624 }
2625
2626 return (arc);
2627}
2628
2629/*
2630 *@@ doshQuickStartSession:
2631 * this is a shortcut to DosStartSession w/out having to
2632 * deal with all the messy parameters.
2633 *
2634 * This one starts pszPath as a child session and passes
2635 * pszParams to it.
2636 *
2637 * In usPgmCtl, OR any or none of the following (V0.9.0):
2638 * -- SSF_CONTROL_NOAUTOCLOSE (0x0008): do not close automatically.
2639 * This bit is used only for VIO Windowable apps and ignored
2640 * for all other applications.
2641 * -- SSF_CONTROL_MINIMIZE (0x0004)
2642 * -- SSF_CONTROL_MAXIMIZE (0x0002)
2643 * -- SSF_CONTROL_INVISIBLE (0x0001)
2644 * -- SSF_CONTROL_VISIBLE (0x0000)
2645 *
2646 * Specifying 0 will therefore auto-close the session and make it
2647 * visible.
2648 *
2649 * If (fWait), this function will create a termination queue
2650 * and not return until the child session has ended. Be warned,
2651 * this blocks the calling thread.
2652 *
2653 * Otherwise the function will return immediately.
2654 *
2655 * The session and process IDs of the child session will be
2656 * written to *pulSID and *ppid. Of course, in "wait" mode,
2657 * these are no longer valid after this function returns.
2658 *
2659 * Returns the error code of DosStartSession.
2660 *
2661 * Note: According to CPREF, calling DosStartSession calls
2662 * DosExecPgm in turn for all full-screen, VIO, and PM sessions.
2663 *
2664 *@@changed V0.9.0 [umoeller]: prototype changed to include usPgmCtl
2665 *@@changed V0.9.1 (99-12-30) [umoeller]: queue was sometimes not closed. Fixed.
2666 *@@changed V0.9.3 (2000-05-03) [umoeller]: added fForeground
2667 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed potential queue leak
2668 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed memory leak in wait mode; added pusReturn to prototype
2669 */
2670
2671APIRET doshQuickStartSession(const char *pcszPath, // in: program to start
2672 const char *pcszParams, // in: parameters for program
2673 BOOL fForeground, // in: if TRUE, session will be in foreground
2674 USHORT usPgmCtl, // in: STARTDATA.PgmControl
2675 BOOL fWait, // in: wait for termination?
2676 PULONG pulSID, // out: session ID (req.)
2677 PPID ppid, // out: process ID (req.)
2678 PUSHORT pusReturn) // out: in wait mode, session's return code (ptr can be NULL)
2679{
2680 APIRET arc = NO_ERROR;
2681 // queue stuff
2682 const char *pcszQueueName = "\\queues\\xwphlpsw.que";
2683 HQUEUE hq = 0;
2684 PID qpid = 0;
2685
2686 if (fWait)
2687 {
2688 if (!(arc = DosCreateQueue(&hq,
2689 QUE_FIFO | QUE_CONVERT_ADDRESS,
2690 (PSZ)pcszQueueName)))
2691 arc = DosOpenQueue(&qpid, &hq, (PSZ)pcszQueueName);
2692 }
2693
2694 if (!arc) // V0.9.14 (2001-08-03) [umoeller]
2695 {
2696 STARTDATA SData;
2697 CHAR szObjBuf[CCHMAXPATH];
2698
2699 SData.Length = sizeof(STARTDATA);
2700 SData.Related = SSF_RELATED_CHILD; //INDEPENDENT;
2701 SData.FgBg = (fForeground) ? SSF_FGBG_FORE : SSF_FGBG_BACK;
2702 // V0.9.3 (2000-05-03) [umoeller]
2703 SData.TraceOpt = SSF_TRACEOPT_NONE;
2704
2705 SData.PgmTitle = (PSZ)pcszPath; // title for window
2706 SData.PgmName = (PSZ)pcszPath;
2707 SData.PgmInputs = (PSZ)pcszParams;
2708
2709 SData.TermQ = (fWait) ? (PSZ)pcszQueueName : NULL;
2710 SData.Environment = 0;
2711 SData.InheritOpt = SSF_INHERTOPT_PARENT;
2712 SData.SessionType = SSF_TYPE_DEFAULT;
2713 SData.IconFile = 0;
2714 SData.PgmHandle = 0;
2715
2716 SData.PgmControl = usPgmCtl;
2717
2718 SData.InitXPos = 30;
2719 SData.InitYPos = 40;
2720 SData.InitXSize = 200;
2721 SData.InitYSize = 140;
2722 SData.Reserved = 0;
2723 SData.ObjectBuffer = szObjBuf;
2724 SData.ObjectBuffLen = (ULONG)sizeof(szObjBuf);
2725
2726 if ( (!(arc = DosStartSession(&SData, pulSID, ppid)))
2727 && (fWait)
2728 )
2729 {
2730 // block on the termination queue, which is written
2731 // to when the subprocess ends
2732 REQUESTDATA rqdata;
2733 ULONG cbData = 0;
2734 PULONG pulData = NULL;
2735 BYTE elpri;
2736
2737 rqdata.pid = qpid;
2738 if (!(arc = DosReadQueue(hq, // in: queue handle
2739 &rqdata, // out: pid and ulData
2740 &cbData, // out: size of data returned
2741 (PVOID*)&pulData, // out: data returned
2742 0, // in: remove first element in queue
2743 0, // in: wait for queue data (block thread)
2744 &elpri, // out: element's priority
2745 0))) // in: event semaphore to be posted
2746 {
2747 if (!rqdata.ulData)
2748 {
2749 // child session ended:
2750 // V0.9.14 (2001-08-03) [umoeller]
2751
2752 // *pulSID = (*pulData) & 0xffff;
2753 if (pusReturn)
2754 *pusReturn = ((*pulData) >> 16) & 0xffff;
2755
2756 }
2757 // else: continue looping
2758
2759 if (pulData)
2760 DosFreeMem(pulData);
2761 }
2762 }
2763 }
2764
2765 if (hq)
2766 DosCloseQueue(hq);
2767
2768 return (arc);
2769}
2770
2771
Note: See TracBrowser for help on using the repository browser.