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

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

Misc updates.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 97.0 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_DOSSEMAPHORES
54#define INCL_DOSMISC
55#define INCL_DOSDEVICES
56#define INCL_DOSDEVIOCTL
57#define INCL_DOSERRORS
58
59#define INCL_KBD
60#include <os2.h>
61
62#include <stdlib.h>
63#include <string.h>
64#include <stdio.h>
65#include <stdarg.h>
66
67#include "setup.h" // code generation and debugging options
68
69#include "helpers\dosh.h"
70#include "helpers\standards.h"
71
72#pragma hdrstop
73
74// static const CHAR G_acDriveLetters[28] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
75
76/*
77 *@@category: Helpers\Control program helpers\Miscellaneous
78 * Miscellaneous helpers in dosh.c that didn't fit into any other
79 * category.
80 */
81
82/* ******************************************************************
83 *
84 * Miscellaneous
85 *
86 ********************************************************************/
87
88/*
89 *@@ doshGetChar:
90 * reads a single character from the keyboard.
91 * Useful for VIO sessions, since there's great
92 * confusion between the various C dialects about
93 * how to use getc(), and getc() doesn't work
94 * with the VAC subsystem library.
95 *
96 *@@added V0.9.4 (2000-07-27) [umoeller]
97 */
98
99CHAR doshGetChar(VOID)
100{
101 // CHAR c;
102 // ULONG ulRead = 0;
103
104 KBDKEYINFO kki;
105 KbdCharIn(&kki,
106 0, // wait
107 0);
108
109 return (kki.chChar);
110}
111
112/*
113 *@@ doshQueryShiftState:
114 * returns TRUE if any of the SHIFT keys are
115 * currently pressed. Useful for checks during
116 * WM_COMMAND messages from menus.
117 *
118 *@@changed V0.9.5 (2000-09-27) [umoeller]: added error checking
119 */
120
121BOOL doshQueryShiftState(VOID)
122{
123 BOOL brc = FALSE;
124 APIRET arc = NO_ERROR;
125 HFILE hfKbd;
126 ULONG ulAction;
127
128 arc = DosOpen("KBD$", &hfKbd, &ulAction, 0,
129 FILE_NORMAL, FILE_OPEN,
130 OPEN_ACCESS_READONLY | OPEN_SHARE_DENYWRITE,
131 (PEAOP2)NULL);
132 if (arc == NO_ERROR)
133 {
134 SHIFTSTATE ShiftState;
135 ULONG cbDataLen = sizeof(ShiftState);
136
137 arc = DosDevIOCtl(hfKbd, IOCTL_KEYBOARD, KBD_GETSHIFTSTATE,
138 NULL, 0, NULL, // no parameters
139 &ShiftState, cbDataLen, &cbDataLen);
140 if (arc == NO_ERROR)
141 brc = ((ShiftState.fsState & 3) != 0);
142
143 DosClose(hfKbd);
144 }
145
146 return brc;
147}
148
149/*
150 *@@ doshIsWarp4:
151 * checks the OS/2 system version number.
152 *
153 * Returns:
154 *
155 * -- 0 (FALSE): OS/2 2.x or Warp 3 is running.
156 *
157 * -- 1: Warp 4.0 is running.
158 *
159 * -- 2: Warp 4.5 is running (WSeB or Warp 4 FP 13+ or eCS
160 * or ACP/MCP), or even something newer.
161 *
162 *@@changed V0.9.2 (2000-03-05) [umoeller]: reported TRUE on Warp 3 also; fixed
163 *@@changed V0.9.6 (2000-10-16) [umoeller]: patched for speed
164 *@@changed V0.9.9 (2001-04-04) [umoeller]: now returning 2 for Warp 4.5 and above
165 */
166
167ULONG doshIsWarp4(VOID)
168{
169 static BOOL s_fQueried = FALSE;
170 static ULONG s_ulrc = 0;
171
172 if (!s_fQueried)
173 {
174 // first call:
175 ULONG aulBuf[3];
176
177 DosQuerySysInfo(QSV_VERSION_MAJOR, // 11
178 QSV_VERSION_MINOR, // 12
179 &aulBuf, sizeof(aulBuf));
180 // Warp 3 is reported as 20.30
181 // Warp 4 is reported as 20.40
182 // Aurora is reported as 20.45
183
184 if ( (aulBuf[0] > 20) // major > 20; not the case with Warp 3, 4, 5
185 || ( (aulBuf[0] == 20) // major == 20 and minor >= 45
186 && (aulBuf[1] >= 45)
187 )
188 )
189 // Warp 4.5 or newer:
190 s_ulrc = 2;
191 else if ( (aulBuf[0] == 20) // major == 20 and minor == 40
192 && (aulBuf[1] == 40)
193 )
194 // Warp 4:
195 s_ulrc = 1;
196
197 s_fQueried = TRUE;
198 }
199
200 return (s_ulrc);
201}
202
203/*
204 *@@ doshQuerySysErrorMsg:
205 * this retrieves the error message for a system error
206 * (APIRET) from the system error message file (OSO001.MSG).
207 * This file better be on the DPATH (it normally is).
208 *
209 * This returns the string in the "SYSxxx: blahblah" style,
210 * which is normally displayed on the command line when
211 * errors occur.
212 *
213 * The error message is returned in a newly allocated
214 * buffer, which should be free()'d afterwards.
215 *
216 * Returns NULL upon errors.
217 */
218
219PSZ doshQuerySysErrorMsg(APIRET arc) // in: DOS error code
220{
221 PSZ pszReturn = 0;
222 CHAR szDosError[1000];
223 ULONG cbDosError = 0;
224 DosGetMessage(NULL, 0, // no string replacements
225 szDosError, sizeof(szDosError),
226 arc,
227 "OSO001.MSG", // default OS/2 message file
228 &cbDosError);
229 if (cbDosError > 2)
230 {
231 szDosError[cbDosError - 2] = 0;
232 pszReturn = strdup(szDosError);
233 }
234 return (pszReturn);
235}
236
237/*
238 *@@ doshQuerySysUptime:
239 * returns the system uptime in milliseconds.
240 * This can be used for time comparisons.
241 *
242 *@@added V0.9.12 (2001-05-18) [umoeller]
243 */
244
245ULONG doshQuerySysUptime(VOID)
246{
247 ULONG ulms;
248 DosQuerySysInfo(QSV_MS_COUNT,
249 QSV_MS_COUNT,
250 &ulms,
251 sizeof(ulms));
252 return (ulms);
253}
254
255/*
256 *@@ doshDevIOCtl:
257 *
258 * Works with those IOCtls where the buffer
259 * size parameters are always the same anyway,
260 * which applies to all IOCtls I have seen
261 * so far.
262 *
263 *@@added V0.9.13 (2001-06-14) [umoeller]
264 */
265
266APIRET doshDevIOCtl(HFILE hf,
267 ULONG ulCategory,
268 ULONG ulFunction,
269 PVOID pvParams,
270 ULONG cbParams,
271 PVOID pvData,
272 ULONG cbData)
273{
274 return (DosDevIOCtl(hf,
275 ulCategory,
276 ulFunction,
277 pvParams, cbParams, &cbParams,
278 pvData, cbData, &cbData));
279}
280
281/*
282 *@@category: Helpers\Control program helpers\Shared memory management
283 * helpers for allocating and requesting shared memory.
284 */
285
286/* ******************************************************************
287 *
288 * Memory helpers
289 *
290 ********************************************************************/
291
292/*
293 *@@ Allocate:
294 * wrapper around malloc() which automatically
295 * sets ERROR_NOT_ENOUGH_MEMORY.
296 *
297 *@@added V0.9.16 (2001-10-19) [umoeller]
298 */
299
300PVOID doshMalloc(ULONG cb,
301 APIRET *parc)
302{
303 PVOID pv;
304 *parc = NO_ERROR;
305 if (!(pv = malloc(cb)))
306 *parc = ERROR_NOT_ENOUGH_MEMORY;
307
308 return (pv);
309}
310
311/*
312 *@@ doshAllocSharedMem:
313 * wrapper for DosAllocSharedMem which has
314 * a malloc()-like syntax. Just due to my
315 * lazyness.
316 *
317 * Note that ulSize is always rounded up to the
318 * next 4KB value, so don't use this hundreds of times.
319 *
320 * Returns NULL upon errors. Possible errors include
321 * that a memory block calle pcszName has already been
322 * allocated.
323 *
324 * Use DosFreeMem(pvrc) to free the memory. The memory
325 * will only be freed if no other process has requested
326 * access.
327 *
328 *@@added V0.9.3 (2000-04-18) [umoeller]
329 */
330
331PVOID doshAllocSharedMem(ULONG ulSize, // in: requested mem block size (rounded up to 4KB)
332 const char* pcszName) // in: name of block ("\\SHAREMEM\\xxx") or NULL
333{
334 PVOID pvrc = NULL;
335 APIRET arc = DosAllocSharedMem((PVOID*)&pvrc,
336 (PSZ)pcszName,
337 ulSize,
338 PAG_COMMIT | PAG_READ | PAG_WRITE);
339 if (arc == NO_ERROR)
340 return (pvrc);
341
342 return (NULL);
343}
344
345/*
346 *@@ doshRequestSharedMem:
347 * requests access to a block of named shared memory
348 * allocated by doshAllocSharedMem.
349 *
350 * Returns NULL upon errors.
351 *
352 * Use DosFreeMem(pvrc) to free the memory. The memory
353 * will only be freed if no other process has requested
354 * access.
355 *
356 *@@added V0.9.3 (2000-04-19) [umoeller]
357 */
358
359PVOID doshRequestSharedMem(const char *pcszName)
360{
361 PVOID pvrc = NULL;
362 APIRET arc = DosGetNamedSharedMem((PVOID*)pvrc,
363 (PSZ)pcszName,
364 PAG_READ | PAG_WRITE);
365 if (arc == NO_ERROR)
366 return (pvrc);
367
368 return (NULL);
369}
370
371/*
372 *@@category: Helpers\Control program helpers\Drive management
373 * functions for managing drives... enumerating, testing,
374 * querying etc.
375 */
376
377/* ******************************************************************
378 *
379 * Drive helpers
380 *
381 ********************************************************************/
382
383/*
384 *@@ doshIsFixedDisk:
385 * checks whether a disk is fixed or removeable.
386 * ulLogicalDrive must be 1 for drive A:, 2 for B:, ...
387 * The result is stored in *pfFixed.
388 * Returns DOS error code.
389 *
390 * From my testing, this function does _not_ provoke
391 * "drive not ready" popups, even if the disk is not
392 * ready.
393 *
394 * Warning: This uses DosDevIOCtl, which has proved
395 * to cause problems with some device drivers for
396 * removeable disks.
397 *
398 *@@changed V0.9.14 (2001-08-03) [umoeller]: added extra fix for A: and B:
399 */
400
401APIRET doshIsFixedDisk(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
402 PBOOL pfFixed) // out: TRUE for fixed disks
403{
404 APIRET arc = ERROR_INVALID_DRIVE;
405
406 if ( (ulLogicalDrive == 1)
407 || (ulLogicalDrive == 2)
408 )
409 {
410 // drive A: and B: can never be fixed V0.9.14 (2001-08-03) [umoeller]
411 *pfFixed = FALSE;
412 return NO_ERROR;
413 }
414
415 if (ulLogicalDrive)
416 {
417 // parameter packet
418 #pragma pack(1)
419 struct {
420 UCHAR command,
421 drive;
422 } parms;
423 #pragma pack()
424
425 // data packet
426 UCHAR ucNonRemoveable;
427
428 parms.drive = (UCHAR)(ulLogicalDrive-1);
429 arc = doshDevIOCtl((HFILE)-1,
430 IOCTL_DISK, // 0x08
431 DSK_BLOCKREMOVABLE, // 0x20
432 &parms, sizeof(parms),
433 &ucNonRemoveable, sizeof(ucNonRemoveable));
434
435 if (arc == NO_ERROR)
436 *pfFixed = (BOOL)ucNonRemoveable;
437 }
438
439 return (arc);
440}
441
442/*
443 *@@ doshQueryDiskParams:
444 * this retrieves more information about a given drive,
445 * which is stored in the specified BIOSPARAMETERBLOCK
446 * structure.
447 *
448 * BIOSPARAMETERBLOCK is defined in the Toolkit headers,
449 * and from my testing, it's the same with the Toolkits
450 * 3 and 4.5.
451 *
452 * If NO_ERROR is returned, the bDeviceType field can
453 * be one of the following (according to CPREF):
454 *
455 * -- 0: 48 TPI low-density diskette drive
456 * -- 1: 96 TPI high-density diskette drive
457 * -- 2: 3.5-inch 720KB diskette drive
458 * -- 3: 8-Inch single-density diskette drive
459 * -- 4: 8-Inch double-density diskette drive
460 * -- 5: Fixed disk
461 * -- 6: Tape drive
462 * -- 7: Other (includes 1.44MB 3.5-inch diskette drive)
463 * -- 8: R/W optical disk
464 * -- 9: 3.5-inch 4.0MB diskette drive (2.88MB formatted)
465 *
466 * From my testing, this function does _not_ provoke
467 * "drive not ready" popups, even if the disk is not
468 * ready.
469 *
470 * Warning: This uses DosDevIOCtl, which has proved
471 * to cause problems with some device drivers for
472 * removeable disks.
473 *
474 * This returns the DOS error code of DosDevIOCtl.
475 *
476 *@@added V0.9.0 [umoeller]
477 *@@changed V0.9.13 (2001-06-14) [umoeller]: changed prototype to use BIOSPARAMETERBLOCK directly
478 *@@changed V0.9.13 (2001-06-14) [umoeller]: now querying standard media, no redetermine
479 */
480
481APIRET doshQueryDiskParams(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
482 PBIOSPARAMETERBLOCK pdp) // out: drive parameters
483{
484 APIRET arc = ERROR_INVALID_DRIVE;
485
486 if (ulLogicalDrive)
487 {
488 #pragma pack(1)
489 // parameter packet
490 struct {
491 UCHAR ucCommand,
492 ucDrive;
493 } parms;
494 #pragma pack()
495
496 parms.ucCommand = 0; // 0 = return standard media,
497 // 1 = read currently inserted media
498 // (1 doesn't work any more, returns arc 87
499 // V0.9.13 (2001-06-14) [umoeller])
500 parms.ucDrive=(UCHAR)(ulLogicalDrive-1);
501
502 // zero the structure V0.9.13 (2001-06-14) [umoeller]
503 memset(pdp, 0, sizeof(BIOSPARAMETERBLOCK));
504
505 arc = doshDevIOCtl((HFILE)-1,
506 IOCTL_DISK, // 0x08
507 DSK_GETDEVICEPARAMS, // 0x63
508 &parms, sizeof(parms),
509 pdp, sizeof(BIOSPARAMETERBLOCK));
510
511 /* if (!arc)
512 {
513 _Pmpf((" bDeviceType: %d", pdp->bDeviceType));
514 _Pmpf((" bytes per sector: %d", pdp->usBytesPerSector));
515 _Pmpf((" sectors per track: %d", pdp->usSectorsPerTrack));
516 } */
517 }
518
519 return (arc);
520}
521
522/*
523 *@@ doshIsCDROM:
524 * tests the specified BIOSPARAMETERBLOCK
525 * for whether it represents a CD-ROM drive.
526 *
527 * The BIOSPARAMETERBLOCK must be filled
528 * first using doshQueryDiskParams.
529 *
530 *@@added V0.9.13 (2001-06-14) [umoeller]
531 */
532
533BOOL doshIsCDROM(PBIOSPARAMETERBLOCK pdp)
534{
535 return ( (pdp)
536 && (pdp->bDeviceType == 7) // "other"
537 && (pdp->usBytesPerSector == 2048)
538 && (pdp->usSectorsPerTrack == (USHORT)-1)
539 );
540}
541
542/*
543 *@@ doshHasAudioCD:
544 * sets *pfAudio to whether ulLogicalDrive
545 * currently has an audio CD inserted.
546 *
547 * Better call this only if you're sure that
548 * ulLogicalDrive is a CD-ROM drive. Use
549 * doshIsCDROM to check.
550 *
551 *@@added V0.9.14 (2001-08-01) [umoeller]
552 */
553
554APIRET doshHasAudioCD(ULONG ulLogicalDrive,
555 HFILE hfDrive, // in: DASD open
556 BOOL fMixedModeCD,
557 PBOOL pfAudio)
558{
559 APIRET arc = NO_ERROR;
560
561 ULONG ulAudioTracks = 0,
562 ulDataTracks = 0;
563
564 CHAR cds1[4] = { 'C', 'D', '0', '1' };
565 CHAR cds2[4];
566
567 *pfAudio = FALSE;
568
569 // check for proper driver signature
570 if (!(arc = doshDevIOCtl(hfDrive,
571 IOCTL_CDROMDISK,
572 CDROMDISK_GETDRIVER,
573 &cds1, sizeof(cds1),
574 &cds2, sizeof(cds2))))
575 {
576 if (memcmp(&cds1, &cds2, 4))
577 // this is not a CD-ROM then:
578 arc = NO_ERROR;
579 else
580 {
581 struct {
582 UCHAR ucFirstTrack,
583 ucLastTrack;
584 ULONG ulLeadOut;
585 } cdat;
586
587 // get track count
588 if (!(arc = doshDevIOCtl(hfDrive,
589 IOCTL_CDROMAUDIO,
590 CDROMAUDIO_GETAUDIODISK,
591 &cds1, sizeof(cds1),
592 &cdat, sizeof(cdat))))
593 {
594 // still no error: build the audio TOC
595 ULONG i;
596 for (i = cdat.ucFirstTrack;
597 i <= cdat.ucLastTrack;
598 i++)
599 {
600 BYTE cdtp[5] =
601 { 'C', 'D', '0', '1', (UCHAR)i };
602
603 struct {
604 ULONG ulTrackAddress;
605 BYTE bFlags;
606 } trackdata;
607
608 if (!(arc = doshDevIOCtl(hfDrive,
609 IOCTL_CDROMAUDIO,
610 CDROMAUDIO_GETAUDIOTRACK,
611 &cdtp, sizeof(cdtp),
612 &trackdata, sizeof(trackdata))))
613 {
614 if (trackdata.bFlags & 64)
615 ulDataTracks++;
616 else
617 {
618 ulAudioTracks++;
619
620 if (!fMixedModeCD)
621 {
622 // caller doesn't want mixed mode:
623 // stop here
624 ulDataTracks = 0;
625 break;
626 }
627 }
628 }
629 }
630
631 // _Pmpf((" got %d audio, %d data tracks",
632 // ulAudioTracks, ulDataTracks));
633
634 if (!ulDataTracks)
635 *pfAudio = TRUE;
636 }
637 else
638 {
639 // not audio disk:
640 // go on then
641 // _Pmpf((" CDROMAUDIO_GETAUDIODISK returned %d", arc));
642 arc = NO_ERROR;
643 }
644 }
645 }
646 else
647 {
648 // not CD-ROM: go on then
649 // _Pmpf((" CDROMDISK_GETDRIVER returned %d", arc));
650 arc = NO_ERROR;
651 }
652
653 return (arc);
654}
655
656/*
657 *@@ doshEnumDrives:
658 * this function enumerates all valid drive letters on
659 * the system by composing a string of drive letters
660 * in the buffer pointed to by pszBuffer, which should
661 * be 27 characters in size to hold information for
662 * all drives. The buffer will be null-terminated.
663 *
664 * If (pcszFileSystem != NULL), only drives matching
665 * the specified file system type (e.g. "HPFS") will
666 * be enumerated. If (pcszFileSystem == NULL), all
667 * drives will be enumerated.
668 *
669 * If (fSkipRemovables == TRUE), removeable drives will
670 * be skipped. This applies to floppy, CD-ROM, and
671 * virtual floppy drives. This will start the search
672 * at drive letter C: so that drives A: and B: will
673 * never be checked (to avoid the hardware bumps).
674 *
675 * Otherwise, the search starts at drive A:. Still,
676 * removeable drives will only be added if valid media
677 * is inserted.
678 *
679 *@@changed V0.9.4 (2000-07-03) [umoeller]: this stopped at the first invalid drive letter; fixed
680 *@@changed V0.9.4 (2000-07-03) [umoeller]: added fSkipRemoveables
681 */
682
683VOID doshEnumDrives(PSZ pszBuffer, // out: drive letters
684 const char *pcszFileSystem, // in: FS's to match or NULL
685 BOOL fSkipRemoveables) // in: if TRUE, only non-removeable disks will be returned
686{
687 CHAR szName[5] = "";
688 ULONG ulLogicalDrive = 1, // start with drive A:
689 ulFound = 0; // found drives count
690 APIRET arc = NO_ERROR; // return code
691
692 if (fSkipRemoveables)
693 // start with drive C:
694 ulLogicalDrive = 3;
695
696 // go thru the drives, start with C: (== 3), stop after Z: (== 26)
697 while (ulLogicalDrive <= 26)
698 {
699 #pragma pack(1)
700 struct
701 {
702 UCHAR dummy,drive;
703 } parms;
704 #pragma pack()
705
706 // data packet
707 UCHAR nonRemovable=0;
708
709 parms.drive=(UCHAR)(ulLogicalDrive-1);
710 arc = doshDevIOCtl((HFILE)-1,
711 IOCTL_DISK,
712 DSK_BLOCKREMOVABLE,
713 &parms, sizeof(parms),
714 &nonRemovable, sizeof(nonRemovable));
715
716 if ( // fixed disk and non-removeable
717 ((arc == NO_ERROR) && (nonRemovable))
718 // or network drive:
719 || (arc == ERROR_NOT_SUPPORTED)
720 )
721 {
722 ULONG ulOrdinal = 0; // ordinal of entry in name list
723 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
724 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
725 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
726
727 szName[0] = ulLogicalDrive + 'A' - 1;
728 szName[1] = ':';
729 szName[2] = '\0';
730
731 arc = DosQueryFSAttach(szName, // logical drive of attached FS
732 ulOrdinal, // ignored for FSAIL_QUERYNAME
733 FSAIL_QUERYNAME, // return data for a Drive or Device
734 pfsqBuffer, // returned data
735 &cbBuffer); // returned data length
736
737 if (arc == NO_ERROR)
738 {
739 // The data for the last three fields in the FSQBUFFER2
740 // structure are stored at the offset of fsqBuffer.szName.
741 // Each data field following fsqBuffer.szName begins
742 // immediately after the previous item.
743 CHAR* pszFSDName = (PSZ)&(pfsqBuffer->szName) + (pfsqBuffer->cbName) + 1;
744 if (pcszFileSystem == NULL)
745 {
746 // enum-all mode: always copy
747 pszBuffer[ulFound] = szName[0]; // drive letter
748 ulFound++;
749 }
750 else if (strcmp(pszFSDName, pcszFileSystem) == 0)
751 {
752 pszBuffer[ulFound] = szName[0]; // drive letter
753 ulFound++;
754 }
755 }
756 }
757
758 ulLogicalDrive++;
759 } // end while (G_acDriveLetters[ulLogicalDrive] <= 'Z')
760
761 pszBuffer[ulFound] = '\0';
762}
763
764/*
765 *@@ doshQueryBootDrive:
766 * returns the letter of the boot drive as a
767 * single (capital) character, which is useful for
768 * constructing file names using sprintf and such.
769 */
770
771CHAR doshQueryBootDrive(VOID)
772{
773 ULONG ulBootDrive;
774 DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
775 &ulBootDrive,
776 sizeof(ulBootDrive));
777 return (ulBootDrive + 'A' - 1);
778}
779
780/*
781 *@@ doshAssertDrive:
782 * this checks for whether the given drive
783 * is currently available without provoking
784 * those ugly white "Drive not ready" popups.
785 *
786 * "fl" can specify additional flags for testing
787 * and can be any combination of:
788 *
789 * -- ASSERTFL_MIXEDMODECD: whether to allow
790 * mixed-mode CD-ROMs. See error codes below.
791 *
792 * This returns (from my testing):
793 *
794 * -- NO_ERROR: drive is available.
795 *
796 * -- ERROR_INVALID_DRIVE (15): drive letter does not exist.
797 *
798 * -- ERROR_NOT_READY (21): drive exists, but is not ready.
799 * This is produced by floppies and CD-ROM drives
800 * which do not have valid media inserted.
801 *
802 * -- ERROR_AUDIO_CD_ROM (10000): special error code returned
803 * only by this function if a CD-ROM drive has audio
804 * media inserted.
805 *
806 * If ASSERTFL_MIXEDMODECD was specified, ERROR_AUDIO_CD_ROM
807 * is returned _only_ if _no_ data tracks are
808 * present on a CD-ROM. Since OS/2 is not very
809 * good at handling mixed-mode CDs, this might not
810 * be desireable.
811 *
812 * If ASSERTFL_MIXEDMODECD was not set, ERROR_AUDIO_CD_ROM
813 * will be returned already if _one_ audio track is present.
814 *
815 *@@changed V0.9.1 (99-12-13) [umoeller]: rewritten, prototype changed. Now using DosOpen on the drive instead of DosError.
816 *@@changed V0.9.1 (2000-01-08) [umoeller]: DosClose was called even if DosOpen failed, which messed up OS/2 error handling.
817 *@@changed V0.9.1 (2000-02-09) [umoeller]: this didn't work for network drives, including RAMFS; fixed.
818 *@@changed V0.9.3 (2000-03-28) [umoeller]: added check for network drives, which weren't working
819 *@@changed V0.9.4 (2000-08-03) [umoeller]: more network fixes
820 *@@changed V0.9.9 (2001-03-19) [pr]: validate drive number
821 *@@changed V0.9.11 (2001-04-23) [umoeller]: added an extra check for floppies
822 *@@changed V0.9.13 (2001-06-14) [umoeller]: added "fl" parameter and lots of CD-ROM checks
823 */
824
825APIRET doshAssertDrive(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
826 ULONG fl) // in: ASSERTFL_* flags
827{
828 APIRET arc = NO_ERROR;
829 BOOL fFixed = FALSE,
830 fCDROM = FALSE;
831
832 if ((ulLogicalDrive < 1) || (ulLogicalDrive > 26))
833 return(ERROR_PATH_NOT_FOUND);
834
835 arc = doshIsFixedDisk(ulLogicalDrive,
836 &fFixed); // V0.9.13 (2001-06-14) [umoeller]
837
838 // _Pmpf((__FUNCTION__ ": doshIsFixedDisk returned %d for disk %d", arc, ulLogicalDrive));
839 // _Pmpf((" fFixed is %d", fFixed));
840
841 if (!arc)
842 if (!fFixed)
843 {
844 // removeable disk:
845 // check if it's a CD-ROM
846 BIOSPARAMETERBLOCK bpb;
847 arc = doshQueryDiskParams(ulLogicalDrive,
848 &bpb);
849 // _Pmpf((" doshQueryDiskParams returned %d", arc));
850
851 if ( (!arc)
852 && (doshIsCDROM(&bpb))
853 )
854 {
855 // _Pmpf((" --> is CD-ROM"));
856 fCDROM = TRUE;
857 }
858 }
859
860 if (!arc)
861 {
862 HFILE hfDrive = NULLHANDLE;
863
864 ULONG ulTemp = 0;
865 CHAR szDrive[3] = "C:";
866 szDrive[0] = 'A' + ulLogicalDrive - 1;
867
868 arc = DosOpen(szDrive, // "C:", "D:", ...
869 &hfDrive,
870 &ulTemp,
871 0,
872 FILE_NORMAL,
873 OPEN_ACTION_FAIL_IF_NEW
874 | OPEN_ACTION_OPEN_IF_EXISTS,
875 OPEN_FLAGS_DASD
876 | OPEN_FLAGS_FAIL_ON_ERROR
877 | OPEN_FLAGS_NOINHERIT // V0.9.6 (2000-11-25) [pr]
878 // | OPEN_ACCESS_READONLY // V0.9.13 (2001-06-14) [umoeller]
879 | OPEN_SHARE_DENYNONE,
880 NULL);
881
882 // _Pmpf((" DosOpen(OPEN_FLAGS_DASD) returned %d", arc));
883
884 // this still returns NO_ERROR for audio CDs in a
885 // CD-ROM drive...
886 // however, the WPS then attempts to read in the
887 // root directory for audio CDs, which produces
888 // a "sector not found" error box...
889
890 if ( (!arc)
891 && (hfDrive)
892 && (fCDROM)
893 )
894 {
895 BOOL fAudio;
896 if ( (!(arc = doshHasAudioCD(ulLogicalDrive,
897 hfDrive,
898 ((fl & ASSERTFL_MIXEDMODECD) != 0),
899 &fAudio)))
900 && (fAudio)
901 )
902 arc = ERROR_AUDIO_CD_ROM; // special private error code (10000)
903 }
904
905 if (hfDrive)
906 DosClose(hfDrive);
907 }
908
909 switch (arc)
910 {
911 case ERROR_NETWORK_ACCESS_DENIED: // 65
912 // added V0.9.3 (2000-03-27) [umoeller];
913 // according to user reports, this is returned
914 // by all network drives, which apparently don't
915 // support DASD DosOpen
916 case ERROR_ACCESS_DENIED: // 5
917 // added V0.9.4 (2000-07-10) [umoeller]
918 // LAN drives still didn't work... apparently
919 // the above only works for NFS drives
920 case ERROR_PATH_NOT_FOUND: // 3
921 // added V0.9.4 (2000-08-03) [umoeller]:
922 // this is returned by some other network types...
923 // sigh...
924 case ERROR_NOT_SUPPORTED: // 50
925 // this is returned by file systems which don't
926 // support DASD DosOpen;
927 // use some other method then, this isn't likely
928 // to fail -- V0.9.1 (2000-02-09) [umoeller]
929
930 // but don't do this for floppies
931 // V0.9.11 (2001-04-23) [umoeller]
932 if (ulLogicalDrive > 2)
933 {
934 FSALLOCATE fsa;
935 arc = DosQueryFSInfo(ulLogicalDrive,
936 FSIL_ALLOC,
937 &fsa,
938 sizeof(fsa));
939 // _Pmpf((" re-checked, DosQueryFSInfo returned %d", arc));
940 }
941 break;
942 }
943
944 return (arc);
945}
946
947/*
948 *@@ doshSetLogicalMap:
949 * sets the mapping of logical floppy drives onto a single
950 * physical floppy drive.
951 * This means selecting either drive A: or drive B: to refer
952 * to the physical drive.
953 *
954 *@@added V0.9.6 (2000-11-24) [pr]
955 */
956
957APIRET doshSetLogicalMap(ULONG ulLogicalDrive)
958{
959 CHAR name[3] = "?:";
960 ULONG fd = 0,
961 action = 0;
962// paramsize = 0;
963// datasize = 0;
964 APIRET rc = NO_ERROR;
965 USHORT data,
966 param;
967
968 name[0] = doshQueryBootDrive();
969 rc = DosOpen(name,
970 &fd,
971 &action,
972 0,
973 0,
974 OPEN_ACTION_FAIL_IF_NEW
975 | OPEN_ACTION_OPEN_IF_EXISTS,
976 OPEN_FLAGS_DASD
977 | OPEN_FLAGS_FAIL_ON_ERROR
978 | OPEN_FLAGS_NOINHERIT
979 | OPEN_ACCESS_READONLY
980 | OPEN_SHARE_DENYNONE,
981 0);
982
983 if (rc == NO_ERROR)
984 {
985 param = 0;
986 data = (USHORT)ulLogicalDrive;
987 // paramsize = sizeof(param);
988 // datasize = sizeof(data);
989 rc = doshDevIOCtl(fd,
990 IOCTL_DISK, DSK_SETLOGICALMAP,
991 &param, sizeof(param),
992 &data, sizeof(data));
993 DosClose(fd);
994 }
995
996 return(rc);
997}
998
999/*
1000 *@@ doshQueryDiskSize:
1001 * returns the size of the specified disk in bytes.
1002 *
1003 * Note: This returns a "double" value, because a ULONG
1004 * can only hold values of some 4 billion, which would
1005 * lead to funny results for drives > 4 GB.
1006 *
1007 *@@added V0.9.11 (2001-04-18) [umoeller]
1008 */
1009
1010APIRET doshQueryDiskSize(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1011 double *pdSize)
1012{
1013 APIRET arc = NO_ERROR;
1014 FSALLOCATE fsa;
1015 // double dbl = -1;
1016
1017 if (!(arc = DosQueryFSInfo(ulLogicalDrive, FSIL_ALLOC, &fsa, sizeof(fsa))))
1018 *pdSize = ((double)fsa.cSectorUnit * fsa.cbSector * fsa.cUnit);
1019
1020 return (arc);
1021}
1022
1023/*
1024 *@@ doshQueryDiskFree:
1025 * returns the number of bytes remaining on the disk
1026 * specified by the given logical drive.
1027 *
1028 * Note: This returns a "double" value, because a ULONG
1029 * can only hold values of some 4 billion, which would
1030 * lead to funny results for drives > 4 GB.
1031 *
1032 *@@changed V0.9.0 [umoeller]: fixed another > 4 GB bug (thanks to Rdiger Ihle)
1033 *@@changed V0.9.7 (2000-12-01) [umoeller]: changed prototype
1034 */
1035
1036APIRET doshQueryDiskFree(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1037 double *pdFree)
1038{
1039 APIRET arc = NO_ERROR;
1040 FSALLOCATE fsa;
1041 // double dbl = -1;
1042
1043 if (!(arc = DosQueryFSInfo(ulLogicalDrive, FSIL_ALLOC, &fsa, sizeof(fsa))))
1044 *pdFree = ((double)fsa.cSectorUnit * fsa.cbSector * fsa.cUnitAvail);
1045 // ^ fixed V0.9.0
1046
1047 return (arc);
1048}
1049
1050/*
1051 *@@ doshQueryDiskFSType:
1052 * copies the file-system type of the given disk object
1053 * (HPFS, FAT, CDFS etc.) to pszBuf.
1054 * Returns the DOS error code.
1055 *
1056 *@@changed V0.9.1 (99-12-12) [umoeller]: added cbBuf to prototype
1057 *@@changed V0.9.14 (2001-08-01) [umoeller]: fixed, this never respected cbBuf
1058 *@@changed V0.9.16 (2001-10-02) [umoeller]: added check for valid logical disk no
1059 */
1060
1061APIRET doshQueryDiskFSType(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1062 PSZ pszBuf, // out: buffer for FS type
1063 ULONG cbBuf) // in: size of that buffer
1064{
1065 APIRET arc = NO_ERROR;
1066 CHAR szName[5];
1067
1068 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
1069 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
1070 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
1071
1072 // compose "D:"-type string from logical drive letter
1073 if (ulLogicalDrive > 0 && ulLogicalDrive < 27)
1074 {
1075 szName[0] = ulLogicalDrive + 'A' - 1;
1076 szName[1] = ':';
1077 szName[2] = '\0';
1078
1079 arc = DosQueryFSAttach(szName, // logical drive of attached FS ("D:"-style)
1080 0, // ulOrdinal, ignored for FSAIL_QUERYNAME
1081 FSAIL_QUERYNAME, // return name for a drive or device
1082 pfsqBuffer, // buffer for returned data
1083 &cbBuffer); // sizeof(*pfsqBuffer)
1084
1085 if (arc == NO_ERROR)
1086 {
1087 if (pszBuf)
1088 {
1089 // The data for the last three fields in the FSQBUFFER2
1090 // structure are stored at the offset of fsqBuffer.szName.
1091 // Each data field following fsqBuffer.szName begins
1092 // immediately after the previous item.
1093 strncpy(pszBuf,
1094 (CHAR*)(&pfsqBuffer->szName) + pfsqBuffer->cbName + 1,
1095 cbBuf); // V0.9.14 (2001-08-01) [umoeller]
1096 *(pszBuf + cbBuf) = '\0';
1097 }
1098 }
1099 }
1100 else
1101 arc = ERROR_INVALID_PARAMETER; // V0.9.16 (2001-10-02) [umoeller]
1102
1103 return (arc);
1104}
1105
1106/*
1107 *@@ doshQueryDiskLabel:
1108 * this returns the label of a disk into
1109 * *pszVolumeLabel, which must be 12 bytes
1110 * in size.
1111 *
1112 * This function was added because the Toolkit
1113 * information for DosQueryFSInfo is only partly
1114 * correct. On OS/2 2.x, that function does not
1115 * take an FSINFO structure as input, but a VOLUMELABEL.
1116 * On Warp, this does take an FSINFO.
1117 *
1118 * DosSetFSInfo is even worse. See doshSetDiskLabel.
1119 *
1120 * See http://zebra.asta.fh-weingarten.de/os2/Snippets/Bugi6787.HTML
1121 * for details.
1122 *
1123 *@@added V0.9.0 [umoeller]
1124 *@@changed V0.9.11 (2001-04-22) [umoeller]: this copied even with errors, fixed
1125 */
1126
1127APIRET doshQueryDiskLabel(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1128 PSZ pszVolumeLabel) // out: volume label (must be 12 chars in size)
1129{
1130 APIRET arc;
1131
1132 #ifdef __OS2V2X__
1133 VOLUMELABEL FSInfoBuf;
1134 #else
1135 FSINFO FSInfoBuf;
1136 #endif
1137
1138 arc = DosQueryFSInfo(ulLogicalDrive,
1139 FSIL_VOLSER,
1140 &FSInfoBuf,
1141 sizeof(FSInfoBuf)); // depends
1142
1143 if (!arc) // V0.9.11 (2001-04-22) [umoeller]
1144 {
1145 #ifdef __OS2V2X__
1146 strcpy(pszVolumeLabel, FSInfoBuf.szVolLabel);
1147 #else
1148 strcpy(pszVolumeLabel, FSInfoBuf.vol.szVolLabel);
1149 #endif
1150 }
1151
1152 return (arc);
1153}
1154
1155/*
1156 *@@ doshSetDiskLabel:
1157 * this sets the label of a disk.
1158 *
1159 * This function was added because the Toolkit
1160 * information for DosSetFSInfo is flat out wrong.
1161 * That function does not take an FSINFO structure
1162 * as input, but a VOLUMELABEL. As a result, using
1163 * that function with the Toolkit's calling specs
1164 * results in ERROR_LABEL_TOO_LONG always.
1165 *
1166 * See http://zebra.asta.fh-weingarten.de/os2/Snippets/Bugi6787.HTML
1167 * for details.
1168 *
1169 *@@added V0.9.0 [umoeller]
1170 */
1171
1172APIRET doshSetDiskLabel(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1173 PSZ pszNewLabel)
1174{
1175 VOLUMELABEL FSInfoBuf;
1176
1177 // check length; 11 chars plus null byte allowed
1178 FSInfoBuf.cch = (BYTE)strlen(pszNewLabel);
1179 if (FSInfoBuf.cch < sizeof(FSInfoBuf.szVolLabel))
1180 {
1181 strcpy(FSInfoBuf.szVolLabel, pszNewLabel);
1182
1183 return (DosSetFSInfo(ulLogicalDrive,
1184 FSIL_VOLSER,
1185 &FSInfoBuf,
1186 sizeof(FSInfoBuf)));
1187 }
1188 else
1189 return (ERROR_LABEL_TOO_LONG);
1190}
1191
1192/*
1193 *@@category: Helpers\Control program helpers\File management
1194 */
1195
1196/* ******************************************************************
1197 *
1198 * File helpers
1199 *
1200 ********************************************************************/
1201
1202/*
1203 *@@ doshGetExtension:
1204 * finds the file name extension of pszFilename,
1205 * which can be a file name only or a fully
1206 * qualified filename.
1207 *
1208 * This returns a pointer into pszFilename to
1209 * the character after the last dot.
1210 *
1211 * Returns NULL if not found (e.g. if the filename
1212 * has no dot in it).
1213 *
1214 *@@added V0.9.6 (2000-10-16) [umoeller]
1215 *@@changed V0.9.7 (2000-12-10) [umoeller]: fixed "F:filename.ext" case
1216 */
1217
1218PSZ doshGetExtension(const char *pcszFilename)
1219{
1220 PSZ pReturn = NULL;
1221
1222 if (pcszFilename)
1223 {
1224 // find filename
1225 const char *p2 = strrchr(pcszFilename + 2, '\\'),
1226 // works on "C:\blah" or "\\unc\blah"
1227 *pStartOfName = NULL,
1228 *pExtension = NULL;
1229
1230 if (p2)
1231 pStartOfName = p2 + 1;
1232 else
1233 {
1234 // no backslash found:
1235 // maybe only a drive letter was specified:
1236 if (*(pcszFilename + 1) == ':')
1237 // yes:
1238 pStartOfName = pcszFilename + 2;
1239 else
1240 // then this is not qualified at all...
1241 // use start of filename
1242 pStartOfName = (PSZ)pcszFilename;
1243 }
1244
1245 // find last dot in filename
1246 pExtension = strrchr(pStartOfName, '.');
1247 if (pExtension)
1248 pReturn = (PSZ)pExtension + 1;
1249 }
1250
1251 return (pReturn);
1252}
1253
1254/*
1255 *@@ doshIsFileOnFAT:
1256 * returns TRUE if pszFileName resides on
1257 * a FAT drive. Note that pszFileName must
1258 * be fully qualified (i.e. the drive letter
1259 * must be the first character), or this will
1260 * return garbage.
1261 */
1262
1263BOOL doshIsFileOnFAT(const char* pcszFileName)
1264{
1265 BOOL brc = FALSE;
1266 CHAR szName[5];
1267
1268 APIRET rc = NO_ERROR; // return code
1269 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
1270 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
1271 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
1272
1273 szName[0] = pcszFileName[0]; // copy drive letter
1274 szName[1] = ':';
1275 szName[2] = '\0';
1276
1277 rc = DosQueryFSAttach(szName, // logical drive of attached FS
1278 0, // ulOrdinal, ignored for FSAIL_QUERYNAME
1279 FSAIL_QUERYNAME, // return data for a Drive or Device
1280 pfsqBuffer, // returned data
1281 &cbBuffer); // returned data length
1282
1283 if (rc == NO_ERROR)
1284 {
1285 // The data for the last three fields in the FSQBUFFER2
1286 // structure are stored at the offset of fsqBuffer.szName.
1287 // Each data field following fsqBuffer.szName begins
1288 // immediately after the previous item.
1289 if (strncmp((PSZ)&(pfsqBuffer->szName) + pfsqBuffer->cbName + 1,
1290 "FAT",
1291 3)
1292 == 0)
1293 brc = TRUE;
1294 }
1295
1296 return (brc);
1297}
1298
1299/*
1300 *@@ doshQueryFileSize:
1301 * returns the size of an already opened file
1302 * or 0 upon errors.
1303 * Use doshQueryPathSize to query the size of
1304 * any file.
1305 *
1306 *@@changed V0.9.16 (2001-10-19) [umoeller]: now returning APIRET
1307 */
1308
1309APIRET doshQueryFileSize(HFILE hFile,
1310 PULONG pulSize)
1311{
1312 APIRET arc;
1313 FILESTATUS3 fs3;
1314 if (!(arc = DosQueryFileInfo(hFile, FIL_STANDARD, &fs3, sizeof(fs3))))
1315 *pulSize = fs3.cbFile;
1316 return (arc);
1317}
1318
1319/*
1320 *@@ doshQueryPathSize:
1321 * returns the size of any file,
1322 * or 0 if the file could not be
1323 * found.
1324 * Use doshQueryFileSize instead to query the
1325 * size if you have a HFILE.
1326 *
1327 *@@changed V0.9.16 (2001-10-19) [umoeller]: now returning APIRET
1328 */
1329
1330APIRET doshQueryPathSize(PCSZ pcszFile,
1331 PULONG pulSize)
1332{
1333 APIRET arc;
1334 FILESTATUS3 fs3;
1335 if (!(arc = DosQueryPathInfo((PSZ)pcszFile, FIL_STANDARD, &fs3, sizeof(fs3))))
1336 *pulSize = fs3.cbFile;
1337 return (arc);
1338}
1339
1340/*
1341 *@@ doshQueryPathAttr:
1342 * returns the file attributes of pszFile,
1343 * which can be fully qualified. The
1344 * attributes will be stored in *pulAttr.
1345 * pszFile can also specify a directory,
1346 * although not all attributes make sense
1347 * for directories.
1348 *
1349 * fAttr can be:
1350 * -- FILE_ARCHIVED
1351 * -- FILE_READONLY
1352 * -- FILE_SYSTEM
1353 * -- FILE_HIDDEN
1354 *
1355 * This returns the APIRET of DosQueryPathInfo.
1356 * *pulAttr is only valid if NO_ERROR is
1357 * returned.
1358 *
1359 *@@added V0.9.0 [umoeller]
1360 */
1361
1362APIRET doshQueryPathAttr(const char* pcszFile, // in: file or directory name
1363 PULONG pulAttr) // out: attributes
1364{
1365 FILESTATUS3 fs3;
1366 APIRET arc = DosQueryPathInfo((PSZ)pcszFile,
1367 FIL_STANDARD,
1368 &fs3,
1369 sizeof(fs3));
1370 if (arc == NO_ERROR)
1371 {
1372 if (pulAttr)
1373 *pulAttr = fs3.attrFile;
1374 }
1375
1376 return (arc);
1377}
1378
1379/*
1380 *@@ doshSetPathAttr:
1381 * sets the file attributes of pszFile,
1382 * which can be fully qualified.
1383 * pszFile can also specify a directory,
1384 * although not all attributes make sense
1385 * for directories.
1386 *
1387 * fAttr can be:
1388 * -- FILE_ARCHIVED
1389 * -- FILE_READONLY
1390 * -- FILE_SYSTEM
1391 * -- FILE_HIDDEN
1392 *
1393 * Note that this simply sets all the given
1394 * attributes; the existing attributes
1395 * are lost.
1396 *
1397 * This returns the APIRET of DosQueryPathInfo.
1398 */
1399
1400APIRET doshSetPathAttr(const char* pcszFile, // in: file or directory name
1401 ULONG ulAttr) // in: new attributes
1402{
1403 FILESTATUS3 fs3;
1404 APIRET rc = DosQueryPathInfo((PSZ)pcszFile,
1405 FIL_STANDARD,
1406 &fs3,
1407 sizeof(fs3));
1408
1409 if (rc == NO_ERROR)
1410 {
1411 fs3.attrFile = ulAttr;
1412 rc = DosSetPathInfo((PSZ)pcszFile,
1413 FIL_STANDARD,
1414 &fs3,
1415 sizeof(fs3),
1416 DSPI_WRTTHRU);
1417 }
1418 return (rc);
1419}
1420
1421/*
1422 *@@ doshOpenExisting:
1423 * opens an existing file for read-write access. Does
1424 * not create a new file if the file doesn't exist.
1425 *
1426 * This is just a simple wrapper around DosOpen.
1427 *
1428 * ulOpenFlags is passed to DosOpen. Should be one
1429 * of:
1430 *
1431 * -- for read-only access:
1432 *
1433 + OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY
1434 *
1435 * -- for read-write access:
1436 *
1437 + OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE
1438 *
1439 * In addition, you can specify
1440 *
1441 + OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_RANDOM
1442 + | OPEN_FLAGS_NOINHERIT
1443 *
1444 *@@added V0.9.13 (2001-06-14) [umoeller]
1445 */
1446
1447APIRET doshOpenExisting(const char *pcszFilename, // in: file name
1448 ULONG ulOpenFlags, // in: open flags
1449 HFILE *phf) // out: OS/2 file handle
1450{
1451 ULONG ulAction;
1452 return (DosOpen((PSZ)pcszFilename,
1453 phf,
1454 &ulAction,
1455 0, // cbFile
1456 0, // attributes
1457 OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
1458 ulOpenFlags,
1459 NULL)); // EAs
1460}
1461
1462/*
1463 *@@ doshWriteAt:
1464 * writes cb bytes (pointed to by pbData) to the
1465 * position specified by ulMethod and lOffset into
1466 * the file specified by hf.
1467 *
1468 * If ulMethod is FILE_BEGIN, lOffset specifies the
1469 * offset from the beginning of the file. With
1470 * FILE_CURRENT, lOffset is considered from the
1471 * current file pointer, and with FILE_END, it is
1472 * considered from the end of the file.
1473 *
1474 *@@added V0.9.13 (2001-06-14) [umoeller]
1475 */
1476
1477APIRET doshWriteAt(HFILE hf, // in: OS/2 file handle
1478 LONG lOffset, // in: offset to write at (depends on ulMethod)
1479 ULONG ulMethod, // in: one of FILE_BEGIN, FILE_CURRENT, FILE_END
1480 ULONG cb, // in: bytes to write
1481 PBYTE pbData) // in: ptr to bytes to write (must be cb bytes)
1482{
1483 APIRET arc;
1484 ULONG ulDummy;
1485 if (!(arc = DosSetFilePtr(hf,
1486 lOffset,
1487 ulMethod,
1488 &ulDummy)))
1489 arc = DosWrite(hf,
1490 pbData,
1491 cb,
1492 &ulDummy);
1493
1494 return (arc);
1495}
1496
1497/*
1498 *@@ doshReadAt:
1499 * reads cb bytes from the position specified by
1500 * ulMethod and lOffset into the buffer pointed to
1501 * by pbData, which should be cb bytes in size.
1502 *
1503 * Use lOffset and ulMethod as with doshWriteAt.
1504 *
1505 *@@added V0.9.13 (2001-06-14) [umoeller]
1506 */
1507
1508APIRET doshReadAt(HFILE hf, // in: OS/2 file handle
1509 LONG lOffset, // in: offset to write at (depends on ulMethod)
1510 ULONG ulMethod, // in: one of FILE_BEGIN, FILE_CURRENT, FILE_END
1511 ULONG cb, // in: bytes to write
1512 PBYTE pbData) // out: read buffer (must be cb bytes)
1513{
1514 APIRET arc;
1515 ULONG ulDummy;
1516 if (!(arc = DosSetFilePtr(hf,
1517 lOffset,
1518 ulMethod,
1519 &ulDummy)))
1520 arc = DosRead(hf,
1521 pbData,
1522 cb,
1523 &ulDummy);
1524
1525 return (arc);
1526}
1527
1528/*
1529 * doshOpen:
1530 * wrapper around DosOpen for simpler opening
1531 * of files.
1532 *
1533 * ulOpenMode determines the mode to open the
1534 * file in:
1535 *
1536 + +-------------------------+------+-----------+-----------+
1537 + | | | | |
1538 + | ulOpenMode | mode | if exists | if new |
1539 + +-------------------------+------+-----------+-----------+
1540 + | XOPEN_READ_EXISTING | read | opens | fails |
1541 + +-------------------------+------+-----------+-----------+
1542 + | XOPEN_READWRITE_APPEND | r/w | opens, | creates |
1543 + | | | appends | |
1544 + +-------------------------+------+-----------+-----------+
1545 + | XOPEN_READWRITE_NEW | r/w | replaces | creates |
1546 + +-------------------------+------+-----------+-----------+
1547 *
1548 * *ppFile receives a new XFILE structure describing
1549 * the open file, if NO_ERROR is returned.
1550 *
1551 *@@added V0.9.16 (2001-10-19) [umoeller]
1552 */
1553
1554APIRET doshOpen(const char *pcszFilename, // in: filename to open
1555 ULONG ulOpenMode, // in: XOPEN_* mode
1556 PULONG pcbFile, // in: new file size (if new file is created)
1557 // out: file size
1558 PXFILE *ppFile)
1559{
1560 APIRET arc = NO_ERROR;
1561
1562 ULONG fsOpenFlags = 0,
1563 fsOpenMode = OPEN_FLAGS_FAIL_ON_ERROR
1564 | OPEN_FLAGS_NO_LOCALITY
1565 | OPEN_FLAGS_NOINHERIT;
1566
1567 switch (ulOpenMode)
1568 {
1569 case XOPEN_READ_EXISTING:
1570 fsOpenFlags = OPEN_ACTION_FAIL_IF_NEW
1571 | OPEN_ACTION_OPEN_IF_EXISTS;
1572 fsOpenMode |= OPEN_SHARE_DENYWRITE
1573 | OPEN_ACCESS_READONLY;
1574 break;
1575
1576 case XOPEN_READWRITE_APPEND:
1577 fsOpenFlags = OPEN_ACTION_CREATE_IF_NEW
1578 | OPEN_ACTION_OPEN_IF_EXISTS;
1579 fsOpenMode |= OPEN_SHARE_DENYREADWRITE
1580 | OPEN_ACCESS_READWRITE;
1581 break;
1582
1583 case XOPEN_READWRITE_NEW:
1584 fsOpenFlags = OPEN_ACTION_CREATE_IF_NEW
1585 | OPEN_ACTION_REPLACE_IF_EXISTS;
1586 fsOpenMode |= OPEN_SHARE_DENYREADWRITE
1587 | OPEN_ACCESS_READWRITE;
1588 break;
1589 }
1590
1591 if (pcszFilename && fsOpenFlags && pcbFile && ppFile)
1592 {
1593 PXFILE pFile;
1594 if (pFile = NEW(XFILE))
1595 {
1596 ULONG ulAction;
1597
1598 ZERO(pFile);
1599
1600 if (!(arc = DosOpen((PSZ)pcszFilename,
1601 &pFile->hf,
1602 &ulAction,
1603 *pcbFile,
1604 FILE_ARCHIVED,
1605 fsOpenFlags,
1606 fsOpenMode,
1607 NULL))) // EAs
1608 {
1609 // alright, got the file:
1610
1611 if ( (ulAction == FILE_EXISTED)
1612 && (ulOpenMode == XOPEN_READWRITE_APPEND)
1613 )
1614 // get its size and set ptr to end for append
1615 arc = DosSetFilePtr(pFile->hf,
1616 0,
1617 FILE_END,
1618 pcbFile);
1619 else
1620 arc = doshQueryFileSize(pFile->hf,
1621 pcbFile);
1622 // file ptr is at beginning
1623
1624 // store file size
1625 pFile->cbInitial
1626 = pFile->cbCurrent
1627 = *pcbFile;
1628 }
1629
1630 if (arc)
1631 doshClose(&pFile);
1632 else
1633 *ppFile = pFile;
1634 }
1635 else
1636 arc = ERROR_NOT_ENOUGH_MEMORY;
1637 }
1638 else
1639 arc = ERROR_INVALID_PARAMETER;
1640
1641 return (arc);
1642}
1643
1644/*
1645 *@@ doshLockFile:
1646 *
1647 *@@added V0.9.16 (2001-10-19) [umoeller]
1648 */
1649
1650APIRET doshLockFile(PXFILE pFile)
1651{
1652 if (!pFile)
1653 return (ERROR_INVALID_PARAMETER);
1654
1655 if (!pFile->hmtx)
1656 // first call:
1657 return (DosCreateMutexSem(NULL,
1658 &pFile->hmtx,
1659 0,
1660 TRUE)); // request!
1661
1662 return (DosRequestMutexSem(pFile->hmtx, SEM_INDEFINITE_WAIT));
1663}
1664
1665/*
1666 *@@ doshUnlockFile:
1667 *
1668 *@@added V0.9.16 (2001-10-19) [umoeller]
1669 */
1670
1671APIRET doshUnlockFile(PXFILE pFile)
1672{
1673 if (pFile)
1674 return (DosReleaseMutexSem(pFile->hmtx));
1675
1676 return (ERROR_INVALID_PARAMETER);
1677}
1678
1679/*
1680 *@@ doshWrite:
1681 * writes the specified data to the file.
1682 * If (cb == 0), this runs strlen on pcsz
1683 * to find out the length.
1684 *
1685 *@@added V0.9.16 (2001-10-19) [umoeller]
1686 */
1687
1688APIRET doshWrite(PXFILE pFile,
1689 PCSZ pcsz,
1690 ULONG cb)
1691{
1692 APIRET arc;
1693 if (!pcsz)
1694 arc = ERROR_INVALID_PARAMETER;
1695 else
1696 {
1697 if (!cb)
1698 cb = strlen(pcsz);
1699
1700 if (!cb)
1701 arc = ERROR_INVALID_PARAMETER;
1702 else
1703 if (!(arc = doshLockFile(pFile))) // this checks for pFile
1704 {
1705 ULONG cbWritten;
1706 if (!(arc = DosWrite(pFile->hf,
1707 (PSZ)pcsz,
1708 cb,
1709 &cbWritten)))
1710 pFile->cbCurrent += cbWritten;
1711
1712 doshUnlockFile(pFile);
1713 }
1714 }
1715
1716 return (arc);
1717}
1718
1719/*
1720 *@@ doshWriteLogEntry
1721 * writes a log string to an XFILE, adding a
1722 * leading timestamp before the line.
1723 *
1724 * The internal string buffer is limited to 2000
1725 * characters. \n is NOT translated to \r\n before
1726 * writing.
1727 */
1728
1729APIRET doshWriteLogEntry(PXFILE pFile,
1730 const char* pcszFormat,
1731 ...)
1732{
1733 APIRET arc;
1734
1735 DATETIME dt;
1736 CHAR szTemp[2000];
1737 ULONG ulLength;
1738
1739 DosGetDateTime(&dt);
1740 if (ulLength = sprintf(szTemp,
1741 "%04d-%02d-%02d %02d:%02d:%02d:%02d ",
1742 dt.year, dt.month, dt.day,
1743 dt.hours, dt.minutes, dt.seconds, dt.hundredths))
1744 {
1745 if (!(arc = doshWrite(pFile,
1746 szTemp,
1747 ulLength)))
1748 {
1749 va_list arg_ptr;
1750 va_start(arg_ptr, pcszFormat);
1751 ulLength = vsprintf(szTemp, pcszFormat, arg_ptr);
1752 va_end(arg_ptr);
1753
1754 szTemp[ulLength++] = '\r';
1755 szTemp[ulLength++] = '\n';
1756
1757 arc = doshWrite(pFile,
1758 (PVOID)szTemp,
1759 ulLength);
1760 }
1761 }
1762
1763 return (arc);
1764}
1765
1766/*
1767 * doshClose:
1768 *
1769 *@@added V0.9.16 (2001-10-19) [umoeller]
1770 */
1771
1772APIRET doshClose(PXFILE *ppFile)
1773{
1774 APIRET arc = NO_ERROR;
1775 PXFILE pFile;
1776
1777 if ( (ppFile)
1778 && (pFile = *ppFile)
1779 )
1780 {
1781 // request the mutex so that we won't be
1782 // taking the file away under someone's butt
1783 if (!(arc = doshLockFile(pFile)))
1784 {
1785 HMTX hmtx = pFile->hmtx;
1786 pFile->hmtx = NULLHANDLE;
1787
1788 // now that the file is locked,
1789 // set the ptr to NULL
1790 *ppFile = NULL;
1791
1792 if (pFile->hf)
1793 {
1794 DosSetFileSize(pFile->hf, pFile->cbCurrent);
1795 DosClose(pFile->hf);
1796 pFile->hf = NULLHANDLE;
1797 }
1798
1799 doshUnlockFile(pFile);
1800 DosCloseMutexSem(pFile->hmtx);
1801 }
1802
1803 free(pFile);
1804 }
1805 else
1806 arc = ERROR_INVALID_PARAMETER;
1807
1808 return (arc);
1809}
1810
1811/*
1812 *@@ doshLoadTextFile:
1813 * reads a text file from disk, allocates memory
1814 * via malloc() and sets a pointer to this
1815 * buffer (or NULL upon errors).
1816 *
1817 * This returns the APIRET of DosOpen and DosRead.
1818 * If any error occured, no buffer was allocated.
1819 * Otherwise, you should free() the buffer when
1820 * no longer needed.
1821 *
1822 *@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from doshReadTextFile
1823 */
1824
1825APIRET doshLoadTextFile(const char *pcszFile, // in: file name to read
1826 PSZ* ppszContent) // out: newly allocated buffer with file's content
1827{
1828 ULONG ulSize,
1829 ulBytesRead = 0,
1830 ulAction, ulLocal;
1831 HFILE hFile;
1832 PSZ pszContent = NULL;
1833
1834 APIRET arc;
1835
1836 *ppszContent = 0;
1837
1838 if (!(arc = DosOpen((PSZ)pcszFile,
1839 &hFile,
1840 &ulAction, // action taken
1841 5000L, // primary allocation size
1842 FILE_ARCHIVED | FILE_NORMAL, // file attribute
1843 OPEN_ACTION_OPEN_IF_EXISTS, // open flags
1844 OPEN_FLAGS_NOINHERIT
1845 | OPEN_SHARE_DENYNONE
1846 | OPEN_ACCESS_READONLY, // read-only mode
1847 NULL))) // no EAs
1848 {
1849 if (!(arc = doshQueryFileSize(hFile, &ulSize)))
1850 {
1851 pszContent = (PSZ)malloc(ulSize+1);
1852
1853 if (!(arc = DosSetFilePtr(hFile,
1854 0L,
1855 FILE_BEGIN,
1856 &ulLocal)))
1857 if (!(arc = DosRead(hFile,
1858 pszContent,
1859 ulSize,
1860 &ulBytesRead)))
1861 {
1862 *(pszContent+ulBytesRead) = 0;
1863 // set output buffer pointer
1864 *ppszContent = pszContent;
1865 }
1866
1867 if (arc)
1868 free(pszContent);
1869 }
1870 DosClose(hFile);
1871 }
1872
1873 return (arc);
1874}
1875
1876/*
1877 *@@ doshCreateBackupFileName:
1878 * creates a valid backup filename of pszExisting
1879 * with a numerical file name extension which does
1880 * not exist in the directory where pszExisting
1881 * resides.
1882 * Returns a PSZ to a new buffer which was allocated
1883 * using malloc().
1884 *
1885 * <B>Example:</B> returns "C:\CONFIG.002" for input
1886 * "C:\CONFIG.SYS" if "C:\CONFIG.001" already exists.
1887 *
1888 *@@changed V0.9.1 (99-12-13) [umoeller]: this crashed if pszExisting had no file extension
1889 */
1890
1891PSZ doshCreateBackupFileName(const char* pszExisting)
1892{
1893 CHAR szFilename[CCHMAXPATH];
1894 PSZ pszLastDot;
1895 ULONG ulCount = 1;
1896 CHAR szCount[5];
1897 ULONG ulDummy;
1898
1899 strcpy(szFilename, pszExisting);
1900 pszLastDot = strrchr(szFilename, '.');
1901 if (!pszLastDot)
1902 // no dot in filename:
1903 pszLastDot = szFilename + strlen(szFilename);
1904 do
1905 {
1906 sprintf(szCount, ".%03lu", ulCount);
1907 strcpy(pszLastDot, szCount);
1908 ulCount++;
1909 } while (!doshQueryPathSize(szFilename, &ulDummy));
1910
1911 return (strdup(szFilename));
1912}
1913
1914/*
1915 *@@ doshCreateTempFileName:
1916 * produces a file name in the the specified directory
1917 * or $(TEMP) which presently doesn't exist. This
1918 * checks the directory for existing files, but does
1919 * not open the temp file.
1920 *
1921 * If (pcszDir != NULL), we look into that directory.
1922 * Otherwise we look into the directory specified
1923 * by the $(TEMP) environment variable.
1924 * If $(TEMP) is not set, $(TMP) is tried next.
1925 *
1926 * If the directory thus specified does not exist, the
1927 * root directory of the boot drive is used instead.
1928 * As a result, this function should be fairly bomb-proof.
1929 *
1930 * If (pcszExt != NULL), the temp file receives
1931 * that extension, or no extension otherwise.
1932 * Do not specify the dot in pcszExt.
1933 *
1934 * pszTempFileName receives the fully qualified
1935 * file name of the temp file in that directory
1936 * and must point to a buffer CCHMAXPATH in size.
1937 * The file name is 8+3 compliant if pcszExt does
1938 * not exceed three characters.
1939 *
1940 * If (pcszPrefix != NULL), the temp file name
1941 * is prefixed with pcszPrefix. Since the temp
1942 * file name must not exceed 8+3 letters, we
1943 * can only use ( 8 - strlen(pcszPrefix ) digits
1944 * for a random number to make the temp file name
1945 * unique. You must therefore use a maximum of
1946 * four characters for the prefix. Otherwise
1947 * ERROR_INVALID_PARAMETER is returned.
1948 *
1949 * Example: Assuming TEMP is set to C:\TEMP,
1950 +
1951 + dosCreateTempFileName(szBuffer,
1952 + NULL, // use $(TEMP)
1953 + "pre", // prefix
1954 + "tmp") // extension
1955 +
1956 * would produce something like "C:\TEMP\pre07FG2.tmp".
1957 *
1958 *@@added V0.9.9 (2001-04-04) [umoeller]
1959 */
1960
1961APIRET doshCreateTempFileName(PSZ pszTempFileName, // out: fully q'fied temp file name
1962 const char *pcszDir, // in: dir or NULL for %TEMP%
1963 const char *pcszPrefix, // in: prefix for temp file or NULL
1964 const char *pcszExt) // in: extension (without dot) or NULL
1965{
1966 APIRET arc = NO_ERROR;
1967
1968 ULONG ulPrefixLen = (pcszPrefix)
1969 ? strlen(pcszPrefix)
1970 : 0;
1971
1972 if ( (!pszTempFileName)
1973 || (ulPrefixLen > 4)
1974 )
1975 arc = ERROR_INVALID_PARAMETER;
1976 else
1977 {
1978 CHAR szDir[CCHMAXPATH] = "";
1979 FILESTATUS3 fs3;
1980
1981 const char *pcszTemp = pcszDir;
1982
1983 if (!pcszTemp)
1984 {
1985 pcszTemp = getenv("TEMP");
1986 if (!pcszTemp)
1987 pcszTemp = getenv("TMP");
1988 }
1989
1990 if (pcszTemp) // either pcszDir or $(TEMP) or $(TMP) now
1991 if (DosQueryPathInfo((PSZ)pcszTemp,
1992 FIL_STANDARD,
1993 &fs3,
1994 sizeof(fs3)))
1995 // TEMP doesn't exist:
1996 pcszTemp = NULL;
1997
1998 if (!pcszTemp)
1999 // not set, or doesn't exist:
2000 // use root directory on boot drive
2001 sprintf(szDir,
2002 "%c:\\",
2003 doshQueryBootDrive());
2004 else
2005 {
2006 strcpy(szDir, pcszTemp);
2007 if (szDir[strlen(szDir) - 1] != '\\')
2008 strcat(szDir, "\\");
2009 }
2010
2011 if (!szDir[0])
2012 arc = ERROR_PATH_NOT_FOUND; // shouldn't happen
2013 else
2014 {
2015 ULONG ulRandom = 0;
2016 ULONG cAttempts = 0;
2017
2018 // produce random number
2019 DosQuerySysInfo(QSV_MS_COUNT,
2020 QSV_MS_COUNT,
2021 &ulRandom,
2022 sizeof(ulRandom));
2023
2024 do
2025 {
2026 CHAR szFile[20] = "",
2027 szFullTryThis[CCHMAXPATH];
2028
2029 // use the lower eight hex digits of the
2030 // system uptime as the temp dir name
2031 sprintf(szFile,
2032 "%08lX",
2033 ulRandom & 0xFFFFFFFF);
2034
2035 // if prefix is specified, overwrite the
2036 // first characters in the random number
2037 if (pcszPrefix)
2038 memcpy(szFile, pcszPrefix, ulPrefixLen);
2039
2040 if (pcszExt)
2041 {
2042 szFile[8] = '.';
2043 strcpy(szFile + 9, pcszExt);
2044 }
2045
2046 // now compose full temp file name
2047 strcpy(szFullTryThis, szDir);
2048 strcat(szFullTryThis, szFile);
2049 // now we have: "C:\temp\wpiXXXXX"
2050 if (DosQueryPathInfo(szFullTryThis,
2051 FIL_STANDARD,
2052 &fs3,
2053 sizeof(fs3))
2054 == ERROR_FILE_NOT_FOUND)
2055 {
2056 // file or dir doesn't exist:
2057 // cool, we're done
2058 strcpy(pszTempFileName, szFullTryThis);
2059 return (NO_ERROR);
2060 }
2061
2062 // if this didn't work, raise ulRandom and try again
2063 ulRandom += 123;
2064
2065 // try only 100 times, just to be sure
2066 cAttempts++;
2067 } while (cAttempts < 100);
2068
2069 // 100 loops elapsed:
2070 arc = ERROR_BAD_FORMAT;
2071 }
2072 }
2073
2074 return (arc);
2075}
2076
2077/*
2078 *@@ doshWriteTextFile:
2079 * writes a text file to disk; pszFile must contain the
2080 * whole path and filename.
2081 *
2082 * An existing file will be backed up if (pszBackup != NULL),
2083 * using doshCreateBackupFileName. In that case, pszBackup
2084 * receives the name of the backup created, so that buffer
2085 * should be CCHMAXPATH in size.
2086 *
2087 * If (pszbackup == NULL), an existing file will be overwritten.
2088 *
2089 * On success (NO_ERROR returned), *pulWritten receives
2090 * the no. of bytes written.
2091 *
2092 *@@changed V0.9.3 (2000-05-01) [umoeller]: optimized DosOpen; added error checking; changed prototype
2093 *@@changed V0.9.3 (2000-05-12) [umoeller]: added pszBackup
2094 */
2095
2096APIRET doshWriteTextFile(const char* pszFile, // in: file name
2097 const char* pszContent, // in: text to write
2098 PULONG pulWritten, // out: bytes written (ptr can be NULL)
2099 PSZ pszBackup) // in/out: create-backup?
2100{
2101 APIRET arc = NO_ERROR;
2102 ULONG ulWritten = 0;
2103
2104 if ((!pszFile) || (!pszContent))
2105 arc = ERROR_INVALID_PARAMETER;
2106 else
2107 {
2108 ULONG ulAction = 0,
2109 ulLocal = 0;
2110 HFILE hFile = 0;
2111
2112 ULONG ulSize = strlen(pszContent); // exclude 0 byte
2113
2114 if (pszBackup)
2115 {
2116 PSZ pszBackup2 = doshCreateBackupFileName(pszFile);
2117 DosCopy((PSZ)pszFile,
2118 pszBackup2,
2119 DCPY_EXISTING); // delete existing
2120 strcpy(pszBackup, pszBackup2);
2121 free(pszBackup2);
2122 }
2123
2124 if (!(arc = DosOpen((PSZ)pszFile,
2125 &hFile,
2126 &ulAction, // action taken
2127 ulSize, // primary allocation size
2128 FILE_ARCHIVED | FILE_NORMAL, // file attribute
2129 OPEN_ACTION_CREATE_IF_NEW
2130 | OPEN_ACTION_REPLACE_IF_EXISTS, // open flags
2131 OPEN_FLAGS_NOINHERIT
2132 | OPEN_FLAGS_SEQUENTIAL // sequential, not random access
2133 | OPEN_SHARE_DENYWRITE // deny write mode
2134 | OPEN_ACCESS_WRITEONLY, // write mode
2135 NULL))) // no EAs
2136 {
2137 if (!(arc = DosSetFilePtr(hFile,
2138 0L,
2139 FILE_BEGIN,
2140 &ulLocal)))
2141 if (!(arc = DosWrite(hFile,
2142 (PVOID)pszContent,
2143 ulSize,
2144 &ulWritten)))
2145 arc = DosSetFileSize(hFile, ulSize);
2146
2147 DosClose(hFile);
2148 }
2149 } // end if ((pszFile) && (pszContent))
2150
2151 if (pulWritten)
2152 *pulWritten = ulWritten;
2153
2154 return (arc);
2155}
2156
2157/*
2158 *@@category: Helpers\Control program helpers\Directory management
2159 * directory helpers (querying, creating, deleting etc.).
2160 */
2161
2162/* ******************************************************************
2163 *
2164 * Directory helpers
2165 *
2166 ********************************************************************/
2167
2168/*
2169 *@@ doshQueryDirExist:
2170 * returns TRUE if the given directory
2171 * exists.
2172 */
2173
2174BOOL doshQueryDirExist(const char *pcszDir)
2175{
2176 FILESTATUS3 fs3;
2177 APIRET arc = DosQueryPathInfo((PSZ)pcszDir,
2178 FIL_STANDARD,
2179 &fs3,
2180 sizeof(fs3));
2181 if (arc == NO_ERROR)
2182 // file found:
2183 return ((fs3.attrFile & FILE_DIRECTORY) != 0);
2184 else
2185 return FALSE;
2186}
2187
2188/*
2189 *@@ doshCreatePath:
2190 * this creates the specified directory.
2191 * As opposed to DosCreateDir, this
2192 * function can create several directories
2193 * at the same time, if the parent
2194 * directories do not exist yet.
2195 */
2196
2197APIRET doshCreatePath(const char *pcszPath,
2198 BOOL fHidden) // in: if TRUE, the new directories will get FILE_HIDDEN
2199{
2200 APIRET arc0 = NO_ERROR;
2201 CHAR path[CCHMAXPATH];
2202 CHAR *cp, c;
2203 ULONG cbPath;
2204
2205 strcpy(path, pcszPath);
2206 cbPath = strlen(path);
2207
2208 if (path[cbPath] != '\\')
2209 {
2210 path[cbPath] = '\\';
2211 path[cbPath+1] = 0;
2212 }
2213
2214 cp = path;
2215 // advance past the drive letter only if we have one
2216 if (*(cp+1) == ':')
2217 cp += 3;
2218
2219 // go!
2220 while (*cp != 0)
2221 {
2222 if (*cp == '\\')
2223 {
2224 c = *cp;
2225 *cp = 0;
2226 if (!doshQueryDirExist(path))
2227 {
2228 APIRET arc = DosCreateDir(path,
2229 0); // no EAs
2230 if (arc != NO_ERROR)
2231 {
2232 arc0 = arc;
2233 break;
2234 }
2235 else
2236 if (fHidden)
2237 {
2238 // hide the directory we just created
2239 doshSetPathAttr(path, FILE_HIDDEN);
2240 }
2241 }
2242 *cp = c;
2243 }
2244 cp++;
2245 }
2246 return (arc0);
2247}
2248
2249/*
2250 *@@ doshQueryCurrentDir:
2251 * writes the current directory into
2252 * the specified buffer, which should be
2253 * CCHMAXPATH in size.
2254 *
2255 * As opposed to DosQueryCurrentDir, this
2256 * includes the drive letter.
2257 *
2258 *@@added V0.9.4 (2000-07-22) [umoeller]
2259 */
2260
2261APIRET doshQueryCurrentDir(PSZ pszBuf)
2262{
2263 APIRET arc = NO_ERROR;
2264 ULONG ulCurDisk = 0;
2265 ULONG ulMap = 0;
2266 if (!(arc = DosQueryCurrentDisk(&ulCurDisk, &ulMap)))
2267 {
2268 ULONG cbBuf = CCHMAXPATH - 3;
2269 *pszBuf = ulCurDisk + 'A' - 1;
2270 *(pszBuf + 1) = ':';
2271 *(pszBuf + 2) = '\\';
2272 arc = DosQueryCurrentDir(0, pszBuf + 3, &cbBuf);
2273 }
2274
2275 return (arc);
2276}
2277
2278/*
2279 *@@ doshDeleteDir:
2280 * deletes a directory. As opposed to DosDeleteDir,
2281 * this removes empty subdirectories and/or files
2282 * as well.
2283 *
2284 * flFlags can be any combination of the following:
2285 *
2286 * -- DOSHDELDIR_RECURSE: recurse into subdirectories.
2287 *
2288 * -- DOSHDELDIR_DELETEFILES: delete all regular files
2289 * which are found on the way.
2290 *
2291 * THIS IS NOT IMPLEMENTED YET.
2292 *
2293 * If 0 is specified, this effectively behaves just as
2294 * DosDeleteDir.
2295 *
2296 * If you specify DOSHDELDIR_RECURSE only, only empty
2297 * subdirectories are deleted as well.
2298 *
2299 * If you specify DOSHDELDIR_RECURSE | DOSHDELDIR_DELETEFILES,
2300 * this removes an entire directory tree, including all
2301 * subdirectories and files.
2302 *
2303 *@@added V0.9.4 (2000-07-01) [umoeller]
2304 */
2305
2306APIRET doshDeleteDir(const char *pcszDir,
2307 ULONG flFlags,
2308 PULONG pulDirs, // out: directories found
2309 PULONG pulFiles) // out: files found
2310{
2311 APIRET arc = NO_ERROR,
2312 arcReturn = NO_ERROR;
2313
2314 HDIR hdirFindHandle = HDIR_CREATE;
2315 FILEFINDBUF3 ffb3 = {0}; // returned from FindFirst/Next
2316 ULONG ulResultBufLen = sizeof(FILEFINDBUF3);
2317 ULONG ulFindCount = 1; // look for 1 file at a time
2318
2319 CHAR szFileMask[2*CCHMAXPATH];
2320 sprintf(szFileMask, "%s\\*", pcszDir);
2321
2322 // find files
2323 arc = DosFindFirst(szFileMask,
2324 &hdirFindHandle, // directory search handle
2325 FILE_ARCHIVED | FILE_DIRECTORY | FILE_SYSTEM
2326 | FILE_HIDDEN | FILE_READONLY,
2327 // search attributes; include all, even dirs
2328 &ffb3, // result buffer
2329 ulResultBufLen, // result buffer length
2330 &ulFindCount, // number of entries to find
2331 FIL_STANDARD); // return level 1 file info
2332
2333 if (arc == NO_ERROR)
2334 {
2335 // keep finding the next file until there are no more files
2336 while (arc == NO_ERROR) // != ERROR_NO_MORE_FILES
2337 {
2338 if (ffb3.attrFile & FILE_DIRECTORY)
2339 {
2340 // we found a directory:
2341
2342 // ignore the pseudo-directories
2343 if ( (strcmp(ffb3.achName, ".") != 0)
2344 && (strcmp(ffb3.achName, "..") != 0)
2345 )
2346 {
2347 // real directory:
2348 if (flFlags & DOSHDELDIR_RECURSE)
2349 {
2350 // recurse!
2351 CHAR szSubDir[2*CCHMAXPATH];
2352 sprintf(szSubDir, "%s\\%s", pcszDir, ffb3.achName);
2353 arcReturn = doshDeleteDir(szSubDir,
2354 flFlags,
2355 pulDirs,
2356 pulFiles);
2357 // this removes ffb3.achName as well
2358 }
2359 else
2360 {
2361 // directory, but no recursion:
2362 // report "access denied"
2363 // (this is what OS/2 reports with DosDeleteDir as well)
2364 arcReturn = ERROR_ACCESS_DENIED; // 5
2365 (*pulDirs)++;
2366 }
2367 }
2368 }
2369 else
2370 {
2371 // it's a file:
2372 arcReturn = ERROR_ACCESS_DENIED;
2373 (*pulFiles)++;
2374 }
2375
2376 if (arc == NO_ERROR)
2377 {
2378 ulFindCount = 1; // reset find count
2379 arc = DosFindNext(hdirFindHandle, // directory handle
2380 &ffb3, // result buffer
2381 ulResultBufLen, // result buffer length
2382 &ulFindCount); // number of entries to find
2383 }
2384 } // endwhile
2385
2386 DosFindClose(hdirFindHandle); // close our find handle
2387 }
2388
2389 if (arcReturn == NO_ERROR)
2390 // success so far:
2391 // delete our directory now
2392 arc = DosDeleteDir((PSZ)pcszDir);
2393
2394 return (arcReturn);
2395}
2396
2397/*
2398 *@@category: Helpers\Control program helpers\Module handling
2399 * helpers for importing functions from a module (DLL).
2400 */
2401
2402/* ******************************************************************
2403 *
2404 * Module handling helpers
2405 *
2406 ********************************************************************/
2407
2408/*
2409 *@@ doshResolveImports:
2410 * this function loads the module called pszModuleName
2411 * and resolves imports dynamically using DosQueryProcAddress.
2412 *
2413 * To specify the functions to be imported, a RESOLVEFUNCTION
2414 * array is used. In each of the array items, specify the
2415 * name of the function and a pointer to a function pointer
2416 * where to store the resolved address.
2417 *
2418 *@@added V0.9.3 (2000-04-29) [umoeller]
2419 */
2420
2421APIRET doshResolveImports(PSZ pszModuleName, // in: DLL to load
2422 HMODULE *phmod, // out: module handle
2423 PRESOLVEFUNCTION paResolves, // in/out: function resolves
2424 ULONG cResolves) // in: array item count (not array size!)
2425{
2426 CHAR szName[CCHMAXPATH];
2427 APIRET arc = DosLoadModule(szName,
2428 sizeof(szName),
2429 pszModuleName,
2430 phmod);
2431 if (arc == NO_ERROR)
2432 {
2433 ULONG ul;
2434 for (ul = 0;
2435 ul < cResolves;
2436 ul++)
2437 {
2438 arc = DosQueryProcAddr(*phmod,
2439 0, // ordinal, ignored
2440 (PSZ)paResolves[ul].pcszFunctionName,
2441 paResolves[ul].ppFuncAddress);
2442
2443 /* _Pmpf(("Resolved %s to 0x%lX, rc: %d",
2444 paResolves[ul].pcszFunctionName,
2445 *paResolves[ul].ppFuncAddress,
2446 arc)); */
2447 if (arc != NO_ERROR)
2448 break;
2449 }
2450 }
2451
2452 return (arc);
2453}
2454
2455/*
2456 *@@category: Helpers\Control program helpers\Performance (CPU load) helpers
2457 * helpers around DosPerfSysCall.
2458 */
2459
2460/* ******************************************************************
2461 *
2462 * Performance Counters (CPU Load)
2463 *
2464 ********************************************************************/
2465
2466/*
2467 *@@ doshPerfOpen:
2468 * initializes the OS/2 DosPerfSysCall API for
2469 * the calling thread.
2470 *
2471 * Note: This API is not supported on all OS/2
2472 * versions. I believe it came up with some Warp 4
2473 * fixpak. The API is resolved dynamically by
2474 * this function (using DosQueryProcAddr). Only
2475 * if NO_ERROR is returned, you may call doshPerfGet
2476 * afterwards.
2477 *
2478 * This properly initializes the internal counters
2479 * which the OS/2 kernel uses for this API. Apparently,
2480 * with newer kernels (FP13/14), IBM has chosen to no
2481 * longer do this automatically, which is the reason
2482 * why many "pulse" utilities display garbage with these
2483 * fixpaks.
2484 *
2485 * After NO_ERROR is returned, DOSHPERFSYS.cProcessors
2486 * contains the no. of processors found on the system.
2487 * All pointers in DOSHPERFSYS then point to arrays
2488 * which have exactly cProcessors array items.
2489 *
2490 * Call doshPerfClose to clean up resources allocated
2491 * by this function.
2492 *
2493 * Example code:
2494 *
2495 + PDOSHPERFSYS pPerf = NULL;
2496 + APIRET arc = doshPerfOpen(&pPerf);
2497 + if (arc == NO_ERROR)
2498 + {
2499 + // this should really be in a timer
2500 + ULONG ulCPU;
2501 + arc = doshPerfGet(&pPerf);
2502 + // go thru all CPUs
2503 + for (ulCPU = 0; ulCPU < pPerf->cProcessors; ulCPU++)
2504 + {
2505 + LONG lLoadThis = pPerf->palLoads[ulCPU];
2506 + ...
2507 + }
2508 +
2509 + ...
2510 +
2511 + // clean up
2512 + doshPerfClose(&pPerf);
2513 + }
2514 +
2515 *
2516 *@@added V0.9.7 (2000-12-02) [umoeller]
2517 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
2518 */
2519
2520APIRET doshPerfOpen(PDOSHPERFSYS *ppPerfSys) // out: new DOSHPERFSYS structure
2521{
2522 APIRET arc = NO_ERROR;
2523
2524 // allocate DOSHPERFSYS structure
2525 *ppPerfSys = (PDOSHPERFSYS)malloc(sizeof(DOSHPERFSYS));
2526 if (!*ppPerfSys)
2527 arc = ERROR_NOT_ENOUGH_MEMORY;
2528 else
2529 {
2530 // initialize structure
2531 PDOSHPERFSYS pPerfSys = *ppPerfSys;
2532 memset(pPerfSys, 0, sizeof(*pPerfSys));
2533
2534 // resolve DosPerfSysCall API entry
2535 if (!(arc = DosLoadModule(NULL, 0, "DOSCALLS", &pPerfSys->hmod)))
2536 {
2537 if (!(arc = DosQueryProcAddr(pPerfSys->hmod,
2538 976,
2539 "DosPerfSysCall",
2540 (PFN*)(&pPerfSys->pDosPerfSysCall))))
2541 {
2542 // OK, we got the API: initialize!
2543 if (!(arc = pPerfSys->pDosPerfSysCall(CMD_KI_ENABLE, 0, 0, 0)))
2544 {
2545 pPerfSys->fInitialized = TRUE;
2546 // call CMD_KI_DISABLE later
2547
2548 if (!(arc = pPerfSys->pDosPerfSysCall(CMD_PERF_INFO,
2549 0,
2550 (ULONG)(&pPerfSys->cProcessors),
2551 0)))
2552 {
2553 ULONG ul = 0,
2554 cProcs = pPerfSys->cProcessors,
2555 cbDouble = cProcs * sizeof(double),
2556 cbLong = cProcs * sizeof(LONG);
2557
2558 // allocate arrays
2559 if ( (!(pPerfSys->paCPUUtils = (PCPUUTIL)calloc(cProcs,
2560 sizeof(CPUUTIL))))
2561 || (!(pPerfSys->padBusyPrev
2562 = (double*)malloc(cbDouble)))
2563 || (!(pPerfSys->padTimePrev
2564 = (double*)malloc(cbDouble)))
2565 || (!(pPerfSys->padIntrPrev
2566 = (double*)malloc(cbDouble)))
2567 || (!(pPerfSys->palLoads
2568 = (PLONG)malloc(cbLong)))
2569 || (!(pPerfSys->palIntrs
2570 = (PLONG)malloc(cbLong)))
2571 )
2572 arc = ERROR_NOT_ENOUGH_MEMORY;
2573 else
2574 {
2575 for (ul = 0; ul < cProcs; ul++)
2576 {
2577 pPerfSys->padBusyPrev[ul] = 0.0;
2578 pPerfSys->padTimePrev[ul] = 0.0;
2579 pPerfSys->padIntrPrev[ul] = 0.0;
2580 pPerfSys->palLoads[ul] = 0;
2581 pPerfSys->palIntrs[ul] = 0;
2582 }
2583 }
2584 }
2585 }
2586 } // end if (arc == NO_ERROR)
2587 } // end if (arc == NO_ERROR)
2588
2589 if (arc)
2590 // error: clean up
2591 doshPerfClose(ppPerfSys);
2592
2593 } // end else if (!*ppPerfSys)
2594
2595 return (arc);
2596}
2597
2598/*
2599 *@@ doshPerfGet:
2600 * calculates a current snapshot of the system load,
2601 * compared with the load which was calculated on
2602 * the previous call.
2603 *
2604 * If you want to continually measure the system CPU
2605 * load, this is the function you will want to call
2606 * regularly -- e.g. with a timer once per second.
2607 *
2608 * Call this ONLY if doshPerfOpen returned NO_ERROR,
2609 * or you'll get crashes.
2610 *
2611 * If this call returns NO_ERROR, you get LONG load
2612 * values for each CPU in the system in the arrays
2613 * in DOSHPERFSYS (in per-mille, 0-1000).
2614 *
2615 * There are two arrays:
2616 *
2617 * -- DOSHPERFSYS.palLoads contains the "user" load
2618 * for each CPU.
2619 *
2620 * -- DOSHPERFSYS.palIntrs contains the "IRQ" load
2621 * for each CPU.
2622 *
2623 * Sum up the two values to get the total load for
2624 * each CPU.
2625 *
2626 * For example, if there are two CPUs, after this call,
2627 *
2628 * -- DOSHPERFSYS.palLoads[0] contains the "user" load
2629 * of the first CPU,
2630 *
2631 * -- DOSHPERFSYS.palLoads[0] contains the "user" load
2632 * of the second CPU.
2633 *
2634 * See doshPerfOpen for example code.
2635 *
2636 *@@added V0.9.7 (2000-12-02) [umoeller]
2637 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
2638 */
2639
2640APIRET doshPerfGet(PDOSHPERFSYS pPerfSys)
2641{
2642 APIRET arc = NO_ERROR;
2643 if (!pPerfSys->pDosPerfSysCall)
2644 arc = ERROR_INVALID_PARAMETER;
2645 else
2646 {
2647 if (!(arc = pPerfSys->pDosPerfSysCall(CMD_KI_RDCNT,
2648 (ULONG)pPerfSys->paCPUUtils,
2649 0, 0)))
2650 {
2651 // go thru all processors
2652 ULONG ul = 0;
2653 for (; ul < pPerfSys->cProcessors; ul++)
2654 {
2655 PCPUUTIL pCPUUtilThis = &pPerfSys->paCPUUtils[ul];
2656
2657 double dTime = LL2F(pCPUUtilThis->ulTimeHigh,
2658 pCPUUtilThis->ulTimeLow);
2659 double dBusy = LL2F(pCPUUtilThis->ulBusyHigh,
2660 pCPUUtilThis->ulBusyLow);
2661 double dIntr = LL2F(pCPUUtilThis->ulIntrHigh,
2662 pCPUUtilThis->ulIntrLow);
2663
2664 double *pdBusyPrevThis = &pPerfSys->padBusyPrev[ul];
2665 double *pdTimePrevThis = &pPerfSys->padTimePrev[ul];
2666 double *pdIntrPrevThis = &pPerfSys->padIntrPrev[ul];
2667
2668 // avoid division by zero
2669 double dTimeDelta = (dTime - *pdTimePrevThis);
2670 if (dTimeDelta)
2671 {
2672 pPerfSys->palLoads[ul]
2673 = (LONG)( (double)( (dBusy - *pdBusyPrevThis)
2674 / dTimeDelta
2675 * 1000.0
2676 )
2677 );
2678 pPerfSys->palIntrs[ul]
2679 = (LONG)( (double)( (dIntr - *pdIntrPrevThis)
2680 / dTimeDelta
2681 * 1000.0
2682 )
2683 );
2684 }
2685 else
2686 {
2687 // no clear readings are available
2688 pPerfSys->palLoads[ul] = 0;
2689 pPerfSys->palIntrs[ul] = 0;
2690 }
2691
2692 *pdTimePrevThis = dTime;
2693 *pdBusyPrevThis = dBusy;
2694 *pdIntrPrevThis = dIntr;
2695 }
2696 }
2697 }
2698
2699 return (arc);
2700}
2701
2702/*
2703 *@@ doshPerfClose:
2704 * frees all resources allocated by doshPerfOpen.
2705 *
2706 *@@added V0.9.7 (2000-12-02) [umoeller]
2707 *@@changed V0.9.9 (2001-02-06) [umoeller]: removed disable; this broke the WarpCenter
2708 *@@changed V0.9.9 (2001-03-14) [umoeller]: fixed memory leak
2709 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
2710 */
2711
2712APIRET doshPerfClose(PDOSHPERFSYS *ppPerfSys)
2713{
2714 APIRET arc = NO_ERROR;
2715 PDOSHPERFSYS pPerfSys = *ppPerfSys;
2716 if (!pPerfSys)
2717 arc = ERROR_INVALID_PARAMETER;
2718 else
2719 {
2720 // do not call this, this messes up the WarpCenter V0.9.9 (2001-02-06) [umoeller]
2721 // if (pPerfSys->fInitialized) pPerfSys->pDosPerfSysCall(CMD_KI_DISABLE, 0, 0, 0);
2722
2723 if (pPerfSys->paCPUUtils)
2724 free(pPerfSys->paCPUUtils);
2725 if (pPerfSys->padBusyPrev)
2726 free(pPerfSys->padBusyPrev);
2727 if (pPerfSys->padTimePrev)
2728 free(pPerfSys->padTimePrev);
2729 if (pPerfSys->padIntrPrev)
2730 free(pPerfSys->padIntrPrev);
2731 if (pPerfSys->palLoads) // was missing V0.9.9 (2001-03-14) [umoeller]
2732 free(pPerfSys->palLoads);
2733 if (pPerfSys->palIntrs)
2734 free(pPerfSys->palIntrs);
2735
2736 if (pPerfSys->hmod)
2737 DosFreeModule(pPerfSys->hmod);
2738 free(pPerfSys);
2739 *ppPerfSys = NULL;
2740 }
2741
2742 return (arc);
2743}
2744
2745/*
2746 *@@category: Helpers\Control program helpers\Process management
2747 * helpers for starting subprocesses.
2748 */
2749
2750/* ******************************************************************
2751 *
2752 * Process helpers
2753 *
2754 ********************************************************************/
2755
2756static PVOID // G_pvGlobalInfoSeg = NULL,
2757 G_pvLocalInfoSeg = NULL;
2758
2759USHORT _Far16 _Pascal Dos16GetInfoSeg(PSEL pselGlobal,
2760 PSEL pselLocal);
2761
2762/*
2763 * GetInfoSegs:
2764 *
2765 */
2766
2767VOID GetInfoSegs(VOID)
2768{
2769 SEL GlobalInfoSegSelector,
2770 LocalInfoSegSelector;
2771
2772 // get selectors (old 16-bit API; this gets called only once)
2773 Dos16GetInfoSeg(&GlobalInfoSegSelector,
2774 &LocalInfoSegSelector);
2775 // thunk
2776 /* G_pvGlobalInfoSeg = (PVOID)( (GlobalInfoSegSelector << 0x000D)
2777 & 0x01fff0000); */
2778 G_pvLocalInfoSeg = (PVOID)( (LocalInfoSegSelector << 0x000D)
2779 & 0x01fff0000);
2780}
2781
2782/*
2783 *@@ doshMyPID:
2784 * returns the PID of the current process.
2785 *
2786 * This uses an interesting hack which is way
2787 * faster than DosGetInfoBlocks.
2788 *
2789 *@@added V0.9.9 (2001-04-04) [umoeller]
2790 */
2791
2792ULONG doshMyPID(VOID)
2793{
2794 if (!G_pvLocalInfoSeg)
2795 // first call:
2796 GetInfoSegs();
2797
2798 // PID is at offset 0 in the local info seg
2799 return (*(PUSHORT)G_pvLocalInfoSeg);
2800}
2801
2802/*
2803 *@@ doshMyTID:
2804 * returns the TID of the current thread.
2805 *
2806 * This uses an interesting hack which is way
2807 * faster than DosGetInfoBlocks.
2808 *
2809 *@@added V0.9.9 (2001-04-04) [umoeller]
2810 */
2811
2812ULONG doshMyTID(VOID)
2813{
2814 if (!G_pvLocalInfoSeg)
2815 // first call:
2816 GetInfoSegs();
2817
2818 // TID is at offset 6 in the local info seg
2819 return (*(PUSHORT)((PBYTE)G_pvLocalInfoSeg + 6));
2820}
2821
2822/*
2823 *@@ doshExecVIO:
2824 * executes cmd.exe with the /c parameter
2825 * and pcszExecWithArgs. This is equivalent
2826 * to the C library system() function,
2827 * except that an OS/2 error code is returned
2828 * and that this works with the VAC subsystem
2829 * library.
2830 *
2831 * This uses DosExecPgm and handles the sick
2832 * argument passing.
2833 *
2834 * If NO_ERROR is returned, *plExitCode receives
2835 * the exit code of the process. If the process
2836 * was terminated abnormally, *plExitCode receives:
2837 *
2838 * -- -1: hard error halt
2839 * -- -2: 16-bit trap
2840 * -- -3: DosKillProcess
2841 * -- -4: 32-bit exception
2842 *
2843 *@@added V0.9.4 (2000-07-27) [umoeller]
2844 */
2845
2846APIRET doshExecVIO(const char *pcszExecWithArgs,
2847 PLONG plExitCode) // out: exit code (ptr can be NULL)
2848{
2849 APIRET arc = NO_ERROR;
2850
2851 if (strlen(pcszExecWithArgs) > 255)
2852 arc = ERROR_INSUFFICIENT_BUFFER;
2853 else
2854 {
2855 CHAR szObj[CCHMAXPATH];
2856 RESULTCODES res = {0};
2857 CHAR szBuffer[300];
2858
2859 // DosExecPgm expects two args in szBuffer:
2860 // -- cmd.exe\0
2861 // -- then the actual argument, terminated by two 0 bytes.
2862 memset(szBuffer, 0, sizeof(szBuffer));
2863 strcpy(szBuffer, "cmd.exe\0");
2864 sprintf(szBuffer + 8, "/c %s",
2865 pcszExecWithArgs);
2866
2867 arc = DosExecPgm(szObj, sizeof(szObj),
2868 EXEC_SYNC,
2869 szBuffer,
2870 0,
2871 &res,
2872 "cmd.exe");
2873 if ((arc == NO_ERROR) && (plExitCode))
2874 {
2875 if (res.codeTerminate == 0)
2876 // normal exit:
2877 *plExitCode = res.codeResult;
2878 else
2879 *plExitCode = -((LONG)res.codeTerminate);
2880 }
2881 }
2882
2883 return (arc);
2884}
2885
2886/*
2887 *@@ doshQuickStartSession:
2888 * this is a shortcut to DosStartSession w/out having to
2889 * deal with all the messy parameters.
2890 *
2891 * This one starts pszPath as a child session and passes
2892 * pszParams to it.
2893 *
2894 * In usPgmCtl, OR any or none of the following (V0.9.0):
2895 * -- SSF_CONTROL_NOAUTOCLOSE (0x0008): do not close automatically.
2896 * This bit is used only for VIO Windowable apps and ignored
2897 * for all other applications.
2898 * -- SSF_CONTROL_MINIMIZE (0x0004)
2899 * -- SSF_CONTROL_MAXIMIZE (0x0002)
2900 * -- SSF_CONTROL_INVISIBLE (0x0001)
2901 * -- SSF_CONTROL_VISIBLE (0x0000)
2902 *
2903 * Specifying 0 will therefore auto-close the session and make it
2904 * visible.
2905 *
2906 * If (fWait), this function will create a termination queue
2907 * and not return until the child session has ended. Be warned,
2908 * this blocks the calling thread.
2909 *
2910 * Otherwise the function will return immediately.
2911 *
2912 * The session and process IDs of the child session will be
2913 * written to *pulSID and *ppid. Of course, in "wait" mode,
2914 * these are no longer valid after this function returns.
2915 *
2916 * Returns the error code of DosStartSession.
2917 *
2918 * Note: According to CPREF, calling DosStartSession calls
2919 * DosExecPgm in turn for all full-screen, VIO, and PM sessions.
2920 *
2921 *@@changed V0.9.0 [umoeller]: prototype changed to include usPgmCtl
2922 *@@changed V0.9.1 (99-12-30) [umoeller]: queue was sometimes not closed. Fixed.
2923 *@@changed V0.9.3 (2000-05-03) [umoeller]: added fForeground
2924 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed potential queue leak
2925 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed memory leak in wait mode; added pusReturn to prototype
2926 */
2927
2928APIRET doshQuickStartSession(const char *pcszPath, // in: program to start
2929 const char *pcszParams, // in: parameters for program
2930 BOOL fForeground, // in: if TRUE, session will be in foreground
2931 USHORT usPgmCtl, // in: STARTDATA.PgmControl
2932 BOOL fWait, // in: wait for termination?
2933 PULONG pulSID, // out: session ID (req.)
2934 PPID ppid, // out: process ID (req.)
2935 PUSHORT pusReturn) // out: in wait mode, session's return code (ptr can be NULL)
2936{
2937 APIRET arc = NO_ERROR;
2938 // queue stuff
2939 const char *pcszQueueName = "\\queues\\xwphlpsw.que";
2940 HQUEUE hq = 0;
2941 PID qpid = 0;
2942
2943 if (fWait)
2944 {
2945 if (!(arc = DosCreateQueue(&hq,
2946 QUE_FIFO | QUE_CONVERT_ADDRESS,
2947 (PSZ)pcszQueueName)))
2948 arc = DosOpenQueue(&qpid, &hq, (PSZ)pcszQueueName);
2949 }
2950
2951 if (!arc) // V0.9.14 (2001-08-03) [umoeller]
2952 {
2953 STARTDATA SData;
2954 CHAR szObjBuf[CCHMAXPATH];
2955
2956 SData.Length = sizeof(STARTDATA);
2957 SData.Related = SSF_RELATED_CHILD; //INDEPENDENT;
2958 SData.FgBg = (fForeground) ? SSF_FGBG_FORE : SSF_FGBG_BACK;
2959 // V0.9.3 (2000-05-03) [umoeller]
2960 SData.TraceOpt = SSF_TRACEOPT_NONE;
2961
2962 SData.PgmTitle = (PSZ)pcszPath; // title for window
2963 SData.PgmName = (PSZ)pcszPath;
2964 SData.PgmInputs = (PSZ)pcszParams;
2965
2966 SData.TermQ = (fWait) ? (PSZ)pcszQueueName : NULL;
2967 SData.Environment = 0;
2968 SData.InheritOpt = SSF_INHERTOPT_PARENT;
2969 SData.SessionType = SSF_TYPE_DEFAULT;
2970 SData.IconFile = 0;
2971 SData.PgmHandle = 0;
2972
2973 SData.PgmControl = usPgmCtl;
2974
2975 SData.InitXPos = 30;
2976 SData.InitYPos = 40;
2977 SData.InitXSize = 200;
2978 SData.InitYSize = 140;
2979 SData.Reserved = 0;
2980 SData.ObjectBuffer = szObjBuf;
2981 SData.ObjectBuffLen = (ULONG)sizeof(szObjBuf);
2982
2983 if ( (!(arc = DosStartSession(&SData, pulSID, ppid)))
2984 && (fWait)
2985 )
2986 {
2987 // block on the termination queue, which is written
2988 // to when the subprocess ends
2989 REQUESTDATA rqdata;
2990 ULONG cbData = 0;
2991 PULONG pulData = NULL;
2992 BYTE elpri;
2993
2994 rqdata.pid = qpid;
2995 if (!(arc = DosReadQueue(hq, // in: queue handle
2996 &rqdata, // out: pid and ulData
2997 &cbData, // out: size of data returned
2998 (PVOID*)&pulData, // out: data returned
2999 0, // in: remove first element in queue
3000 0, // in: wait for queue data (block thread)
3001 &elpri, // out: element's priority
3002 0))) // in: event semaphore to be posted
3003 {
3004 if (!rqdata.ulData)
3005 {
3006 // child session ended:
3007 // V0.9.14 (2001-08-03) [umoeller]
3008
3009 // *pulSID = (*pulData) & 0xffff;
3010 if (pusReturn)
3011 *pusReturn = ((*pulData) >> 16) & 0xffff;
3012
3013 }
3014 // else: continue looping
3015
3016 if (pulData)
3017 DosFreeMem(pulData);
3018 }
3019 }
3020 }
3021
3022 if (hq)
3023 DosCloseQueue(hq);
3024
3025 return (arc);
3026}
3027
3028
Note: See TracBrowser for help on using the repository browser.