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

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