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

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

Sources as for V0.9.11.

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