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

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

Misc changes

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