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

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

Misc updates.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 90.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
71static 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] = G_acDriveLetters[ulLogicalDrive];
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 (G_acDriveLetters[ulBootDrive]);
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 */
1037
1038APIRET doshQueryDiskFSType(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1039 PSZ pszBuf, // out: buffer for FS type
1040 ULONG cbBuf) // in: size of that buffer
1041{
1042 APIRET arc = NO_ERROR;
1043 CHAR szName[5];
1044
1045 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
1046 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
1047 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
1048
1049 // compose "D:"-type string from logical drive letter
1050 szName[0] = G_acDriveLetters[ulLogicalDrive];
1051 szName[1] = ':';
1052 szName[2] = '\0';
1053
1054 arc = DosQueryFSAttach(szName, // logical drive of attached FS ("D:"-style)
1055 0, // ulOrdinal, ignored for FSAIL_QUERYNAME
1056 FSAIL_QUERYNAME, // return name for a drive or device
1057 pfsqBuffer, // buffer for returned data
1058 &cbBuffer); // sizeof(*pfsqBuffer)
1059
1060 if (arc == NO_ERROR)
1061 {
1062 if (pszBuf)
1063 {
1064 // The data for the last three fields in the FSQBUFFER2
1065 // structure are stored at the offset of fsqBuffer.szName.
1066 // Each data field following fsqBuffer.szName begins
1067 // immediately after the previous item.
1068 strncpy(pszBuf,
1069 (CHAR*)(&pfsqBuffer->szName) + pfsqBuffer->cbName + 1,
1070 cbBuf); // V0.9.14 (2001-08-01) [umoeller]
1071 *(pszBuf + cbBuf) = '\0';
1072 }
1073 }
1074
1075 return (arc);
1076}
1077
1078/*
1079 *@@ doshQueryDiskLabel:
1080 * this returns the label of a disk into
1081 * *pszVolumeLabel, which must be 12 bytes
1082 * in size.
1083 *
1084 * This function was added because the Toolkit
1085 * information for DosQueryFSInfo is only partly
1086 * correct. On OS/2 2.x, that function does not
1087 * take an FSINFO structure as input, but a VOLUMELABEL.
1088 * On Warp, this does take an FSINFO.
1089 *
1090 * DosSetFSInfo is even worse. See doshSetDiskLabel.
1091 *
1092 * See http://zebra.asta.fh-weingarten.de/os2/Snippets/Bugi6787.HTML
1093 * for details.
1094 *
1095 *@@added V0.9.0 [umoeller]
1096 *@@changed V0.9.11 (2001-04-22) [umoeller]: this copied even with errors, fixed
1097 */
1098
1099APIRET doshQueryDiskLabel(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1100 PSZ pszVolumeLabel) // out: volume label (must be 12 chars in size)
1101{
1102 APIRET arc;
1103
1104 #ifdef __OS2V2X__
1105 VOLUMELABEL FSInfoBuf;
1106 #else
1107 FSINFO FSInfoBuf;
1108 #endif
1109
1110 arc = DosQueryFSInfo(ulLogicalDrive,
1111 FSIL_VOLSER,
1112 &FSInfoBuf,
1113 sizeof(FSInfoBuf)); // depends
1114
1115 if (!arc) // V0.9.11 (2001-04-22) [umoeller]
1116 {
1117 #ifdef __OS2V2X__
1118 strcpy(pszVolumeLabel, FSInfoBuf.szVolLabel);
1119 #else
1120 strcpy(pszVolumeLabel, FSInfoBuf.vol.szVolLabel);
1121 #endif
1122 }
1123
1124 return (arc);
1125}
1126
1127/*
1128 *@@ doshSetDiskLabel:
1129 * this sets the label of a disk.
1130 *
1131 * This function was added because the Toolkit
1132 * information for DosSetFSInfo is flat out wrong.
1133 * That function does not take an FSINFO structure
1134 * as input, but a VOLUMELABEL. As a result, using
1135 * that function with the Toolkit's calling specs
1136 * results in ERROR_LABEL_TOO_LONG always.
1137 *
1138 * See http://zebra.asta.fh-weingarten.de/os2/Snippets/Bugi6787.HTML
1139 * for details.
1140 *
1141 *@@added V0.9.0 [umoeller]
1142 */
1143
1144APIRET doshSetDiskLabel(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1145 PSZ pszNewLabel)
1146{
1147 VOLUMELABEL FSInfoBuf;
1148
1149 // check length; 11 chars plus null byte allowed
1150 FSInfoBuf.cch = (BYTE)strlen(pszNewLabel);
1151 if (FSInfoBuf.cch < sizeof(FSInfoBuf.szVolLabel))
1152 {
1153 strcpy(FSInfoBuf.szVolLabel, pszNewLabel);
1154
1155 return (DosSetFSInfo(ulLogicalDrive,
1156 FSIL_VOLSER,
1157 &FSInfoBuf,
1158 sizeof(FSInfoBuf)));
1159 }
1160 else
1161 return (ERROR_LABEL_TOO_LONG);
1162}
1163
1164/*
1165 *@@category: Helpers\Control program helpers\File management
1166 */
1167
1168/* ******************************************************************
1169 *
1170 * File helpers
1171 *
1172 ********************************************************************/
1173
1174/*
1175 *@@ doshGetExtension:
1176 * finds the file name extension of pszFilename,
1177 * which can be a file name only or a fully
1178 * qualified filename.
1179 *
1180 * This returns a pointer into pszFilename to
1181 * the character after the last dot.
1182 *
1183 * Returns NULL if not found (e.g. if the filename
1184 * has no dot in it).
1185 *
1186 *@@added V0.9.6 (2000-10-16) [umoeller]
1187 *@@changed V0.9.7 (2000-12-10) [umoeller]: fixed "F:filename.ext" case
1188 */
1189
1190PSZ doshGetExtension(const char *pcszFilename)
1191{
1192 PSZ pReturn = NULL;
1193
1194 if (pcszFilename)
1195 {
1196 // find filename
1197 const char *p2 = strrchr(pcszFilename + 2, '\\'),
1198 // works on "C:\blah" or "\\unc\blah"
1199 *pStartOfName = NULL,
1200 *pExtension = NULL;
1201
1202 if (p2)
1203 pStartOfName = p2 + 1;
1204 else
1205 {
1206 // no backslash found:
1207 // maybe only a drive letter was specified:
1208 if (*(pcszFilename + 1) == ':')
1209 // yes:
1210 pStartOfName = pcszFilename + 2;
1211 else
1212 // then this is not qualified at all...
1213 // use start of filename
1214 pStartOfName = (PSZ)pcszFilename;
1215 }
1216
1217 // find last dot in filename
1218 pExtension = strrchr(pStartOfName, '.');
1219 if (pExtension)
1220 pReturn = (PSZ)pExtension + 1;
1221 }
1222
1223 return (pReturn);
1224}
1225
1226/*
1227 *@@ doshIsFileOnFAT:
1228 * returns TRUE if pszFileName resides on
1229 * a FAT drive. Note that pszFileName must
1230 * be fully qualified (i.e. the drive letter
1231 * must be the first character), or this will
1232 * return garbage.
1233 */
1234
1235BOOL doshIsFileOnFAT(const char* pcszFileName)
1236{
1237 BOOL brc = FALSE;
1238 CHAR szName[5];
1239
1240 APIRET rc = NO_ERROR; // return code
1241 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
1242 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
1243 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
1244
1245 szName[0] = pcszFileName[0]; // copy drive letter
1246 szName[1] = ':';
1247 szName[2] = '\0';
1248
1249 rc = DosQueryFSAttach(szName, // logical drive of attached FS
1250 0, // ulOrdinal, ignored for FSAIL_QUERYNAME
1251 FSAIL_QUERYNAME, // return data for a Drive or Device
1252 pfsqBuffer, // returned data
1253 &cbBuffer); // returned data length
1254
1255 if (rc == NO_ERROR)
1256 {
1257 // The data for the last three fields in the FSQBUFFER2
1258 // structure are stored at the offset of fsqBuffer.szName.
1259 // Each data field following fsqBuffer.szName begins
1260 // immediately after the previous item.
1261 if (strncmp((PSZ)&(pfsqBuffer->szName) + pfsqBuffer->cbName + 1,
1262 "FAT",
1263 3)
1264 == 0)
1265 brc = TRUE;
1266 }
1267
1268 return (brc);
1269}
1270
1271/*
1272 *@@ doshQueryFileSize:
1273 * returns the size of an already opened file
1274 * or 0 upon errors.
1275 * Use doshQueryPathSize to query the size of
1276 * any file.
1277 */
1278
1279ULONG doshQueryFileSize(HFILE hFile)
1280{
1281 FILESTATUS3 fs3;
1282 if (DosQueryFileInfo(hFile, FIL_STANDARD, &fs3, sizeof(fs3)))
1283 return (0);
1284 else
1285 return (fs3.cbFile);
1286}
1287
1288/*
1289 *@@ doshQueryPathSize:
1290 * returns the size of any file,
1291 * or 0 if the file could not be
1292 * found.
1293 * Use doshQueryFileSize instead to query the
1294 * size if you have a HFILE.
1295 */
1296
1297ULONG doshQueryPathSize(PSZ pszFile)
1298{
1299 FILESTATUS3 fs3;
1300 if (DosQueryPathInfo(pszFile, FIL_STANDARD, &fs3, sizeof(fs3)))
1301 return (0);
1302 else
1303 return (fs3.cbFile);
1304}
1305
1306/*
1307 *@@ doshQueryPathAttr:
1308 * returns the file attributes of pszFile,
1309 * which can be fully qualified. The
1310 * attributes will be stored in *pulAttr.
1311 * pszFile can also specify a directory,
1312 * although not all attributes make sense
1313 * for directories.
1314 *
1315 * fAttr can be:
1316 * -- FILE_ARCHIVED
1317 * -- FILE_READONLY
1318 * -- FILE_SYSTEM
1319 * -- FILE_HIDDEN
1320 *
1321 * This returns the APIRET of DosQueryPathInfo.
1322 * *pulAttr is only valid if NO_ERROR is
1323 * returned.
1324 *
1325 *@@added V0.9.0 [umoeller]
1326 */
1327
1328APIRET doshQueryPathAttr(const char* pcszFile, // in: file or directory name
1329 PULONG pulAttr) // out: attributes
1330{
1331 FILESTATUS3 fs3;
1332 APIRET arc = DosQueryPathInfo((PSZ)pcszFile,
1333 FIL_STANDARD,
1334 &fs3,
1335 sizeof(fs3));
1336 if (arc == NO_ERROR)
1337 {
1338 if (pulAttr)
1339 *pulAttr = fs3.attrFile;
1340 }
1341
1342 return (arc);
1343}
1344
1345/*
1346 *@@ doshSetPathAttr:
1347 * sets the file attributes of pszFile,
1348 * which can be fully qualified.
1349 * pszFile can also specify a directory,
1350 * although not all attributes make sense
1351 * for directories.
1352 *
1353 * fAttr can be:
1354 * -- FILE_ARCHIVED
1355 * -- FILE_READONLY
1356 * -- FILE_SYSTEM
1357 * -- FILE_HIDDEN
1358 *
1359 * Note that this simply sets all the given
1360 * attributes; the existing attributes
1361 * are lost.
1362 *
1363 * This returns the APIRET of DosQueryPathInfo.
1364 */
1365
1366APIRET doshSetPathAttr(const char* pcszFile, // in: file or directory name
1367 ULONG ulAttr) // in: new attributes
1368{
1369 FILESTATUS3 fs3;
1370 APIRET rc = DosQueryPathInfo((PSZ)pcszFile,
1371 FIL_STANDARD,
1372 &fs3,
1373 sizeof(fs3));
1374
1375 if (rc == NO_ERROR)
1376 {
1377 fs3.attrFile = ulAttr;
1378 rc = DosSetPathInfo((PSZ)pcszFile,
1379 FIL_STANDARD,
1380 &fs3,
1381 sizeof(fs3),
1382 DSPI_WRTTHRU);
1383 }
1384 return (rc);
1385}
1386
1387/*
1388 *@@ doshOpenExisting:
1389 * opens an existing file for read-write access. Does
1390 * not create a new file if the file doesn't exist.
1391 *
1392 * This is just a simple wrapper around DosOpen.
1393 *
1394 * ulOpenFlags is passed to DosOpen. Should be one
1395 * of:
1396 *
1397 * -- for read-only access:
1398 *
1399 + OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY
1400 *
1401 * -- for read-write access:
1402 *
1403 + OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE
1404 *
1405 * In addition, you can specify
1406 *
1407 + OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_RANDOM
1408 + | OPEN_FLAGS_NOINHERIT
1409 *
1410 *@@added V0.9.13 (2001-06-14) [umoeller]
1411 */
1412
1413APIRET doshOpenExisting(const char *pcszFilename, // in: file name
1414 ULONG ulOpenFlags, // in: open flags
1415 HFILE *phf) // out: OS/2 file handle
1416{
1417 ULONG ulAction;
1418 return (DosOpen((PSZ)pcszFilename,
1419 phf,
1420 &ulAction,
1421 0, // cbFile
1422 0, // attributes
1423 OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
1424 ulOpenFlags,
1425 NULL)); // EAs
1426}
1427
1428/*
1429 *@@ doshWriteAt:
1430 * writes cb bytes (pointed to by pbData) to the
1431 * position specified by ulMethod and lOffset into
1432 * the file specified by hf.
1433 *
1434 * If ulMethod is FILE_BEGIN, lOffset specifies the
1435 * offset from the beginning of the file. With
1436 * FILE_CURRENT, lOffset is considered from the
1437 * current file pointer, and with FILE_END, it is
1438 * considered from the end of the file.
1439 *
1440 *@@added V0.9.13 (2001-06-14) [umoeller]
1441 */
1442
1443APIRET doshWriteAt(HFILE hf, // in: OS/2 file handle
1444 LONG lOffset, // in: offset to write at (depends on ulMethod)
1445 ULONG ulMethod, // in: one of FILE_BEGIN, FILE_CURRENT, FILE_END
1446 ULONG cb, // in: bytes to write
1447 PBYTE pbData) // in: ptr to bytes to write (must be cb bytes)
1448{
1449 APIRET arc;
1450 ULONG ulDummy;
1451 if (!(arc = DosSetFilePtr(hf,
1452 lOffset,
1453 ulMethod,
1454 &ulDummy)))
1455 arc = DosWrite(hf,
1456 pbData,
1457 cb,
1458 &ulDummy);
1459
1460 return (arc);
1461}
1462
1463/*
1464 *@@ doshReadAt:
1465 * reads cb bytes from the position specified by
1466 * ulMethod and lOffset into the buffer pointed to
1467 * by pbData, which should be cb bytes in size.
1468 *
1469 * Use lOffset and ulMethod as with doshWriteAt.
1470 *
1471 *@@added V0.9.13 (2001-06-14) [umoeller]
1472 */
1473
1474APIRET doshReadAt(HFILE hf, // in: OS/2 file handle
1475 LONG lOffset, // in: offset to write at (depends on ulMethod)
1476 ULONG ulMethod, // in: one of FILE_BEGIN, FILE_CURRENT, FILE_END
1477 ULONG cb, // in: bytes to write
1478 PBYTE pbData) // out: read buffer (must be cb bytes)
1479{
1480 APIRET arc;
1481 ULONG ulDummy;
1482 if (!(arc = DosSetFilePtr(hf,
1483 lOffset,
1484 ulMethod,
1485 &ulDummy)))
1486 arc = DosRead(hf,
1487 pbData,
1488 cb,
1489 &ulDummy);
1490
1491 return (arc);
1492}
1493
1494/*
1495 *@@ doshLoadTextFile:
1496 * reads a text file from disk, allocates memory
1497 * via malloc() and sets a pointer to this
1498 * buffer (or NULL upon errors).
1499 *
1500 * This returns the APIRET of DosOpen and DosRead.
1501 * If any error occured, no buffer was allocated.
1502 * Otherwise, you should free() the buffer when
1503 * no longer needed.
1504 *
1505 *@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from doshReadTextFile
1506 */
1507
1508APIRET doshLoadTextFile(const char *pcszFile, // in: file name to read
1509 PSZ* ppszContent) // out: newly allocated buffer with file's content
1510{
1511 ULONG ulSize,
1512 ulBytesRead = 0,
1513 ulAction, ulLocal;
1514 HFILE hFile;
1515 PSZ pszContent = NULL;
1516
1517 APIRET arc = DosOpen((PSZ)pcszFile,
1518 &hFile,
1519 &ulAction, // action taken
1520 5000L, // primary allocation size
1521 FILE_ARCHIVED | FILE_NORMAL, // file attribute
1522 OPEN_ACTION_OPEN_IF_EXISTS, // open flags
1523 OPEN_FLAGS_NOINHERIT
1524 | OPEN_SHARE_DENYNONE
1525 | OPEN_ACCESS_READONLY, // read-only mode
1526 NULL); // no EAs
1527
1528 if (arc == NO_ERROR)
1529 {
1530 ulSize = doshQueryFileSize(hFile);
1531 pszContent = (PSZ)malloc(ulSize+1);
1532 arc = DosSetFilePtr(hFile,
1533 0L,
1534 FILE_BEGIN,
1535 &ulLocal);
1536 arc = DosRead(hFile,
1537 pszContent,
1538 ulSize,
1539 &ulBytesRead);
1540 DosClose(hFile);
1541 *(pszContent+ulBytesRead) = 0;
1542
1543 // set output buffer pointer
1544 *ppszContent = pszContent;
1545 }
1546 else
1547 *ppszContent = 0;
1548
1549 return (arc);
1550}
1551
1552/*
1553 *@@ doshCreateBackupFileName:
1554 * creates a valid backup filename of pszExisting
1555 * with a numerical file name extension which does
1556 * not exist in the directory where pszExisting
1557 * resides.
1558 * Returns a PSZ to a new buffer which was allocated
1559 * using malloc().
1560 *
1561 * <B>Example:</B> returns "C:\CONFIG.002" for input
1562 * "C:\CONFIG.SYS" if "C:\CONFIG.001" already exists.
1563 *
1564 *@@changed V0.9.1 (99-12-13) [umoeller]: this crashed if pszExisting had no file extension
1565 */
1566
1567PSZ doshCreateBackupFileName(const char* pszExisting)
1568{
1569 CHAR szFilename[CCHMAXPATH];
1570 PSZ pszLastDot;
1571 ULONG ulCount = 1;
1572 CHAR szCount[5];
1573
1574 strcpy(szFilename, pszExisting);
1575 pszLastDot = strrchr(szFilename, '.');
1576 if (!pszLastDot)
1577 // no dot in filename:
1578 pszLastDot = szFilename + strlen(szFilename);
1579 do
1580 {
1581 sprintf(szCount, ".%03lu", ulCount);
1582 strcpy(pszLastDot, szCount);
1583 ulCount++;
1584 } while (doshQueryPathSize(szFilename) != 0);
1585
1586 return (strdup(szFilename));
1587}
1588
1589/*
1590 *@@ doshCreateTempFileName:
1591 * produces a file name in the the specified directory
1592 * or $(TEMP) which presently doesn't exist. This
1593 * checks the directory for existing files, but does
1594 * not open the temp file.
1595 *
1596 * If (pcszDir != NULL), we look into that directory.
1597 * Otherwise we look into the directory specified
1598 * by the $(TEMP) environment variable.
1599 * If $(TEMP) is not set, $(TMP) is tried next.
1600 *
1601 * If the directory thus specified does not exist, the
1602 * root directory of the boot drive is used instead.
1603 * As a result, this function should be fairly bomb-proof.
1604 *
1605 * If (pcszExt != NULL), the temp file receives
1606 * that extension, or no extension otherwise.
1607 * Do not specify the dot in pcszExt.
1608 *
1609 * pszTempFileName receives the fully qualified
1610 * file name of the temp file in that directory
1611 * and must point to a buffer CCHMAXPATH in size.
1612 * The file name is 8+3 compliant if pcszExt does
1613 * not exceed three characters.
1614 *
1615 * If (pcszPrefix != NULL), the temp file name
1616 * is prefixed with pcszPrefix. Since the temp
1617 * file name must not exceed 8+3 letters, we
1618 * can only use ( 8 - strlen(pcszPrefix ) digits
1619 * for a random number to make the temp file name
1620 * unique. You must therefore use a maximum of
1621 * four characters for the prefix. Otherwise
1622 * ERROR_INVALID_PARAMETER is returned.
1623 *
1624 * Example: Assuming TEMP is set to C:\TEMP,
1625 +
1626 + dosCreateTempFileName(szBuffer,
1627 + NULL, // use $(TEMP)
1628 + "pre", // prefix
1629 + "tmp") // extension
1630 +
1631 * would produce something like "C:\TEMP\pre07FG2.tmp".
1632 *
1633 *@@added V0.9.9 (2001-04-04) [umoeller]
1634 */
1635
1636APIRET doshCreateTempFileName(PSZ pszTempFileName, // out: fully q'fied temp file name
1637 const char *pcszDir, // in: dir or NULL for %TEMP%
1638 const char *pcszPrefix, // in: prefix for temp file or NULL
1639 const char *pcszExt) // in: extension (without dot) or NULL
1640{
1641 APIRET arc = NO_ERROR;
1642
1643 ULONG ulPrefixLen = (pcszPrefix)
1644 ? strlen(pcszPrefix)
1645 : 0;
1646
1647 if ( (!pszTempFileName)
1648 || (ulPrefixLen > 4)
1649 )
1650 arc = ERROR_INVALID_PARAMETER;
1651 else
1652 {
1653 CHAR szDir[CCHMAXPATH] = "";
1654 FILESTATUS3 fs3;
1655
1656 const char *pcszTemp = pcszDir;
1657
1658 if (!pcszTemp)
1659 {
1660 pcszTemp = getenv("TEMP");
1661 if (!pcszTemp)
1662 pcszTemp = getenv("TMP");
1663 }
1664
1665 if (pcszTemp) // either pcszDir or $(TEMP) or $(TMP) now
1666 if (DosQueryPathInfo((PSZ)pcszTemp,
1667 FIL_STANDARD,
1668 &fs3,
1669 sizeof(fs3)))
1670 // TEMP doesn't exist:
1671 pcszTemp = NULL;
1672
1673 if (!pcszTemp)
1674 // not set, or doesn't exist:
1675 // use root directory on boot drive
1676 sprintf(szDir,
1677 "%c:\\",
1678 doshQueryBootDrive());
1679 else
1680 {
1681 strcpy(szDir, pcszTemp);
1682 if (szDir[strlen(szDir) - 1] != '\\')
1683 strcat(szDir, "\\");
1684 }
1685
1686 if (!szDir[0])
1687 arc = ERROR_PATH_NOT_FOUND; // shouldn't happen
1688 else
1689 {
1690 ULONG ulRandom = 0;
1691 ULONG cAttempts = 0;
1692
1693 // produce random number
1694 DosQuerySysInfo(QSV_MS_COUNT,
1695 QSV_MS_COUNT,
1696 &ulRandom,
1697 sizeof(ulRandom));
1698
1699 do
1700 {
1701 CHAR szFile[20] = "",
1702 szFullTryThis[CCHMAXPATH];
1703
1704 // use the lower eight hex digits of the
1705 // system uptime as the temp dir name
1706 sprintf(szFile,
1707 "%08lX",
1708 ulRandom & 0xFFFFFFFF);
1709
1710 // if prefix is specified, overwrite the
1711 // first characters in the random number
1712 if (pcszPrefix)
1713 memcpy(szFile, pcszPrefix, ulPrefixLen);
1714
1715 if (pcszExt)
1716 {
1717 szFile[8] = '.';
1718 strcpy(szFile + 9, pcszExt);
1719 }
1720
1721 // now compose full temp file name
1722 strcpy(szFullTryThis, szDir);
1723 strcat(szFullTryThis, szFile);
1724 // now we have: "C:\temp\wpiXXXXX"
1725 if (DosQueryPathInfo(szFullTryThis,
1726 FIL_STANDARD,
1727 &fs3,
1728 sizeof(fs3))
1729 == ERROR_FILE_NOT_FOUND)
1730 {
1731 // file or dir doesn't exist:
1732 // cool, we're done
1733 strcpy(pszTempFileName, szFullTryThis);
1734 return (NO_ERROR);
1735 }
1736
1737 // if this didn't work, raise ulRandom and try again
1738 ulRandom += 123;
1739
1740 // try only 100 times, just to be sure
1741 cAttempts++;
1742 } while (cAttempts < 100);
1743
1744 // 100 loops elapsed:
1745 arc = ERROR_BAD_FORMAT;
1746 }
1747 }
1748
1749 return (arc);
1750}
1751
1752/*
1753 *@@ doshWriteTextFile:
1754 * writes a text file to disk; pszFile must contain the
1755 * whole path and filename.
1756 *
1757 * An existing file will be backed up if (pszBackup != NULL),
1758 * using doshCreateBackupFileName. In that case, pszBackup
1759 * receives the name of the backup created, so that buffer
1760 * should be CCHMAXPATH in size.
1761 *
1762 * If (pszbackup == NULL), an existing file will be overwritten.
1763 *
1764 * On success (NO_ERROR returned), *pulWritten receives
1765 * the no. of bytes written.
1766 *
1767 *@@changed V0.9.3 (2000-05-01) [umoeller]: optimized DosOpen; added error checking; changed prototype
1768 *@@changed V0.9.3 (2000-05-12) [umoeller]: added pszBackup
1769 */
1770
1771APIRET doshWriteTextFile(const char* pszFile, // in: file name
1772 const char* pszContent, // in: text to write
1773 PULONG pulWritten, // out: bytes written (ptr can be NULL)
1774 PSZ pszBackup) // in/out: create-backup?
1775{
1776 APIRET arc = NO_ERROR;
1777 ULONG ulWritten = 0;
1778
1779 if ((!pszFile) || (!pszContent))
1780 arc = ERROR_INVALID_PARAMETER;
1781 else
1782 {
1783 ULONG ulAction = 0,
1784 ulLocal = 0;
1785 HFILE hFile = 0;
1786
1787 ULONG ulSize = strlen(pszContent); // exclude 0 byte
1788
1789 if (pszBackup)
1790 {
1791 PSZ pszBackup2 = doshCreateBackupFileName(pszFile);
1792 DosCopy((PSZ)pszFile,
1793 pszBackup2,
1794 DCPY_EXISTING); // delete existing
1795 strcpy(pszBackup, pszBackup2);
1796 free(pszBackup2);
1797 }
1798
1799 arc = DosOpen((PSZ)pszFile,
1800 &hFile,
1801 &ulAction, // action taken
1802 ulSize, // primary allocation size
1803 FILE_ARCHIVED | FILE_NORMAL, // file attribute
1804 OPEN_ACTION_CREATE_IF_NEW
1805 | OPEN_ACTION_REPLACE_IF_EXISTS, // open flags
1806 OPEN_FLAGS_NOINHERIT
1807 | OPEN_FLAGS_SEQUENTIAL // sequential, not random access
1808 | OPEN_SHARE_DENYWRITE // deny write mode
1809 | OPEN_ACCESS_WRITEONLY, // write mode
1810 NULL); // no EAs
1811
1812 if (arc == NO_ERROR)
1813 {
1814 arc = DosSetFilePtr(hFile,
1815 0L,
1816 FILE_BEGIN,
1817 &ulLocal);
1818 if (arc == NO_ERROR)
1819 {
1820 arc = DosWrite(hFile,
1821 (PVOID)pszContent,
1822 ulSize,
1823 &ulWritten);
1824 if (arc == NO_ERROR)
1825 arc = DosSetFileSize(hFile, ulSize);
1826 }
1827
1828 DosClose(hFile);
1829 }
1830 } // end if ((pszFile) && (pszContent))
1831
1832 if (pulWritten)
1833 *pulWritten = ulWritten;
1834
1835 return (arc);
1836}
1837
1838/*
1839 *@@ doshOpenLogFile:
1840 * this opens a log file in the root directory of
1841 * the boot drive; it is titled pszFilename, and
1842 * the file handle is returned.
1843 */
1844
1845HFILE doshOpenLogFile(const char* pcszFilename)
1846{
1847 APIRET rc;
1848 CHAR szFileName[CCHMAXPATH];
1849 HFILE hfLog;
1850 ULONG ulAction;
1851 ULONG ibActual;
1852
1853 sprintf(szFileName, "%c:\\%s", doshQueryBootDrive(), pcszFilename);
1854 rc = DosOpen(szFileName,
1855 &hfLog,
1856 &ulAction,
1857 0, // file size
1858 FILE_NORMAL,
1859 OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
1860 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYWRITE,
1861 (PEAOP2)NULL);
1862 if (rc == NO_ERROR)
1863 {
1864 DosSetFilePtr(hfLog, 0, FILE_END, &ibActual);
1865 return (hfLog);
1866 }
1867 else
1868 return (0);
1869}
1870
1871/*
1872 * doshWriteToLogFile
1873 * writes a string to a log file, adding a
1874 * leading timestamp.
1875 */
1876
1877APIRET doshWriteToLogFile(HFILE hfLog, const char* pcsz)
1878{
1879 if (hfLog)
1880 {
1881 DATETIME dt;
1882 CHAR szTemp[2000];
1883 ULONG cbWritten;
1884 DosGetDateTime(&dt);
1885 sprintf(szTemp, "Time: %02d:%02d:%02d %s",
1886 dt.hours, dt.minutes, dt.seconds,
1887 pcsz);
1888 return (DosWrite(hfLog, (PVOID)szTemp, strlen(szTemp), &cbWritten));
1889 }
1890 else return (ERROR_INVALID_HANDLE);
1891}
1892
1893/*
1894 *@@category: Helpers\Control program helpers\Directory management
1895 * directory helpers (querying, creating, deleting etc.).
1896 */
1897
1898/* ******************************************************************
1899 *
1900 * Directory helpers
1901 *
1902 ********************************************************************/
1903
1904/*
1905 *@@ doshQueryDirExist:
1906 * returns TRUE if the given directory
1907 * exists.
1908 */
1909
1910BOOL doshQueryDirExist(const char *pcszDir)
1911{
1912 FILESTATUS3 fs3;
1913 APIRET arc = DosQueryPathInfo((PSZ)pcszDir,
1914 FIL_STANDARD,
1915 &fs3,
1916 sizeof(fs3));
1917 if (arc == NO_ERROR)
1918 // file found:
1919 return ((fs3.attrFile & FILE_DIRECTORY) != 0);
1920 else
1921 return FALSE;
1922}
1923
1924/*
1925 *@@ doshCreatePath:
1926 * this creates the specified directory.
1927 * As opposed to DosCreateDir, this
1928 * function can create several directories
1929 * at the same time, if the parent
1930 * directories do not exist yet.
1931 */
1932
1933APIRET doshCreatePath(const char *pcszPath,
1934 BOOL fHidden) // in: if TRUE, the new directories will get FILE_HIDDEN
1935{
1936 APIRET arc0 = NO_ERROR;
1937 CHAR path[CCHMAXPATH];
1938 CHAR *cp, c;
1939 ULONG cbPath;
1940
1941 strcpy(path, pcszPath);
1942 cbPath = strlen(path);
1943
1944 if (path[cbPath] != '\\')
1945 {
1946 path[cbPath] = '\\';
1947 path[cbPath+1] = 0;
1948 }
1949
1950 cp = path;
1951 // advance past the drive letter only if we have one
1952 if (*(cp+1) == ':')
1953 cp += 3;
1954
1955 // go!
1956 while (*cp != 0)
1957 {
1958 if (*cp == '\\')
1959 {
1960 c = *cp;
1961 *cp = 0;
1962 if (!doshQueryDirExist(path))
1963 {
1964 APIRET arc = DosCreateDir(path,
1965 0); // no EAs
1966 if (arc != NO_ERROR)
1967 {
1968 arc0 = arc;
1969 break;
1970 }
1971 else
1972 if (fHidden)
1973 {
1974 // hide the directory we just created
1975 doshSetPathAttr(path, FILE_HIDDEN);
1976 }
1977 }
1978 *cp = c;
1979 }
1980 cp++;
1981 }
1982 return (arc0);
1983}
1984
1985/*
1986 *@@ doshQueryCurrentDir:
1987 * writes the current directory into
1988 * the specified buffer, which should be
1989 * CCHMAXPATH in size.
1990 *
1991 * As opposed to DosQueryCurrentDir, this
1992 * includes the drive letter.
1993 *
1994 *@@added V0.9.4 (2000-07-22) [umoeller]
1995 */
1996
1997APIRET doshQueryCurrentDir(PSZ pszBuf)
1998{
1999 APIRET arc = NO_ERROR;
2000 ULONG ulCurDisk = 0;
2001 ULONG ulMap = 0;
2002 arc = DosQueryCurrentDisk(&ulCurDisk, &ulMap);
2003 if (arc == NO_ERROR)
2004 {
2005 ULONG cbBuf = CCHMAXPATH - 3;
2006 *pszBuf = G_acDriveLetters[ulCurDisk];
2007 *(pszBuf + 1) = ':';
2008 *(pszBuf + 2) = '\\';
2009 arc = DosQueryCurrentDir(0, pszBuf + 3, &cbBuf);
2010 }
2011
2012 return (arc);
2013}
2014
2015/*
2016 *@@ doshDeleteDir:
2017 * deletes a directory. As opposed to DosDeleteDir,
2018 * this removes empty subdirectories and/or files
2019 * as well.
2020 *
2021 * flFlags can be any combination of the following:
2022 *
2023 * -- DOSHDELDIR_RECURSE: recurse into subdirectories.
2024 *
2025 * -- DOSHDELDIR_DELETEFILES: delete all regular files
2026 * which are found on the way.
2027 *
2028 * THIS IS NOT IMPLEMENTED YET.
2029 *
2030 * If 0 is specified, this effectively behaves just as
2031 * DosDeleteDir.
2032 *
2033 * If you specify DOSHDELDIR_RECURSE only, only empty
2034 * subdirectories are deleted as well.
2035 *
2036 * If you specify DOSHDELDIR_RECURSE | DOSHDELDIR_DELETEFILES,
2037 * this removes an entire directory tree, including all
2038 * subdirectories and files.
2039 *
2040 *@@added V0.9.4 (2000-07-01) [umoeller]
2041 */
2042
2043APIRET doshDeleteDir(const char *pcszDir,
2044 ULONG flFlags,
2045 PULONG pulDirs, // out: directories found
2046 PULONG pulFiles) // out: files found
2047{
2048 APIRET arc = NO_ERROR,
2049 arcReturn = NO_ERROR;
2050
2051 HDIR hdirFindHandle = HDIR_CREATE;
2052 FILEFINDBUF3 ffb3 = {0}; // returned from FindFirst/Next
2053 ULONG ulResultBufLen = sizeof(FILEFINDBUF3);
2054 ULONG ulFindCount = 1; // look for 1 file at a time
2055
2056 CHAR szFileMask[2*CCHMAXPATH];
2057 sprintf(szFileMask, "%s\\*", pcszDir);
2058
2059 // find files
2060 arc = DosFindFirst(szFileMask,
2061 &hdirFindHandle, // directory search handle
2062 FILE_ARCHIVED | FILE_DIRECTORY | FILE_SYSTEM
2063 | FILE_HIDDEN | FILE_READONLY,
2064 // search attributes; include all, even dirs
2065 &ffb3, // result buffer
2066 ulResultBufLen, // result buffer length
2067 &ulFindCount, // number of entries to find
2068 FIL_STANDARD); // return level 1 file info
2069
2070 if (arc == NO_ERROR)
2071 {
2072 // keep finding the next file until there are no more files
2073 while (arc == NO_ERROR) // != ERROR_NO_MORE_FILES
2074 {
2075 if (ffb3.attrFile & FILE_DIRECTORY)
2076 {
2077 // we found a directory:
2078
2079 // ignore the pseudo-directories
2080 if ( (strcmp(ffb3.achName, ".") != 0)
2081 && (strcmp(ffb3.achName, "..") != 0)
2082 )
2083 {
2084 // real directory:
2085 if (flFlags & DOSHDELDIR_RECURSE)
2086 {
2087 // recurse!
2088 CHAR szSubDir[2*CCHMAXPATH];
2089 sprintf(szSubDir, "%s\\%s", pcszDir, ffb3.achName);
2090 arcReturn = doshDeleteDir(szSubDir,
2091 flFlags,
2092 pulDirs,
2093 pulFiles);
2094 // this removes ffb3.achName as well
2095 }
2096 else
2097 {
2098 // directory, but no recursion:
2099 // report "access denied"
2100 // (this is what OS/2 reports with DosDeleteDir as well)
2101 arcReturn = ERROR_ACCESS_DENIED; // 5
2102 (*pulDirs)++;
2103 }
2104 }
2105 }
2106 else
2107 {
2108 // it's a file:
2109 arcReturn = ERROR_ACCESS_DENIED;
2110 (*pulFiles)++;
2111 }
2112
2113 if (arc == NO_ERROR)
2114 {
2115 ulFindCount = 1; // reset find count
2116 arc = DosFindNext(hdirFindHandle, // directory handle
2117 &ffb3, // result buffer
2118 ulResultBufLen, // result buffer length
2119 &ulFindCount); // number of entries to find
2120 }
2121 } // endwhile
2122
2123 DosFindClose(hdirFindHandle); // close our find handle
2124 }
2125
2126 if (arcReturn == NO_ERROR)
2127 // success so far:
2128 // delete our directory now
2129 arc = DosDeleteDir((PSZ)pcszDir);
2130
2131 return (arcReturn);
2132}
2133
2134/*
2135 *@@category: Helpers\Control program helpers\Module handling
2136 * helpers for importing functions from a module (DLL).
2137 */
2138
2139/* ******************************************************************
2140 *
2141 * Module handling helpers
2142 *
2143 ********************************************************************/
2144
2145/*
2146 *@@ doshResolveImports:
2147 * this function loads the module called pszModuleName
2148 * and resolves imports dynamically using DosQueryProcAddress.
2149 *
2150 * To specify the functions to be imported, a RESOLVEFUNCTION
2151 * array is used. In each of the array items, specify the
2152 * name of the function and a pointer to a function pointer
2153 * where to store the resolved address.
2154 *
2155 *@@added V0.9.3 (2000-04-29) [umoeller]
2156 */
2157
2158APIRET doshResolveImports(PSZ pszModuleName, // in: DLL to load
2159 HMODULE *phmod, // out: module handle
2160 PRESOLVEFUNCTION paResolves, // in/out: function resolves
2161 ULONG cResolves) // in: array item count (not array size!)
2162{
2163 CHAR szName[CCHMAXPATH];
2164 APIRET arc = DosLoadModule(szName,
2165 sizeof(szName),
2166 pszModuleName,
2167 phmod);
2168 if (arc == NO_ERROR)
2169 {
2170 ULONG ul;
2171 for (ul = 0;
2172 ul < cResolves;
2173 ul++)
2174 {
2175 arc = DosQueryProcAddr(*phmod,
2176 0, // ordinal, ignored
2177 (PSZ)paResolves[ul].pcszFunctionName,
2178 paResolves[ul].ppFuncAddress);
2179
2180 /* _Pmpf(("Resolved %s to 0x%lX, rc: %d",
2181 paResolves[ul].pcszFunctionName,
2182 *paResolves[ul].ppFuncAddress,
2183 arc)); */
2184 if (arc != NO_ERROR)
2185 break;
2186 }
2187 }
2188
2189 return (arc);
2190}
2191
2192/*
2193 *@@category: Helpers\Control program helpers\Performance (CPU load) helpers
2194 * helpers around DosPerfSysCall.
2195 */
2196
2197/* ******************************************************************
2198 *
2199 * Performance Counters (CPU Load)
2200 *
2201 ********************************************************************/
2202
2203/*
2204 *@@ doshPerfOpen:
2205 * initializes the OS/2 DosPerfSysCall API for
2206 * the calling thread.
2207 *
2208 * Note: This API is not supported on all OS/2
2209 * versions. I believe it came up with some Warp 4
2210 * fixpak. The API is resolved dynamically by
2211 * this function (using DosQueryProcAddr). Only
2212 * if NO_ERROR is returned, you may call doshPerfGet
2213 * afterwards.
2214 *
2215 * This properly initializes the internal counters
2216 * which the OS/2 kernel uses for this API. Apparently,
2217 * with newer kernels (FP13/14), IBM has chosen to no
2218 * longer do this automatically, which is the reason
2219 * why many "pulse" utilities display garbage with these
2220 * fixpaks.
2221 *
2222 * After NO_ERROR is returned, DOSHPERFSYS.cProcessors
2223 * contains the no. of processors found on the system.
2224 * All pointers in DOSHPERFSYS then point to arrays
2225 * which have exactly cProcessors array items.
2226 *
2227 * Call doshPerfClose to clean up resources allocated
2228 * by this function.
2229 *
2230 * Example code:
2231 *
2232 + PDOSHPERFSYS pPerf = NULL;
2233 + APIRET arc = doshPerfOpen(&pPerf);
2234 + if (arc == NO_ERROR)
2235 + {
2236 + // this should really be in a timer
2237 + ULONG ulCPU;
2238 + arc = doshPerfGet(&pPerf);
2239 + // go thru all CPUs
2240 + for (ulCPU = 0; ulCPU < pPerf->cProcessors; ulCPU++)
2241 + {
2242 + LONG lLoadThis = pPerf->palLoads[ulCPU];
2243 + ...
2244 + }
2245 +
2246 + ...
2247 +
2248 + // clean up
2249 + doshPerfClose(&pPerf);
2250 + }
2251 +
2252 *
2253 *@@added V0.9.7 (2000-12-02) [umoeller]
2254 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
2255 */
2256
2257APIRET doshPerfOpen(PDOSHPERFSYS *ppPerfSys) // out: new DOSHPERFSYS structure
2258{
2259 APIRET arc = NO_ERROR;
2260
2261 // allocate DOSHPERFSYS structure
2262 *ppPerfSys = (PDOSHPERFSYS)malloc(sizeof(DOSHPERFSYS));
2263 if (!*ppPerfSys)
2264 arc = ERROR_NOT_ENOUGH_MEMORY;
2265 else
2266 {
2267 // initialize structure
2268 PDOSHPERFSYS pPerfSys = *ppPerfSys;
2269 memset(pPerfSys, 0, sizeof(*pPerfSys));
2270
2271 // resolve DosPerfSysCall API entry
2272 arc = DosLoadModule(NULL, 0, "DOSCALLS", &pPerfSys->hmod);
2273 if (arc == NO_ERROR)
2274 {
2275 arc = DosQueryProcAddr(pPerfSys->hmod,
2276 976,
2277 "DosPerfSysCall",
2278 (PFN*)(&pPerfSys->pDosPerfSysCall));
2279 if (arc == NO_ERROR)
2280 {
2281 // OK, we got the API: initialize!
2282 arc = pPerfSys->pDosPerfSysCall(CMD_KI_ENABLE, 0, 0, 0);
2283 if (arc == NO_ERROR)
2284 {
2285 pPerfSys->fInitialized = TRUE;
2286 // call CMD_KI_DISABLE later
2287
2288 arc = pPerfSys->pDosPerfSysCall(CMD_PERF_INFO,
2289 0,
2290 (ULONG)(&pPerfSys->cProcessors),
2291 0);
2292 if (arc == NO_ERROR)
2293 {
2294 ULONG ul = 0;
2295
2296 // allocate arrays
2297 pPerfSys->paCPUUtils = (PCPUUTIL)calloc(pPerfSys->cProcessors,
2298 sizeof(CPUUTIL));
2299 if (!pPerfSys->paCPUUtils)
2300 arc = ERROR_NOT_ENOUGH_MEMORY;
2301 else
2302 {
2303 pPerfSys->padBusyPrev = (double*)malloc(pPerfSys->cProcessors * sizeof(double));
2304 if (!pPerfSys->padBusyPrev)
2305 arc = ERROR_NOT_ENOUGH_MEMORY;
2306 else
2307 {
2308 pPerfSys->padTimePrev
2309 = (double*)malloc(pPerfSys->cProcessors * sizeof(double));
2310 if (!pPerfSys->padTimePrev)
2311 arc = ERROR_NOT_ENOUGH_MEMORY;
2312 else
2313 {
2314 pPerfSys->padIntrPrev
2315 = (double*)malloc(pPerfSys->cProcessors * sizeof(double));
2316 if (!pPerfSys->padIntrPrev)
2317 arc = ERROR_NOT_ENOUGH_MEMORY;
2318 else
2319 {
2320 pPerfSys->palLoads = (PLONG)malloc(pPerfSys->cProcessors * sizeof(LONG));
2321 if (!pPerfSys->palLoads)
2322 arc = ERROR_NOT_ENOUGH_MEMORY;
2323 else
2324 {
2325 pPerfSys->palIntrs = (PLONG)malloc(pPerfSys->cProcessors * sizeof(LONG));
2326 if (!pPerfSys->palIntrs)
2327 arc = ERROR_NOT_ENOUGH_MEMORY;
2328 else
2329 {
2330 for (ul = 0; ul < pPerfSys->cProcessors; ul++)
2331 {
2332 pPerfSys->padBusyPrev[ul] = 0.0;
2333 pPerfSys->padTimePrev[ul] = 0.0;
2334 pPerfSys->padIntrPrev[ul] = 0.0;
2335 pPerfSys->palLoads[ul] = 0;
2336 pPerfSys->palIntrs[ul] = 0;
2337 }
2338 }
2339 }
2340 }
2341 }
2342 }
2343 }
2344 }
2345 }
2346 } // end if (arc == NO_ERROR)
2347 } // end if (arc == NO_ERROR)
2348
2349 if (arc != NO_ERROR)
2350 {
2351 doshPerfClose(ppPerfSys);
2352 }
2353 } // end else if (!*ppPerfSys)
2354
2355 return (arc);
2356}
2357
2358/*
2359 *@@ doshPerfGet:
2360 * calculates a current snapshot of the system load,
2361 * compared with the load which was calculated on
2362 * the previous call.
2363 *
2364 * If you want to continually measure the system CPU
2365 * load, this is the function you will want to call
2366 * regularly -- e.g. with a timer once per second.
2367 *
2368 * Call this ONLY if doshPerfOpen returned NO_ERROR,
2369 * or you'll get crashes.
2370 *
2371 * If this call returns NO_ERROR, you get LONG load
2372 * values for each CPU in the system in the arrays
2373 * in DOSHPERFSYS (in per-mille, 0-1000).
2374 *
2375 * There are two arrays:
2376 *
2377 * -- DOSHPERFSYS.palLoads contains the "user" load
2378 * for each CPU.
2379 *
2380 * -- DOSHPERFSYS.palIntrs contains the "IRQ" load
2381 * for each CPU.
2382 *
2383 * Sum up the two values to get the total load for
2384 * each CPU.
2385 *
2386 * For example, if there are two CPUs, after this call,
2387 *
2388 * -- DOSHPERFSYS.palLoads[0] contains the "user" load
2389 * of the first CPU,
2390 *
2391 * -- DOSHPERFSYS.palLoads[0] contains the "user" load
2392 * of the second CPU.
2393 *
2394 * See doshPerfOpen for example code.
2395 *
2396 *@@added V0.9.7 (2000-12-02) [umoeller]
2397 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
2398 */
2399
2400APIRET doshPerfGet(PDOSHPERFSYS pPerfSys)
2401{
2402 APIRET arc = NO_ERROR;
2403 if (!pPerfSys->pDosPerfSysCall)
2404 arc = ERROR_INVALID_PARAMETER;
2405 else
2406 {
2407 arc = pPerfSys->pDosPerfSysCall(CMD_KI_RDCNT,
2408 (ULONG)pPerfSys->paCPUUtils,
2409 0, 0);
2410 if (arc == NO_ERROR)
2411 {
2412 // go thru all processors
2413 ULONG ul = 0;
2414 for (; ul < pPerfSys->cProcessors; ul++)
2415 {
2416 PCPUUTIL pCPUUtilThis = &pPerfSys->paCPUUtils[ul];
2417
2418 double dTime = LL2F(pCPUUtilThis->ulTimeHigh,
2419 pCPUUtilThis->ulTimeLow);
2420 double dBusy = LL2F(pCPUUtilThis->ulBusyHigh,
2421 pCPUUtilThis->ulBusyLow);
2422 double dIntr = LL2F(pCPUUtilThis->ulIntrHigh,
2423 pCPUUtilThis->ulIntrLow);
2424
2425 double *pdBusyPrevThis = &pPerfSys->padBusyPrev[ul];
2426 double *pdTimePrevThis = &pPerfSys->padTimePrev[ul];
2427 double *pdIntrPrevThis = &pPerfSys->padIntrPrev[ul];
2428
2429 // avoid division by zero
2430 double dTimeDelta = (dTime - *pdTimePrevThis);
2431 if (dTimeDelta)
2432 {
2433 pPerfSys->palLoads[ul]
2434 = (LONG)( (double)( (dBusy - *pdBusyPrevThis)
2435 / dTimeDelta
2436 * 1000.0
2437 )
2438 );
2439 pPerfSys->palIntrs[ul]
2440 = (LONG)( (double)( (dIntr - *pdIntrPrevThis)
2441 / dTimeDelta
2442 * 1000.0
2443 )
2444 );
2445 }
2446 else
2447 {
2448 // no clear readings are available
2449 pPerfSys->palLoads[ul] = 0;
2450 pPerfSys->palIntrs[ul] = 0;
2451 }
2452
2453 *pdTimePrevThis = dTime;
2454 *pdBusyPrevThis = dBusy;
2455 *pdIntrPrevThis = dIntr;
2456 }
2457 }
2458 }
2459
2460 return (arc);
2461}
2462
2463/*
2464 *@@ doshPerfClose:
2465 * frees all resources allocated by doshPerfOpen.
2466 *
2467 *@@added V0.9.7 (2000-12-02) [umoeller]
2468 *@@changed V0.9.9 (2001-02-06) [umoeller]: removed disable; this broke the WarpCenter
2469 *@@changed V0.9.9 (2001-03-14) [umoeller]: fixed memory leak
2470 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
2471 */
2472
2473APIRET doshPerfClose(PDOSHPERFSYS *ppPerfSys)
2474{
2475 APIRET arc = NO_ERROR;
2476 PDOSHPERFSYS pPerfSys = *ppPerfSys;
2477 if (!pPerfSys)
2478 arc = ERROR_INVALID_PARAMETER;
2479 else
2480 {
2481 // do not call this, this messes up the WarpCenter V0.9.9 (2001-02-06) [umoeller]
2482 // if (pPerfSys->fInitialized) pPerfSys->pDosPerfSysCall(CMD_KI_DISABLE, 0, 0, 0);
2483
2484 if (pPerfSys->paCPUUtils)
2485 free(pPerfSys->paCPUUtils);
2486 if (pPerfSys->padBusyPrev)
2487 free(pPerfSys->padBusyPrev);
2488 if (pPerfSys->padTimePrev)
2489 free(pPerfSys->padTimePrev);
2490 if (pPerfSys->padIntrPrev)
2491 free(pPerfSys->padIntrPrev);
2492 if (pPerfSys->palLoads) // was missing V0.9.9 (2001-03-14) [umoeller]
2493 free(pPerfSys->palLoads);
2494 if (pPerfSys->palIntrs)
2495 free(pPerfSys->palIntrs);
2496
2497 if (pPerfSys->hmod)
2498 DosFreeModule(pPerfSys->hmod);
2499 free(pPerfSys);
2500 *ppPerfSys = NULL;
2501 }
2502
2503 return (arc);
2504}
2505
2506/*
2507 *@@category: Helpers\Control program helpers\Process management
2508 * helpers for starting subprocesses.
2509 */
2510
2511/* ******************************************************************
2512 *
2513 * Process helpers
2514 *
2515 ********************************************************************/
2516
2517static PVOID // G_pvGlobalInfoSeg = NULL,
2518 G_pvLocalInfoSeg = NULL;
2519
2520USHORT _Far16 _Pascal Dos16GetInfoSeg(PSEL pselGlobal,
2521 PSEL pselLocal);
2522
2523/*
2524 * GetInfoSegs:
2525 *
2526 */
2527
2528VOID GetInfoSegs(VOID)
2529{
2530 SEL GlobalInfoSegSelector,
2531 LocalInfoSegSelector;
2532
2533 // get selectors (old 16-bit API; this gets called only once)
2534 Dos16GetInfoSeg(&GlobalInfoSegSelector,
2535 &LocalInfoSegSelector);
2536 // thunk
2537 /* G_pvGlobalInfoSeg = (PVOID)( (GlobalInfoSegSelector << 0x000D)
2538 & 0x01fff0000); */
2539 G_pvLocalInfoSeg = (PVOID)( (LocalInfoSegSelector << 0x000D)
2540 & 0x01fff0000);
2541}
2542
2543/*
2544 *@@ doshMyPID:
2545 * returns the PID of the current process.
2546 *
2547 * This uses an interesting hack which is way
2548 * faster than DosGetInfoBlocks.
2549 *
2550 *@@added V0.9.9 (2001-04-04) [umoeller]
2551 */
2552
2553ULONG doshMyPID(VOID)
2554{
2555 if (!G_pvLocalInfoSeg)
2556 // first call:
2557 GetInfoSegs();
2558
2559 // PID is at offset 0 in the local info seg
2560 return (*(PUSHORT)G_pvLocalInfoSeg);
2561}
2562
2563/*
2564 *@@ doshMyTID:
2565 * returns the TID of the current thread.
2566 *
2567 * This uses an interesting hack which is way
2568 * faster than DosGetInfoBlocks.
2569 *
2570 *@@added V0.9.9 (2001-04-04) [umoeller]
2571 */
2572
2573ULONG doshMyTID(VOID)
2574{
2575 if (!G_pvLocalInfoSeg)
2576 // first call:
2577 GetInfoSegs();
2578
2579 // TID is at offset 6 in the local info seg
2580 return (*(PUSHORT)((PBYTE)G_pvLocalInfoSeg + 6));
2581}
2582
2583/*
2584 *@@ doshExecVIO:
2585 * executes cmd.exe with the /c parameter
2586 * and pcszExecWithArgs. This is equivalent
2587 * to the C library system() function,
2588 * except that an OS/2 error code is returned
2589 * and that this works with the VAC subsystem
2590 * library.
2591 *
2592 * This uses DosExecPgm and handles the sick
2593 * argument passing.
2594 *
2595 * If NO_ERROR is returned, *plExitCode receives
2596 * the exit code of the process. If the process
2597 * was terminated abnormally, *plExitCode receives:
2598 *
2599 * -- -1: hard error halt
2600 * -- -2: 16-bit trap
2601 * -- -3: DosKillProcess
2602 * -- -4: 32-bit exception
2603 *
2604 *@@added V0.9.4 (2000-07-27) [umoeller]
2605 */
2606
2607APIRET doshExecVIO(const char *pcszExecWithArgs,
2608 PLONG plExitCode) // out: exit code (ptr can be NULL)
2609{
2610 APIRET arc = NO_ERROR;
2611
2612 if (strlen(pcszExecWithArgs) > 255)
2613 arc = ERROR_INSUFFICIENT_BUFFER;
2614 else
2615 {
2616 CHAR szObj[CCHMAXPATH];
2617 RESULTCODES res = {0};
2618 CHAR szBuffer[300];
2619
2620 // DosExecPgm expects two args in szBuffer:
2621 // -- cmd.exe\0
2622 // -- then the actual argument, terminated by two 0 bytes.
2623 memset(szBuffer, 0, sizeof(szBuffer));
2624 strcpy(szBuffer, "cmd.exe\0");
2625 sprintf(szBuffer + 8, "/c %s",
2626 pcszExecWithArgs);
2627
2628 arc = DosExecPgm(szObj, sizeof(szObj),
2629 EXEC_SYNC,
2630 szBuffer,
2631 0,
2632 &res,
2633 "cmd.exe");
2634 if ((arc == NO_ERROR) && (plExitCode))
2635 {
2636 if (res.codeTerminate == 0)
2637 // normal exit:
2638 *plExitCode = res.codeResult;
2639 else
2640 *plExitCode = -((LONG)res.codeTerminate);
2641 }
2642 }
2643
2644 return (arc);
2645}
2646
2647/*
2648 *@@ doshQuickStartSession:
2649 * this is a shortcut to DosStartSession w/out having to
2650 * deal with all the messy parameters.
2651 *
2652 * This one starts pszPath as a child session and passes
2653 * pszParams to it.
2654 *
2655 * In usPgmCtl, OR any or none of the following (V0.9.0):
2656 * -- SSF_CONTROL_NOAUTOCLOSE (0x0008): do not close automatically.
2657 * This bit is used only for VIO Windowable apps and ignored
2658 * for all other applications.
2659 * -- SSF_CONTROL_MINIMIZE (0x0004)
2660 * -- SSF_CONTROL_MAXIMIZE (0x0002)
2661 * -- SSF_CONTROL_INVISIBLE (0x0001)
2662 * -- SSF_CONTROL_VISIBLE (0x0000)
2663 *
2664 * Specifying 0 will therefore auto-close the session and make it
2665 * visible.
2666 *
2667 * If (fWait), this function will create a termination queue
2668 * and not return until the child session has ended. Be warned,
2669 * this blocks the calling thread.
2670 *
2671 * Otherwise the function will return immediately.
2672 *
2673 * The session and process IDs of the child session will be
2674 * written to *pulSID and *ppid. Of course, in "wait" mode,
2675 * these are no longer valid after this function returns.
2676 *
2677 * Returns the error code of DosStartSession.
2678 *
2679 * Note: According to CPREF, calling DosStartSession calls
2680 * DosExecPgm in turn for all full-screen, VIO, and PM sessions.
2681 *
2682 *@@changed V0.9.0 [umoeller]: prototype changed to include usPgmCtl
2683 *@@changed V0.9.1 (99-12-30) [umoeller]: queue was sometimes not closed. Fixed.
2684 *@@changed V0.9.3 (2000-05-03) [umoeller]: added fForeground
2685 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed potential queue leak
2686 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed memory leak in wait mode; added pusReturn to prototype
2687 */
2688
2689APIRET doshQuickStartSession(const char *pcszPath, // in: program to start
2690 const char *pcszParams, // in: parameters for program
2691 BOOL fForeground, // in: if TRUE, session will be in foreground
2692 USHORT usPgmCtl, // in: STARTDATA.PgmControl
2693 BOOL fWait, // in: wait for termination?
2694 PULONG pulSID, // out: session ID (req.)
2695 PPID ppid, // out: process ID (req.)
2696 PUSHORT pusReturn) // out: in wait mode, session's return code (ptr can be NULL)
2697{
2698 APIRET arc = NO_ERROR;
2699 // queue stuff
2700 const char *pcszQueueName = "\\queues\\xwphlpsw.que";
2701 HQUEUE hq = 0;
2702 PID qpid = 0;
2703
2704 if (fWait)
2705 {
2706 if (!(arc = DosCreateQueue(&hq,
2707 QUE_FIFO | QUE_CONVERT_ADDRESS,
2708 (PSZ)pcszQueueName)))
2709 arc = DosOpenQueue(&qpid, &hq, (PSZ)pcszQueueName);
2710 }
2711
2712 if (!arc) // V0.9.14 (2001-08-03) [umoeller]
2713 {
2714 STARTDATA SData;
2715 CHAR szObjBuf[CCHMAXPATH];
2716
2717 SData.Length = sizeof(STARTDATA);
2718 SData.Related = SSF_RELATED_CHILD; //INDEPENDENT;
2719 SData.FgBg = (fForeground) ? SSF_FGBG_FORE : SSF_FGBG_BACK;
2720 // V0.9.3 (2000-05-03) [umoeller]
2721 SData.TraceOpt = SSF_TRACEOPT_NONE;
2722
2723 SData.PgmTitle = (PSZ)pcszPath; // title for window
2724 SData.PgmName = (PSZ)pcszPath;
2725 SData.PgmInputs = (PSZ)pcszParams;
2726
2727 SData.TermQ = (fWait) ? (PSZ)pcszQueueName : NULL;
2728 SData.Environment = 0;
2729 SData.InheritOpt = SSF_INHERTOPT_PARENT;
2730 SData.SessionType = SSF_TYPE_DEFAULT;
2731 SData.IconFile = 0;
2732 SData.PgmHandle = 0;
2733
2734 SData.PgmControl = usPgmCtl;
2735
2736 SData.InitXPos = 30;
2737 SData.InitYPos = 40;
2738 SData.InitXSize = 200;
2739 SData.InitYSize = 140;
2740 SData.Reserved = 0;
2741 SData.ObjectBuffer = szObjBuf;
2742 SData.ObjectBuffLen = (ULONG)sizeof(szObjBuf);
2743
2744 if ( (!(arc = DosStartSession(&SData, pulSID, ppid)))
2745 && (fWait)
2746 )
2747 {
2748 // block on the termination queue, which is written
2749 // to when the subprocess ends
2750 REQUESTDATA rqdata;
2751 ULONG cbData = 0;
2752 PULONG pulData = NULL;
2753 BYTE elpri;
2754
2755 rqdata.pid = qpid;
2756 if (!(arc = DosReadQueue(hq, // in: queue handle
2757 &rqdata, // out: pid and ulData
2758 &cbData, // out: size of data returned
2759 (PVOID*)&pulData, // out: data returned
2760 0, // in: remove first element in queue
2761 0, // in: wait for queue data (block thread)
2762 &elpri, // out: element's priority
2763 0))) // in: event semaphore to be posted
2764 {
2765 if (!rqdata.ulData)
2766 {
2767 // child session ended:
2768 // V0.9.14 (2001-08-03) [umoeller]
2769
2770 // *pulSID = (*pulData) & 0xffff;
2771 if (pusReturn)
2772 *pusReturn = ((*pulData) >> 16) & 0xffff;
2773
2774 }
2775 // else: continue looping
2776
2777 if (pulData)
2778 DosFreeMem(pulData);
2779 }
2780 }
2781 }
2782
2783 if (hq)
2784 DosCloseQueue(hq);
2785
2786 return (arc);
2787}
2788
2789
Note: See TracBrowser for help on using the repository browser.