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

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