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

Last change on this file since 142 was 142, 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: 137.1 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_DOSEXCEPTIONS
52#define INCL_DOSSESMGR
53#define INCL_DOSQUEUES
54#define INCL_DOSSEMAPHORES
55#define INCL_DOSMISC
56#define INCL_DOSDEVICES
57#define INCL_DOSDEVIOCTL
58#define INCL_DOSERRORS
59
60#define INCL_KBD
61#include <os2.h>
62
63#include <stdlib.h>
64#include <string.h>
65#include <stdio.h>
66#include <stdarg.h>
67#include <ctype.h>
68
69#include "setup.h" // code generation and debugging options
70
71#include "helpers\dosh.h"
72#include "helpers\standards.h"
73
74#pragma hdrstop
75
76// static const CHAR G_acDriveLetters[28] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
77
78/*
79 *@@category: Helpers\Control program helpers\Wrappers
80 */
81
82/* ******************************************************************
83 *
84 * Wrappers
85 *
86 ********************************************************************/
87
88#ifdef DOSH_STANDARDWRAPPERS
89
90 /*
91 *@@ doshSleep:
92 *
93 *@@added V0.9.16 (2002-01-26) [umoeller]
94 */
95
96 APIRET doshSleep(ULONG msec)
97 {
98 // put the call in brackets so the macro won't apply here
99 return (DosSleep)(msec);
100 }
101
102 /*
103 *@@ doshCreateMutexSem:
104 *
105 *@@added V0.9.16 (2002-01-26) [umoeller]
106 */
107
108 APIRET doshCreateMutexSem(PSZ pszName,
109 PHMTX phmtx,
110 ULONG flAttr,
111 BOOL32 fState)
112 {
113 // put the call in brackets so the macro won't apply here
114 return (DosCreateMutexSem)(pszName, phmtx, flAttr, fState);
115 }
116
117 /*
118 *@@ doshRequestMutexSem:
119 *
120 *@@added V0.9.16 (2002-01-26) [umoeller]
121 */
122
123 APIRET doshRequestMutexSem(HMTX hmtx, ULONG ulTimeout)
124 {
125 return (DosRequestMutexSem)(hmtx, ulTimeout);
126 }
127
128 /*
129 *@@ doshReleaseMutexSem:
130 *
131 *@@added V0.9.16 (2002-01-26) [umoeller]
132 */
133
134 APIRET doshReleaseMutexSem(HMTX hmtx)
135 {
136 return (DosReleaseMutexSem)(hmtx);
137 }
138
139 /*
140 *@@ doshSetExceptionHandler:
141 *
142 *@@added V0.9.16 (2002-01-26) [umoeller]
143 */
144
145 APIRET doshSetExceptionHandler(PEXCEPTIONREGISTRATIONRECORD pERegRec)
146 {
147 // put the call in brackets so the macro won't apply here
148 return (DosSetExceptionHandler)(pERegRec);
149 }
150
151 /*
152 *@@ doshUnsetExceptionHandler:
153 *
154 *@@added V0.9.16 (2002-01-26) [umoeller]
155 */
156
157 APIRET doshUnsetExceptionHandler(PEXCEPTIONREGISTRATIONRECORD pERegRec)
158 {
159 // put the call in brackets so the macro won't apply here
160 return (DosUnsetExceptionHandler)(pERegRec);
161 }
162
163#endif
164
165/*
166 *@@category: Helpers\Control program helpers\Miscellaneous
167 * Miscellaneous helpers in dosh.c that didn't fit into any other
168 * category.
169 */
170
171/* ******************************************************************
172 *
173 * Miscellaneous
174 *
175 ********************************************************************/
176
177/*
178 *@@ doshGetChar:
179 * reads a single character from the keyboard.
180 * Useful for VIO sessions, since there's great
181 * confusion between the various C dialects about
182 * how to use getc(), and getc() doesn't work
183 * with the VAC subsystem library.
184 *
185 *@@added V0.9.4 (2000-07-27) [umoeller]
186 */
187
188CHAR doshGetChar(VOID)
189{
190 // CHAR c;
191 // ULONG ulRead = 0;
192
193 KBDKEYINFO kki;
194 KbdCharIn(&kki,
195 0, // wait
196 0);
197
198 return (kki.chChar);
199}
200
201/*
202 *@@ doshQueryShiftState:
203 * returns TRUE if any of the SHIFT keys are
204 * currently pressed. Useful for checks during
205 * WM_COMMAND messages from menus.
206 *
207 *@@changed V0.9.5 (2000-09-27) [umoeller]: added error checking
208 */
209
210BOOL doshQueryShiftState(VOID)
211{
212 BOOL brc = FALSE;
213 APIRET arc = NO_ERROR;
214 HFILE hfKbd;
215 ULONG ulAction;
216
217 if (!(arc = DosOpen("KBD$", &hfKbd, &ulAction, 0,
218 FILE_NORMAL,
219 FILE_OPEN,
220 OPEN_ACCESS_READONLY | OPEN_SHARE_DENYWRITE,
221 (PEAOP2)NULL)))
222 {
223 SHIFTSTATE ShiftState;
224 ULONG cbDataLen = sizeof(ShiftState);
225
226 if (!(arc = DosDevIOCtl(hfKbd, IOCTL_KEYBOARD, KBD_GETSHIFTSTATE,
227 NULL, 0, NULL, // no parameters
228 &ShiftState, cbDataLen, &cbDataLen)))
229 brc = ((ShiftState.fsState & 3) != 0);
230
231 DosClose(hfKbd);
232 }
233
234 return brc;
235}
236
237/*
238 *@@ doshIsWarp4:
239 * checks the OS/2 system version number.
240 *
241 * Returns:
242 *
243 * -- 0 (FALSE): OS/2 2.x or Warp 3 is running.
244 *
245 * -- 1: Warp 4.0 is running.
246 *
247 * -- 2: Warp 4.5 is running (WSeB or Warp 4 FP 13+ or eCS
248 * or ACP/MCP), or even something newer.
249 *
250 *@@changed V0.9.2 (2000-03-05) [umoeller]: reported TRUE on Warp 3 also; fixed
251 *@@changed V0.9.6 (2000-10-16) [umoeller]: patched for speed
252 *@@changed V0.9.9 (2001-04-04) [umoeller]: now returning 2 for Warp 4.5 and above
253 */
254
255ULONG doshIsWarp4(VOID)
256{
257 static BOOL s_fQueried = FALSE;
258 static ULONG s_ulrc = 0;
259
260 if (!s_fQueried)
261 {
262 // first call:
263 ULONG aulBuf[3];
264
265 DosQuerySysInfo(QSV_VERSION_MAJOR, // 11
266 QSV_VERSION_MINOR, // 12
267 &aulBuf, sizeof(aulBuf));
268 // Warp 3 is reported as 20.30
269 // Warp 4 is reported as 20.40
270 // Aurora is reported as 20.45
271
272 if ( (aulBuf[0] > 20) // major > 20; not the case with Warp 3, 4, 5
273 || ( (aulBuf[0] == 20) // major == 20 and minor >= 45
274 && (aulBuf[1] >= 45)
275 )
276 )
277 // Warp 4.5 or newer:
278 s_ulrc = 2;
279 else if ( (aulBuf[0] == 20) // major == 20 and minor == 40
280 && (aulBuf[1] == 40)
281 )
282 // Warp 4:
283 s_ulrc = 1;
284
285 s_fQueried = TRUE;
286 }
287
288 return (s_ulrc);
289}
290
291/*
292 *@@ doshQuerySysErrorMsg:
293 * this retrieves the error message for a system error
294 * (APIRET) from the system error message file (OSO001.MSG).
295 * This file better be on the DPATH (it normally is).
296 *
297 * This returns the string in the "SYSxxx: blahblah" style,
298 * which is normally displayed on the command line when
299 * errors occur.
300 *
301 * The error message is returned in a newly allocated
302 * buffer, which should be free()'d afterwards.
303 *
304 * Returns NULL upon errors.
305 */
306
307PSZ doshQuerySysErrorMsg(APIRET arc) // in: DOS error code
308{
309 PSZ pszReturn = 0;
310 CHAR szDosError[1000];
311 ULONG cbDosError = 0;
312 DosGetMessage(NULL, 0, // no string replacements
313 szDosError, sizeof(szDosError),
314 arc,
315 "OSO001.MSG", // default OS/2 message file
316 &cbDosError);
317 if (cbDosError > 2)
318 {
319 szDosError[cbDosError - 2] = 0;
320 pszReturn = strdup(szDosError);
321 }
322 return (pszReturn);
323}
324
325/*
326 *@@ doshQuerySysUptime:
327 * returns the system uptime in milliseconds.
328 * This can be used for time comparisons.
329 *
330 *@@added V0.9.12 (2001-05-18) [umoeller]
331 */
332
333ULONG doshQuerySysUptime(VOID)
334{
335 ULONG ulms;
336 DosQuerySysInfo(QSV_MS_COUNT,
337 QSV_MS_COUNT,
338 &ulms,
339 sizeof(ulms));
340 return (ulms);
341}
342
343/*
344 *@@ doshDevIOCtl:
345 *
346 * Works with those IOCtls where the buffer
347 * size parameters are always the same anyway,
348 * which applies to all IOCtls I have seen
349 * so far.
350 *
351 *@@added V0.9.13 (2001-06-14) [umoeller]
352 */
353
354APIRET doshDevIOCtl(HFILE hf,
355 ULONG ulCategory,
356 ULONG ulFunction,
357 PVOID pvParams,
358 ULONG cbParams,
359 PVOID pvData,
360 ULONG cbData)
361{
362 return (DosDevIOCtl(hf,
363 ulCategory,
364 ulFunction,
365 pvParams, cbParams, &cbParams,
366 pvData, cbData, &cbData));
367}
368
369/*
370 *@@category: Helpers\Control program helpers\Shared memory management
371 * helpers for allocating and requesting shared memory.
372 */
373
374/* ******************************************************************
375 *
376 * Memory helpers
377 *
378 ********************************************************************/
379
380/*
381 *@@ doshMalloc:
382 * wrapper around malloc() which automatically
383 * sets ERROR_NOT_ENOUGH_MEMORY.
384 *
385 *@@added V0.9.16 (2001-10-19) [umoeller]
386 */
387
388PVOID doshMalloc(ULONG cb,
389 APIRET *parc)
390{
391 PVOID pv;
392 *parc = NO_ERROR;
393 if (!(pv = malloc(cb)))
394 *parc = ERROR_NOT_ENOUGH_MEMORY;
395
396 return (pv);
397}
398
399/*
400 *@@ doshAllocArray:
401 * allocates c * cbArrayItem bytes.
402 * Similar to calloc(), but returns
403 * error codes:
404 *
405 * -- NO_ERROR: *ppv and *pcbAllocated were set.
406 *
407 * -- ERROR_NO_DATA: either c or cbArrayItem are
408 * zero.
409 *
410 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
411 *
412 *@@added V0.9.16 (2001-12-08) [umoeller]
413 */
414
415APIRET doshAllocArray(ULONG c, // in: array item count
416 ULONG cbArrayItem, // in: size of one array item
417 PBYTE *ppv, // out: memory ptr if NO_ERROR is returned
418 PULONG pcbAllocated) // out: # of bytes allocated
419{
420 if (!c || !cbArrayItem)
421 return ERROR_NO_DATA;
422
423 *pcbAllocated = c * cbArrayItem;
424 if (!(*ppv = (PBYTE)malloc(*pcbAllocated)))
425 return ERROR_NOT_ENOUGH_MEMORY;
426
427 return NO_ERROR;
428}
429
430/*
431 *@@ doshAllocSharedMem:
432 * wrapper for DosAllocSharedMem which has
433 * a malloc()-like syntax. Just due to my
434 * lazyness.
435 *
436 * Note that ulSize is always rounded up to the
437 * next 4KB value, so don't use this hundreds of times.
438 *
439 * Returns NULL upon errors. Possible errors include
440 * that a memory block calle pcszName has already been
441 * allocated.
442 *
443 * Use DosFreeMem(pvrc) to free the memory. The memory
444 * will only be freed if no other process has requested
445 * access.
446 *
447 *@@added V0.9.3 (2000-04-18) [umoeller]
448 */
449
450PVOID doshAllocSharedMem(ULONG ulSize, // in: requested mem block size (rounded up to 4KB)
451 const char* pcszName) // in: name of block ("\\SHAREMEM\\xxx") or NULL
452{
453 PVOID pvrc = NULL;
454 APIRET arc = DosAllocSharedMem((PVOID*)&pvrc,
455 (PSZ)pcszName,
456 ulSize,
457 PAG_COMMIT | PAG_READ | PAG_WRITE);
458 if (arc == NO_ERROR)
459 return (pvrc);
460
461 return (NULL);
462}
463
464/*
465 *@@ doshRequestSharedMem:
466 * requests access to a block of named shared memory
467 * allocated by doshAllocSharedMem.
468 *
469 * Returns NULL upon errors.
470 *
471 * Use DosFreeMem(pvrc) to free the memory. The memory
472 * will only be freed if no other process has requested
473 * access.
474 *
475 *@@added V0.9.3 (2000-04-19) [umoeller]
476 */
477
478PVOID doshRequestSharedMem(PCSZ pcszName)
479{
480 PVOID pvrc = NULL;
481 APIRET arc = DosGetNamedSharedMem((PVOID*)pvrc,
482 (PSZ)pcszName,
483 PAG_READ | PAG_WRITE);
484 if (arc == NO_ERROR)
485 return (pvrc);
486
487 return (NULL);
488}
489
490/*
491 *@@category: Helpers\Control program helpers\Drive management
492 * functions for managing drives... enumerating, testing,
493 * querying etc.
494 */
495
496/* ******************************************************************
497 *
498 * Drive helpers
499 *
500 ********************************************************************/
501
502/*
503 *@@ doshIsFixedDisk:
504 * checks whether a disk is fixed or removeable.
505 * ulLogicalDrive must be 1 for drive A:, 2 for B:, ...
506 * The result is stored in *pfFixed.
507 * Returns DOS error code.
508 *
509 * From my testing, this function does _not_ provoke
510 * "drive not ready" popups, even if the disk is not
511 * ready.
512 *
513 * Warning: This uses DosDevIOCtl, which has proved
514 * to cause problems with some device drivers for
515 * removeable disks.
516 *
517 * Returns:
518 *
519 * -- NO_ERROR: *pfFixed was set.
520 *
521 * -- ERROR_INVALID_DRIVE: drive letter invalid
522 *
523 * -- ERROR_NOT_SUPPORTED (50): for network drives.
524 *
525 *@@changed V0.9.14 (2001-08-03) [umoeller]: added extra fix for A: and B:
526 */
527
528APIRET doshIsFixedDisk(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
529 PBOOL pfFixed) // out: TRUE for fixed disks
530{
531 APIRET arc = ERROR_INVALID_DRIVE;
532
533 if ( (ulLogicalDrive == 1)
534 || (ulLogicalDrive == 2)
535 )
536 {
537 // drive A: and B: can never be fixed V0.9.14 (2001-08-03) [umoeller]
538 *pfFixed = FALSE;
539 return NO_ERROR;
540 }
541
542 if (ulLogicalDrive)
543 {
544 // parameter packet
545 #pragma pack(1)
546 struct {
547 UCHAR command,
548 drive;
549 } parms;
550 #pragma pack()
551
552 // data packet
553 UCHAR ucNonRemoveable;
554
555 parms.drive = (UCHAR)(ulLogicalDrive - 1);
556 if (!(arc = doshDevIOCtl((HFILE)-1,
557 IOCTL_DISK, // 0x08
558 DSK_BLOCKREMOVABLE, // 0x20
559 &parms, sizeof(parms),
560 &ucNonRemoveable, sizeof(ucNonRemoveable))))
561 *pfFixed = (BOOL)ucNonRemoveable;
562 }
563
564 return (arc);
565}
566
567/*
568 *@@ doshQueryDiskParams:
569 * this retrieves more information about a given drive,
570 * which is stored in the specified BIOSPARAMETERBLOCK
571 * structure.
572 *
573 * BIOSPARAMETERBLOCK is defined in the Toolkit headers,
574 * and from my testing, it's the same with the Toolkits
575 * 3 and 4.5.
576 *
577 * If NO_ERROR is returned, the bDeviceType field can
578 * be one of the following (according to CPREF):
579 *
580 * -- 0: 48 TPI low-density diskette drive
581 * -- 1: 96 TPI high-density diskette drive
582 * -- 2: 3.5-inch 720KB diskette drive
583 * -- 3: 8-Inch single-density diskette drive
584 * -- 4: 8-Inch double-density diskette drive
585 * -- 5: Fixed disk
586 * -- 6: Tape drive
587 * -- 7: Other (includes 1.44MB 3.5-inch diskette drive)
588 * -- 8: R/W optical disk
589 * -- 9: 3.5-inch 4.0MB diskette drive (2.88MB formatted)
590 *
591 * From my testing, this function does _not_ provoke
592 * "drive not ready" popups, even if the disk is not
593 * ready.
594 *
595 * Warning: This uses DosDevIOCtl, which has proved
596 * to cause problems with some device drivers for
597 * removeable disks.
598 *
599 * This returns the DOS error code of DosDevIOCtl.
600 * This will be:
601 *
602 * -- NO_ERROR for all local disks;
603 *
604 * -- ERROR_NOT_SUPPORTED (50) for network drives.
605 *
606 *@@added V0.9.0 [umoeller]
607 *@@changed V0.9.13 (2001-06-14) [umoeller]: changed prototype to use BIOSPARAMETERBLOCK directly
608 *@@changed V0.9.13 (2001-06-14) [umoeller]: now querying standard media, no redetermine
609 */
610
611APIRET doshQueryDiskParams(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
612 PBIOSPARAMETERBLOCK pdp) // out: drive parameters
613{
614 APIRET arc = ERROR_INVALID_DRIVE;
615
616 if (ulLogicalDrive)
617 {
618 #pragma pack(1)
619 // parameter packet
620 struct {
621 UCHAR ucCommand,
622 ucDrive;
623 } parms;
624 #pragma pack()
625
626 parms.ucCommand = 0; // 0 = return standard media,
627 // 1 = read currently inserted media
628 // (1 doesn't work any more, returns arc 87
629 // V0.9.13 (2001-06-14) [umoeller])
630 parms.ucDrive=(UCHAR)(ulLogicalDrive-1);
631
632 // zero the structure V0.9.13 (2001-06-14) [umoeller]
633 memset(pdp, 0, sizeof(BIOSPARAMETERBLOCK));
634
635 arc = doshDevIOCtl((HFILE)-1,
636 IOCTL_DISK, // 0x08
637 DSK_GETDEVICEPARAMS, // 0x63
638 &parms, sizeof(parms),
639 pdp, sizeof(BIOSPARAMETERBLOCK));
640
641 /* if (!arc)
642 {
643 _Pmpf((" bDeviceType: %d", pdp->bDeviceType));
644 _Pmpf((" bytes per sector: %d", pdp->usBytesPerSector));
645 _Pmpf((" sectors per track: %d", pdp->usSectorsPerTrack));
646 } */
647 }
648
649 return (arc);
650}
651
652/*
653 *@@ doshQueryDriveType:
654 * tests the specified BIOSPARAMETERBLOCK
655 * for whether it represents a CD-ROM or
656 * some other removeable drive type.
657 *
658 * Returns one of:
659 *
660 * -- DRVTYPE_HARDDISK (0)
661 *
662 * -- DRVTYPE_PARTITIONABLEREMOVEABLE
663 *
664 * -- DRVTYPE_CDROM
665 *
666 * -- DRVTYPE_TAPE
667 *
668 * -- DRVTYPE_VDISK
669 *
670 * -- DRVTYPE_FLOPPY
671 *
672 * -- DRVTYPE_UNKNOWN (255)
673 *
674 * The BIOSPARAMETERBLOCK must be filled
675 * first using doshQueryDiskParams.
676 *
677 *@@added V0.9.16 (2002-01-13) [umoeller]
678 */
679
680BYTE doshQueryDriveType(ULONG ulLogicalDrive,
681 PBIOSPARAMETERBLOCK pdp,
682 BOOL fFixed)
683{
684 if (pdp)
685 {
686 if (pdp->fsDeviceAttr & DEVATTR_PARTITIONALREMOVEABLE) // 0x08
687 return DRVTYPE_PARTITIONABLEREMOVEABLE;
688 else if (fFixed)
689 return DRVTYPE_HARDDISK;
690 else if ( (pdp->bDeviceType == 7) // "other"
691 && (pdp->usBytesPerSector == 2048)
692 && (pdp->usSectorsPerTrack == (USHORT)-1)
693 )
694 return DRVTYPE_CDROM;
695 else switch (pdp->bDeviceType)
696 {
697 case DEVTYPE_TAPE: // 6
698 return DRVTYPE_TAPE;
699
700 case DEVTYPE_48TPI: // 0, 360k 5.25" floppy
701 case DEVTYPE_96TPI: // 1, 1.2M 5.25" floppy
702 case DEVTYPE_35: // 2, 720k 3.5" floppy
703 case DEVTYPE_OTHER: // 7, 1.44 3.5" floppy
704 // 1.84M 3.5" floppy
705 case DEVTYPE_35_288MB:
706 if ( (ulLogicalDrive == 1)
707 || (ulLogicalDrive == 2)
708 )
709 return DRVTYPE_FLOPPY;
710 else
711 return DRVTYPE_VDISK;
712
713 case DEVTYPE_RWOPTICAL: // 8, what is this?!?
714 return DRVTYPE_FLOPPY;
715 }
716 }
717
718 return (DRVTYPE_UNKNOWN);
719}
720
721/*
722 *@@ doshHasAudioCD:
723 * sets *pfAudio to whether ulLogicalDrive
724 * currently has an audio CD inserted.
725 *
726 * Better call this only if you're sure that
727 * ulLogicalDrive is a CD-ROM drive. Use
728 * doshQueryRemoveableType to check.
729 *
730 *@@added V0.9.14 (2001-08-01) [umoeller]
731 */
732
733APIRET doshHasAudioCD(ULONG ulLogicalDrive,
734 HFILE hfDrive, // in: DASD open
735 BOOL fMixedModeCD,
736 PBOOL pfAudio)
737{
738 APIRET arc = NO_ERROR;
739
740 ULONG ulAudioTracks = 0,
741 ulDataTracks = 0;
742
743 CHAR cds1[4] = { 'C', 'D', '0', '1' };
744 CHAR cds2[4];
745
746 *pfAudio = FALSE;
747
748 // check for proper driver signature
749 if (!(arc = doshDevIOCtl(hfDrive,
750 IOCTL_CDROMDISK,
751 CDROMDISK_GETDRIVER,
752 &cds1, sizeof(cds1),
753 &cds2, sizeof(cds2))))
754 {
755 if (memcmp(&cds1, &cds2, 4))
756 // this is not a CD-ROM then:
757 arc = NO_ERROR;
758 else
759 {
760 struct {
761 UCHAR ucFirstTrack,
762 ucLastTrack;
763 ULONG ulLeadOut;
764 } cdat;
765
766 // get track count
767 if (!(arc = doshDevIOCtl(hfDrive,
768 IOCTL_CDROMAUDIO,
769 CDROMAUDIO_GETAUDIODISK,
770 &cds1, sizeof(cds1),
771 &cdat, sizeof(cdat))))
772 {
773 // still no error: build the audio TOC
774 ULONG i;
775 for (i = cdat.ucFirstTrack;
776 i <= cdat.ucLastTrack;
777 i++)
778 {
779 BYTE cdtp[5] =
780 { 'C', 'D', '0', '1', (UCHAR)i };
781
782 struct {
783 ULONG ulTrackAddress;
784 BYTE bFlags;
785 } trackdata;
786
787 if (!(arc = doshDevIOCtl(hfDrive,
788 IOCTL_CDROMAUDIO,
789 CDROMAUDIO_GETAUDIOTRACK,
790 &cdtp, sizeof(cdtp),
791 &trackdata, sizeof(trackdata))))
792 {
793 if (trackdata.bFlags & 64)
794 ulDataTracks++;
795 else
796 {
797 ulAudioTracks++;
798
799 if (!fMixedModeCD)
800 {
801 // caller doesn't want mixed mode:
802 // stop here
803 ulDataTracks = 0;
804 break;
805 }
806 }
807 }
808 }
809
810 // _Pmpf((" got %d audio, %d data tracks",
811 // ulAudioTracks, ulDataTracks));
812
813 if (!ulDataTracks)
814 *pfAudio = TRUE;
815 }
816 else
817 {
818 // not audio disk:
819 // go on then
820 // _Pmpf((" CDROMAUDIO_GETAUDIODISK returned %d", arc));
821 arc = NO_ERROR;
822 }
823 }
824 }
825 else
826 {
827 // not CD-ROM: go on then
828 // _Pmpf((" CDROMDISK_GETDRIVER returned %d", arc));
829 arc = NO_ERROR;
830 }
831
832 return (arc);
833}
834
835/*
836 *@@ doshEnumDrives:
837 * this function enumerates all valid drive letters on
838 * the system by composing a string of drive letters
839 * in the buffer pointed to by pszBuffer, which should
840 * be 27 characters in size to hold information for
841 * all drives. The buffer will be null-terminated.
842 *
843 * If (pcszFileSystem != NULL), only drives matching
844 * the specified file system type (e.g. "HPFS") will
845 * be enumerated. If (pcszFileSystem == NULL), all
846 * drives will be enumerated.
847 *
848 * If (fSkipRemovables == TRUE), removeable drives will
849 * be skipped. This applies to floppy, CD-ROM, and
850 * virtual floppy drives. This will start the search
851 * at drive letter C: so that drives A: and B: will
852 * never be checked (to avoid the hardware bumps).
853 *
854 * Otherwise, the search starts at drive A:. Still,
855 * removeable drives will only be added if valid media
856 * is inserted.
857 *
858 *@@changed V0.9.4 (2000-07-03) [umoeller]: this stopped at the first invalid drive letter; fixed
859 *@@changed V0.9.4 (2000-07-03) [umoeller]: added fSkipRemoveables
860 */
861
862VOID doshEnumDrives(PSZ pszBuffer, // out: drive letters
863 PCSZ pcszFileSystem, // in: FS's to match or NULL
864 BOOL fSkipRemoveables) // in: if TRUE, only non-removeable disks will be returned
865{
866 CHAR szName[5] = "";
867 ULONG ulLogicalDrive = 1, // start with drive A:
868 ulFound = 0; // found drives count
869 APIRET arc = NO_ERROR; // return code
870
871 if (fSkipRemoveables)
872 // start with drive C:
873 ulLogicalDrive = 3;
874
875 // go thru the drives, start with C: (== 3), stop after Z: (== 26)
876 while (ulLogicalDrive <= 26)
877 {
878 #pragma pack(1)
879 struct
880 {
881 UCHAR dummy,drive;
882 } parms;
883 #pragma pack()
884
885 // data packet
886 UCHAR nonRemovable=0;
887
888 parms.drive=(UCHAR)(ulLogicalDrive-1);
889 arc = doshDevIOCtl((HFILE)-1,
890 IOCTL_DISK,
891 DSK_BLOCKREMOVABLE,
892 &parms, sizeof(parms),
893 &nonRemovable, sizeof(nonRemovable));
894
895 if ( // fixed disk and non-removeable
896 ((arc == NO_ERROR) && (nonRemovable))
897 // or network drive:
898 || (arc == ERROR_NOT_SUPPORTED)
899 )
900 {
901 ULONG ulOrdinal = 0; // ordinal of entry in name list
902 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
903 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
904 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
905
906 szName[0] = ulLogicalDrive + 'A' - 1;
907 szName[1] = ':';
908 szName[2] = '\0';
909
910 arc = DosQueryFSAttach(szName, // logical drive of attached FS
911 ulOrdinal, // ignored for FSAIL_QUERYNAME
912 FSAIL_QUERYNAME, // return data for a Drive or Device
913 pfsqBuffer, // returned data
914 &cbBuffer); // returned data length
915
916 if (arc == NO_ERROR)
917 {
918 // The data for the last three fields in the FSQBUFFER2
919 // structure are stored at the offset of fsqBuffer.szName.
920 // Each data field following fsqBuffer.szName begins
921 // immediately after the previous item.
922 CHAR* pszFSDName = (PSZ)&(pfsqBuffer->szName) + (pfsqBuffer->cbName) + 1;
923 if (pcszFileSystem == NULL)
924 {
925 // enum-all mode: always copy
926 pszBuffer[ulFound] = szName[0]; // drive letter
927 ulFound++;
928 }
929 else if (strcmp(pszFSDName, pcszFileSystem) == 0)
930 {
931 pszBuffer[ulFound] = szName[0]; // drive letter
932 ulFound++;
933 }
934 }
935 }
936
937 ulLogicalDrive++;
938 } // end while (G_acDriveLetters[ulLogicalDrive] <= 'Z')
939
940 pszBuffer[ulFound] = '\0';
941}
942
943/*
944 *@@ doshQueryBootDrive:
945 * returns the letter of the boot drive as a
946 * single (capital) character, which is useful for
947 * constructing file names using sprintf and such.
948 *
949 *@@changed V0.9.16 (2002-01-13) [umoeller]: optimized
950 */
951
952CHAR doshQueryBootDrive(VOID)
953{
954 // this can never change, so query this only once
955 // V0.9.16 (2002-01-13) [umoeller]
956 static CHAR cBootDrive = '\0';
957
958 if (!cBootDrive)
959 {
960 ULONG ulBootDrive;
961 DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
962 &ulBootDrive,
963 sizeof(ulBootDrive));
964 cBootDrive = (CHAR)ulBootDrive + 'A' - 1;
965 }
966
967 return (cBootDrive);
968}
969
970/*
971 *@@ doshQueryMedia:
972 * determines whether the given drive currently
973 * has media inserted.
974 *
975 * Call this only for non-fixed (removable) disks.
976 * Use doshIsFixedDisk to find out.
977 *
978 *@@added V0.9.16 (2002-01-13) [umoeller]
979 */
980
981APIRET doshQueryMedia(ULONG ulLogicalDrive,
982 BOOL fCDROM, // in: is drive CD-ROM?
983 ULONG fl) // in: DRVFL_* flags
984{
985 APIRET arc;
986
987 HFILE hf = NULLHANDLE;
988 ULONG dummy;
989
990 CHAR szDrive[3] = "C:";
991 szDrive[0] = 'A' + ulLogicalDrive - 1;
992
993 arc = DosOpen(szDrive, // "C:", "D:", ...
994 &hf,
995 &dummy,
996 0,
997 FILE_NORMAL,
998 OPEN_ACTION_FAIL_IF_NEW
999 | OPEN_ACTION_OPEN_IF_EXISTS,
1000 OPEN_FLAGS_DASD
1001 | OPEN_FLAGS_FAIL_ON_ERROR
1002 | OPEN_FLAGS_NOINHERIT // V0.9.6 (2000-11-25) [pr]
1003 // | OPEN_ACCESS_READONLY // V0.9.13 (2001-06-14) [umoeller]
1004 | OPEN_SHARE_DENYNONE,
1005 NULL);
1006
1007 // this still returns NO_ERROR for audio CDs in a
1008 // CD-ROM drive...
1009 // however, the WPS then attempts to read in the
1010 // root directory for audio CDs, which produces
1011 // a "sector not found" error box...
1012
1013 if ( (!arc)
1014 && (hf)
1015 && (fCDROM)
1016 )
1017 {
1018 BOOL fAudio;
1019 if ( (!(arc = doshHasAudioCD(ulLogicalDrive,
1020 hf,
1021 ((fl & DRVFL_MIXEDMODECD) != 0),
1022 &fAudio)))
1023 && (fAudio)
1024 )
1025 arc = ERROR_AUDIO_CD_ROM; // special private error code (10000)
1026 }
1027
1028 if (hf)
1029 DosClose(hf);
1030
1031 return (arc);
1032}
1033
1034/*
1035 *@@ doshAssertDrive:
1036 * this checks for whether the given drive
1037 * is currently available without provoking
1038 * those ugly white "Drive not ready" popups.
1039 *
1040 * "fl" can specify additional flags for testing
1041 * and can be any combination of:
1042 *
1043 * -- DRVFL_MIXEDMODECD: whether to allow
1044 * mixed-mode CD-ROMs. See error codes below.
1045 *
1046 * This returns (from my testing):
1047 *
1048 * -- NO_ERROR: drive is available.
1049 *
1050 * -- ERROR_INVALID_DRIVE (15): drive letter does not exist.
1051 *
1052 * -- ERROR_NOT_READY (21): drive exists, but is not ready.
1053 * This is produced by floppies and CD-ROM drives
1054 * which do not have valid media inserted.
1055 *
1056 * -- ERROR_AUDIO_CD_ROM (10000): special error code returned
1057 * only by this function if a CD-ROM drive has audio
1058 * media inserted.
1059 *
1060 * If DRVFL_MIXEDMODECD was specified, ERROR_AUDIO_CD_ROM
1061 * is returned _only_ if _no_ data tracks are
1062 * present on a CD-ROM. Since OS/2 is not very
1063 * good at handling mixed-mode CDs, this might not
1064 * be desireable.
1065 *
1066 * If DRVFL_MIXEDMODECD was not set, ERROR_AUDIO_CD_ROM
1067 * will be returned already if _one_ audio track is present.
1068 *
1069 *@@changed V0.9.1 (99-12-13) [umoeller]: rewritten, prototype changed. Now using DosOpen on the drive instead of DosError.
1070 *@@changed V0.9.1 (2000-01-08) [umoeller]: DosClose was called even if DosOpen failed, which messed up OS/2 error handling.
1071 *@@changed V0.9.1 (2000-02-09) [umoeller]: this didn't work for network drives, including RAMFS; fixed.
1072 *@@changed V0.9.3 (2000-03-28) [umoeller]: added check for network drives, which weren't working
1073 *@@changed V0.9.4 (2000-08-03) [umoeller]: more network fixes
1074 *@@changed V0.9.9 (2001-03-19) [pr]: validate drive number
1075 *@@changed V0.9.11 (2001-04-23) [umoeller]: added an extra check for floppies
1076 *@@changed V0.9.13 (2001-06-14) [umoeller]: added "fl" parameter and lots of CD-ROM checks
1077 */
1078
1079APIRET doshAssertDrive(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1080 ULONG fl) // in: DRVFL_* flags
1081{
1082 APIRET arc = NO_ERROR;
1083 BOOL fFixed = FALSE,
1084 fCDROM = FALSE;
1085
1086 if ((ulLogicalDrive < 1) || (ulLogicalDrive > 26))
1087 return(ERROR_PATH_NOT_FOUND);
1088
1089 arc = doshIsFixedDisk(ulLogicalDrive,
1090 &fFixed); // V0.9.13 (2001-06-14) [umoeller]
1091
1092 // _Pmpf((__FUNCTION__ ": doshIsFixedDisk returned %d for disk %d", arc, ulLogicalDrive));
1093 // _Pmpf((" fFixed is %d", fFixed));
1094
1095 if (!arc)
1096 if (!fFixed)
1097 {
1098 // removeable disk:
1099 // check if it's a CD-ROM
1100 BIOSPARAMETERBLOCK bpb;
1101 arc = doshQueryDiskParams(ulLogicalDrive,
1102 &bpb);
1103 // _Pmpf((" doshQueryDiskParams returned %d", arc));
1104
1105 if ( (!arc)
1106 && (DRVTYPE_CDROM == doshQueryDriveType(ulLogicalDrive,
1107 &bpb,
1108 fFixed))
1109 )
1110 {
1111 // _Pmpf((" --> is CD-ROM"));
1112 fCDROM = TRUE;
1113 }
1114 }
1115
1116 if (!arc)
1117 arc = doshQueryMedia(ulLogicalDrive,
1118 fCDROM,
1119 fl);
1120
1121 switch (arc)
1122 {
1123 case ERROR_NETWORK_ACCESS_DENIED: // 65
1124 // added V0.9.3 (2000-03-27) [umoeller];
1125 // according to user reports, this is returned
1126 // by all network drives, which apparently don't
1127 // support DASD DosOpen
1128 case ERROR_ACCESS_DENIED: // 5
1129 // added V0.9.4 (2000-07-10) [umoeller]
1130 // LAN drives still didn't work... apparently
1131 // the above only works for NFS drives
1132 case ERROR_PATH_NOT_FOUND: // 3
1133 // added V0.9.4 (2000-08-03) [umoeller]:
1134 // this is returned by some other network types...
1135 // sigh...
1136 case ERROR_NOT_SUPPORTED: // 50
1137 // this is returned by file systems which don't
1138 // support DASD DosOpen;
1139 // use some other method then, this isn't likely
1140 // to fail -- V0.9.1 (2000-02-09) [umoeller]
1141
1142 // but don't do this for floppies
1143 // V0.9.11 (2001-04-23) [umoeller]
1144 if (ulLogicalDrive > 2)
1145 {
1146 FSALLOCATE fsa;
1147 arc = DosQueryFSInfo(ulLogicalDrive,
1148 FSIL_ALLOC,
1149 &fsa,
1150 sizeof(fsa));
1151 // _Pmpf((" re-checked, DosQueryFSInfo returned %d", arc));
1152 }
1153 break;
1154 }
1155
1156 return (arc);
1157}
1158
1159/*
1160 *@@ doshGetDriveInfo:
1161 * fills the given XDISKINFO buffer with
1162 * information about the given logical drive.
1163 *
1164 * This function will not provoke "Drive not
1165 * ready" popups, hopefully.
1166 *
1167 * fl can be any combination of the following:
1168 *
1169 * -- DRVFL_MIXEDMODECD: see doshAssertDrive.
1170 *
1171 * -- DRVFL_TOUCHFLOPPIES: drive A: and B: should
1172 * be touched for media checks (click, click);
1173 * otherwise they will be left alone and
1174 * default values will be returned.
1175 *
1176 * -- DRVFL_CHECKEAS: drive should always be
1177 * checked for EA support. If this is set,
1178 * we will call DosFSCtl for the non-well-known
1179 * file systems so we will always have a
1180 * value for the DFL_SUPPORTS_EAS flags.
1181 * Otherwise that flag might or might not
1182 * be set correctly.
1183 *
1184 * The EA support returned by DosFSCtl
1185 * might not be correct for remote file
1186 * systems since not all of them support
1187 * that query.
1188 *
1189 * -- DRVFL_CHECKLONGNAMES: drive should always be
1190 * checked for longname support. If this is
1191 * set, we will try a DosOpen on the drive
1192 * to see if it supports long filenames
1193 * (unless it's a "well-known" file-system
1194 * and we know it does). In enabled, the
1195 * DFL_SUPPORTS_LONGNAMES flag is reliable.
1196 *
1197 * This should return only one of the following:
1198 *
1199 * -- NO_ERROR: disk info was filled, but not
1200 * necessarily all info was available (e.g.
1201 * if no media was present in CD-ROM drive).
1202 * See remarks below.
1203 *
1204 * -- ERROR_INVALID_DRIVE 15): ulLogicalDrive
1205 * is not used at all (invalid drive letter)
1206 *
1207 * -- ERROR_BAD_UNIT (20): if drive was renamed for
1208 * some reason (according to user reports
1209 *
1210 * -- ERROR_NOT_READY (21): for ZIP disks where
1211 * no media is inserted, depending on the
1212 * driver apparently... normally ZIP drive
1213 * letters should disappear when no media
1214 * is present
1215 *
1216 * -- ERROR_DRIVE_LOCKED (108)
1217 *
1218 * So in order to check whether a drive is present
1219 * and available, use this function as follows:
1220 *
1221 * 1) Call this function and check whether it
1222 * returns NO_ERROR for the given drive.
1223 * This will rule out invalid drive letters
1224 * and drives that are presently locked.
1225 *
1226 * 2) If so, check whether XDISKINFO.flDevice
1227 * has the DFL_MEDIA_PRESENT flag set.
1228 * This will rule out removeable drives without
1229 * media and unformatted hard disks.
1230 *
1231 * 3) If so, you can test the other fields if
1232 * you need more information. For example,
1233 * it would not be a good idea to create
1234 * a new file if the bType field is
1235 * DRVTYPE_CDROM.
1236 *
1237 * If you want to exclude removeable disks,
1238 * instead of checking bType, you should
1239 * rather check flDevice for the DFL_FIXED
1240 * flag, which will be set for ZIP drives also.
1241 *
1242 * Remarks for special drive types:
1243 *
1244 * -- Hard disks always have bType == DRVTYPE_HARDDISK.
1245 * For them, we always check the file system.
1246 * If this is reported as "UNKNOWN", this means
1247 * that the drive is unformatted or formatted
1248 * with a file system that OS/2 does not understand
1249 * (e.g. NTFS). Only in that case, flDevice
1250 * has the DFL_MEDIA_PRESENT bit clear.
1251 *
1252 * DFL_FIXED is always set.
1253 *
1254 * -- Remote (LAN) drives always have bType == DRVTYPE_LAN.
1255 * flDevice will always have the DFL_REMOTE and
1256 * DFL_MEDIA_PRESENT bits set.
1257 *
1258 * -- ZIP disks will have bType == DRVTYPE_PARTITIONABLEREMOVEABLE.
1259 * For them, flDevice will have both the
1260 * and DFL_PARTITIONABLEREMOVEABLE and DFL_FIXED
1261 * bits set.
1262 *
1263 * ZIP disks are a bit special because they are
1264 * dynamically mounted and unmounted when media
1265 * is inserted and removed. In other words, if
1266 * no media is present, the drive letter becomes
1267 * invalid.
1268 *
1269 * -- CD-ROM and DVD drives and CD writers will always
1270 * be reported as DRVTYPE_CDROM. The DFL_FIXED bit
1271 * will be clear always. For them, always check the
1272 * DFL_MEDIA_PRESENT present bit to avoid "Drive not
1273 * ready" popups.
1274 *
1275 * As a special goody, we can also determine if the
1276 * drive currently has audio media inserted (which
1277 * would provoke errors also), by setting the
1278 * DFL_AUDIO_CD bit.
1279 *
1280 *@@added V0.9.16 (2002-01-13) [umoeller]
1281 */
1282
1283APIRET doshGetDriveInfo(ULONG ulLogicalDrive,
1284 ULONG fl, // in: DRVFL_* flags
1285 PXDISKINFO pdi)
1286{
1287 APIRET arc = NO_ERROR;
1288
1289 HFILE hf;
1290 ULONG dummy;
1291 BOOL fCheck = TRUE,
1292 fCheckFS = FALSE,
1293 fCheckLongnames = FALSE,
1294 fCheckEAs = FALSE;
1295
1296 memset(pdi, 0, sizeof(XDISKINFO));
1297
1298 pdi->cDriveLetter = 'A' + ulLogicalDrive - 1;
1299 pdi->cLogicalDrive = ulLogicalDrive;
1300
1301 pdi->bType = DRVTYPE_UNKNOWN;
1302 pdi->fPresent = TRUE; // for now
1303
1304 if ( (ulLogicalDrive == 1)
1305 || (ulLogicalDrive == 2)
1306 )
1307 {
1308 // drive A: and B: are special cases,
1309 // we don't even want to touch them (click, click)
1310 pdi->bType = DRVTYPE_FLOPPY;
1311
1312 if (0 == (fl & DRVFL_TOUCHFLOPPIES))
1313 {
1314 fCheck = FALSE;
1315 // these support EAs too
1316 pdi->flDevice = DFL_MEDIA_PRESENT | DFL_SUPPORTS_EAS;
1317 strcpy(pdi->szFileSystem, "FAT");
1318 pdi->lFileSystem = FSYS_FAT;
1319 }
1320 }
1321
1322 if (fCheck)
1323 {
1324 // any other drive:
1325 // check if it's removeable first
1326 BOOL fFixed = FALSE;
1327 arc = doshIsFixedDisk(ulLogicalDrive,
1328 &fFixed);
1329
1330 switch (arc)
1331 {
1332 case ERROR_INVALID_DRIVE:
1333 // drive letter doesn't exist at all:
1334 pdi->fPresent = FALSE;
1335 // return this APIRET
1336 break;
1337
1338 case ERROR_NOT_SUPPORTED: // 50 for network drives
1339 // we get this for remote drives added
1340 // via "net use", so set these flags
1341 pdi->bType = DRVTYPE_LAN;
1342 pdi->lFileSystem = FSYS_REMOTE;
1343 pdi->flDevice |= DFL_REMOTE | DFL_MEDIA_PRESENT;
1344 // but still check what file-system we
1345 // have and whether longnames are supported
1346 fCheckFS = TRUE;
1347 fCheckLongnames = TRUE;
1348 fCheckEAs = TRUE;
1349 break;
1350
1351 case NO_ERROR:
1352 {
1353 if (fFixed)
1354 {
1355 // fixed drive:
1356 pdi->flDevice |= DFL_FIXED | DFL_MEDIA_PRESENT;
1357
1358 fCheckFS = TRUE;
1359 fCheckLongnames = TRUE;
1360 fCheckEAs = TRUE;
1361 }
1362
1363 if (!(arc = doshQueryDiskParams(ulLogicalDrive,
1364 &pdi->bpb)))
1365 {
1366 BYTE bTemp = doshQueryDriveType(ulLogicalDrive,
1367 &pdi->bpb,
1368 fFixed);
1369 if (bTemp != DRVTYPE_UNKNOWN)
1370 {
1371 // recognized: store it then
1372 pdi->bType = bTemp;
1373
1374 if (bTemp == DRVTYPE_PARTITIONABLEREMOVEABLE)
1375 pdi->flDevice |= DFL_FIXED
1376 | DFL_PARTITIONABLEREMOVEABLE;
1377 }
1378
1379 if (!fFixed)
1380 {
1381 // removeable:
1382
1383 // before checking the drive, try if we have media
1384 if (!(arc = doshQueryMedia(ulLogicalDrive,
1385 (pdi->bType == DRVTYPE_CDROM),
1386 fl)))
1387 {
1388 pdi->flDevice |= DFL_MEDIA_PRESENT;
1389 fCheckFS = TRUE;
1390 fCheckLongnames = TRUE;
1391 // but never EAs
1392 }
1393 else if (arc == ERROR_AUDIO_CD_ROM)
1394 {
1395 pdi->flDevice |= DFL_AUDIO_CD;
1396 // do not check longnames and file-system
1397 }
1398 else
1399 pdi->arcQueryMedia = arc;
1400
1401 arc = NO_ERROR;
1402 }
1403 }
1404 else
1405 pdi->arcQueryDiskParams = arc;
1406 }
1407 break;
1408
1409 default:
1410 pdi->arcIsFixedDisk = arc;
1411 // and return this
1412 break;
1413
1414 } // end swich arc = doshIsFixedDisk(ulLogicalDrive, &fFixed);
1415 }
1416
1417 if (fCheckFS)
1418 {
1419 // TRUE only for local fixed disks or
1420 // remote drives or if media was present above
1421 if (!(arc = doshQueryDiskFSType(ulLogicalDrive,
1422 pdi->szFileSystem,
1423 sizeof(pdi->szFileSystem))))
1424 {
1425 if (!stricmp(pdi->szFileSystem, "UNKNOWN"))
1426 {
1427 // this is returned by the stupid DosQueryFSAttach
1428 // if the file system is not recognized by OS/2,
1429 // or if the drive is unformatted
1430 pdi->lFileSystem = FSYS_UNKNOWN;
1431 pdi->flDevice &= ~DFL_MEDIA_PRESENT;
1432 fCheckLongnames = FALSE;
1433 fCheckEAs = FALSE;
1434 // should we return ERROR_NOT_DOS_DISK (26)
1435 // in this case?
1436 }
1437 else if (!stricmp(pdi->szFileSystem, "FAT"))
1438 {
1439 pdi->lFileSystem = FSYS_FAT;
1440 pdi->flDevice |= DFL_SUPPORTS_EAS;
1441 fCheckLongnames = FALSE;
1442 fCheckEAs = FALSE;
1443 }
1444 else if ( (!stricmp(pdi->szFileSystem, "HPFS"))
1445 || (!stricmp(pdi->szFileSystem, "JFS"))
1446 )
1447 {
1448 pdi->lFileSystem = FSYS_HPFS_JFS;
1449 pdi->flDevice |= DFL_SUPPORTS_EAS | DFL_SUPPORTS_LONGNAMES;
1450 fCheckLongnames = FALSE;
1451 fCheckEAs = FALSE;
1452 }
1453 else if (!stricmp(pdi->szFileSystem, "CDFS"))
1454 pdi->lFileSystem = FSYS_CDFS;
1455 else if ( (!stricmp(pdi->szFileSystem, "FAT32"))
1456 || (!stricmp(pdi->szFileSystem, "ext2"))
1457 )
1458 {
1459 pdi->lFileSystem = FSYS_FAT32_EXT2;
1460 fCheckLongnames = TRUE;
1461 fCheckEAs = TRUE;
1462 }
1463 else if (!stricmp(pdi->szFileSystem, "RAMFS"))
1464 {
1465 pdi->lFileSystem = FSYS_RAMFS;
1466 pdi->flDevice |= DFL_SUPPORTS_EAS | DFL_SUPPORTS_LONGNAMES;
1467 fCheckLongnames = FALSE;
1468 fCheckEAs = FALSE;
1469 }
1470 else if (!stricmp(pdi->szFileSystem, "TVFS"))
1471 {
1472 pdi->lFileSystem = FSYS_TVFS;
1473 fCheckLongnames = TRUE;
1474 fCheckEAs = TRUE;
1475 }
1476 }
1477 else
1478 // store negative error code
1479 pdi->lFileSystem = -(LONG)arc;
1480 }
1481
1482 if ( (!arc)
1483 && (fCheckLongnames)
1484 && (fl & DRVFL_CHECKLONGNAMES)
1485 )
1486 {
1487 CHAR szTemp[30] = "?:\\long.name.file";
1488 szTemp[0] = ulLogicalDrive + 'A' - 1;
1489 if (!(arc = DosOpen(szTemp,
1490 &hf,
1491 &dummy,
1492 0,
1493 0,
1494 FILE_READONLY,
1495 OPEN_SHARE_DENYNONE | OPEN_FLAGS_NOINHERIT,
1496 0)))
1497 {
1498 DosClose(hf);
1499 }
1500
1501 switch (arc)
1502 {
1503 case NO_ERROR:
1504 case ERROR_OPEN_FAILED:
1505 case ERROR_FILE_NOT_FOUND: // returned by TVFS
1506 pdi->flDevice |= DFL_SUPPORTS_LONGNAMES;
1507 break;
1508
1509 // if longnames are not supported,
1510 // we get ERROR_INVALID_NAME
1511 default:
1512 pdi->arcOpenLongnames = arc;
1513 break;
1514
1515 // default:
1516 // printf(" drive %d returned %d\n", ulLogicalDrive, arc);
1517 }
1518
1519 arc = NO_ERROR;
1520 }
1521
1522 if ( (!arc)
1523 && (fCheckEAs)
1524 && (fl & DRVFL_CHECKEAS)
1525 )
1526 {
1527 EASIZEBUF easb = {0};
1528 ULONG cbData = sizeof(easb),
1529 cbParams = 0;
1530 CHAR szDrive[] = "?:\\";
1531 szDrive[0] = pdi->cDriveLetter;
1532 if (!(arc = DosFSCtl(&easb,
1533 cbData,
1534 &cbData,
1535 NULL, // params,
1536 cbParams,
1537 &cbParams,
1538 FSCTL_MAX_EASIZE,
1539 szDrive,
1540 -1, // HFILE
1541 FSCTL_PATHNAME)))
1542 if (easb.cbMaxEASize != 0)
1543 // the other field (cbMaxEAListSize) is 0 always, I think
1544 pdi->flDevice |= DFL_SUPPORTS_EAS;
1545 }
1546
1547 if (doshQueryBootDrive() == pdi->cDriveLetter)
1548 pdi->flDevice |= DFL_BOOTDRIVE;
1549
1550 return (arc);
1551}
1552
1553/*
1554 *@@ doshSetLogicalMap:
1555 * sets the mapping of logical floppy drives onto a single
1556 * physical floppy drive.
1557 * This means selecting either drive A: or drive B: to refer
1558 * to the physical drive.
1559 *
1560 *@@added V0.9.6 (2000-11-24) [pr]
1561 */
1562
1563APIRET doshSetLogicalMap(ULONG ulLogicalDrive)
1564{
1565 CHAR name[3] = "?:";
1566 ULONG fd = 0,
1567 action = 0;
1568// paramsize = 0;
1569// datasize = 0;
1570 APIRET rc = NO_ERROR;
1571 USHORT data,
1572 param;
1573
1574 name[0] = doshQueryBootDrive();
1575 rc = DosOpen(name,
1576 &fd,
1577 &action,
1578 0,
1579 0,
1580 OPEN_ACTION_FAIL_IF_NEW
1581 | OPEN_ACTION_OPEN_IF_EXISTS,
1582 OPEN_FLAGS_DASD
1583 | OPEN_FLAGS_FAIL_ON_ERROR
1584 | OPEN_FLAGS_NOINHERIT
1585 | OPEN_ACCESS_READONLY
1586 | OPEN_SHARE_DENYNONE,
1587 0);
1588
1589 if (rc == NO_ERROR)
1590 {
1591 param = 0;
1592 data = (USHORT)ulLogicalDrive;
1593 // paramsize = sizeof(param);
1594 // datasize = sizeof(data);
1595 rc = doshDevIOCtl(fd,
1596 IOCTL_DISK, DSK_SETLOGICALMAP,
1597 &param, sizeof(param),
1598 &data, sizeof(data));
1599 DosClose(fd);
1600 }
1601
1602 return(rc);
1603}
1604
1605/*
1606 *@@ doshQueryDiskSize:
1607 * returns the size of the specified disk in bytes.
1608 *
1609 * Note: This returns a "double" value, because a ULONG
1610 * can only hold values of some 4 billion, which would
1611 * lead to funny results for drives > 4 GB.
1612 *
1613 *@@added V0.9.11 (2001-04-18) [umoeller]
1614 */
1615
1616APIRET doshQueryDiskSize(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1617 double *pdSize)
1618{
1619 APIRET arc = NO_ERROR;
1620 FSALLOCATE fsa;
1621 // double dbl = -1;
1622
1623 if (!(arc = DosQueryFSInfo(ulLogicalDrive, FSIL_ALLOC, &fsa, sizeof(fsa))))
1624 *pdSize = ((double)fsa.cSectorUnit * fsa.cbSector * fsa.cUnit);
1625
1626 return (arc);
1627}
1628
1629/*
1630 *@@ doshQueryDiskFree:
1631 * returns the number of bytes remaining on the disk
1632 * specified by the given logical drive.
1633 *
1634 * Note: This returns a "double" value, because a ULONG
1635 * can only hold values of some 4 billion, which would
1636 * lead to funny results for drives > 4 GB.
1637 *
1638 *@@changed V0.9.0 [umoeller]: fixed another > 4 GB bug (thanks to Rdiger Ihle)
1639 *@@changed V0.9.7 (2000-12-01) [umoeller]: changed prototype
1640 */
1641
1642APIRET doshQueryDiskFree(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1643 double *pdFree)
1644{
1645 APIRET arc = NO_ERROR;
1646 FSALLOCATE fsa;
1647 // double dbl = -1;
1648
1649 if (!(arc = DosQueryFSInfo(ulLogicalDrive, FSIL_ALLOC, &fsa, sizeof(fsa))))
1650 *pdFree = ((double)fsa.cSectorUnit * fsa.cbSector * fsa.cUnitAvail);
1651 // ^ fixed V0.9.0
1652
1653 return (arc);
1654}
1655
1656/*
1657 *@@ doshQueryDiskFSType:
1658 * copies the file-system type of the given disk object
1659 * (HPFS, FAT, CDFS etc.) to pszBuf.
1660 * Returns the DOS error code.
1661 *
1662 *@@changed V0.9.1 (99-12-12) [umoeller]: added cbBuf to prototype
1663 *@@changed V0.9.14 (2001-08-01) [umoeller]: fixed, this never respected cbBuf
1664 *@@changed V0.9.16 (2001-10-02) [umoeller]: added check for valid logical disk no
1665 */
1666
1667APIRET doshQueryDiskFSType(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1668 PSZ pszBuf, // out: buffer for FS type
1669 ULONG cbBuf) // in: size of that buffer
1670{
1671 APIRET arc = NO_ERROR;
1672 CHAR szName[5];
1673
1674 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
1675 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
1676 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
1677
1678 // compose "D:"-type string from logical drive letter
1679 if (ulLogicalDrive > 0 && ulLogicalDrive < 27)
1680 {
1681 szName[0] = ulLogicalDrive + 'A' - 1;
1682 szName[1] = ':';
1683 szName[2] = '\0';
1684
1685 arc = DosQueryFSAttach(szName, // logical drive of attached FS ("D:"-style)
1686 0, // ulOrdinal, ignored for FSAIL_QUERYNAME
1687 FSAIL_QUERYNAME, // return name for a drive or device
1688 pfsqBuffer, // buffer for returned data
1689 &cbBuffer); // sizeof(*pfsqBuffer)
1690
1691 if (arc == NO_ERROR)
1692 {
1693 if (pszBuf)
1694 {
1695 // The data for the last three fields in the FSQBUFFER2
1696 // structure are stored at the offset of fsqBuffer.szName.
1697 // Each data field following fsqBuffer.szName begins
1698 // immediately after the previous item.
1699 strncpy(pszBuf,
1700 (CHAR*)(&pfsqBuffer->szName) + pfsqBuffer->cbName + 1,
1701 cbBuf); // V0.9.14 (2001-08-01) [umoeller]
1702 *(pszBuf + cbBuf) = '\0';
1703 }
1704 }
1705 }
1706 else
1707 arc = ERROR_INVALID_PARAMETER; // V0.9.16 (2001-10-02) [umoeller]
1708
1709 return (arc);
1710}
1711
1712/*
1713 *@@ doshQueryDiskLabel:
1714 * this returns the label of a disk into
1715 * *pszVolumeLabel, which must be 12 bytes
1716 * in size.
1717 *
1718 * This function was added because the Toolkit
1719 * information for DosQueryFSInfo is only partly
1720 * correct. On OS/2 2.x, that function does not
1721 * take an FSINFO structure as input, but a VOLUMELABEL.
1722 * On Warp, this does take an FSINFO.
1723 *
1724 * DosSetFSInfo is even worse. See doshSetDiskLabel.
1725 *
1726 * See http://zebra.asta.fh-weingarten.de/os2/Snippets/Bugi6787.HTML
1727 * for details.
1728 *
1729 *@@added V0.9.0 [umoeller]
1730 *@@changed V0.9.11 (2001-04-22) [umoeller]: this copied even with errors, fixed
1731 */
1732
1733APIRET doshQueryDiskLabel(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1734 PSZ pszVolumeLabel) // out: volume label (must be 12 chars in size)
1735{
1736 APIRET arc;
1737
1738 #ifdef __OS2V2X__
1739 VOLUMELABEL FSInfoBuf;
1740 #else
1741 FSINFO FSInfoBuf;
1742 #endif
1743
1744 arc = DosQueryFSInfo(ulLogicalDrive,
1745 FSIL_VOLSER,
1746 &FSInfoBuf,
1747 sizeof(FSInfoBuf)); // depends
1748
1749 if (!arc) // V0.9.11 (2001-04-22) [umoeller]
1750 {
1751 #ifdef __OS2V2X__
1752 strcpy(pszVolumeLabel, FSInfoBuf.szVolLabel);
1753 #else
1754 strcpy(pszVolumeLabel, FSInfoBuf.vol.szVolLabel);
1755 #endif
1756 }
1757
1758 return (arc);
1759}
1760
1761/*
1762 *@@ doshSetDiskLabel:
1763 * this sets the label of a disk.
1764 *
1765 * This function was added because the Toolkit
1766 * information for DosSetFSInfo is flat out wrong.
1767 * That function does not take an FSINFO structure
1768 * as input, but a VOLUMELABEL. As a result, using
1769 * that function with the Toolkit's calling specs
1770 * results in ERROR_LABEL_TOO_LONG always.
1771 *
1772 * See http://zebra.asta.fh-weingarten.de/os2/Snippets/Bugi6787.HTML
1773 * for details.
1774 *
1775 *@@added V0.9.0 [umoeller]
1776 */
1777
1778APIRET doshSetDiskLabel(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1779 PSZ pszNewLabel)
1780{
1781 VOLUMELABEL FSInfoBuf;
1782
1783 // check length; 11 chars plus null byte allowed
1784 FSInfoBuf.cch = (BYTE)strlen(pszNewLabel);
1785 if (FSInfoBuf.cch < sizeof(FSInfoBuf.szVolLabel))
1786 {
1787 strcpy(FSInfoBuf.szVolLabel, pszNewLabel);
1788
1789 return (DosSetFSInfo(ulLogicalDrive,
1790 FSIL_VOLSER,
1791 &FSInfoBuf,
1792 sizeof(FSInfoBuf)));
1793 }
1794 else
1795 return (ERROR_LABEL_TOO_LONG);
1796}
1797
1798/*
1799 *@@category: Helpers\Control program helpers\File name parsing
1800 */
1801
1802/* ******************************************************************
1803 *
1804 * File name parsing
1805 *
1806 ********************************************************************/
1807
1808/*
1809 *@@ doshGetDriveSpec:
1810 * returns the drive specification in pcszFullFile,
1811 * if any is present. This is useful for UNC support.
1812 *
1813 * This returns:
1814 *
1815 * -- NO_ERROR: drive spec was given, and the output
1816 * fields have been set.
1817 *
1818 * -- ERROR_INVALID_NAME: incorrect UNC syntax.
1819 *
1820 * -- ERROR_INVALID_DRIVE: second char is ':', but
1821 * drive letter is not in the range [A-Z].
1822 *
1823 * -- ERROR_INVALID_PARAMETER: no drive spec given
1824 * at all; apparently pcszFullFile is not fully
1825 * qualified in the first place, or it is NULL,
1826 * or its length is <= 2.
1827 *
1828 *@@added V0.9.16 (2001-10-25) [umoeller]
1829 */
1830
1831APIRET doshGetDriveSpec(PCSZ pcszFullFile, // in: fully q'fied file spec
1832 PSZ pszDrive, // out: drive spec ("C:" or "\\SERVER\RESOURCE"; ptr can be NULL)
1833 PULONG pulDriveLen, // out: length of drive spec (2 if local drive; ptr can be NULL)
1834 PBOOL pfIsUNC) // out: set to TRUE if UNC name, FALSE otherwise (ptr can be NULL)
1835{
1836 APIRET arc = NO_ERROR;
1837 ULONG ulFileSpecLength;
1838
1839 if ( (pcszFullFile)
1840 && (ulFileSpecLength = strlen(pcszFullFile))
1841 && (ulFileSpecLength >= 2)
1842 )
1843 {
1844 // upper-case the drive letter
1845 if (pcszFullFile[1] == ':')
1846 {
1847 CHAR cDrive = toupper(*pcszFullFile);
1848 // local drive specified:
1849 if ( (cDrive >= 'A')
1850 && (cDrive <= 'Z')
1851 )
1852 {
1853 if (pszDrive)
1854 {
1855 pszDrive[0] = cDrive;
1856 pszDrive[1] = ':';
1857 pszDrive[2] = '\0';
1858 }
1859
1860 if (pulDriveLen)
1861 *pulDriveLen = 2;
1862 if (pfIsUNC)
1863 *pfIsUNC = FALSE;
1864 }
1865 else
1866 // this is not a valid drive:
1867 arc = ERROR_INVALID_DRIVE;
1868 }
1869 else if ( (pcszFullFile[0] == '\\')
1870 && (pcszFullFile[1] == '\\')
1871 )
1872 {
1873 // UNC drive specified:
1874 // this better be a full \\SERVER\RESOURCE string
1875 PCSZ pResource;
1876 if (pResource = strchr(pcszFullFile + 3, '\\'))
1877 {
1878 // we got at least \\SERVER\:
1879 ULONG ulLength;
1880 PCSZ p;
1881
1882 // check if more stuff is coming
1883 if (p = strchr(pResource + 1, '\\'))
1884 {
1885 // yes: copy server and resource excluding that backslash
1886 if (p == pResource + 1)
1887 // "\\SERVER\\" is invalid
1888 arc = ERROR_INVALID_NAME;
1889 else
1890 // we got "\\SERVER\something\":
1891 // drop the last backslash
1892 ulLength = p - pcszFullFile;
1893 }
1894 else
1895 // "\\SERVER\something" only:
1896 ulLength = ulFileSpecLength;
1897
1898 if (!arc)
1899 {
1900 if (pszDrive)
1901 {
1902 memcpy(pszDrive,
1903 pcszFullFile,
1904 ulLength);
1905 pszDrive[ulLength] = '\0';
1906 }
1907
1908 if (pulDriveLen)
1909 *pulDriveLen = ulLength;
1910 if (pfIsUNC)
1911 *pfIsUNC = TRUE;
1912 }
1913 }
1914 else
1915 // invalid UNC name:
1916 arc = ERROR_INVALID_NAME;
1917 }
1918 else
1919 // neither local, nor UNC:
1920 arc = ERROR_INVALID_PARAMETER;
1921 }
1922 else
1923 arc = ERROR_INVALID_PARAMETER;
1924
1925 return (arc);
1926}
1927
1928/*
1929 *@@ doshGetExtension:
1930 * finds the file name extension of pszFilename,
1931 * which can be a file name only or a fully
1932 * qualified filename.
1933 *
1934 * This returns a pointer into pszFilename to
1935 * the character after the last dot.
1936 *
1937 * Returns NULL if not found (e.g. if the filename
1938 * has no dot in it).
1939 *
1940 * In the pathological case of a dot in the path
1941 * but not in the filename itself, this correctly
1942 * returns NULL.
1943 *
1944 *@@added V0.9.6 (2000-10-16) [umoeller]
1945 *@@changed V0.9.7 (2000-12-10) [umoeller]: fixed "F:filename.ext" case
1946 */
1947
1948PSZ doshGetExtension(PCSZ pcszFilename)
1949{
1950 PSZ pReturn = NULL;
1951
1952 if (pcszFilename)
1953 {
1954 // find filename
1955 PCSZ p2,
1956 pStartOfName = NULL,
1957 pExtension = NULL;
1958
1959 if (p2 = strrchr(pcszFilename + 2, '\\'))
1960 // works on "C:\blah" or "\\unc\blah"
1961 pStartOfName = p2 + 1;
1962 else
1963 {
1964 // no backslash found:
1965 // maybe only a drive letter was specified:
1966 if (pcszFilename[1] == ':')
1967 // yes:
1968 pStartOfName = pcszFilename + 2;
1969 else
1970 // then this is not qualified at all...
1971 // use start of filename
1972 pStartOfName = (PSZ)pcszFilename;
1973 }
1974
1975 // find last dot in filename
1976 if (pExtension = strrchr(pStartOfName, '.'))
1977 pReturn = (PSZ)pExtension + 1;
1978 }
1979
1980 return (pReturn);
1981}
1982
1983/*
1984 *@@category: Helpers\Control program helpers\File management
1985 */
1986
1987/* ******************************************************************
1988 *
1989 * File helpers
1990 *
1991 ********************************************************************/
1992
1993/*
1994 *@@ doshIsFileOnFAT:
1995 * returns TRUE if pszFileName resides on
1996 * a FAT drive. Note that pszFileName must
1997 * be fully qualified (i.e. the drive letter
1998 * must be the first character), or this will
1999 * return garbage.
2000 */
2001
2002BOOL doshIsFileOnFAT(const char* pcszFileName)
2003{
2004 BOOL brc = FALSE;
2005 CHAR szName[5];
2006
2007 APIRET rc = NO_ERROR; // return code
2008 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
2009 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
2010 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
2011
2012 szName[0] = pcszFileName[0]; // copy drive letter
2013 szName[1] = ':';
2014 szName[2] = '\0';
2015
2016 rc = DosQueryFSAttach(szName, // logical drive of attached FS
2017 0, // ulOrdinal, ignored for FSAIL_QUERYNAME
2018 FSAIL_QUERYNAME, // return data for a Drive or Device
2019 pfsqBuffer, // returned data
2020 &cbBuffer); // returned data length
2021
2022 if (rc == NO_ERROR)
2023 {
2024 // The data for the last three fields in the FSQBUFFER2
2025 // structure are stored at the offset of fsqBuffer.szName.
2026 // Each data field following fsqBuffer.szName begins
2027 // immediately after the previous item.
2028 if (!strncmp((PSZ)&(pfsqBuffer->szName) + pfsqBuffer->cbName + 1,
2029 "FAT",
2030 3))
2031 brc = TRUE;
2032 }
2033
2034 return (brc);
2035}
2036
2037/*
2038 *@@ doshQueryFileSize:
2039 * returns the size of an already opened file
2040 * or 0 upon errors.
2041 * Use doshQueryPathSize to query the size of
2042 * any file.
2043 *
2044 *@@changed V0.9.16 (2001-10-19) [umoeller]: now returning APIRET
2045 */
2046
2047APIRET doshQueryFileSize(HFILE hFile, // in: file handle
2048 PULONG pulSize) // out: file size (ptr can be NULL)
2049{
2050 APIRET arc;
2051 FILESTATUS3 fs3;
2052 if (!(arc = DosQueryFileInfo(hFile, FIL_STANDARD, &fs3, sizeof(fs3))))
2053 if (pulSize)
2054 *pulSize = fs3.cbFile;
2055 return (arc);
2056}
2057
2058/*
2059 *@@ doshQueryPathSize:
2060 * returns the size of any file,
2061 * or 0 if the file could not be
2062 * found.
2063 *
2064 * Use doshQueryFileSize instead to query the
2065 * size if you have a HFILE.
2066 *
2067 * Otherwise this returns:
2068 *
2069 * -- ERROR_FILE_NOT_FOUND
2070 * -- ERROR_PATH_NOT_FOUND
2071 * -- ERROR_SHARING_VIOLATION
2072 * -- ERROR_FILENAME_EXCED_RANGE
2073 * -- ERROR_INVALID_PARAMETER
2074 *
2075 *@@changed V0.9.16 (2001-10-19) [umoeller]: now returning APIRET
2076 */
2077
2078APIRET doshQueryPathSize(PCSZ pcszFile, // in: filename
2079 PULONG pulSize) // out: file size (ptr can be NULL)
2080{
2081 APIRET arc;
2082
2083 if (pcszFile) // V0.9.16 (2001-12-08) [umoeller]
2084 {
2085 FILESTATUS3 fs3;
2086 if (!(arc = DosQueryPathInfo((PSZ)pcszFile, FIL_STANDARD, &fs3, sizeof(fs3))))
2087 if (pulSize)
2088 *pulSize = fs3.cbFile;
2089 }
2090 else
2091 arc = ERROR_INVALID_PARAMETER;
2092
2093 return (arc);
2094}
2095
2096/*
2097 *@@ doshQueryPathAttr:
2098 * returns the file attributes of pszFile,
2099 * which can be fully qualified. The
2100 * attributes will be stored in *pulAttr.
2101 * pszFile can also specify a directory,
2102 * although not all attributes make sense
2103 * for directories.
2104 *
2105 * fAttr can be:
2106 * -- FILE_ARCHIVED
2107 * -- FILE_READONLY
2108 * -- FILE_SYSTEM
2109 * -- FILE_HIDDEN
2110 *
2111 * This returns the APIRET of DosQueryPathInfo.
2112 * *pulAttr is only valid if NO_ERROR is
2113 * returned.
2114 *
2115 * Otherwise this returns:
2116 *
2117 * -- ERROR_FILE_NOT_FOUND
2118 * -- ERROR_PATH_NOT_FOUND
2119 * -- ERROR_SHARING_VIOLATION
2120 * -- ERROR_FILENAME_EXCED_RANGE
2121 *
2122 *@@added V0.9.0 [umoeller]
2123 */
2124
2125APIRET doshQueryPathAttr(const char* pcszFile, // in: file or directory name
2126 PULONG pulAttr) // out: attributes (ptr can be NULL)
2127{
2128 FILESTATUS3 fs3;
2129 APIRET arc;
2130
2131 if (!(arc = DosQueryPathInfo((PSZ)pcszFile,
2132 FIL_STANDARD,
2133 &fs3,
2134 sizeof(fs3))))
2135 {
2136 if (pulAttr)
2137 *pulAttr = fs3.attrFile;
2138 }
2139
2140 return (arc);
2141}
2142
2143/*
2144 *@@ doshSetPathAttr:
2145 * sets the file attributes of pszFile,
2146 * which can be fully qualified.
2147 * pszFile can also specify a directory,
2148 * although not all attributes make sense
2149 * for directories.
2150 *
2151 * fAttr can be:
2152 * -- FILE_ARCHIVED
2153 * -- FILE_READONLY
2154 * -- FILE_SYSTEM
2155 * -- FILE_HIDDEN
2156 *
2157 * Note that this simply sets all the given
2158 * attributes; the existing attributes
2159 * are lost.
2160 *
2161 * This returns the APIRET of DosQueryPathInfo.
2162 */
2163
2164APIRET doshSetPathAttr(const char* pcszFile, // in: file or directory name
2165 ULONG ulAttr) // in: new attributes
2166{
2167 APIRET arc;
2168
2169 if (pcszFile)
2170 {
2171 FILESTATUS3 fs3;
2172 if (!(arc = DosQueryPathInfo((PSZ)pcszFile,
2173 FIL_STANDARD,
2174 &fs3,
2175 sizeof(fs3))))
2176 {
2177 fs3.attrFile = ulAttr;
2178 arc = DosSetPathInfo((PSZ)pcszFile,
2179 FIL_STANDARD,
2180 &fs3,
2181 sizeof(fs3),
2182 DSPI_WRTTHRU);
2183 }
2184 }
2185 else
2186 arc = ERROR_INVALID_PARAMETER;
2187
2188 return (arc);
2189}
2190
2191/*
2192 *@@ doshOpenExisting:
2193 * opens an existing file for read-write access. Does
2194 * not create a new file if the file doesn't exist.
2195 *
2196 * This is just a simple wrapper around DosOpen.
2197 *
2198 * ulOpenFlags is passed to DosOpen. Should be one
2199 * of:
2200 *
2201 * -- for read-only access:
2202 *
2203 + OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY
2204 *
2205 * -- for read-write access:
2206 *
2207 + OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE
2208 *
2209 * In addition, you can specify
2210 *
2211 + OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_RANDOM
2212 + | OPEN_FLAGS_NOINHERIT
2213 *
2214 *@@added V0.9.13 (2001-06-14) [umoeller]
2215 */
2216
2217/*
2218APIRET doshOpenExisting(PCSZ pcszFilename, // in: file name
2219 ULONG ulOpenFlags, // in: open flags
2220 HFILE *phf) // out: OS/2 file handle
2221{
2222 ULONG ulAction;
2223 return (DosOpen((PSZ)pcszFilename,
2224 phf,
2225 &ulAction,
2226 0, // cbFile
2227 0, // attributes
2228 OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
2229 ulOpenFlags,
2230 NULL)); // EAs
2231}
2232*/
2233
2234/*
2235 * doshOpen:
2236 * wrapper around DosOpen for simpler opening
2237 * of files.
2238 *
2239 * ulOpenMode determines the mode to open the
2240 * file in (fptr specifies the position after
2241 * the open):
2242 *
2243 + +-------------------------+------+------------+-----------+
2244 + | ulOpenMode | mode | if exists | if new |
2245 + +-------------------------+------+------------+-----------+
2246 + | XOPEN_READ_EXISTING | read | opens | fails |
2247 + | | | fptr = 0 | |
2248 + +-------------------------+------+------------+-----------+
2249 + | XOPEN_READWRITE_EXISTING r/w | opens | fails |
2250 + | | | fptr = 0 | |
2251 + +-------------------------+------+------------+-----------+
2252 + | XOPEN_READWRITE_APPEND | r/w | opens, | creates |
2253 + | | | appends | |
2254 + | | | fptr = end | fptr = 0 |
2255 + +-------------------------+------+------------+-----------+
2256 + | XOPEN_READWRITE_NEW | r/w | replaces | creates |
2257 + | | | fptr = 0 | fptr = 0 |
2258 + +-------------------------+------+------------+-----------+
2259 *
2260 * In addition, you can OR one of the above values with
2261 * the XOPEN_BINARY flag:
2262 *
2263 * -- If XOPEN_BINARY is set, no conversion is performed
2264 * on read and write.
2265 *
2266 * -- If XOPEN_BINARY is _not_ set, all \n chars are
2267 * converted to \r\n on write.
2268 *
2269 * *ppFile receives a new XFILE structure describing
2270 * the open file, if NO_ERROR is returned.
2271 *
2272 * The file pointer is then set to the beginning of the
2273 * file _unless_ XOPEN_READWRITE_APPEND was specified;
2274 * in that case only, the file pointer is set to the
2275 * end of the file so data can be appended (see above).
2276 *
2277 * Otherwise this returns:
2278 *
2279 * -- ERROR_FILE_NOT_FOUND
2280 * -- ERROR_PATH_NOT_FOUND
2281 * -- ERROR_SHARING_VIOLATION
2282 * -- ERROR_FILENAME_EXCED_RANGE
2283 *
2284 * -- ERROR_NOT_ENOUGH_MEMORY
2285 * -- ERROR_INVALID_PARAMETER
2286 *
2287 *@@added V0.9.16 (2001-10-19) [umoeller]
2288 *@@changed V0.9.16 (2001-12-18) [umoeller]: fixed error codes
2289 */
2290
2291APIRET doshOpen(PCSZ pcszFilename, // in: filename to open
2292 ULONG flOpenMode, // in: XOPEN_* mode
2293 PULONG pcbFile, // in: new file size (if new file is created)
2294 // out: file size
2295 PXFILE *ppFile)
2296{
2297 APIRET arc = NO_ERROR;
2298
2299 ULONG fsOpenFlags = 0,
2300 fsOpenMode = OPEN_FLAGS_FAIL_ON_ERROR
2301 | OPEN_FLAGS_NO_LOCALITY
2302 | OPEN_FLAGS_NOINHERIT;
2303
2304 switch (flOpenMode & XOPEN_ACCESS_MASK)
2305 {
2306 case XOPEN_READ_EXISTING:
2307 fsOpenFlags = OPEN_ACTION_FAIL_IF_NEW
2308 | OPEN_ACTION_OPEN_IF_EXISTS;
2309 fsOpenMode |= OPEN_SHARE_DENYWRITE
2310 | OPEN_ACCESS_READONLY;
2311
2312 // run this first, because if the file doesn't
2313 // exists, DosOpen only returns ERROR_OPEN_FAILED,
2314 // which isn't that meaningful
2315 // V0.9.16 (2001-12-08) [umoeller]
2316 arc = doshQueryPathSize(pcszFilename,
2317 pcbFile);
2318 break;
2319
2320 case XOPEN_READWRITE_EXISTING:
2321 fsOpenFlags = OPEN_ACTION_FAIL_IF_NEW
2322 | OPEN_ACTION_OPEN_IF_EXISTS;
2323 fsOpenMode |= OPEN_SHARE_DENYWRITE
2324 | OPEN_ACCESS_READWRITE;
2325
2326 arc = doshQueryPathSize(pcszFilename,
2327 pcbFile);
2328 break;
2329
2330 case XOPEN_READWRITE_APPEND:
2331 fsOpenFlags = OPEN_ACTION_CREATE_IF_NEW
2332 | OPEN_ACTION_OPEN_IF_EXISTS;
2333 fsOpenMode |= OPEN_SHARE_DENYREADWRITE
2334 | OPEN_ACCESS_READWRITE;
2335 // _Pmpf((__FUNCTION__ ": opening XOPEN_READWRITE_APPEND"));
2336 break;
2337
2338 case XOPEN_READWRITE_NEW:
2339 fsOpenFlags = OPEN_ACTION_CREATE_IF_NEW
2340 | OPEN_ACTION_REPLACE_IF_EXISTS;
2341 fsOpenMode |= OPEN_SHARE_DENYREADWRITE
2342 | OPEN_ACCESS_READWRITE;
2343 // _Pmpf((__FUNCTION__ ": opening XOPEN_READWRITE_NEW"));
2344 break;
2345 }
2346
2347 if ((!arc) && fsOpenFlags && pcbFile && ppFile)
2348 {
2349 PXFILE pFile;
2350 if (pFile = NEW(XFILE))
2351 {
2352 ULONG ulAction;
2353
2354 ZERO(pFile);
2355
2356 // copy open flags
2357 pFile->flOpenMode = flOpenMode;
2358
2359 if (!(arc = DosOpen((PSZ)pcszFilename,
2360 &pFile->hf,
2361 &ulAction,
2362 *pcbFile,
2363 FILE_ARCHIVED,
2364 fsOpenFlags,
2365 fsOpenMode,
2366 NULL))) // EAs
2367 {
2368 // alright, got the file:
2369
2370 if ( (ulAction == FILE_EXISTED)
2371 && ((flOpenMode & XOPEN_ACCESS_MASK) == XOPEN_READWRITE_APPEND)
2372 )
2373 // get its size and set ptr to end for append
2374 arc = DosSetFilePtr(pFile->hf,
2375 0,
2376 FILE_END,
2377 pcbFile);
2378 else
2379 arc = doshQueryFileSize(pFile->hf,
2380 pcbFile);
2381 // file ptr is at beginning
2382
2383 #ifdef DEBUG_DOSOPEN
2384 if (arc)
2385 _Pmpf((__FUNCTION__ ": DosSetFilePtr/queryfilesize returned %d for %s",
2386 arc, pcszFilename));
2387 #endif
2388
2389 // store file size
2390 pFile->cbInitial
2391 = pFile->cbCurrent
2392 = *pcbFile;
2393
2394 pFile->pszFilename = strdup(pcszFilename);
2395 }
2396 #ifdef DEBUG_DOSOPEN
2397 else
2398 _Pmpf((__FUNCTION__ ": DosOpen returned %d for %s",
2399 arc, pcszFilename));
2400 #endif
2401
2402 if (arc)
2403 doshClose(&pFile);
2404 else
2405 *ppFile = pFile;
2406 }
2407 else
2408 arc = ERROR_NOT_ENOUGH_MEMORY;
2409 }
2410 else
2411 arc = ERROR_INVALID_PARAMETER;
2412
2413 return (arc);
2414}
2415
2416/*
2417 *@@ doshLockFile:
2418 *
2419 *@@added V0.9.16 (2001-10-19) [umoeller]
2420 */
2421
2422/* APIRET doshLockFile(PXFILE pFile)
2423{
2424 if (!pFile)
2425 return (ERROR_INVALID_PARAMETER);
2426
2427 if (!pFile->hmtx)
2428 // first call:
2429 return (DosCreateMutexSem(NULL,
2430 &pFile->hmtx,
2431 0,
2432 TRUE)); // request!
2433
2434 return (DosRequestMutexSem(pFile->hmtx, SEM_INDEFINITE_WAIT));
2435} */
2436
2437/*
2438 *@@ doshUnlockFile:
2439 *
2440 *@@added V0.9.16 (2001-10-19) [umoeller]
2441 */
2442
2443/* APIRET doshUnlockFile(PXFILE pFile)
2444{
2445 if (pFile)
2446 return (DosReleaseMutexSem(pFile->hmtx));
2447
2448 return (ERROR_INVALID_PARAMETER);
2449} */
2450
2451/*
2452 *@@ doshReadAt:
2453 * reads cb bytes from the position specified by
2454 * lOffset into the buffer pointed to by pbData,
2455 * which should be cb bytes in size.
2456 *
2457 * Note that lOffset is always considered to
2458 * be from the beginning of the file (FILE_BEGIN
2459 * method).
2460 *
2461 * This implements a small cache so that several
2462 * calls with a near offset will not touch the
2463 * disk always. The cache has been optimized for
2464 * the exeh* functions and works quite nicely
2465 * there.
2466 *
2467 * Note that the position of the file pointer is
2468 * undefined after calling this function because
2469 * the data might have been returned from the
2470 * cache.
2471 *
2472 * fl may be any combination of the following:
2473 *
2474 * -- DRFL_NOCACHE: do not fill the cache with
2475 * new data if the data is not in the cache
2476 * currently.
2477 *
2478 * -- DRFL_FAILIFLESS: return ERROR_NO_DATA
2479 * if the data returned by DosRead is less
2480 * than what was specified. This might
2481 * simplify error handling.
2482 *
2483 *@@added V0.9.13 (2001-06-14) [umoeller]
2484 *@@changed V0.9.16 (2001-12-18) [umoeller]: now with XFILE, and always using FILE_BEGIN
2485 */
2486
2487APIRET doshReadAt(PXFILE pFile,
2488 ULONG ulOffset, // in: offset to read from (from beginning of file)
2489 PULONG pcb, // in: bytes to read, out: bytes read
2490 PBYTE pbData, // out: read buffer (must be cb bytes)
2491 ULONG fl) // in: DRFL_* flags
2492{
2493 APIRET arc = NO_ERROR;
2494 ULONG cb = *pcb;
2495 ULONG ulDummy;
2496
2497 // if (!(arc = doshLockFile(pFile))) // this checks for pFile
2498 {
2499 *pcb = 0;
2500
2501 // check if we have the data in the cache already;
2502
2503 if ( (pFile->pbCache)
2504 // first byte must be in cache
2505 && (ulOffset >= pFile->ulReadFrom)
2506 // last byte must be in cache
2507 && ( ulOffset + cb
2508 <= pFile->ulReadFrom + pFile->cbCache
2509 )
2510 )
2511 {
2512 // alright, return data from cache simply
2513 ULONG ulOfsInCache = ulOffset - pFile->ulReadFrom;
2514
2515 memcpy(pbData,
2516 pFile->pbCache + ulOfsInCache,
2517 cb);
2518 *pcb = cb;
2519
2520 #ifdef DEBUG_DOSOPEN
2521 _Pmpf((__FUNCTION__ " %s: data is fully in cache",
2522 pFile->pszFilename));
2523 _Pmpf((" caller wants %d bytes from %d",
2524 cb, ulOffset));
2525 _Pmpf((" we got %d bytes from %d",
2526 pFile->cbCache, pFile->ulReadFrom));
2527 _Pmpf((" so copied %d bytes from cache ofs %d",
2528 cb, ulOfsInCache));
2529 #endif
2530 }
2531 else
2532 {
2533 // data is not in cache:
2534 // check how much it is... for small amounts,
2535 // we load the cache first
2536 if ( (cb <= 4096 - 512)
2537 && (!(fl & DRFL_NOCACHE))
2538 )
2539 {
2540 #ifdef DEBUG_DOSOPEN
2541 _Pmpf((__FUNCTION__ " %s: filling cache anew",
2542 pFile->pszFilename));
2543 _Pmpf((" caller wants %d bytes from %d",
2544 cb, ulOffset));
2545 #endif
2546
2547 // OK, then fix the offset to read from
2548 // to a multiple of 512 to get a full sector
2549 pFile->ulReadFrom = ulOffset / 512L * 512L;
2550 // and read 4096 bytes always plus the
2551 // value we cut off above
2552 pFile->cbCache = 4096;
2553
2554 #ifdef DEBUG_DOSOPEN
2555 _Pmpf((" getting %d bytes from %d",
2556 pFile->cbCache, pFile->ulReadFrom));
2557 #endif
2558
2559 // free old cache
2560 if (pFile->pbCache)
2561 free(pFile->pbCache);
2562
2563 // allocate new cache
2564 if (!(pFile->pbCache = (PBYTE)malloc(pFile->cbCache)))
2565 arc = ERROR_NOT_ENOUGH_MEMORY;
2566 else
2567 {
2568 ULONG ulOfsInCache = 0;
2569
2570 if (!(arc = DosSetFilePtr(pFile->hf,
2571 (LONG)pFile->ulReadFrom,
2572 FILE_BEGIN,
2573 &ulDummy)))
2574 {
2575 if (!(arc = DosRead(pFile->hf,
2576 pFile->pbCache,
2577 pFile->cbCache,
2578 &ulDummy)))
2579 {
2580 // got data:
2581 #ifdef DEBUG_DOSOPEN
2582 _Pmpf((" %d bytes read", ulDummy));
2583 #endif
2584
2585 pFile->cbCache = ulDummy;
2586
2587 // check bounds
2588 ulOfsInCache = ulOffset - pFile->ulReadFrom;
2589
2590 /*
2591 if (ulOfsInCache + cb > pFile->cbCache)
2592 {
2593 cb = pFile->cbCache - ulOfsInCache;
2594 if (fl & DRFL_FAILIFLESS)
2595 arc = ERROR_NO_DATA;
2596 }
2597 */
2598 }
2599 }
2600
2601 if (!arc)
2602 {
2603 // copy to caller
2604 memcpy(pbData,
2605 pFile->pbCache + ulOfsInCache,
2606 cb);
2607 *pcb = cb;
2608
2609 #ifdef DEBUG_DOSOPEN
2610 _Pmpf((" so copied %d bytes from cache ofs %d",
2611 cb, ulOfsInCache));
2612 #endif
2613 }
2614 else
2615 {
2616 free(pFile->pbCache);
2617 pFile->pbCache = NULL;
2618 }
2619 } // end else if (!(pFile->pbCache = (PBYTE)malloc(pFile->cbCache)))
2620 }
2621 else
2622 {
2623 // read uncached:
2624 #ifdef DEBUG_DOSOPEN
2625 _Pmpf((" " __FUNCTION__ " %s: reading uncached",
2626 pFile->pszFilename));
2627 _Pmpf((" caller wants %d bytes from %d",
2628 cb, ulOffset));
2629 #endif
2630
2631 if (!(arc = DosSetFilePtr(pFile->hf,
2632 (LONG)ulOffset,
2633 FILE_BEGIN,
2634 &ulDummy)))
2635 {
2636 if (!(arc = DosRead(pFile->hf,
2637 pbData,
2638 cb,
2639 &ulDummy)))
2640 {
2641 if ( (fl & DRFL_FAILIFLESS)
2642 && (ulDummy != cb)
2643 )
2644 arc = ERROR_NO_DATA;
2645 else
2646 *pcb = ulDummy; // bytes read
2647 }
2648 }
2649 }
2650 }
2651
2652 // doshUnlockFile(pFile);
2653 }
2654
2655 return (arc);
2656}
2657
2658/*
2659 *@@ doshWrite:
2660 * writes the specified data to the file.
2661 * If (cb == 0), this runs strlen on pcsz
2662 * to find out the length.
2663 *
2664 * If the file is not in binary mode, all
2665 * \n chars are converted to \r\n before
2666 * writing.
2667 *
2668 * Note that this expects that the file
2669 * pointer is at the end of the file, or
2670 * you will get garbage.
2671 *
2672 *@@added V0.9.16 (2001-10-19) [umoeller]
2673 *@@changed V0.9.16 (2001-12-02) [umoeller]: added XOPEN_BINARY \r\n support
2674 *@@changed V0.9.16 (2001-12-06) [umoeller]: added check for pFile != NULL
2675 */
2676
2677APIRET doshWrite(PXFILE pFile,
2678 ULONG cb,
2679 PCSZ pbData)
2680{
2681 APIRET arc = NO_ERROR;
2682 if ((!pFile) || (!pbData))
2683 arc = ERROR_INVALID_PARAMETER;
2684 else
2685 {
2686 if (!cb)
2687 cb = strlen(pbData);
2688
2689 if (!cb)
2690 arc = ERROR_INVALID_PARAMETER;
2691 else
2692 {
2693 PSZ pszNew = NULL;
2694
2695 if (!(pFile->flOpenMode & XOPEN_BINARY))
2696 {
2697 // convert all \n to \r\n:
2698 // V0.9.16 (2001-12-02) [umoeller]
2699
2700 // count all \n first
2701 ULONG cNewLines = 0;
2702 PCSZ pSource = pbData;
2703 ULONG ul;
2704 for (ul = 0;
2705 ul < cb;
2706 ul++)
2707 {
2708 if (*pSource++ == '\n')
2709 cNewLines++;
2710 }
2711
2712 if (cNewLines)
2713 {
2714 // we have '\n' chars:
2715 // then we need just as many \r chars inserted
2716 ULONG cbNew = cb + cNewLines;
2717 if (!(pszNew = (PSZ)malloc(cbNew)))
2718 arc = ERROR_NOT_ENOUGH_MEMORY;
2719 else
2720 {
2721 PSZ pTarget = pszNew;
2722 pSource = pbData;
2723 for (ul = 0;
2724 ul < cb;
2725 ul++)
2726 {
2727 CHAR c = *pSource++;
2728 if (c == '\n')
2729 *pTarget++ = '\r';
2730 *pTarget++ = c;
2731 }
2732
2733 cb = cbNew;
2734 }
2735 }
2736 }
2737
2738 if (!arc)
2739 // if (!(arc = doshLockFile(pFile))) // this checks for pFile
2740 {
2741 ULONG cbWritten;
2742 if (!(arc = DosWrite(pFile->hf,
2743 (pszNew)
2744 ? pszNew
2745 : (PSZ)pbData,
2746 cb,
2747 &cbWritten)))
2748 {
2749 pFile->cbCurrent += cbWritten;
2750 // invalidate the cache
2751 FREE(pFile->pbCache);
2752 }
2753
2754 // doshUnlockFile(pFile);
2755 }
2756
2757 if (pszNew)
2758 free(pszNew);
2759 }
2760 }
2761
2762 return (arc);
2763}
2764
2765/*
2766 *@@ doshWriteAt:
2767 * writes cb bytes (pointed to by pbData) to the
2768 * specified file at the position lOffset (from
2769 * the beginning of the file).
2770 *
2771 *@@added V0.9.13 (2001-06-14) [umoeller]
2772 */
2773
2774APIRET doshWriteAt(PXFILE pFile,
2775 ULONG ulOffset, // in: offset to write at
2776 ULONG cb, // in: bytes to write
2777 PCSZ pbData) // in: ptr to bytes to write (must be cb bytes)
2778{
2779 APIRET arc = NO_ERROR;
2780 // if (!(arc = doshLockFile(pFile))) // this checks for pFile
2781 {
2782 ULONG cbWritten;
2783 if (!(arc = DosSetFilePtr(pFile->hf,
2784 (LONG)ulOffset,
2785 FILE_BEGIN,
2786 &cbWritten)))
2787 {
2788 if (!(arc = DosWrite(pFile->hf,
2789 (PSZ)pbData,
2790 cb,
2791 &cbWritten)))
2792 {
2793 if (ulOffset + cbWritten > pFile->cbCurrent)
2794 pFile->cbCurrent = ulOffset + cbWritten;
2795 }
2796 }
2797
2798 // doshUnlockFile(pFile);
2799 }
2800
2801 return (arc);
2802}
2803
2804/*
2805 *@@ doshWriteLogEntry
2806 * writes a log string to an XFILE, adding a
2807 * leading timestamp before the line.
2808 *
2809 * The internal string buffer is limited to 2000
2810 * characters. Length checking is _not_ performed.
2811 *
2812 *@@added V0.9.16 (2001-10-19) [umoeller]
2813 *@@changed V0.9.16 (2001-12-06) [umoeller]: added check for pFile != NULL
2814 */
2815
2816APIRET doshWriteLogEntry(PXFILE pFile,
2817 const char* pcszFormat,
2818 ...)
2819{
2820 APIRET arc = NO_ERROR;
2821
2822 if ((!pFile) || (!pcszFormat))
2823 arc = ERROR_INVALID_PARAMETER;
2824 else
2825 {
2826 DATETIME dt;
2827 CHAR szTemp[2000];
2828 ULONG ulLength;
2829
2830 DosGetDateTime(&dt);
2831 if (ulLength = sprintf(szTemp,
2832 "%04d-%02d-%02d %02d:%02d:%02d:%02d ",
2833 dt.year, dt.month, dt.day,
2834 dt.hours, dt.minutes, dt.seconds, dt.hundredths))
2835 {
2836 if (!(arc = doshWrite(pFile,
2837 ulLength,
2838 szTemp)))
2839 {
2840 va_list arg_ptr;
2841 va_start(arg_ptr, pcszFormat);
2842 ulLength = vsprintf(szTemp, pcszFormat, arg_ptr);
2843 va_end(arg_ptr);
2844
2845 if (pFile->flOpenMode & XOPEN_BINARY)
2846 // if we're in binary mode, we need to add \r too
2847 szTemp[ulLength++] = '\r';
2848 szTemp[ulLength++] = '\n';
2849
2850 arc = doshWrite(pFile,
2851 ulLength,
2852 szTemp);
2853 }
2854 }
2855 }
2856
2857 return (arc);
2858}
2859
2860/*
2861 * doshClose:
2862 *
2863 *@@added V0.9.16 (2001-10-19) [umoeller]
2864 */
2865
2866APIRET doshClose(PXFILE *ppFile)
2867{
2868 APIRET arc = NO_ERROR;
2869 PXFILE pFile;
2870
2871 if ( (ppFile)
2872 && (pFile = *ppFile)
2873 )
2874 {
2875 // request the mutex so that we won't be
2876 // taking the file away under someone's butt
2877 // if (!(arc = doshLockFile(pFile)))
2878 {
2879 // HMTX hmtx = pFile->hmtx;
2880 // pFile->hmtx = NULLHANDLE;
2881
2882 // now that the file is locked,
2883 // set the ptr to NULL
2884 *ppFile = NULL;
2885
2886 FREE(pFile->pbCache);
2887 FREE(pFile->pszFilename);
2888
2889 if (pFile->hf)
2890 {
2891 DosSetFileSize(pFile->hf, pFile->cbCurrent);
2892 DosClose(pFile->hf);
2893 pFile->hf = NULLHANDLE;
2894 }
2895
2896 // doshUnlockFile(pFile);
2897 // DosCloseMutexSem(pFile->hmtx);
2898 }
2899
2900 free(pFile);
2901 }
2902 else
2903 arc = ERROR_INVALID_PARAMETER;
2904
2905 return (arc);
2906}
2907
2908/*
2909 *@@ doshLoadTextFile:
2910 * reads a text file from disk, allocates memory
2911 * via malloc() and sets a pointer to this
2912 * buffer (or NULL upon errors).
2913 *
2914 * This allocates one extra byte to make the
2915 * buffer null-terminated always. The buffer
2916 * is _not_ converted WRT the line format.
2917 *
2918 * This returns the APIRET of DosOpen and DosRead.
2919 * If any error occured, no buffer was allocated.
2920 * Otherwise, you should free() the buffer when
2921 * no longer needed.
2922 *
2923 *@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from doshReadTextFile
2924 *@@changed V0.9.16 (2002-01-05) [umoeller]: added pcbRead
2925 *@@changed V0.9.16 (2002-01-05) [umoeller]: rewritten using doshOpen
2926 */
2927
2928APIRET doshLoadTextFile(PCSZ pcszFile, // in: file name to read
2929 PSZ* ppszContent, // out: newly allocated buffer with file's content
2930 PULONG pcbRead) // out: size of that buffer including null byte (ptr can be NULL)
2931{
2932 APIRET arc;
2933
2934 ULONG cbFile = 0;
2935 PXFILE pFile = NULL;
2936
2937 if (!(arc = doshOpen(pcszFile,
2938 XOPEN_READ_EXISTING,
2939 &cbFile,
2940 &pFile)))
2941 {
2942 PSZ pszContent;
2943 if (!(pszContent = (PSZ)malloc(cbFile + 1)))
2944 arc = ERROR_NOT_ENOUGH_MEMORY;
2945 else
2946 {
2947 ULONG cbRead = 0;
2948 if (!(arc = DosRead(pFile->hf,
2949 pszContent,
2950 cbFile,
2951 &cbRead)))
2952 {
2953 if (cbRead != cbFile)
2954 arc = ERROR_NO_DATA;
2955 else
2956 {
2957 pszContent[cbRead] = '\0';
2958 *ppszContent = pszContent;
2959 if (pcbRead)
2960 *pcbRead = cbRead + 1;
2961 }
2962 }
2963
2964 if (arc)
2965 free(pszContent);
2966 }
2967
2968 doshClose(&pFile);
2969 }
2970
2971 /*
2972 ULONG ulSize,
2973 ulBytesRead = 0,
2974 ulAction, ulLocal;
2975 HFILE hFile;
2976 PSZ pszContent = NULL;
2977
2978 APIRET arc;
2979
2980 *ppszContent = 0;
2981
2982 if (!(arc = DosOpen((PSZ)pcszFile,
2983 &hFile,
2984 &ulAction, // action taken
2985 5000L, // primary allocation size
2986 FILE_ARCHIVED | FILE_NORMAL, // file attribute
2987 OPEN_ACTION_OPEN_IF_EXISTS, // open flags
2988 OPEN_FLAGS_NOINHERIT
2989 | OPEN_SHARE_DENYNONE
2990 | OPEN_ACCESS_READONLY, // read-only mode
2991 NULL))) // no EAs
2992 {
2993 if (!(arc = doshQueryFileSize(hFile, &ulSize)))
2994 {
2995 pszContent = (PSZ)malloc(ulSize+1);
2996
2997 if (!(arc = DosSetFilePtr(hFile,
2998 0L,
2999 FILE_BEGIN,
3000 &ulLocal)))
3001 if (!(arc = DosRead(hFile,
3002 pszContent,
3003 ulSize,
3004 &ulBytesRead)))
3005 {
3006 *(pszContent+ulBytesRead) = 0;
3007 // set output buffer pointer
3008 *ppszContent = pszContent;
3009
3010 if (pcbRead)
3011 *pcbRead = ulBytesRead + 1;
3012 }
3013
3014 if (arc)
3015 free(pszContent);
3016 }
3017 DosClose(hFile);
3018 }
3019 */
3020
3021 return (arc);
3022}
3023
3024/*
3025 *@@ doshCreateBackupFileName:
3026 * creates a valid backup filename of pszExisting
3027 * with a numerical file name extension which does
3028 * not exist in the directory where pszExisting
3029 * resides.
3030 * Returns a PSZ to a new buffer which was allocated
3031 * using malloc().
3032 *
3033 * <B>Example:</B> returns "C:\CONFIG.002" for input
3034 * "C:\CONFIG.SYS" if "C:\CONFIG.001" already exists.
3035 *
3036 *@@changed V0.9.1 (99-12-13) [umoeller]: this crashed if pszExisting had no file extension
3037 */
3038
3039PSZ doshCreateBackupFileName(const char* pszExisting)
3040{
3041 CHAR szFilename[CCHMAXPATH];
3042 PSZ pszLastDot;
3043 ULONG ulCount = 1;
3044 CHAR szCount[5];
3045 ULONG ulDummy;
3046
3047 strcpy(szFilename, pszExisting);
3048
3049 if (!(pszLastDot = strrchr(szFilename, '.')))
3050 // no dot in filename:
3051 pszLastDot = szFilename + strlen(szFilename);
3052
3053 do
3054 {
3055 sprintf(szCount, ".%03lu", ulCount);
3056 strcpy(pszLastDot, szCount);
3057 ulCount++;
3058 } while (!doshQueryPathSize(szFilename, &ulDummy));
3059
3060 return (strdup(szFilename));
3061}
3062
3063/*
3064 *@@ doshCreateTempFileName:
3065 * produces a file name in the the specified directory
3066 * or $(TEMP) which presently doesn't exist. This
3067 * checks the directory for existing files, but does
3068 * not open the temp file.
3069 *
3070 * If (pcszDir != NULL), we look into that directory.
3071 * Otherwise we look into the directory specified
3072 * by the $(TEMP) environment variable.
3073 * If $(TEMP) is not set, $(TMP) is tried next.
3074 *
3075 * If the directory thus specified does not exist, the
3076 * root directory of the boot drive is used instead.
3077 * As a result, this function should be fairly bomb-proof.
3078 *
3079 * If (pcszExt != NULL), the temp file receives
3080 * that extension, or no extension otherwise.
3081 * Do not specify the dot in pcszExt.
3082 *
3083 * pszTempFileName receives the fully qualified
3084 * file name of the temp file in that directory
3085 * and must point to a buffer CCHMAXPATH in size.
3086 * The file name is 8+3 compliant if pcszExt does
3087 * not exceed three characters.
3088 *
3089 * If (pcszPrefix != NULL), the temp file name
3090 * is prefixed with pcszPrefix. Since the temp
3091 * file name must not exceed 8+3 letters, we
3092 * can only use ( 8 - strlen(pcszPrefix ) digits
3093 * for a random number to make the temp file name
3094 * unique. You must therefore use a maximum of
3095 * four characters for the prefix. Otherwise
3096 * ERROR_INVALID_PARAMETER is returned.
3097 *
3098 * Example: Assuming TEMP is set to C:\TEMP,
3099 +
3100 + doshCreateTempFileName(szBuffer,
3101 + NULL, // use $(TEMP)
3102 + "pre", // prefix
3103 + "tmp") // extension
3104 +
3105 * would produce something like "C:\TEMP\pre07FG2.tmp".
3106 *
3107 *@@added V0.9.9 (2001-04-04) [umoeller]
3108 */
3109
3110APIRET doshCreateTempFileName(PSZ pszTempFileName, // out: fully q'fied temp file name
3111 PCSZ pcszDir, // in: dir or NULL for %TEMP%
3112 PCSZ pcszPrefix, // in: prefix for temp file or NULL
3113 PCSZ pcszExt) // in: extension (without dot) or NULL
3114{
3115 APIRET arc = NO_ERROR;
3116
3117 ULONG ulPrefixLen = (pcszPrefix)
3118 ? strlen(pcszPrefix)
3119 : 0;
3120
3121 if ( (!pszTempFileName)
3122 || (ulPrefixLen > 4)
3123 )
3124 arc = ERROR_INVALID_PARAMETER;
3125 else
3126 {
3127 CHAR szDir[CCHMAXPATH] = "";
3128 FILESTATUS3 fs3;
3129
3130 const char *pcszTemp = pcszDir;
3131
3132 if (!pcszTemp)
3133 {
3134 if (!(pcszTemp = getenv("TEMP")))
3135 pcszTemp = getenv("TMP");
3136 }
3137
3138 if (pcszTemp) // either pcszDir or $(TEMP) or $(TMP) now
3139 if (DosQueryPathInfo((PSZ)pcszTemp,
3140 FIL_STANDARD,
3141 &fs3,
3142 sizeof(fs3)))
3143 // TEMP doesn't exist:
3144 pcszTemp = NULL;
3145
3146 if (!pcszTemp)
3147 // not set, or doesn't exist:
3148 // use root directory on boot drive
3149 sprintf(szDir,
3150 "%c:\\",
3151 doshQueryBootDrive());
3152 else
3153 {
3154 strcpy(szDir, pcszTemp);
3155 if (szDir[strlen(szDir) - 1] != '\\')
3156 strcat(szDir, "\\");
3157 }
3158
3159 if (!szDir[0])
3160 arc = ERROR_PATH_NOT_FOUND; // shouldn't happen
3161 else
3162 {
3163 ULONG ulRandom = 0;
3164 ULONG cAttempts = 0;
3165
3166 // produce random number
3167 DosQuerySysInfo(QSV_MS_COUNT,
3168 QSV_MS_COUNT,
3169 &ulRandom,
3170 sizeof(ulRandom));
3171
3172 do
3173 {
3174 CHAR szFile[20] = "",
3175 szFullTryThis[CCHMAXPATH];
3176
3177 // use the lower eight hex digits of the
3178 // system uptime as the temp dir name
3179 sprintf(szFile,
3180 "%08lX",
3181 ulRandom & 0xFFFFFFFF);
3182
3183 // if prefix is specified, overwrite the
3184 // first characters in the random number
3185 if (pcszPrefix)
3186 memcpy(szFile, pcszPrefix, ulPrefixLen);
3187
3188 if (pcszExt)
3189 {
3190 szFile[8] = '.';
3191 strcpy(szFile + 9, pcszExt);
3192 }
3193
3194 // now compose full temp file name
3195 strcpy(szFullTryThis, szDir);
3196 strcat(szFullTryThis, szFile);
3197 // now we have: "C:\temp\wpiXXXXX"
3198 if (DosQueryPathInfo(szFullTryThis,
3199 FIL_STANDARD,
3200 &fs3,
3201 sizeof(fs3))
3202 == ERROR_FILE_NOT_FOUND)
3203 {
3204 // file or dir doesn't exist:
3205 // cool, we're done
3206 strcpy(pszTempFileName, szFullTryThis);
3207 return (NO_ERROR);
3208 }
3209
3210 // if this didn't work, raise ulRandom and try again
3211 ulRandom += 123;
3212
3213 // try only 100 times, just to be sure
3214 cAttempts++;
3215 } while (cAttempts < 100);
3216
3217 // 100 loops elapsed:
3218 arc = ERROR_BAD_FORMAT;
3219 }
3220 }
3221
3222 return (arc);
3223}
3224
3225/*
3226 *@@ doshWriteTextFile:
3227 * writes a text file to disk; pszFile must contain the
3228 * whole path and filename.
3229 *
3230 * An existing file will be backed up if (pszBackup != NULL),
3231 * using doshCreateBackupFileName. In that case, pszBackup
3232 * receives the name of the backup created, so that buffer
3233 * should be CCHMAXPATH in size.
3234 *
3235 * If (pszbackup == NULL), an existing file will be overwritten.
3236 *
3237 * On success (NO_ERROR returned), *pulWritten receives
3238 * the no. of bytes written.
3239 *
3240 *@@changed V0.9.3 (2000-05-01) [umoeller]: optimized DosOpen; added error checking; changed prototype
3241 *@@changed V0.9.3 (2000-05-12) [umoeller]: added pszBackup
3242 */
3243
3244APIRET doshWriteTextFile(const char* pszFile, // in: file name
3245 const char* pszContent, // in: text to write
3246 PULONG pulWritten, // out: bytes written (ptr can be NULL)
3247 PSZ pszBackup) // in/out: create-backup?
3248{
3249 APIRET arc = NO_ERROR;
3250 ULONG ulWritten = 0;
3251
3252 if ((!pszFile) || (!pszContent))
3253 arc = ERROR_INVALID_PARAMETER;
3254 else
3255 {
3256 ULONG ulAction = 0,
3257 ulLocal = 0;
3258 HFILE hFile = 0;
3259
3260 ULONG ulSize = strlen(pszContent); // exclude 0 byte
3261
3262 if (pszBackup)
3263 {
3264 PSZ pszBackup2 = doshCreateBackupFileName(pszFile);
3265 DosCopy((PSZ)pszFile,
3266 pszBackup2,
3267 DCPY_EXISTING); // delete existing
3268 strcpy(pszBackup, pszBackup2);
3269 free(pszBackup2);
3270 }
3271
3272 if (!(arc = DosOpen((PSZ)pszFile,
3273 &hFile,
3274 &ulAction, // action taken
3275 ulSize, // primary allocation size
3276 FILE_ARCHIVED | FILE_NORMAL, // file attribute
3277 OPEN_ACTION_CREATE_IF_NEW
3278 | OPEN_ACTION_REPLACE_IF_EXISTS, // open flags
3279 OPEN_FLAGS_NOINHERIT
3280 | OPEN_FLAGS_SEQUENTIAL // sequential, not random access
3281 | OPEN_SHARE_DENYWRITE // deny write mode
3282 | OPEN_ACCESS_WRITEONLY, // write mode
3283 NULL))) // no EAs
3284 {
3285 if (!(arc = DosSetFilePtr(hFile,
3286 0L,
3287 FILE_BEGIN,
3288 &ulLocal)))
3289 if (!(arc = DosWrite(hFile,
3290 (PVOID)pszContent,
3291 ulSize,
3292 &ulWritten)))
3293 arc = DosSetFileSize(hFile, ulSize);
3294
3295 DosClose(hFile);
3296 }
3297 } // end if ((pszFile) && (pszContent))
3298
3299 if (pulWritten)
3300 *pulWritten = ulWritten;
3301
3302 return (arc);
3303}
3304
3305/*
3306 *@@category: Helpers\Control program helpers\Directory management
3307 * directory helpers (querying, creating, deleting etc.).
3308 */
3309
3310/* ******************************************************************
3311 *
3312 * Directory helpers
3313 *
3314 ********************************************************************/
3315
3316/*
3317 *@@ doshQueryDirExist:
3318 * returns TRUE if the given directory
3319 * exists and really is a directory.
3320 */
3321
3322BOOL doshQueryDirExist(PCSZ pcszDir)
3323{
3324 FILESTATUS3 fs3;
3325 APIRET arc;
3326
3327 if (!(arc = DosQueryPathInfo((PSZ)pcszDir,
3328 FIL_STANDARD,
3329 &fs3,
3330 sizeof(fs3))))
3331 // file found:
3332 return ((fs3.attrFile & FILE_DIRECTORY) != 0);
3333 else
3334 return FALSE;
3335}
3336
3337/*
3338 *@@ doshCreatePath:
3339 * this creates the specified directory.
3340 * As opposed to DosCreateDir, this
3341 * function can create several directories
3342 * at the same time, if the parent
3343 * directories do not exist yet.
3344 */
3345
3346APIRET doshCreatePath(PCSZ pcszPath,
3347 BOOL fHidden) // in: if TRUE, the new directories will get FILE_HIDDEN
3348{
3349 APIRET arc0 = NO_ERROR;
3350 CHAR path[CCHMAXPATH];
3351 CHAR *cp, c;
3352 ULONG cbPath;
3353
3354 strcpy(path, pcszPath);
3355 cbPath = strlen(path);
3356
3357 if (path[cbPath] != '\\')
3358 {
3359 path[cbPath] = '\\';
3360 path[cbPath+1] = 0;
3361 }
3362
3363 cp = path;
3364 // advance past the drive letter only if we have one
3365 if (*(cp+1) == ':')
3366 cp += 3;
3367
3368 // go!
3369 while (*cp != 0)
3370 {
3371 if (*cp == '\\')
3372 {
3373 c = *cp;
3374 *cp = 0;
3375 if (!doshQueryDirExist(path))
3376 {
3377 APIRET arc = DosCreateDir(path,
3378 0); // no EAs
3379 if (arc != NO_ERROR)
3380 {
3381 arc0 = arc;
3382 break;
3383 }
3384 else
3385 if (fHidden)
3386 {
3387 // hide the directory we just created
3388 doshSetPathAttr(path, FILE_HIDDEN);
3389 }
3390 }
3391 *cp = c;
3392 }
3393 cp++;
3394 }
3395 return (arc0);
3396}
3397
3398/*
3399 *@@ doshQueryCurrentDir:
3400 * writes the current directory into
3401 * the specified buffer, which should be
3402 * CCHMAXPATH in size.
3403 *
3404 * As opposed to DosQueryCurrentDir, this
3405 * includes the drive letter.
3406 *
3407 *@@added V0.9.4 (2000-07-22) [umoeller]
3408 */
3409
3410APIRET doshQueryCurrentDir(PSZ pszBuf)
3411{
3412 APIRET arc = NO_ERROR;
3413 ULONG ulCurDisk = 0;
3414 ULONG ulMap = 0;
3415 if (!(arc = DosQueryCurrentDisk(&ulCurDisk, &ulMap)))
3416 {
3417 ULONG cbBuf = CCHMAXPATH - 3;
3418 pszBuf[0] = ulCurDisk + 'A' - 1;
3419 pszBuf[1] = ':';
3420 pszBuf[2] = '\\';
3421 pszBuf[3] = '\0';
3422 arc = DosQueryCurrentDir(0, pszBuf + 3, &cbBuf);
3423 }
3424
3425 return (arc);
3426}
3427
3428/*
3429 *@@ doshDeleteDir:
3430 * deletes a directory. As opposed to DosDeleteDir,
3431 * this removes empty subdirectories and/or files
3432 * as well.
3433 *
3434 * flFlags can be any combination of the following:
3435 *
3436 * -- DOSHDELDIR_RECURSE: recurse into subdirectories.
3437 *
3438 * -- DOSHDELDIR_DELETEFILES: delete all regular files
3439 * which are found on the way.
3440 *
3441 * THIS IS NOT IMPLEMENTED YET.
3442 *
3443 * If 0 is specified, this effectively behaves just as
3444 * DosDeleteDir.
3445 *
3446 * If you specify DOSHDELDIR_RECURSE only, only empty
3447 * subdirectories are deleted as well.
3448 *
3449 * If you specify DOSHDELDIR_RECURSE | DOSHDELDIR_DELETEFILES,
3450 * this removes an entire directory tree, including all
3451 * subdirectories and files.
3452 *
3453 *@@added V0.9.4 (2000-07-01) [umoeller]
3454 */
3455
3456APIRET doshDeleteDir(PCSZ pcszDir,
3457 ULONG flFlags,
3458 PULONG pulDirs, // out: directories found
3459 PULONG pulFiles) // out: files found
3460{
3461 APIRET arc = NO_ERROR,
3462 arcReturn = NO_ERROR;
3463
3464 HDIR hdirFindHandle = HDIR_CREATE;
3465 FILEFINDBUF3 ffb3 = {0}; // returned from FindFirst/Next
3466 ULONG ulResultBufLen = sizeof(FILEFINDBUF3);
3467 ULONG ulFindCount = 1; // look for 1 file at a time
3468
3469 CHAR szFileMask[2*CCHMAXPATH];
3470 sprintf(szFileMask, "%s\\*", pcszDir);
3471
3472 // find files
3473 arc = DosFindFirst(szFileMask,
3474 &hdirFindHandle, // directory search handle
3475 FILE_ARCHIVED | FILE_DIRECTORY | FILE_SYSTEM
3476 | FILE_HIDDEN | FILE_READONLY,
3477 // search attributes; include all, even dirs
3478 &ffb3, // result buffer
3479 ulResultBufLen, // result buffer length
3480 &ulFindCount, // number of entries to find
3481 FIL_STANDARD); // return level 1 file info
3482
3483 if (arc == NO_ERROR)
3484 {
3485 // keep finding the next file until there are no more files
3486 while (arc == NO_ERROR) // != ERROR_NO_MORE_FILES
3487 {
3488 if (ffb3.attrFile & FILE_DIRECTORY)
3489 {
3490 // we found a directory:
3491
3492 // ignore the pseudo-directories
3493 if ( (strcmp(ffb3.achName, ".") != 0)
3494 && (strcmp(ffb3.achName, "..") != 0)
3495 )
3496 {
3497 // real directory:
3498 if (flFlags & DOSHDELDIR_RECURSE)
3499 {
3500 // recurse!
3501 CHAR szSubDir[2*CCHMAXPATH];
3502 sprintf(szSubDir, "%s\\%s", pcszDir, ffb3.achName);
3503 arcReturn = doshDeleteDir(szSubDir,
3504 flFlags,
3505 pulDirs,
3506 pulFiles);
3507 // this removes ffb3.achName as well
3508 }
3509 else
3510 {
3511 // directory, but no recursion:
3512 // report "access denied"
3513 // (this is what OS/2 reports with DosDeleteDir as well)
3514 arcReturn = ERROR_ACCESS_DENIED; // 5
3515 (*pulDirs)++;
3516 }
3517 }
3518 }
3519 else
3520 {
3521 // it's a file:
3522 arcReturn = ERROR_ACCESS_DENIED;
3523 (*pulFiles)++;
3524 }
3525
3526 if (arc == NO_ERROR)
3527 {
3528 ulFindCount = 1; // reset find count
3529 arc = DosFindNext(hdirFindHandle, // directory handle
3530 &ffb3, // result buffer
3531 ulResultBufLen, // result buffer length
3532 &ulFindCount); // number of entries to find
3533 }
3534 } // endwhile
3535
3536 DosFindClose(hdirFindHandle); // close our find handle
3537 }
3538
3539 if (arcReturn == NO_ERROR)
3540 // success so far:
3541 // delete our directory now
3542 arc = DosDeleteDir((PSZ)pcszDir);
3543
3544 return (arcReturn);
3545}
3546
3547/*
3548 *@@category: Helpers\Control program helpers\Module handling
3549 * helpers for importing functions from a module (DLL).
3550 */
3551
3552/* ******************************************************************
3553 *
3554 * Module handling helpers
3555 *
3556 ********************************************************************/
3557
3558/*
3559 *@@ doshQueryProcAddr:
3560 * attempts to resolve the given procedure from
3561 * the given module name. Saves you from querying
3562 * the module handle and all that.
3563 *
3564 * This is intended for resolving undocumented
3565 * APIs from OS/2 system DLls such as PMMERGE
3566 * and DOSCALLS. It is assumed that the specified
3567 * module is already loaded.
3568 *
3569 * Returns:
3570 *
3571 * -- NO_ERROR
3572 *
3573 * -- ERROR_INVALID_NAME
3574 *
3575 * -- ERROR_INVALID_ORDINAL
3576 *
3577 * plus the error codes of DosLoadModule.
3578 *
3579 *@@added V0.9.16 (2002-01-13) [umoeller]
3580 */
3581
3582APIRET doshQueryProcAddr(PCSZ pcszModuleName, // in: module name (e.g. "PMMERGE")
3583 ULONG ulOrdinal, // in: proc ordinal
3584 PFN *ppfn) // out: proc address
3585{
3586 HMODULE hmod;
3587 APIRET arc;
3588 if (!(arc = DosQueryModuleHandle((PSZ)pcszModuleName,
3589 &hmod)))
3590 {
3591 if ((arc = DosQueryProcAddr(hmod,
3592 ulOrdinal,
3593 NULL,
3594 ppfn)))
3595 {
3596 // the CP programming guide and reference says use
3597 // DosLoadModule if DosQueryProcAddr fails with this error
3598 if (arc == ERROR_INVALID_HANDLE)
3599 {
3600 if (!(arc = DosLoadModule(NULL,
3601 0,
3602 (PSZ)pcszModuleName,
3603 &hmod)))
3604 {
3605 arc = DosQueryProcAddr(hmod,
3606 ulOrdinal,
3607 NULL,
3608 ppfn);
3609 }
3610 }
3611 }
3612 }
3613
3614 return (arc);
3615}
3616
3617/*
3618 *@@ doshResolveImports:
3619 * this function loads the module called pszModuleName
3620 * and resolves imports dynamically using DosQueryProcAddress.
3621 *
3622 * To specify the functions to be imported, a RESOLVEFUNCTION
3623 * array is used. In each of the array items, specify the
3624 * name of the function and a pointer to a function pointer
3625 * where to store the resolved address.
3626 *
3627 *@@added V0.9.3 (2000-04-29) [umoeller]
3628 */
3629
3630APIRET doshResolveImports(PCSZ pcszModuleName, // in: DLL to load
3631 HMODULE *phmod, // out: module handle
3632 PCRESOLVEFUNCTION paResolves, // in/out: function resolves
3633 ULONG cResolves) // in: array item count (not array size!)
3634{
3635 CHAR szName[CCHMAXPATH];
3636 APIRET arc;
3637
3638 if (!(arc = DosLoadModule(szName,
3639 sizeof(szName),
3640 (PSZ)pcszModuleName,
3641 phmod)))
3642 {
3643 ULONG ul;
3644 for (ul = 0;
3645 ul < cResolves;
3646 ul++)
3647 {
3648 if (arc = DosQueryProcAddr(*phmod,
3649 0, // ordinal, ignored
3650 (PSZ)paResolves[ul].pcszFunctionName,
3651 paResolves[ul].ppFuncAddress))
3652 // error:
3653 break;
3654 }
3655
3656 if (arc)
3657 // V0.9.16 (2001-12-08) [umoeller]
3658 DosFreeModule(*phmod);
3659 }
3660
3661 return (arc);
3662}
3663
3664/*
3665 *@@category: Helpers\Control program helpers\Performance (CPU load) helpers
3666 * helpers around DosPerfSysCall.
3667 */
3668
3669/* ******************************************************************
3670 *
3671 * Performance Counters (CPU Load)
3672 *
3673 ********************************************************************/
3674
3675/*
3676 *@@ doshPerfOpen:
3677 * initializes the OS/2 DosPerfSysCall API for
3678 * the calling thread.
3679 *
3680 * Note: This API is not supported on all OS/2
3681 * versions. I believe it came up with some Warp 4
3682 * fixpak. The API is resolved dynamically by
3683 * this function (using DosQueryProcAddr).
3684 *
3685 * This properly initializes the internal counters
3686 * which the OS/2 kernel uses for this API. Apparently,
3687 * with newer kernels (FP13/14), IBM has chosen to no
3688 * longer do this automatically, which is the reason
3689 * why many "pulse" utilities display garbage with these
3690 * fixpaks.
3691 *
3692 * After NO_ERROR is returned, DOSHPERFSYS.cProcessors
3693 * contains the no. of processors found on the system.
3694 * All pointers in DOSHPERFSYS then point to arrays
3695 * which have exactly cProcessors array items.
3696 *
3697 * So after NO_ERROR was returned here, you can keep
3698 * calling doshPerfGet to get a current snapshot of the
3699 * IRQ and user loads for each CPU. Note that interrupts
3700 * are only processed on CPU 0 on SMP systems.
3701 *
3702 * Call doshPerfClose to clean up resources allocated
3703 * by this function.
3704 *
3705 * For more sample code, take a look at the "Pulse" widget
3706 * in the XWorkplace sources (src\xcenter\w_pulse.c).
3707 *
3708 * Example code:
3709 *
3710 + PDOSHPERFSYS pPerf = NULL;
3711 + APIRET arc;
3712 + if (!(arc = arc = doshPerfOpen(&pPerf)))
3713 + {
3714 + // this should really be in a timer,
3715 + // e.g. once per second
3716 + ULONG ulCPU;
3717 + if (!(arc = doshPerfGet(&pPerf)))
3718 + {
3719 + // go thru all CPUs
3720 + for (ulCPU = 0; ulCPU < pPerf->cProcessors; ulCPU++)
3721 + {
3722 + LONG lUserLoadThis = pPerf->palLoads[ulCPU];
3723 + ...
3724 + }
3725 + }
3726 +
3727 + // clean up
3728 + doshPerfClose(&pPerf);
3729 + }
3730 +
3731 *
3732 *@@added V0.9.7 (2000-12-02) [umoeller]
3733 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
3734 */
3735
3736APIRET doshPerfOpen(PDOSHPERFSYS *ppPerfSys) // out: new DOSHPERFSYS structure
3737{
3738 APIRET arc = NO_ERROR;
3739
3740 // allocate DOSHPERFSYS structure
3741 *ppPerfSys = (PDOSHPERFSYS)malloc(sizeof(DOSHPERFSYS));
3742 if (!*ppPerfSys)
3743 arc = ERROR_NOT_ENOUGH_MEMORY;
3744 else
3745 {
3746 // initialize structure
3747 PDOSHPERFSYS pPerfSys = *ppPerfSys;
3748 memset(pPerfSys, 0, sizeof(*pPerfSys));
3749
3750 // resolve DosPerfSysCall API entry
3751 if (!(arc = DosLoadModule(NULL, 0, "DOSCALLS", &pPerfSys->hmod)))
3752 {
3753 if (!(arc = DosQueryProcAddr(pPerfSys->hmod,
3754 976,
3755 "DosPerfSysCall",
3756 (PFN*)(&pPerfSys->pDosPerfSysCall))))
3757 {
3758 // OK, we got the API: initialize!
3759 if (!(arc = pPerfSys->pDosPerfSysCall(CMD_KI_ENABLE, 0, 0, 0)))
3760 {
3761 pPerfSys->fInitialized = TRUE;
3762 // call CMD_KI_DISABLE later
3763
3764 if (!(arc = pPerfSys->pDosPerfSysCall(CMD_PERF_INFO,
3765 0,
3766 (ULONG)(&pPerfSys->cProcessors),
3767 0)))
3768 {
3769 ULONG ul = 0,
3770 cProcs = pPerfSys->cProcessors,
3771 cbDouble = cProcs * sizeof(double),
3772 cbLong = cProcs * sizeof(LONG);
3773
3774 // allocate arrays
3775 if ( (!(pPerfSys->paCPUUtils = (PCPUUTIL)calloc(cProcs,
3776 sizeof(CPUUTIL))))
3777 || (!(pPerfSys->padBusyPrev
3778 = (double*)malloc(cbDouble)))
3779 || (!(pPerfSys->padTimePrev
3780 = (double*)malloc(cbDouble)))
3781 || (!(pPerfSys->padIntrPrev
3782 = (double*)malloc(cbDouble)))
3783 || (!(pPerfSys->palLoads
3784 = (PLONG)malloc(cbLong)))
3785 || (!(pPerfSys->palIntrs
3786 = (PLONG)malloc(cbLong)))
3787 )
3788 arc = ERROR_NOT_ENOUGH_MEMORY;
3789 else
3790 {
3791 for (ul = 0; ul < cProcs; ul++)
3792 {
3793 pPerfSys->padBusyPrev[ul] = 0.0;
3794 pPerfSys->padTimePrev[ul] = 0.0;
3795 pPerfSys->padIntrPrev[ul] = 0.0;
3796 pPerfSys->palLoads[ul] = 0;
3797 pPerfSys->palIntrs[ul] = 0;
3798 }
3799 }
3800 }
3801 }
3802 } // end if (arc == NO_ERROR)
3803 } // end if (arc == NO_ERROR)
3804
3805 if (arc)
3806 // error: clean up
3807 doshPerfClose(ppPerfSys);
3808
3809 } // end else if (!*ppPerfSys)
3810
3811 return (arc);
3812}
3813
3814/*
3815 *@@ doshPerfGet:
3816 * calculates a current snapshot of the system load,
3817 * compared with the load which was calculated on
3818 * the previous call.
3819 *
3820 * If you want to continually measure the system CPU
3821 * load, this is the function you will want to call
3822 * regularly -- e.g. with a timer once per second.
3823 *
3824 * Call this ONLY if doshPerfOpen returned NO_ERROR,
3825 * or you'll get crashes.
3826 *
3827 * If this call returns NO_ERROR, you get LONG load
3828 * values for each CPU in the system in the arrays
3829 * in DOSHPERFSYS (in per-mille, 0-1000).
3830 *
3831 * There are two arrays:
3832 *
3833 * -- DOSHPERFSYS.palLoads contains the "user" load
3834 * for each CPU.
3835 *
3836 * -- DOSHPERFSYS.palIntrs contains the "IRQ" load
3837 * for each CPU.
3838 *
3839 * Sum up the two values to get the total load for
3840 * each CPU.
3841 *
3842 * For example, if there are two CPUs, after this call,
3843 *
3844 * -- DOSHPERFSYS.palLoads[0] contains the "user" load
3845 * of the first CPU,
3846 *
3847 * -- DOSHPERFSYS.palLoads[0] contains the "user" load
3848 * of the second CPU.
3849 *
3850 * See doshPerfOpen for example code.
3851 *
3852 *@@added V0.9.7 (2000-12-02) [umoeller]
3853 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
3854 */
3855
3856APIRET doshPerfGet(PDOSHPERFSYS pPerfSys)
3857{
3858 APIRET arc = NO_ERROR;
3859 if (!pPerfSys->pDosPerfSysCall)
3860 arc = ERROR_INVALID_PARAMETER;
3861 else
3862 {
3863 if (!(arc = pPerfSys->pDosPerfSysCall(CMD_KI_RDCNT,
3864 (ULONG)pPerfSys->paCPUUtils,
3865 0, 0)))
3866 {
3867 // go thru all processors
3868 ULONG ul = 0;
3869 for (; ul < pPerfSys->cProcessors; ul++)
3870 {
3871 PCPUUTIL pCPUUtilThis = &pPerfSys->paCPUUtils[ul];
3872
3873 double dTime = LL2F(pCPUUtilThis->ulTimeHigh,
3874 pCPUUtilThis->ulTimeLow);
3875 double dBusy = LL2F(pCPUUtilThis->ulBusyHigh,
3876 pCPUUtilThis->ulBusyLow);
3877 double dIntr = LL2F(pCPUUtilThis->ulIntrHigh,
3878 pCPUUtilThis->ulIntrLow);
3879
3880 double *pdBusyPrevThis = &pPerfSys->padBusyPrev[ul];
3881 double *pdTimePrevThis = &pPerfSys->padTimePrev[ul];
3882 double *pdIntrPrevThis = &pPerfSys->padIntrPrev[ul];
3883
3884 // avoid division by zero
3885 double dTimeDelta;
3886 if (dTimeDelta = (dTime - *pdTimePrevThis))
3887 {
3888 pPerfSys->palLoads[ul]
3889 = (LONG)( (double)( (dBusy - *pdBusyPrevThis)
3890 / dTimeDelta
3891 * 1000.0
3892 )
3893 );
3894 pPerfSys->palIntrs[ul]
3895 = (LONG)( (double)( (dIntr - *pdIntrPrevThis)
3896 / dTimeDelta
3897 * 1000.0
3898 )
3899 );
3900 }
3901 else
3902 {
3903 // no clear readings are available
3904 pPerfSys->palLoads[ul] = 0;
3905 pPerfSys->palIntrs[ul] = 0;
3906 }
3907
3908 *pdTimePrevThis = dTime;
3909 *pdBusyPrevThis = dBusy;
3910 *pdIntrPrevThis = dIntr;
3911 }
3912 }
3913 }
3914
3915 return (arc);
3916}
3917
3918/*
3919 *@@ doshPerfClose:
3920 * frees all resources allocated by doshPerfOpen.
3921 *
3922 *@@added V0.9.7 (2000-12-02) [umoeller]
3923 *@@changed V0.9.9 (2001-02-06) [umoeller]: removed disable; this broke the WarpCenter
3924 *@@changed V0.9.9 (2001-03-14) [umoeller]: fixed memory leak
3925 *@@changed V0.9.9 (2001-03-14) [umoeller]: added interrupt loads; thanks phaller
3926 */
3927
3928APIRET doshPerfClose(PDOSHPERFSYS *ppPerfSys)
3929{
3930 APIRET arc = NO_ERROR;
3931 PDOSHPERFSYS pPerfSys;
3932 if ( (!ppPerfSys)
3933 || (!(pPerfSys = *ppPerfSys))
3934 )
3935 arc = ERROR_INVALID_PARAMETER;
3936 else
3937 {
3938 // do not call this, this messes up the WarpCenter V0.9.9 (2001-02-06) [umoeller]
3939 // if (pPerfSys->fInitialized) pPerfSys->pDosPerfSysCall(CMD_KI_DISABLE, 0, 0, 0);
3940
3941 if (pPerfSys->paCPUUtils)
3942 free(pPerfSys->paCPUUtils);
3943 if (pPerfSys->padBusyPrev)
3944 free(pPerfSys->padBusyPrev);
3945 if (pPerfSys->padTimePrev)
3946 free(pPerfSys->padTimePrev);
3947 if (pPerfSys->padIntrPrev)
3948 free(pPerfSys->padIntrPrev);
3949 if (pPerfSys->palLoads) // was missing V0.9.9 (2001-03-14) [umoeller]
3950 free(pPerfSys->palLoads);
3951 if (pPerfSys->palIntrs)
3952 free(pPerfSys->palIntrs);
3953
3954 if (pPerfSys->hmod)
3955 DosFreeModule(pPerfSys->hmod);
3956 free(pPerfSys);
3957 *ppPerfSys = NULL;
3958 }
3959
3960 return (arc);
3961}
3962
3963/*
3964 *@@category: Helpers\Control program helpers\Process management
3965 * helpers for starting subprocesses.
3966 */
3967
3968/* ******************************************************************
3969 *
3970 * Process helpers
3971 *
3972 ********************************************************************/
3973
3974static PVOID // G_pvGlobalInfoSeg = NULL,
3975 G_pvLocalInfoSeg = NULL;
3976
3977USHORT _Far16 _Pascal Dos16GetInfoSeg(PSEL pselGlobal,
3978 PSEL pselLocal);
3979
3980/*
3981 * GetInfoSegs:
3982 *
3983 */
3984
3985static VOID GetInfoSegs(VOID)
3986{
3987 SEL GlobalInfoSegSelector,
3988 LocalInfoSegSelector;
3989
3990 // get selectors (old 16-bit API; this gets called only once)
3991 Dos16GetInfoSeg(&GlobalInfoSegSelector,
3992 &LocalInfoSegSelector);
3993 // thunk
3994 /* G_pvGlobalInfoSeg = (PVOID)( (GlobalInfoSegSelector << 0x000D)
3995 & 0x01fff0000); */
3996 G_pvLocalInfoSeg = (PVOID)( (LocalInfoSegSelector << 0x000D)
3997 & 0x01fff0000);
3998}
3999
4000/*
4001 *@@ doshMyPID:
4002 * returns the PID of the current process.
4003 *
4004 * This uses an interesting hack which is way
4005 * faster than DosGetInfoBlocks.
4006 *
4007 *@@added V0.9.9 (2001-04-04) [umoeller]
4008 */
4009
4010ULONG doshMyPID(VOID)
4011{
4012 if (!G_pvLocalInfoSeg)
4013 // first call:
4014 GetInfoSegs();
4015
4016 // PID is at offset 0 in the local info seg
4017 return (*(PUSHORT)G_pvLocalInfoSeg);
4018}
4019
4020/*
4021 *@@ doshMyTID:
4022 * returns the TID of the current thread.
4023 *
4024 * This uses an interesting hack which is way
4025 * faster than DosGetInfoBlocks.
4026 *
4027 *@@added V0.9.9 (2001-04-04) [umoeller]
4028 */
4029
4030ULONG doshMyTID(VOID)
4031{
4032 if (!G_pvLocalInfoSeg)
4033 // first call:
4034 GetInfoSegs();
4035
4036 // TID is at offset 6 in the local info seg
4037 return (*(PUSHORT)((PBYTE)G_pvLocalInfoSeg + 6));
4038}
4039
4040/*
4041 *@@ doshExecVIO:
4042 * executes cmd.exe with the /c parameter
4043 * and pcszExecWithArgs. This is equivalent
4044 * to the C library system() function,
4045 * except that an OS/2 error code is returned
4046 * and that this works with the VAC subsystem
4047 * library.
4048 *
4049 * This uses DosExecPgm and handles the sick
4050 * argument passing.
4051 *
4052 * If NO_ERROR is returned, *plExitCode receives
4053 * the exit code of the process. If the process
4054 * was terminated abnormally, *plExitCode receives:
4055 *
4056 * -- -1: hard error halt
4057 * -- -2: 16-bit trap
4058 * -- -3: DosKillProcess
4059 * -- -4: 32-bit exception
4060 *
4061 *@@added V0.9.4 (2000-07-27) [umoeller]
4062 */
4063
4064APIRET doshExecVIO(PCSZ pcszExecWithArgs,
4065 PLONG plExitCode) // out: exit code (ptr can be NULL)
4066{
4067 APIRET arc = NO_ERROR;
4068
4069 if (strlen(pcszExecWithArgs) > 255)
4070 arc = ERROR_INSUFFICIENT_BUFFER;
4071 else
4072 {
4073 CHAR szObj[CCHMAXPATH];
4074 RESULTCODES res = {0};
4075 CHAR szBuffer[300];
4076
4077 // DosExecPgm expects two args in szBuffer:
4078 // -- cmd.exe\0
4079 // -- then the actual argument, terminated by two 0 bytes.
4080 memset(szBuffer, 0, sizeof(szBuffer));
4081 strcpy(szBuffer, "cmd.exe\0");
4082 sprintf(szBuffer + 8, "/c %s",
4083 pcszExecWithArgs);
4084
4085 arc = DosExecPgm(szObj, sizeof(szObj),
4086 EXEC_SYNC,
4087 szBuffer,
4088 0,
4089 &res,
4090 "cmd.exe");
4091 if ((arc == NO_ERROR) && (plExitCode))
4092 {
4093 if (res.codeTerminate == 0)
4094 // normal exit:
4095 *plExitCode = res.codeResult;
4096 else
4097 *plExitCode = -((LONG)res.codeTerminate);
4098 }
4099 }
4100
4101 return (arc);
4102}
4103
4104/*
4105 *@@ doshQuickStartSession:
4106 * this is a shortcut to DosStartSession w/out having to
4107 * deal with all the messy parameters.
4108 *
4109 * This one starts pszPath as a child session and passes
4110 * pszParams to it.
4111 *
4112 * In usPgmCtl, OR any or none of the following (V0.9.0):
4113 * -- SSF_CONTROL_NOAUTOCLOSE (0x0008): do not close automatically.
4114 * This bit is used only for VIO Windowable apps and ignored
4115 * for all other applications.
4116 * -- SSF_CONTROL_MINIMIZE (0x0004)
4117 * -- SSF_CONTROL_MAXIMIZE (0x0002)
4118 * -- SSF_CONTROL_INVISIBLE (0x0001)
4119 * -- SSF_CONTROL_VISIBLE (0x0000)
4120 *
4121 * Specifying 0 will therefore auto-close the session and make it
4122 * visible.
4123 *
4124 * If (fWait), this function will create a termination queue
4125 * and not return until the child session has ended. Be warned,
4126 * this blocks the calling thread.
4127 *
4128 * Otherwise the function will return immediately.
4129 *
4130 * The session and process IDs of the child session will be
4131 * written to *pulSID and *ppid. Of course, in "wait" mode,
4132 * these are no longer valid after this function returns.
4133 *
4134 * Returns the error code of DosStartSession.
4135 *
4136 * Note: According to CPREF, calling DosStartSession calls
4137 * DosExecPgm in turn for all full-screen, VIO, and PM sessions.
4138 *
4139 *@@changed V0.9.0 [umoeller]: prototype changed to include usPgmCtl
4140 *@@changed V0.9.1 (99-12-30) [umoeller]: queue was sometimes not closed. Fixed.
4141 *@@changed V0.9.3 (2000-05-03) [umoeller]: added fForeground
4142 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed potential queue leak
4143 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed memory leak in wait mode; added pusReturn to prototype
4144 */
4145
4146APIRET doshQuickStartSession(PCSZ pcszPath, // in: program to start
4147 PCSZ pcszParams, // in: parameters for program
4148 BOOL fForeground, // in: if TRUE, session will be in foreground
4149 USHORT usPgmCtl, // in: STARTDATA.PgmControl
4150 BOOL fWait, // in: wait for termination?
4151 PULONG pulSID, // out: session ID (req.)
4152 PPID ppid, // out: process ID (req.)
4153 PUSHORT pusReturn) // out: in wait mode, session's return code (ptr can be NULL)
4154{
4155 APIRET arc = NO_ERROR;
4156 // queue stuff
4157 const char *pcszQueueName = "\\queues\\xwphlpsw.que";
4158 HQUEUE hq = 0;
4159 PID qpid = 0;
4160
4161 if (fWait)
4162 {
4163 if (!(arc = DosCreateQueue(&hq,
4164 QUE_FIFO | QUE_CONVERT_ADDRESS,
4165 (PSZ)pcszQueueName)))
4166 arc = DosOpenQueue(&qpid, &hq, (PSZ)pcszQueueName);
4167 }
4168
4169 if (!arc) // V0.9.14 (2001-08-03) [umoeller]
4170 {
4171 STARTDATA SData;
4172 CHAR szObjBuf[CCHMAXPATH];
4173
4174 SData.Length = sizeof(STARTDATA);
4175 SData.Related = SSF_RELATED_CHILD; //INDEPENDENT;
4176 SData.FgBg = (fForeground) ? SSF_FGBG_FORE : SSF_FGBG_BACK;
4177 // V0.9.3 (2000-05-03) [umoeller]
4178 SData.TraceOpt = SSF_TRACEOPT_NONE;
4179
4180 SData.PgmTitle = (PSZ)pcszPath; // title for window
4181 SData.PgmName = (PSZ)pcszPath;
4182 SData.PgmInputs = (PSZ)pcszParams;
4183
4184 SData.TermQ = (fWait) ? (PSZ)pcszQueueName : NULL;
4185 SData.Environment = 0;
4186 SData.InheritOpt = SSF_INHERTOPT_PARENT;
4187 SData.SessionType = SSF_TYPE_DEFAULT;
4188 SData.IconFile = 0;
4189 SData.PgmHandle = 0;
4190
4191 SData.PgmControl = usPgmCtl;
4192
4193 SData.InitXPos = 30;
4194 SData.InitYPos = 40;
4195 SData.InitXSize = 200;
4196 SData.InitYSize = 140;
4197 SData.Reserved = 0;
4198 SData.ObjectBuffer = szObjBuf;
4199 SData.ObjectBuffLen = (ULONG)sizeof(szObjBuf);
4200
4201 if ( (!(arc = DosStartSession(&SData, pulSID, ppid)))
4202 && (fWait)
4203 )
4204 {
4205 // block on the termination queue, which is written
4206 // to when the subprocess ends
4207 REQUESTDATA rqdata;
4208 ULONG cbData = 0;
4209 PULONG pulData = NULL;
4210 BYTE elpri;
4211
4212 rqdata.pid = qpid;
4213 if (!(arc = DosReadQueue(hq, // in: queue handle
4214 &rqdata, // out: pid and ulData
4215 &cbData, // out: size of data returned
4216 (PVOID*)&pulData, // out: data returned
4217 0, // in: remove first element in queue
4218 0, // in: wait for queue data (block thread)
4219 &elpri, // out: element's priority
4220 0))) // in: event semaphore to be posted
4221 {
4222 if (!rqdata.ulData)
4223 {
4224 // child session ended:
4225 // V0.9.14 (2001-08-03) [umoeller]
4226
4227 // *pulSID = (*pulData) & 0xffff;
4228 if (pusReturn)
4229 *pusReturn = ((*pulData) >> 16) & 0xffff;
4230
4231 }
4232 // else: continue looping
4233
4234 if (pulData)
4235 DosFreeMem(pulData);
4236 }
4237 }
4238 }
4239
4240 if (hq)
4241 DosCloseQueue(hq);
4242
4243 return (arc);
4244}
4245
4246
Note: See TracBrowser for help on using the repository browser.