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

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

Tons of changes from the last weeks.

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