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

Last change on this file since 14 was 14, checked in by umoeller, 25 years ago

Major updates; timers, LVM, miscellaneous.

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