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

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

misc changes

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