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

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

New folder sorting. Updated folder refresh. Misc other changes.

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