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

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

Coupla bugfixes.

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