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

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

Misc updates.

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