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

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

program plus other fixes

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