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

Last change on this file since 204 was 201, checked in by umoeller, 23 years ago

Major work on textview control and dialogs.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 137.2 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 (regardless of convenience packs)
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("\\long.name.file")
1192 * on the drive to see if it supports long
1193 * filenames (unless it's a "well-known"
1194 * file-system and we know it does). If enabled,
1195 * the DFL_SUPPORTS_LONGNAMES flag is reliable.
1196 * Note that this does not check for what special
1197 * characters are supported in file names.
1198 *
1199 * This should return only one of the following:
1200 *
1201 * -- NO_ERROR: disk info was filled, but not
1202 * necessarily all info was available (e.g.
1203 * if no media was present in CD-ROM drive).
1204 * See remarks below.
1205 *
1206 * -- ERROR_INVALID_DRIVE 15): ulLogicalDrive
1207 * is not used at all (invalid drive letter)
1208 *
1209 * -- ERROR_BAD_UNIT (20): if drive was renamed for
1210 * some reason (according to user reports
1211 *
1212 * -- ERROR_NOT_READY (21): for ZIP disks where
1213 * no media is inserted, depending on the
1214 * driver apparently... normally ZIP drive
1215 * letters should disappear when no media
1216 * is present
1217 *
1218 * -- ERROR_DRIVE_LOCKED (108)
1219 *
1220 * So in order to check whether a drive is present
1221 * and available, use this function as follows:
1222 *
1223 * 1) Call this function and check whether it
1224 * returns NO_ERROR for the given drive.
1225 * This will rule out invalid drive letters
1226 * and drives that are presently locked by
1227 * CHKDSK or something.
1228 *
1229 * 2) If so, check whether XDISKINFO.flDevice
1230 * has the DFL_MEDIA_PRESENT flag set.
1231 * This will rule out removeable drives without
1232 * media and unformatted hard disks.
1233 *
1234 * 3) If so, you can test the other fields if
1235 * you need more information. For example,
1236 * it would not be a good idea to create
1237 * a new file if the bType field is
1238 * DRVTYPE_CDROM.
1239 *
1240 * If you want to exclude removeable disks,
1241 * instead of checking bType, you should
1242 * rather check flDevice for the DFL_FIXED
1243 * flag, which will be set for ZIP drives also.
1244 *
1245 * Remarks for special drive types:
1246 *
1247 * -- Hard disks always have bType == DRVTYPE_HARDDISK.
1248 * For them, we always check the file system.
1249 * If this is reported as "UNKNOWN", this means
1250 * that the drive is unformatted or formatted
1251 * with a file system that OS/2 does not understand
1252 * (e.g. NTFS). Only in that case, flDevice
1253 * has the DFL_MEDIA_PRESENT bit clear.
1254 *
1255 * DFL_FIXED is always set.
1256 *
1257 * -- Remote (LAN) drives always have bType == DRVTYPE_LAN.
1258 * flDevice will always have the DFL_REMOTE and
1259 * DFL_MEDIA_PRESENT bits set.
1260 *
1261 * -- ZIP disks will have bType == DRVTYPE_PARTITIONABLEREMOVEABLE.
1262 * For them, flDevice will have both the
1263 * and DFL_PARTITIONABLEREMOVEABLE and DFL_FIXED
1264 * bits set.
1265 *
1266 * ZIP disks are a bit special because they are
1267 * dynamically mounted and unmounted when media
1268 * is inserted and removed. In other words, if
1269 * no media is present, the drive letter becomes
1270 * invalid.
1271 *
1272 * -- CD-ROM and DVD drives and CD writers will always
1273 * be reported as DRVTYPE_CDROM. The DFL_FIXED bit
1274 * will be clear always. For them, always check the
1275 * DFL_MEDIA_PRESENT present bit to avoid "Drive not
1276 * ready" popups.
1277 *
1278 * As a special goody, we can also determine if the
1279 * drive currently has audio media inserted (which
1280 * would provoke errors also), by setting the
1281 * DFL_AUDIO_CD bit.
1282 *
1283 *@@added V0.9.16 (2002-01-13) [umoeller]
1284 *@@changed V0.9.19 (2002-04-25) [umoeller]: added CDWFS (RSJ CD-Writer)
1285 */
1286
1287APIRET doshGetDriveInfo(ULONG ulLogicalDrive,
1288 ULONG fl, // in: DRVFL_* flags
1289 PXDISKINFO pdi)
1290{
1291 APIRET arc = NO_ERROR;
1292
1293 HFILE hf;
1294 ULONG dummy;
1295 BOOL fCheck = TRUE,
1296 fCheckFS = FALSE,
1297 fCheckLongnames = FALSE,
1298 fCheckEAs = FALSE;
1299
1300 memset(pdi, 0, sizeof(XDISKINFO));
1301
1302 pdi->cDriveLetter = 'A' + ulLogicalDrive - 1;
1303 pdi->cLogicalDrive = ulLogicalDrive;
1304
1305 pdi->bType = DRVTYPE_UNKNOWN;
1306 pdi->fPresent = TRUE; // for now
1307
1308 if ( (ulLogicalDrive == 1)
1309 || (ulLogicalDrive == 2)
1310 )
1311 {
1312 // drive A: and B: are special cases,
1313 // we don't even want to touch them (click, click)
1314 pdi->bType = DRVTYPE_FLOPPY;
1315
1316 if (0 == (fl & DRVFL_TOUCHFLOPPIES))
1317 {
1318 fCheck = FALSE;
1319 // these support EAs too
1320 pdi->flDevice = DFL_MEDIA_PRESENT | DFL_SUPPORTS_EAS;
1321 strcpy(pdi->szFileSystem, "FAT");
1322 pdi->lFileSystem = FSYS_FAT;
1323 }
1324 }
1325
1326 if (fCheck)
1327 {
1328 // any other drive:
1329 // check if it's removeable first
1330 BOOL fFixed = FALSE;
1331 arc = doshIsFixedDisk(ulLogicalDrive,
1332 &fFixed);
1333
1334 switch (arc)
1335 {
1336 case ERROR_INVALID_DRIVE:
1337 // drive letter doesn't exist at all:
1338 pdi->fPresent = FALSE;
1339 // return this APIRET
1340 break;
1341
1342 case ERROR_NOT_SUPPORTED: // 50 for network drives
1343 // we get this for remote drives added
1344 // via "net use", so set these flags
1345 pdi->bType = DRVTYPE_LAN;
1346 pdi->lFileSystem = FSYS_REMOTE;
1347 pdi->flDevice |= DFL_REMOTE | DFL_MEDIA_PRESENT;
1348 // but still check what file-system we
1349 // have and whether longnames are supported
1350 fCheckFS = TRUE;
1351 fCheckLongnames = TRUE;
1352 fCheckEAs = TRUE;
1353 break;
1354
1355 case NO_ERROR:
1356 {
1357 if (fFixed)
1358 {
1359 // fixed drive:
1360 pdi->flDevice |= DFL_FIXED | DFL_MEDIA_PRESENT;
1361
1362 fCheckFS = TRUE;
1363 fCheckLongnames = TRUE;
1364 fCheckEAs = TRUE;
1365 }
1366
1367 if (!(arc = doshQueryDiskParams(ulLogicalDrive,
1368 &pdi->bpb)))
1369 {
1370 BYTE bTemp = doshQueryDriveType(ulLogicalDrive,
1371 &pdi->bpb,
1372 fFixed);
1373 if (bTemp != DRVTYPE_UNKNOWN)
1374 {
1375 // recognized: store it then
1376 pdi->bType = bTemp;
1377
1378 if (bTemp == DRVTYPE_PARTITIONABLEREMOVEABLE)
1379 pdi->flDevice |= DFL_FIXED
1380 | DFL_PARTITIONABLEREMOVEABLE;
1381 }
1382
1383 if (!fFixed)
1384 {
1385 // removeable:
1386
1387 // before checking the drive, try if we have media
1388 if (!(arc = doshQueryMedia(ulLogicalDrive,
1389 (pdi->bType == DRVTYPE_CDROM),
1390 fl)))
1391 {
1392 pdi->flDevice |= DFL_MEDIA_PRESENT;
1393 fCheckFS = TRUE;
1394 fCheckLongnames = TRUE;
1395 // but never EAs
1396 }
1397 else if (arc == ERROR_AUDIO_CD_ROM)
1398 {
1399 pdi->flDevice |= DFL_AUDIO_CD;
1400 // do not check longnames and file-system
1401 }
1402 else
1403 pdi->arcQueryMedia = arc;
1404
1405 arc = NO_ERROR;
1406 }
1407 }
1408 else
1409 pdi->arcQueryDiskParams = arc;
1410 }
1411 break;
1412
1413 default:
1414 pdi->arcIsFixedDisk = arc;
1415 // and return this
1416 break;
1417
1418 } // end swich arc = doshIsFixedDisk(ulLogicalDrive, &fFixed);
1419 }
1420
1421 if (fCheckFS)
1422 {
1423 // TRUE only for local fixed disks or
1424 // remote drives or if media was present above
1425 if (!(arc = doshQueryDiskFSType(ulLogicalDrive,
1426 pdi->szFileSystem,
1427 sizeof(pdi->szFileSystem))))
1428 {
1429 if (!stricmp(pdi->szFileSystem, "UNKNOWN"))
1430 {
1431 // this is returned by the stupid DosQueryFSAttach
1432 // if the file system is not recognized by OS/2,
1433 // or if the drive is unformatted
1434 pdi->lFileSystem = FSYS_UNKNOWN;
1435 pdi->flDevice &= ~DFL_MEDIA_PRESENT;
1436 fCheckLongnames = FALSE;
1437 fCheckEAs = FALSE;
1438 // should we return ERROR_NOT_DOS_DISK (26)
1439 // in this case?
1440 }
1441 else if (!stricmp(pdi->szFileSystem, "FAT"))
1442 {
1443 pdi->lFileSystem = FSYS_FAT;
1444 pdi->flDevice |= DFL_SUPPORTS_EAS;
1445 fCheckLongnames = FALSE;
1446 fCheckEAs = FALSE;
1447 }
1448 else if ( (!stricmp(pdi->szFileSystem, "HPFS"))
1449 || (!stricmp(pdi->szFileSystem, "JFS"))
1450 )
1451 {
1452 pdi->lFileSystem = FSYS_HPFS_JFS;
1453 pdi->flDevice |= DFL_SUPPORTS_EAS | DFL_SUPPORTS_LONGNAMES;
1454 fCheckLongnames = FALSE;
1455 fCheckEAs = FALSE;
1456 }
1457 else if (!stricmp(pdi->szFileSystem, "CDFS"))
1458 pdi->lFileSystem = FSYS_CDFS;
1459 else if ( (!stricmp(pdi->szFileSystem, "FAT32"))
1460 || (!stricmp(pdi->szFileSystem, "ext2"))
1461 )
1462 {
1463 pdi->lFileSystem = FSYS_FAT32_EXT2;
1464 fCheckLongnames = TRUE;
1465 fCheckEAs = TRUE;
1466 }
1467 else if (!stricmp(pdi->szFileSystem, "RAMFS"))
1468 {
1469 pdi->lFileSystem = FSYS_RAMFS;
1470 pdi->flDevice |= DFL_SUPPORTS_EAS | DFL_SUPPORTS_LONGNAMES;
1471 fCheckLongnames = FALSE;
1472 fCheckEAs = FALSE;
1473 }
1474 else if (!stricmp(pdi->szFileSystem, "TVFS"))
1475 {
1476 pdi->lFileSystem = FSYS_TVFS;
1477 fCheckLongnames = TRUE;
1478 fCheckEAs = TRUE;
1479 }
1480 else if (!stricmp(pdi->szFileSystem, "CDWFS"))
1481 // V0.9.19 (2002-04-25) [umoeller]
1482 {
1483 pdi->lFileSystem = FSYS_CDWFS;
1484 pdi->flDevice |= DFL_SUPPORTS_LONGNAMES;
1485 fCheckLongnames = FALSE;
1486 fCheckEAs = FALSE;
1487 }
1488 }
1489 else
1490 // store negative error code
1491 pdi->lFileSystem = -(LONG)arc;
1492 }
1493
1494 if ( (!arc)
1495 && (fCheckLongnames)
1496 && (fl & DRVFL_CHECKLONGNAMES)
1497 )
1498 {
1499 CHAR szTemp[30] = "?:\\long.name.file";
1500 szTemp[0] = ulLogicalDrive + 'A' - 1;
1501 if (!(arc = DosOpen(szTemp,
1502 &hf,
1503 &dummy,
1504 0,
1505 0,
1506 FILE_READONLY,
1507 OPEN_SHARE_DENYNONE | OPEN_FLAGS_NOINHERIT,
1508 0)))
1509 {
1510 DosClose(hf);
1511 }
1512
1513 switch (arc)
1514 {
1515 case NO_ERROR:
1516 case ERROR_OPEN_FAILED:
1517 case ERROR_FILE_NOT_FOUND: // returned by TVFS
1518 pdi->flDevice |= DFL_SUPPORTS_LONGNAMES;
1519 break;
1520
1521 // if longnames are not supported,
1522 // we get ERROR_INVALID_NAME
1523 default:
1524 pdi->arcOpenLongnames = arc;
1525 break;
1526
1527 // default:
1528 // printf(" drive %d returned %d\n", ulLogicalDrive, arc);
1529 }
1530
1531 arc = NO_ERROR;
1532 }
1533
1534 if ( (!arc)
1535 && (fCheckEAs)
1536 && (fl & DRVFL_CHECKEAS)
1537 )
1538 {
1539 EASIZEBUF easb = {0};
1540 ULONG cbData = sizeof(easb),
1541 cbParams = 0;
1542 CHAR szDrive[] = "?:\\";
1543 szDrive[0] = pdi->cDriveLetter;
1544 if (!(arc = DosFSCtl(&easb,
1545 cbData,
1546 &cbData,
1547 NULL, // params,
1548 cbParams,
1549 &cbParams,
1550 FSCTL_MAX_EASIZE,
1551 szDrive,
1552 -1, // HFILE
1553 FSCTL_PATHNAME)))
1554 if (easb.cbMaxEASize != 0)
1555 // the other field (cbMaxEAListSize) is 0 always, I think
1556 pdi->flDevice |= DFL_SUPPORTS_EAS;
1557 }
1558
1559 if (doshQueryBootDrive() == pdi->cDriveLetter)
1560 pdi->flDevice |= DFL_BOOTDRIVE;
1561
1562 return arc;
1563}
1564
1565/*
1566 *@@ doshSetLogicalMap:
1567 * sets the mapping of logical floppy drives onto a single
1568 * physical floppy drive.
1569 * This means selecting either drive A: or drive B: to refer
1570 * to the physical drive.
1571 *
1572 * Paul explained this to me as follows:
1573 *
1574 * "It is really very simple - in a single physical floppy
1575 * drive system, you still have 2 logical floppy drives
1576 * A: and B:. This was primarily to support disk copying
1577 * e.g. diskcopy a: b: on a single floppy system without
1578 * having to rewrite applications. Whenever the application
1579 * accessed the other logical drive, the user would get a
1580 * prompt from the OS to swap disks.
1581 *
1582 * "These calls allow applications to bypass the prompt by
1583 * doing the mapping themselves. They just get/set which
1584 * logical drive is currently mapped to the physical drive.
1585 * This concept existed in DOS as well although the specifics
1586 * of how it was done escape me now.... actually I just
1587 * looked it up - the byte at 0:504h in low memory on
1588 * DOS controlled this (it doesn't work in VDMs)."
1589 *
1590 *@@added V0.9.6 (2000-11-24) [pr]
1591 */
1592
1593APIRET doshSetLogicalMap(ULONG ulLogicalDrive)
1594{
1595 CHAR name[3] = "?:";
1596 ULONG fd = 0,
1597 action = 0;
1598// paramsize = 0;
1599// datasize = 0;
1600 APIRET rc = NO_ERROR;
1601 USHORT data,
1602 param;
1603
1604 name[0] = doshQueryBootDrive();
1605 rc = DosOpen(name,
1606 &fd,
1607 &action,
1608 0,
1609 0,
1610 OPEN_ACTION_FAIL_IF_NEW
1611 | OPEN_ACTION_OPEN_IF_EXISTS,
1612 OPEN_FLAGS_DASD
1613 | OPEN_FLAGS_FAIL_ON_ERROR
1614 | OPEN_FLAGS_NOINHERIT
1615 | OPEN_ACCESS_READONLY
1616 | OPEN_SHARE_DENYNONE,
1617 0);
1618
1619 if (rc == NO_ERROR)
1620 {
1621 param = 0;
1622 data = (USHORT)ulLogicalDrive;
1623 // paramsize = sizeof(param);
1624 // datasize = sizeof(data);
1625 rc = doshDevIOCtl(fd,
1626 IOCTL_DISK, DSK_SETLOGICALMAP,
1627 &param, sizeof(param),
1628 &data, sizeof(data));
1629 DosClose(fd);
1630 }
1631
1632 return(rc);
1633}
1634
1635/*
1636 *@@ doshQueryDiskSize:
1637 * returns the size of the specified disk in bytes.
1638 *
1639 * Note: This returns a "double" value, because a ULONG
1640 * can only hold values of some 4 billion, which would
1641 * lead to funny results for drives > 4 GB.
1642 *
1643 *@@added V0.9.11 (2001-04-18) [umoeller]
1644 */
1645
1646APIRET doshQueryDiskSize(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1647 double *pdSize)
1648{
1649 APIRET arc = NO_ERROR;
1650 FSALLOCATE fsa;
1651 // double dbl = -1;
1652
1653 if (!(arc = DosQueryFSInfo(ulLogicalDrive, FSIL_ALLOC, &fsa, sizeof(fsa))))
1654 *pdSize = ((double)fsa.cSectorUnit * fsa.cbSector * fsa.cUnit);
1655
1656 return arc;
1657}
1658
1659/*
1660 *@@ doshQueryDiskFree:
1661 * returns the number of bytes remaining on the disk
1662 * specified by the given logical drive.
1663 *
1664 * Note: This returns a "double" value, because a ULONG
1665 * can only hold values of some 4 billion, which would
1666 * lead to funny results for drives > 4 GB.
1667 *
1668 *@@changed V0.9.0 [umoeller]: fixed another > 4 GB bug (thanks to Rdiger Ihle)
1669 *@@changed V0.9.7 (2000-12-01) [umoeller]: changed prototype
1670 */
1671
1672APIRET doshQueryDiskFree(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1673 double *pdFree)
1674{
1675 APIRET arc = NO_ERROR;
1676 FSALLOCATE fsa;
1677 // double dbl = -1;
1678
1679 if (!(arc = DosQueryFSInfo(ulLogicalDrive, FSIL_ALLOC, &fsa, sizeof(fsa))))
1680 *pdFree = ((double)fsa.cSectorUnit * fsa.cbSector * fsa.cUnitAvail);
1681 // ^ fixed V0.9.0
1682
1683 return arc;
1684}
1685
1686/*
1687 *@@ doshQueryDiskFSType:
1688 * copies the file-system type of the given disk object
1689 * (HPFS, FAT, CDFS etc.) to pszBuf.
1690 * Returns the DOS error code.
1691 *
1692 *@@changed V0.9.1 (99-12-12) [umoeller]: added cbBuf to prototype
1693 *@@changed V0.9.14 (2001-08-01) [umoeller]: fixed, this never respected cbBuf
1694 *@@changed V0.9.16 (2001-10-02) [umoeller]: added check for valid logical disk no
1695 */
1696
1697APIRET doshQueryDiskFSType(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1698 PSZ pszBuf, // out: buffer for FS type
1699 ULONG cbBuf) // in: size of that buffer
1700{
1701 APIRET arc = NO_ERROR;
1702 CHAR szName[5];
1703
1704 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
1705 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
1706 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
1707
1708 // compose "D:"-type string from logical drive letter
1709 if (ulLogicalDrive > 0 && ulLogicalDrive < 27)
1710 {
1711 szName[0] = ulLogicalDrive + 'A' - 1;
1712 szName[1] = ':';
1713 szName[2] = '\0';
1714
1715 arc = DosQueryFSAttach(szName, // logical drive of attached FS ("D:"-style)
1716 0, // ulOrdinal, ignored for FSAIL_QUERYNAME
1717 FSAIL_QUERYNAME, // return name for a drive or device
1718 pfsqBuffer, // buffer for returned data
1719 &cbBuffer); // sizeof(*pfsqBuffer)
1720
1721 if (arc == NO_ERROR)
1722 {
1723 if (pszBuf)
1724 {
1725 // The data for the last three fields in the FSQBUFFER2
1726 // structure are stored at the offset of fsqBuffer.szName.
1727 // Each data field following fsqBuffer.szName begins
1728 // immediately after the previous item.
1729 strncpy(pszBuf,
1730 (CHAR*)(&pfsqBuffer->szName) + pfsqBuffer->cbName + 1,
1731 cbBuf); // V0.9.14 (2001-08-01) [umoeller]
1732 *(pszBuf + cbBuf) = '\0';
1733 }
1734 }
1735 }
1736 else
1737 arc = ERROR_INVALID_PARAMETER; // V0.9.16 (2001-10-02) [umoeller]
1738
1739 return arc;
1740}
1741
1742/*
1743 *@@ doshQueryDiskLabel:
1744 * this returns the label of a disk into
1745 * *pszVolumeLabel, which must be 12 bytes
1746 * in size.
1747 *
1748 * This function was added because the Toolkit
1749 * information for DosQueryFSInfo is only partly
1750 * correct. On OS/2 2.x, that function does not
1751 * take an FSINFO structure as input, but a VOLUMELABEL.
1752 * On Warp, this does take an FSINFO.
1753 *
1754 * DosSetFSInfo is even worse. See doshSetDiskLabel.
1755 *
1756 * See http://zebra.asta.fh-weingarten.de/os2/Snippets/Bugi6787.HTML
1757 * for details.
1758 *
1759 *@@added V0.9.0 [umoeller]
1760 *@@changed V0.9.11 (2001-04-22) [umoeller]: this copied even with errors, fixed
1761 */
1762
1763APIRET doshQueryDiskLabel(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1764 PSZ pszVolumeLabel) // out: volume label (must be 12 chars in size)
1765{
1766 APIRET arc;
1767
1768 #ifdef __OS2V2X__
1769 VOLUMELABEL FSInfoBuf;
1770 #else
1771 FSINFO FSInfoBuf;
1772 #endif
1773
1774 arc = DosQueryFSInfo(ulLogicalDrive,
1775 FSIL_VOLSER,
1776 &FSInfoBuf,
1777 sizeof(FSInfoBuf)); // depends
1778
1779 if (!arc) // V0.9.11 (2001-04-22) [umoeller]
1780 {
1781 #ifdef __OS2V2X__
1782 strcpy(pszVolumeLabel, FSInfoBuf.szVolLabel);
1783 #else
1784 strcpy(pszVolumeLabel, FSInfoBuf.vol.szVolLabel);
1785 #endif
1786 }
1787
1788 return arc;
1789}
1790
1791/*
1792 *@@ doshSetDiskLabel:
1793 * this sets the label of a disk.
1794 *
1795 * This function was added because the Toolkit
1796 * information for DosSetFSInfo is flat out wrong.
1797 * That function does not take an FSINFO structure
1798 * as input, but a VOLUMELABEL. As a result, using
1799 * that function with the Toolkit's calling specs
1800 * results in ERROR_LABEL_TOO_LONG always.
1801 *
1802 * See http://zebra.asta.fh-weingarten.de/os2/Snippets/Bugi6787.HTML
1803 * for details.
1804 *
1805 *@@added V0.9.0 [umoeller]
1806 */
1807
1808APIRET doshSetDiskLabel(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
1809 PSZ pszNewLabel)
1810{
1811 VOLUMELABEL FSInfoBuf;
1812
1813 // check length; 11 chars plus null byte allowed
1814 FSInfoBuf.cch = (BYTE)strlen(pszNewLabel);
1815 if (FSInfoBuf.cch < sizeof(FSInfoBuf.szVolLabel))
1816 {
1817 strcpy(FSInfoBuf.szVolLabel, pszNewLabel);
1818
1819 return (DosSetFSInfo(ulLogicalDrive,
1820 FSIL_VOLSER,
1821 &FSInfoBuf,
1822 sizeof(FSInfoBuf)));
1823 }
1824 else
1825 return (ERROR_LABEL_TOO_LONG);
1826}
1827
1828/*
1829 *@@category: Helpers\Control program helpers\File name parsing
1830 */
1831
1832/* ******************************************************************
1833 *
1834 * File name parsing
1835 *
1836 ********************************************************************/
1837
1838/*
1839 *@@ doshGetDriveSpec:
1840 * returns the drive specification in pcszFullFile,
1841 * if any is present. This is useful for UNC support.
1842 *
1843 * This returns:
1844 *
1845 * -- NO_ERROR: drive spec was given, and the output
1846 * fields have been set.
1847 *
1848 * -- ERROR_INVALID_NAME: incorrect UNC syntax.
1849 *
1850 * -- ERROR_INVALID_DRIVE: second char is ':', but
1851 * drive letter is not in the range [A-Z].
1852 *
1853 * -- ERROR_INVALID_PARAMETER: no drive spec given
1854 * at all; apparently pcszFullFile is not fully
1855 * qualified in the first place, or it is NULL,
1856 * or its length is <= 2.
1857 *
1858 *@@added V0.9.16 (2001-10-25) [umoeller]
1859 */
1860
1861APIRET doshGetDriveSpec(PCSZ pcszFullFile, // in: fully q'fied file spec
1862 PSZ pszDrive, // out: drive spec ("C:" or "\\SERVER\RESOURCE"; ptr can be NULL)
1863 PULONG pulDriveLen, // out: length of drive spec (2 if local drive; ptr can be NULL)
1864 PBOOL pfIsUNC) // out: set to TRUE if UNC name, FALSE otherwise (ptr can be NULL)
1865{
1866 APIRET arc = NO_ERROR;
1867 ULONG ulFileSpecLength;
1868
1869 if ( (pcszFullFile)
1870 && (ulFileSpecLength = strlen(pcszFullFile))
1871 && (ulFileSpecLength >= 2)
1872 )
1873 {
1874 // upper-case the drive letter
1875 if (pcszFullFile[1] == ':')
1876 {
1877 CHAR cDrive = toupper(*pcszFullFile);
1878 // local drive specified:
1879 if ( (cDrive >= 'A')
1880 && (cDrive <= 'Z')
1881 )
1882 {
1883 if (pszDrive)
1884 {
1885 pszDrive[0] = cDrive;
1886 pszDrive[1] = ':';
1887 pszDrive[2] = '\0';
1888 }
1889
1890 if (pulDriveLen)
1891 *pulDriveLen = 2;
1892 if (pfIsUNC)
1893 *pfIsUNC = FALSE;
1894 }
1895 else
1896 // this is not a valid drive:
1897 arc = ERROR_INVALID_DRIVE;
1898 }
1899 else if ( (pcszFullFile[0] == '\\')
1900 && (pcszFullFile[1] == '\\')
1901 )
1902 {
1903 // UNC drive specified:
1904 // this better be a full \\SERVER\RESOURCE string
1905 PCSZ pResource;
1906 if (pResource = strchr(pcszFullFile + 3, '\\'))
1907 {
1908 // we got at least \\SERVER\:
1909 ULONG ulLength;
1910 PCSZ p;
1911
1912 // check if more stuff is coming
1913 if (p = strchr(pResource + 1, '\\'))
1914 {
1915 // yes: copy server and resource excluding that backslash
1916 if (p == pResource + 1)
1917 // "\\SERVER\\" is invalid
1918 arc = ERROR_INVALID_NAME;
1919 else
1920 // we got "\\SERVER\something\":
1921 // drop the last backslash
1922 ulLength = p - pcszFullFile;
1923 }
1924 else
1925 // "\\SERVER\something" only:
1926 ulLength = ulFileSpecLength;
1927
1928 if (!arc)
1929 {
1930 if (pszDrive)
1931 {
1932 memcpy(pszDrive,
1933 pcszFullFile,
1934 ulLength);
1935 pszDrive[ulLength] = '\0';
1936 }
1937
1938 if (pulDriveLen)
1939 *pulDriveLen = ulLength;
1940 if (pfIsUNC)
1941 *pfIsUNC = TRUE;
1942 }
1943 }
1944 else
1945 // invalid UNC name:
1946 arc = ERROR_INVALID_NAME;
1947 }
1948 else
1949 // neither local, nor UNC:
1950 arc = ERROR_INVALID_PARAMETER;
1951 }
1952 else
1953 arc = ERROR_INVALID_PARAMETER;
1954
1955 return arc;
1956}
1957
1958/*
1959 *@@ doshGetExtension:
1960 * finds the file name extension of pszFilename,
1961 * which can be a file name only or a fully
1962 * qualified filename.
1963 *
1964 * This returns a pointer into pszFilename to
1965 * the character after the last dot.
1966 *
1967 * Returns NULL if not found (e.g. if the filename
1968 * has no dot in it).
1969 *
1970 * In the pathological case of a dot in the path
1971 * but not in the filename itself (e.g.
1972 * "C:\files.new\readme"), this correctly returns
1973 * NULL.
1974 *
1975 *@@added V0.9.6 (2000-10-16) [umoeller]
1976 *@@changed V0.9.7 (2000-12-10) [umoeller]: fixed "F:filename.ext" case
1977 */
1978
1979PSZ doshGetExtension(PCSZ pcszFilename)
1980{
1981 PSZ pReturn = NULL;
1982
1983 if (pcszFilename)
1984 {
1985 // find filename
1986 PCSZ p2,
1987 pStartOfName = NULL,
1988 pExtension = NULL;
1989
1990 if (p2 = strrchr(pcszFilename + 2, '\\'))
1991 // works on "C:\blah" or "\\unc\blah"
1992 pStartOfName = p2 + 1;
1993 else
1994 {
1995 // no backslash found:
1996 // maybe only a drive letter was specified:
1997 if (pcszFilename[1] == ':')
1998 // yes:
1999 pStartOfName = pcszFilename + 2;
2000 else
2001 // then this is not qualified at all...
2002 // use start of filename
2003 pStartOfName = (PSZ)pcszFilename;
2004 }
2005
2006 // find last dot in filename
2007 if (pExtension = strrchr(pStartOfName, '.'))
2008 pReturn = (PSZ)pExtension + 1;
2009 }
2010
2011 return (pReturn);
2012}
2013
2014/*
2015 *@@category: Helpers\Control program helpers\File management
2016 */
2017
2018/* ******************************************************************
2019 *
2020 * File helpers
2021 *
2022 ********************************************************************/
2023
2024/*
2025 *@@ doshIsFileOnFAT:
2026 * returns TRUE if pszFileName resides on
2027 * a FAT drive. Note that pszFileName must
2028 * be fully qualified (i.e. the drive letter
2029 * must be the first character), or this will
2030 * return garbage.
2031 */
2032
2033BOOL doshIsFileOnFAT(const char* pcszFileName)
2034{
2035 BOOL brc = FALSE;
2036 CHAR szName[5];
2037
2038 APIRET arc;
2039 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
2040 ULONG cbBuffer = sizeof(fsqBuffer); // Buffer length)
2041 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
2042
2043 szName[0] = pcszFileName[0]; // copy drive letter
2044 szName[1] = ':';
2045 szName[2] = '\0';
2046
2047 if (!(arc = DosQueryFSAttach(szName, // logical drive of attached FS
2048 0, // ulOrdinal, ignored for FSAIL_QUERYNAME
2049 FSAIL_QUERYNAME, // return data for a Drive or Device
2050 pfsqBuffer, // returned data
2051 &cbBuffer))) // returned data length
2052 {
2053 // The data for the last three fields in the FSQBUFFER2
2054 // structure are stored at the offset of fsqBuffer.szName.
2055 // Each data field following fsqBuffer.szName begins
2056 // immediately after the previous item.
2057 if (!strncmp((PSZ)&(pfsqBuffer->szName) + pfsqBuffer->cbName + 1,
2058 "FAT",
2059 3))
2060 brc = TRUE;
2061 }
2062
2063 return brc;
2064}
2065
2066/*
2067 *@@ doshQueryFileSize:
2068 * returns the size of an already opened file
2069 * or 0 upon errors.
2070 * Use doshQueryPathSize to query the size of
2071 * any file.
2072 *
2073 *@@changed V0.9.16 (2001-10-19) [umoeller]: now returning APIRET
2074 */
2075
2076APIRET doshQueryFileSize(HFILE hFile, // in: file handle
2077 PULONG pulSize) // out: file size (ptr can be NULL)
2078{
2079 APIRET arc;
2080 FILESTATUS3 fs3;
2081 if (!(arc = DosQueryFileInfo(hFile, FIL_STANDARD, &fs3, sizeof(fs3))))
2082 if (pulSize)
2083 *pulSize = fs3.cbFile;
2084 return arc;
2085}
2086
2087/*
2088 *@@ doshQueryPathSize:
2089 * returns the size of any file,
2090 * or 0 if the file could not be
2091 * found.
2092 *
2093 * Use doshQueryFileSize instead to query the
2094 * size if you have a HFILE.
2095 *
2096 * Otherwise this returns:
2097 *
2098 * -- ERROR_FILE_NOT_FOUND
2099 * -- ERROR_PATH_NOT_FOUND
2100 * -- ERROR_SHARING_VIOLATION
2101 * -- ERROR_FILENAME_EXCED_RANGE
2102 * -- ERROR_INVALID_PARAMETER
2103 *
2104 *@@changed V0.9.16 (2001-10-19) [umoeller]: now returning APIRET
2105 */
2106
2107APIRET doshQueryPathSize(PCSZ pcszFile, // in: filename
2108 PULONG pulSize) // out: file size (ptr can be NULL)
2109{
2110 APIRET arc;
2111
2112 if (pcszFile) // V0.9.16 (2001-12-08) [umoeller]
2113 {
2114 FILESTATUS3 fs3;
2115 if (!(arc = DosQueryPathInfo((PSZ)pcszFile, FIL_STANDARD, &fs3, sizeof(fs3))))
2116 if (pulSize)
2117 *pulSize = fs3.cbFile;
2118 }
2119 else
2120 arc = ERROR_INVALID_PARAMETER;
2121
2122 return arc;
2123}
2124
2125/*
2126 *@@ doshQueryPathAttr:
2127 * returns the file attributes of pszFile,
2128 * which can be fully qualified. The
2129 * attributes will be stored in *pulAttr.
2130 * pszFile can also specify a directory,
2131 * although not all attributes make sense
2132 * for directories.
2133 *
2134 * fAttr can be:
2135 * -- FILE_ARCHIVED
2136 * -- FILE_READONLY
2137 * -- FILE_SYSTEM
2138 * -- FILE_HIDDEN
2139 *
2140 * This returns the APIRET of DosQueryPathInfo.
2141 * *pulAttr is only valid if NO_ERROR is
2142 * returned.
2143 *
2144 * Otherwise this returns:
2145 *
2146 * -- ERROR_FILE_NOT_FOUND
2147 * -- ERROR_PATH_NOT_FOUND
2148 * -- ERROR_SHARING_VIOLATION
2149 * -- ERROR_FILENAME_EXCED_RANGE
2150 *
2151 *@@added V0.9.0 [umoeller]
2152 */
2153
2154APIRET doshQueryPathAttr(const char* pcszFile, // in: file or directory name
2155 PULONG pulAttr) // out: attributes (ptr can be NULL)
2156{
2157 FILESTATUS3 fs3;
2158 APIRET arc;
2159
2160 if (!(arc = DosQueryPathInfo((PSZ)pcszFile,
2161 FIL_STANDARD,
2162 &fs3,
2163 sizeof(fs3))))
2164 {
2165 if (pulAttr)
2166 *pulAttr = fs3.attrFile;
2167 }
2168
2169 return arc;
2170}
2171
2172/*
2173 *@@ doshSetPathAttr:
2174 * sets the file attributes of pszFile,
2175 * which can be fully qualified.
2176 * pszFile can also specify a directory,
2177 * although not all attributes make sense
2178 * for directories.
2179 *
2180 * fAttr can be:
2181 * -- FILE_ARCHIVED
2182 * -- FILE_READONLY
2183 * -- FILE_SYSTEM
2184 * -- FILE_HIDDEN
2185 *
2186 * Note that this simply sets all the given
2187 * attributes; the existing attributes
2188 * are lost.
2189 *
2190 * This returns the APIRET of DosQueryPathInfo.
2191 */
2192
2193APIRET doshSetPathAttr(const char* pcszFile, // in: file or directory name
2194 ULONG ulAttr) // in: new attributes
2195{
2196 APIRET arc;
2197
2198 if (pcszFile)
2199 {
2200 FILESTATUS3 fs3;
2201 if (!(arc = DosQueryPathInfo((PSZ)pcszFile,
2202 FIL_STANDARD,
2203 &fs3,
2204 sizeof(fs3))))
2205 {
2206 fs3.attrFile = ulAttr;
2207 arc = DosSetPathInfo((PSZ)pcszFile,
2208 FIL_STANDARD,
2209 &fs3,
2210 sizeof(fs3),
2211 DSPI_WRTTHRU);
2212 }
2213 }
2214 else
2215 arc = ERROR_INVALID_PARAMETER;
2216
2217 return arc;
2218}
2219
2220/*
2221 *@@category: Helpers\Control program helpers\File management\XFILEs
2222 */
2223
2224/* ******************************************************************
2225 *
2226 * XFILEs
2227 *
2228 ********************************************************************/
2229
2230/*
2231 * doshOpen:
2232 * wrapper around DosOpen for simpler opening
2233 * of files.
2234 *
2235 * ulOpenMode determines the mode to open the
2236 * file in (fptr specifies the position after
2237 * the open):
2238 *
2239 + +-------------------------+------+------------+-----------+
2240 + | ulOpenMode | mode | if exists | if new |
2241 + +-------------------------+------+------------+-----------+
2242 + | XOPEN_READ_EXISTING | read | opens | fails |
2243 + | | | fptr = 0 | |
2244 + +-------------------------+------+------------+-----------+
2245 + | XOPEN_READWRITE_EXISTING r/w | opens | fails |
2246 + | | | fptr = 0 | |
2247 + +-------------------------+------+------------+-----------+
2248 + | XOPEN_READWRITE_APPEND | r/w | opens, | creates |
2249 + | | | appends | |
2250 + | | | fptr = end | fptr = 0 |
2251 + +-------------------------+------+------------+-----------+
2252 + | XOPEN_READWRITE_NEW | r/w | replaces | creates |
2253 + | | | fptr = 0 | fptr = 0 |
2254 + +-------------------------+------+------------+-----------+
2255 *
2256 * In addition, you can OR one of the above values with
2257 * the XOPEN_BINARY flag:
2258 *
2259 * -- If XOPEN_BINARY is set, no conversion is performed
2260 * on read and write.
2261 *
2262 * -- If XOPEN_BINARY is _not_ set, all \n chars are
2263 * converted to \r\n on write.
2264 *
2265 * *ppFile receives a new XFILE structure describing
2266 * the open file, if NO_ERROR is returned.
2267 *
2268 * The file pointer is then set to the beginning of the
2269 * file _unless_ XOPEN_READWRITE_APPEND was specified;
2270 * in that case only, the file pointer is set to the
2271 * end of the file so data can be appended (see above).
2272 *
2273 * Otherwise this returns:
2274 *
2275 * -- ERROR_FILE_NOT_FOUND
2276 * -- ERROR_PATH_NOT_FOUND
2277 * -- ERROR_SHARING_VIOLATION
2278 * -- ERROR_FILENAME_EXCED_RANGE
2279 *
2280 * -- ERROR_NOT_ENOUGH_MEMORY
2281 * -- ERROR_INVALID_PARAMETER
2282 *
2283 *@@added V0.9.16 (2001-10-19) [umoeller]
2284 *@@changed V0.9.16 (2001-12-18) [umoeller]: fixed error codes
2285 */
2286
2287APIRET doshOpen(PCSZ pcszFilename, // in: filename to open
2288 ULONG flOpenMode, // in: XOPEN_* mode
2289 PULONG pcbFile, // in: new file size (if new file is created)
2290 // out: file size
2291 PXFILE *ppFile)
2292{
2293 APIRET arc = NO_ERROR;
2294
2295 ULONG fsOpenFlags = 0,
2296 fsOpenMode = OPEN_FLAGS_FAIL_ON_ERROR
2297 | OPEN_FLAGS_NO_LOCALITY
2298 | OPEN_FLAGS_NOINHERIT;
2299
2300 switch (flOpenMode & XOPEN_ACCESS_MASK)
2301 {
2302 case XOPEN_READ_EXISTING:
2303 fsOpenFlags = OPEN_ACTION_FAIL_IF_NEW
2304 | OPEN_ACTION_OPEN_IF_EXISTS;
2305 fsOpenMode |= OPEN_SHARE_DENYWRITE
2306 | OPEN_ACCESS_READONLY;
2307
2308 // run this first, because if the file doesn't
2309 // exists, DosOpen only returns ERROR_OPEN_FAILED,
2310 // which isn't that meaningful
2311 // V0.9.16 (2001-12-08) [umoeller]
2312 arc = doshQueryPathSize(pcszFilename,
2313 pcbFile);
2314 break;
2315
2316 case XOPEN_READWRITE_EXISTING:
2317 fsOpenFlags = OPEN_ACTION_FAIL_IF_NEW
2318 | OPEN_ACTION_OPEN_IF_EXISTS;
2319 fsOpenMode |= OPEN_SHARE_DENYWRITE
2320 | OPEN_ACCESS_READWRITE;
2321
2322 arc = doshQueryPathSize(pcszFilename,
2323 pcbFile);
2324 break;
2325
2326 case XOPEN_READWRITE_APPEND:
2327 fsOpenFlags = OPEN_ACTION_CREATE_IF_NEW
2328 | OPEN_ACTION_OPEN_IF_EXISTS;
2329 fsOpenMode |= OPEN_SHARE_DENYREADWRITE
2330 | OPEN_ACCESS_READWRITE;
2331 // _Pmpf((__FUNCTION__ ": opening XOPEN_READWRITE_APPEND"));
2332 break;
2333
2334 case XOPEN_READWRITE_NEW:
2335 fsOpenFlags = OPEN_ACTION_CREATE_IF_NEW
2336 | OPEN_ACTION_REPLACE_IF_EXISTS;
2337 fsOpenMode |= OPEN_SHARE_DENYREADWRITE
2338 | OPEN_ACCESS_READWRITE;
2339 // _Pmpf((__FUNCTION__ ": opening XOPEN_READWRITE_NEW"));
2340 break;
2341 }
2342
2343 if ((!arc) && fsOpenFlags && pcbFile && ppFile)
2344 {
2345 PXFILE pFile;
2346 if (pFile = NEW(XFILE))
2347 {
2348 ULONG ulAction;
2349
2350 ZERO(pFile);
2351
2352 // copy open flags
2353 pFile->flOpenMode = flOpenMode;
2354
2355 if (!(arc = DosOpen((PSZ)pcszFilename,
2356 &pFile->hf,
2357 &ulAction,
2358 *pcbFile,
2359 FILE_ARCHIVED,
2360 fsOpenFlags,
2361 fsOpenMode,
2362 NULL))) // EAs
2363 {
2364 // alright, got the file:
2365
2366 if ( (ulAction == FILE_EXISTED)
2367 && ((flOpenMode & XOPEN_ACCESS_MASK) == XOPEN_READWRITE_APPEND)
2368 )
2369 // get its size and set ptr to end for append
2370 arc = DosSetFilePtr(pFile->hf,
2371 0,
2372 FILE_END,
2373 pcbFile);
2374 else
2375 arc = doshQueryFileSize(pFile->hf,
2376 pcbFile);
2377 // file ptr is at beginning
2378
2379 #ifdef DEBUG_DOSOPEN
2380 if (arc)
2381 _Pmpf((__FUNCTION__ ": DosSetFilePtr/queryfilesize returned %d for %s",
2382 arc, pcszFilename));
2383 #endif
2384
2385 // store file size
2386 pFile->cbInitial
2387 = pFile->cbCurrent
2388 = *pcbFile;
2389
2390 pFile->pszFilename = strdup(pcszFilename);
2391 }
2392 #ifdef DEBUG_DOSOPEN
2393 else
2394 _Pmpf((__FUNCTION__ ": DosOpen returned %d for %s",
2395 arc, pcszFilename));
2396 #endif
2397
2398 if (arc)
2399 doshClose(&pFile);
2400 else
2401 *ppFile = pFile;
2402 }
2403 else
2404 arc = ERROR_NOT_ENOUGH_MEMORY;
2405 }
2406 else
2407 if (!arc) // V0.9.19 (2002-04-02) [umoeller]
2408 arc = ERROR_INVALID_PARAMETER;
2409
2410 return arc;
2411}
2412
2413/*
2414 *@@ doshReadAt:
2415 * reads cb bytes from the position specified by
2416 * lOffset into the buffer pointed to by pbData,
2417 * which should be cb bytes in size.
2418 *
2419 * Note that lOffset is always considered to
2420 * be from the beginning of the file (FILE_BEGIN
2421 * method).
2422 *
2423 * This implements a small cache so that several
2424 * calls with a near offset will not touch the
2425 * disk always. The cache has been optimized for
2426 * the exeh* functions and works quite nicely
2427 * there.
2428 *
2429 * Note that the position of the file pointer is
2430 * undefined after calling this function because
2431 * the data might have been returned from the
2432 * cache.
2433 *
2434 * fl may be any combination of the following:
2435 *
2436 * -- DRFL_NOCACHE: do not fill the cache with
2437 * new data if the data is not in the cache
2438 * currently.
2439 *
2440 * -- DRFL_FAILIFLESS: return ERROR_NO_DATA
2441 * if the data returned by DosRead is less
2442 * than what was specified. This might
2443 * simplify error handling.
2444 *
2445 *@@added V0.9.13 (2001-06-14) [umoeller]
2446 *@@changed V0.9.16 (2001-12-18) [umoeller]: now with XFILE, and always using FILE_BEGIN
2447 *@@chaanged V0.9.19 (2002-04-02) [umoeller]: added params checking
2448 */
2449
2450APIRET doshReadAt(PXFILE pFile,
2451 ULONG ulOffset, // in: offset to read from (from beginning of file)
2452 PULONG pcb, // in: bytes to read, out: bytes read (req.)
2453 PBYTE pbData, // out: read buffer (must be cb bytes)
2454 ULONG fl) // in: DRFL_* flags
2455{
2456 APIRET arc = NO_ERROR;
2457 ULONG cb;
2458 ULONG ulDummy;
2459
2460 if (!pFile || !pcb)
2461 // V0.9.19 (2002-04-02) [umoeller]
2462 return ERROR_INVALID_PARAMETER;
2463
2464 cb = *pcb;
2465 *pcb = 0;
2466
2467 // check if we have the data in the cache already;
2468
2469 if ( (pFile->pbCache)
2470 // first byte must be in cache
2471 && (ulOffset >= pFile->ulReadFrom)
2472 // last byte must be in cache
2473 && ( ulOffset + cb
2474 <= pFile->ulReadFrom + pFile->cbCache
2475 )
2476 )
2477 {
2478 // alright, return data from cache simply
2479 ULONG ulOfsInCache = ulOffset - pFile->ulReadFrom;
2480
2481 memcpy(pbData,
2482 pFile->pbCache + ulOfsInCache,
2483 cb);
2484 *pcb = cb;
2485
2486 #ifdef DEBUG_DOSOPEN
2487 _Pmpf((__FUNCTION__ " %s: data is fully in cache",
2488 pFile->pszFilename));
2489 _Pmpf((" caller wants %d bytes from %d",
2490 cb, ulOffset));
2491 _Pmpf((" we got %d bytes from %d",
2492 pFile->cbCache, pFile->ulReadFrom));
2493 _Pmpf((" so copied %d bytes from cache ofs %d",
2494 cb, ulOfsInCache));
2495 #endif
2496 }
2497 else
2498 {
2499 // data is not in cache:
2500 // check how much it is... for small amounts,
2501 // we load the cache first
2502 if ( (cb <= 4096 - 512)
2503 && (!(fl & DRFL_NOCACHE))
2504 )
2505 {
2506 #ifdef DEBUG_DOSOPEN
2507 _Pmpf((__FUNCTION__ " %s: filling cache anew",
2508 pFile->pszFilename));
2509 _Pmpf((" caller wants %d bytes from %d",
2510 cb, ulOffset));
2511 #endif
2512
2513 // OK, then fix the offset to read from
2514 // to a multiple of 512 to get a full sector
2515 pFile->ulReadFrom = ulOffset / 512L * 512L;
2516 // and read 4096 bytes always plus the
2517 // value we cut off above
2518 pFile->cbCache = 4096;
2519
2520 #ifdef DEBUG_DOSOPEN
2521 _Pmpf((" getting %d bytes from %d",
2522 pFile->cbCache, pFile->ulReadFrom));
2523 #endif
2524
2525 // free old cache
2526 if (pFile->pbCache)
2527 free(pFile->pbCache);
2528
2529 // allocate new cache
2530 if (!(pFile->pbCache = (PBYTE)malloc(pFile->cbCache)))
2531 arc = ERROR_NOT_ENOUGH_MEMORY;
2532 else
2533 {
2534 ULONG ulOfsInCache = 0;
2535
2536 if (!(arc = DosSetFilePtr(pFile->hf,
2537 (LONG)pFile->ulReadFrom,
2538 FILE_BEGIN,
2539 &ulDummy)))
2540 {
2541 if (!(arc = DosRead(pFile->hf,
2542 pFile->pbCache,
2543 pFile->cbCache,
2544 &ulDummy)))
2545 {
2546 // got data:
2547 #ifdef DEBUG_DOSOPEN
2548 _Pmpf((" %d bytes read", ulDummy));
2549 #endif
2550
2551 pFile->cbCache = ulDummy;
2552
2553 // check bounds
2554 ulOfsInCache = ulOffset - pFile->ulReadFrom;
2555
2556 /*
2557 if (ulOfsInCache + cb > pFile->cbCache)
2558 {
2559 cb = pFile->cbCache - ulOfsInCache;
2560 if (fl & DRFL_FAILIFLESS)
2561 arc = ERROR_NO_DATA;
2562 }
2563 */
2564 }
2565 }
2566
2567 if (!arc)
2568 {
2569 // copy to caller
2570 memcpy(pbData,
2571 pFile->pbCache + ulOfsInCache,
2572 cb);
2573 *pcb = cb;
2574
2575 #ifdef DEBUG_DOSOPEN
2576 _Pmpf((" so copied %d bytes from cache ofs %d",
2577 cb, ulOfsInCache));
2578 #endif
2579 }
2580 else
2581 {
2582 free(pFile->pbCache);
2583 pFile->pbCache = NULL;
2584 }
2585 } // end else if (!(pFile->pbCache = (PBYTE)malloc(pFile->cbCache)))
2586 }
2587 else
2588 {
2589 // read uncached:
2590 #ifdef DEBUG_DOSOPEN
2591 _Pmpf((" " __FUNCTION__ " %s: reading uncached",
2592 pFile->pszFilename));
2593 _Pmpf((" caller wants %d bytes from %d",
2594 cb, ulOffset));
2595 #endif
2596
2597 if (!(arc = DosSetFilePtr(pFile->hf,
2598 (LONG)ulOffset,
2599 FILE_BEGIN,
2600 &ulDummy)))
2601 {
2602 if (!(arc = DosRead(pFile->hf,
2603 pbData,
2604 cb,
2605 &ulDummy)))
2606 {
2607 if ( (fl & DRFL_FAILIFLESS)
2608 && (ulDummy != cb)
2609 )
2610 arc = ERROR_NO_DATA;
2611 else
2612 *pcb = ulDummy; // bytes read
2613 }
2614 }
2615 }
2616 }
2617
2618 return arc;
2619}
2620
2621/*
2622 *@@ doshWrite:
2623 * writes the specified data to the file.
2624 * If (cb == 0), this runs strlen on pcsz
2625 * to find out the length.
2626 *
2627 * If the file is not in binary mode, all
2628 * \n chars are converted to \r\n before
2629 * writing.
2630 *
2631 * Note that this expects that the file
2632 * pointer is at the end of the file, or
2633 * you will get garbage.
2634 *
2635 *@@added V0.9.16 (2001-10-19) [umoeller]
2636 *@@changed V0.9.16 (2001-12-02) [umoeller]: added XOPEN_BINARY \r\n support
2637 *@@changed V0.9.16 (2001-12-06) [umoeller]: added check for pFile != NULL
2638 */
2639
2640APIRET doshWrite(PXFILE pFile,
2641 ULONG cb,
2642 PCSZ pbData)
2643{
2644 APIRET arc = NO_ERROR;
2645 if ((!pFile) || (!pbData))
2646 arc = ERROR_INVALID_PARAMETER;
2647 else
2648 {
2649 if (!cb)
2650 cb = strlen(pbData);
2651
2652 if (!cb)
2653 arc = ERROR_INVALID_PARAMETER;
2654 else
2655 {
2656 PSZ pszNew = NULL;
2657
2658 if (!(pFile->flOpenMode & XOPEN_BINARY))
2659 {
2660 // convert all \n to \r\n:
2661 // V0.9.16 (2001-12-02) [umoeller]
2662
2663 // count all \n first
2664 ULONG cNewLines = 0;
2665 PCSZ pSource = pbData;
2666 ULONG ul;
2667 for (ul = 0;
2668 ul < cb;
2669 ul++)
2670 {
2671 if (*pSource++ == '\n')
2672 cNewLines++;
2673 }
2674
2675 if (cNewLines)
2676 {
2677 // we have '\n' chars:
2678 // then we need just as many \r chars inserted
2679 ULONG cbNew = cb + cNewLines;
2680 if (!(pszNew = (PSZ)malloc(cbNew)))
2681 arc = ERROR_NOT_ENOUGH_MEMORY;
2682 else
2683 {
2684 PSZ pTarget = pszNew;
2685 pSource = pbData;
2686 for (ul = 0;
2687 ul < cb;
2688 ul++)
2689 {
2690 CHAR c = *pSource++;
2691 if (c == '\n')
2692 *pTarget++ = '\r';
2693 *pTarget++ = c;
2694 }
2695
2696 cb = cbNew;
2697 }
2698 }
2699 }
2700
2701 if (!arc)
2702 {
2703 ULONG cbWritten;
2704 if (!(arc = DosWrite(pFile->hf,
2705 (pszNew)
2706 ? pszNew
2707 : (PSZ)pbData,
2708 cb,
2709 &cbWritten)))
2710 {
2711 pFile->cbCurrent += cbWritten;
2712 // invalidate the cache
2713 FREE(pFile->pbCache);
2714 }
2715 }
2716
2717 if (pszNew)
2718 free(pszNew);
2719 }
2720 }
2721
2722 return arc;
2723}
2724
2725/*
2726 *@@ doshWriteAt:
2727 * writes cb bytes (pointed to by pbData) to the
2728 * specified file at the position lOffset (from
2729 * the beginning of the file).
2730 *
2731 *@@added V0.9.13 (2001-06-14) [umoeller]
2732 */
2733
2734APIRET doshWriteAt(PXFILE pFile,
2735 ULONG ulOffset, // in: offset to write at
2736 ULONG cb, // in: bytes to write
2737 PCSZ pbData) // in: ptr to bytes to write (must be cb bytes)
2738{
2739 APIRET arc = NO_ERROR;
2740 ULONG cbWritten;
2741 if (!(arc = DosSetFilePtr(pFile->hf,
2742 (LONG)ulOffset,
2743 FILE_BEGIN,
2744 &cbWritten)))
2745 {
2746 if (!(arc = DosWrite(pFile->hf,
2747 (PSZ)pbData,
2748 cb,
2749 &cbWritten)))
2750 {
2751 if (ulOffset + cbWritten > pFile->cbCurrent)
2752 pFile->cbCurrent = ulOffset + cbWritten;
2753 // invalidate the cache V0.9.19 (2002-04-02) [umoeller]
2754 FREE(pFile->pbCache);
2755 }
2756 }
2757
2758 return arc;
2759}
2760
2761/*
2762 *@@ doshWriteLogEntry
2763 * writes a log string to an XFILE, adding a
2764 * leading timestamp before the line.
2765 *
2766 * The internal string buffer is limited to 2000
2767 * characters. Length checking is _not_ performed.
2768 *
2769 *@@added V0.9.16 (2001-10-19) [umoeller]
2770 *@@changed V0.9.16 (2001-12-06) [umoeller]: added check for pFile != NULL
2771 */
2772
2773APIRET doshWriteLogEntry(PXFILE pFile,
2774 const char* pcszFormat,
2775 ...)
2776{
2777 APIRET arc = NO_ERROR;
2778
2779 if ((!pFile) || (!pcszFormat))
2780 arc = ERROR_INVALID_PARAMETER;
2781 else
2782 {
2783 DATETIME dt;
2784 CHAR szTemp[2000];
2785 ULONG ulLength;
2786
2787 DosGetDateTime(&dt);
2788 if (ulLength = sprintf(szTemp,
2789 "%04d-%02d-%02d %02d:%02d:%02d:%02d ",
2790 dt.year, dt.month, dt.day,
2791 dt.hours, dt.minutes, dt.seconds, dt.hundredths))
2792 {
2793 if (!(arc = doshWrite(pFile,
2794 ulLength,
2795 szTemp)))
2796 {
2797 va_list arg_ptr;
2798 va_start(arg_ptr, pcszFormat);
2799 ulLength = vsprintf(szTemp, pcszFormat, arg_ptr);
2800 va_end(arg_ptr);
2801
2802 if (pFile->flOpenMode & XOPEN_BINARY)
2803 // if we're in binary mode, we need to add \r too
2804 szTemp[ulLength++] = '\r';
2805 szTemp[ulLength++] = '\n';
2806
2807 arc = doshWrite(pFile,
2808 ulLength,
2809 szTemp);
2810 }
2811 }
2812 }
2813
2814 return arc;
2815}
2816
2817/*
2818 * doshClose:
2819 * closes an XFILE opened by doshOpen and
2820 * sets *ppFile to NULL.
2821 *
2822 *@@added V0.9.16 (2001-10-19) [umoeller]
2823 */
2824
2825APIRET doshClose(PXFILE *ppFile)
2826{
2827 APIRET arc = NO_ERROR;
2828 PXFILE pFile;
2829
2830 if ( (ppFile)
2831 && (pFile = *ppFile)
2832 )
2833 {
2834 // set the ptr to NULL
2835 *ppFile = NULL;
2836
2837 FREE(pFile->pbCache);
2838 FREE(pFile->pszFilename);
2839
2840 if (pFile->hf)
2841 {
2842 DosSetFileSize(pFile->hf, pFile->cbCurrent);
2843 DosClose(pFile->hf);
2844 pFile->hf = NULLHANDLE;
2845 }
2846
2847 free(pFile);
2848 }
2849 else
2850 arc = ERROR_INVALID_PARAMETER;
2851
2852 return arc;
2853}
2854
2855/*
2856 *@@ doshReadText:
2857 * reads all the contents of the given XFILE into
2858 * a newly allocated buffer. Handles Ctrl-Z properly.
2859 *
2860 * Implementation for doshLoadTextFile, but can
2861 * be called separately now.
2862 *
2863 *@@added V0.9.20 (2002-07-19) [umoeller]
2864 */
2865
2866APIRET doshReadText(PXFILE pFile,
2867 PSZ* ppszContent, // out: newly allocated buffer with file's content
2868 PULONG pcbRead) // out: size of that buffer including null byte (ptr can be NULL)
2869{
2870 APIRET arc;
2871 PSZ pszContent;
2872
2873 if (!(pszContent = (PSZ)malloc(pFile->cbCurrent + 1)))
2874 arc = ERROR_NOT_ENOUGH_MEMORY;
2875 else
2876 {
2877 ULONG cbRead = 0;
2878 if (!(arc = DosRead(pFile->hf,
2879 pszContent,
2880 pFile->cbCurrent,
2881 &cbRead)))
2882 {
2883 if (cbRead != pFile->cbCurrent)
2884 arc = ERROR_NO_DATA;
2885 else
2886 {
2887 PSZ p;
2888 pszContent[cbRead] = '\0';
2889
2890 // check if we have a ctrl-z (EOF) marker
2891 // this is present, for example, in config.sys
2892 // after install, and stupid E.EXE writes this
2893 // all the time when saving a file
2894 // V0.9.18 (2002-03-08) [umoeller]
2895 if (p = strchr(pszContent, '\26'))
2896 {
2897 *p = '\0';
2898 cbRead = p - pszContent;
2899 }
2900
2901 *ppszContent = pszContent;
2902 if (pcbRead)
2903 *pcbRead = cbRead + 1;
2904 }
2905 }
2906
2907 if (arc)
2908 free(pszContent);
2909 }
2910
2911 return arc;
2912}
2913
2914/*
2915 *@@ doshLoadTextFile:
2916 * reads a text file from disk, allocates memory
2917 * via malloc() and sets a pointer to this
2918 * buffer (or NULL upon errors).
2919 *
2920 * This allocates one extra byte to make the
2921 * buffer null-terminated always. The buffer
2922 * is _not_ converted WRT the line format.
2923 *
2924 * If CTRL-Z (ASCII 26) is encountered in the
2925 * content, it is set to the null character
2926 * instead (V0.9.18).
2927 *
2928 * This returns the APIRET of DosOpen and DosRead.
2929 * If any error occured, no buffer was allocated.
2930 * Otherwise, you should free() the buffer when
2931 * no longer needed.
2932 *
2933 *@@changed V0.9.7 (2001-01-15) [umoeller]: renamed from doshReadTextFile
2934 *@@changed V0.9.16 (2002-01-05) [umoeller]: added pcbRead
2935 *@@changed V0.9.16 (2002-01-05) [umoeller]: rewritten using doshOpen
2936 *@@changed V0.9.18 (2002-03-08) [umoeller]: fixed ctrl-z (EOF) bug
2937 */
2938
2939APIRET doshLoadTextFile(PCSZ pcszFile, // in: file name to read
2940 PSZ* ppszContent, // out: newly allocated buffer with file's content
2941 PULONG pcbRead) // out: size of that buffer including null byte (ptr can be NULL)
2942{
2943 APIRET arc;
2944
2945 ULONG cbFile = 0;
2946 PXFILE pFile = NULL;
2947
2948 if (!(arc = doshOpen(pcszFile,
2949 XOPEN_READ_EXISTING,
2950 &cbFile,
2951 &pFile)))
2952 {
2953 doshReadText(pFile,
2954 ppszContent,
2955 pcbRead);
2956 doshClose(&pFile);
2957 }
2958
2959 return arc;
2960}
2961
2962/*
2963 *@@ doshCreateBackupFileName:
2964 * creates a valid backup filename of pszExisting
2965 * with a numerical file name extension which does
2966 * not exist in the directory where pszExisting
2967 * resides.
2968 * Returns a PSZ to a new buffer which was allocated
2969 * using malloc().
2970 *
2971 * <B>Example:</B> returns "C:\CONFIG.002" for input
2972 * "C:\CONFIG.SYS" if "C:\CONFIG.001" already exists.
2973 *
2974 *@@changed V0.9.1 (99-12-13) [umoeller]: this crashed if pszExisting had no file extension
2975 */
2976
2977PSZ doshCreateBackupFileName(const char* pszExisting)
2978{
2979 CHAR szFilename[CCHMAXPATH];
2980 PSZ pszLastDot;
2981 ULONG ulCount = 1;
2982 CHAR szCount[5];
2983 ULONG ulDummy;
2984
2985 strcpy(szFilename, pszExisting);
2986
2987 if (!(pszLastDot = strrchr(szFilename, '.')))
2988 // no dot in filename:
2989 pszLastDot = szFilename + strlen(szFilename);
2990
2991 do
2992 {
2993 sprintf(szCount, ".%03lu", ulCount);
2994 strcpy(pszLastDot, szCount);
2995 ulCount++;
2996 } while (!doshQueryPathSize(szFilename, &ulDummy));
2997
2998 return (strdup(szFilename));
2999}
3000
3001/*
3002 *@@ doshCreateTempFileName:
3003 * produces a file name in the the specified directory
3004 * or $(TEMP) which presently doesn't exist. This
3005 * checks the directory for existing files, but does
3006 * not open the temp file.
3007 *
3008 * If (pcszDir != NULL), we look into that directory.
3009 * Otherwise we look into the directory specified
3010 * by the $(TEMP) environment variable.
3011 * If $(TEMP) is not set, $(TMP) is tried next.
3012 *
3013 * If the directory thus specified does not exist, the
3014 * root directory of the boot drive is used instead.
3015 * As a result, this function should be fairly bomb-proof.
3016 *
3017 * If (pcszExt != NULL), the temp file receives
3018 * that extension, or no extension otherwise.
3019 * Do not specify the dot in pcszExt.
3020 *
3021 * pszTempFileName receives the fully qualified
3022 * file name of the temp file in that directory
3023 * and must point to a buffer CCHMAXPATH in size.
3024 * The file name is 8+3 compliant if pcszExt does
3025 * not exceed three characters.
3026 *
3027 * If (pcszPrefix != NULL), the temp file name
3028 * is prefixed with pcszPrefix. Since the temp
3029 * file name must not exceed 8+3 letters, we
3030 * can only use ( 8 - strlen(pcszPrefix ) digits
3031 * for a random number to make the temp file name
3032 * unique. You must therefore use a maximum of
3033 * four characters for the prefix. Otherwise
3034 * ERROR_INVALID_PARAMETER is returned.
3035 *
3036 * Example: Assuming TEMP is set to C:\TEMP,
3037 +
3038 + doshCreateTempFileName(szBuffer,
3039 + NULL, // use $(TEMP)
3040 + "pre", // prefix
3041 + "tmp") // extension
3042 +
3043 * would produce something like "C:\TEMP\pre07FG2.tmp".
3044 *
3045 *@@added V0.9.9 (2001-04-04) [umoeller]
3046 */
3047
3048APIRET doshCreateTempFileName(PSZ pszTempFileName, // out: fully q'fied temp file name
3049 PCSZ pcszDir, // in: dir or NULL for %TEMP%
3050 PCSZ pcszPrefix, // in: prefix for temp file or NULL
3051 PCSZ pcszExt) // in: extension (without dot) or NULL
3052{
3053 APIRET arc = NO_ERROR;
3054
3055 ULONG ulPrefixLen = (pcszPrefix)
3056 ? strlen(pcszPrefix)
3057 : 0;
3058
3059 if ( (!pszTempFileName)
3060 || (ulPrefixLen > 4)
3061 )
3062 arc = ERROR_INVALID_PARAMETER;
3063 else
3064 {
3065 CHAR szDir[CCHMAXPATH] = "";
3066 FILESTATUS3 fs3;
3067
3068 const char *pcszTemp = pcszDir;
3069
3070 if (!pcszTemp)
3071 {
3072 if (!(pcszTemp = getenv("TEMP")))
3073 pcszTemp = getenv("TMP");
3074 }
3075
3076 if (pcszTemp) // either pcszDir or $(TEMP) or $(TMP) now
3077 if (DosQueryPathInfo((PSZ)pcszTemp,
3078 FIL_STANDARD,
3079 &fs3,
3080 sizeof(fs3)))
3081 // TEMP doesn't exist:
3082 pcszTemp = NULL;
3083
3084 if (!pcszTemp)
3085 // not set, or doesn't exist:
3086 // use root directory on boot drive
3087 sprintf(szDir,
3088 "%c:\\",
3089 doshQueryBootDrive());
3090 else
3091 {
3092 strcpy(szDir, pcszTemp);
3093 if (szDir[strlen(szDir) - 1] != '\\')
3094 strcat(szDir, "\\");
3095 }
3096
3097 if (!szDir[0])
3098 arc = ERROR_PATH_NOT_FOUND; // shouldn't happen
3099 else
3100 {
3101 ULONG ulRandom = 0;
3102 ULONG cAttempts = 0;
3103
3104 // produce random number
3105 DosQuerySysInfo(QSV_MS_COUNT,
3106 QSV_MS_COUNT,
3107 &ulRandom,
3108 sizeof(ulRandom));
3109
3110 do
3111 {
3112 CHAR szFile[20] = "",
3113 szFullTryThis[CCHMAXPATH];
3114
3115 // use the lower eight hex digits of the
3116 // system uptime as the temp dir name
3117 sprintf(szFile,
3118 "%08lX",
3119 ulRandom & 0xFFFFFFFF);
3120
3121 // if prefix is specified, overwrite the
3122 // first characters in the random number
3123 if (pcszPrefix)
3124 memcpy(szFile, pcszPrefix, ulPrefixLen);
3125
3126 if (pcszExt)
3127 {
3128 szFile[8] = '.';
3129 strcpy(szFile + 9, pcszExt);
3130 }
3131
3132 // now compose full temp file name
3133 strcpy(szFullTryThis, szDir);
3134 strcat(szFullTryThis, szFile);
3135 // now we have: "C:\temp\wpiXXXXX"
3136 if (DosQueryPathInfo(szFullTryThis,
3137 FIL_STANDARD,
3138 &fs3,
3139 sizeof(fs3))
3140 == ERROR_FILE_NOT_FOUND)
3141 {
3142 // file or dir doesn't exist:
3143 // cool, we're done
3144 strcpy(pszTempFileName, szFullTryThis);
3145 return NO_ERROR;
3146 }
3147
3148 // if this didn't work, raise ulRandom and try again
3149 ulRandom += 123;
3150
3151 // try only 100 times, just to be sure
3152 cAttempts++;
3153 } while (cAttempts < 100);
3154
3155 // 100 loops elapsed:
3156 arc = ERROR_BAD_FORMAT;
3157 }
3158 }
3159
3160 return arc;
3161}
3162
3163/*
3164 *@@ doshWriteTextFile:
3165 * writes a text file to disk; pszFile must contain the
3166 * whole path and filename.
3167 *
3168 * An existing file will be backed up if (pszBackup != NULL),
3169 * using doshCreateBackupFileName. In that case, pszBackup
3170 * receives the name of the backup created, so that buffer
3171 * should be CCHMAXPATH in size.
3172 *
3173 * If (pszbackup == NULL), an existing file will be overwritten.
3174 *
3175 * On success (NO_ERROR returned), *pulWritten receives
3176 * the no. of bytes written.
3177 *
3178 *@@changed V0.9.3 (2000-05-01) [umoeller]: optimized DosOpen; added error checking; changed prototype
3179 *@@changed V0.9.3 (2000-05-12) [umoeller]: added pszBackup
3180 */
3181
3182APIRET doshWriteTextFile(const char* pszFile, // in: file name
3183 const char* pszContent, // in: text to write
3184 PULONG pulWritten, // out: bytes written (ptr can be NULL)
3185 PSZ pszBackup) // in/out: create-backup?
3186{
3187 APIRET arc = NO_ERROR;
3188 ULONG ulWritten = 0;
3189
3190 if ((!pszFile) || (!pszContent))
3191 arc = ERROR_INVALID_PARAMETER;
3192 else
3193 {
3194 ULONG ulAction = 0,
3195 ulLocal = 0;
3196 HFILE hFile = 0;
3197
3198 ULONG ulSize = strlen(pszContent); // exclude 0 byte
3199
3200 if (pszBackup)
3201 {
3202 PSZ pszBackup2 = doshCreateBackupFileName(pszFile);
3203 DosCopy((PSZ)pszFile,
3204 pszBackup2,
3205 DCPY_EXISTING); // delete existing
3206 strcpy(pszBackup, pszBackup2);
3207 free(pszBackup2);
3208 }
3209
3210 if (!(arc = DosOpen((PSZ)pszFile,
3211 &hFile,
3212 &ulAction, // action taken
3213 ulSize, // primary allocation size
3214 FILE_ARCHIVED | FILE_NORMAL, // file attribute
3215 OPEN_ACTION_CREATE_IF_NEW
3216 | OPEN_ACTION_REPLACE_IF_EXISTS, // open flags
3217 OPEN_FLAGS_NOINHERIT
3218 | OPEN_FLAGS_SEQUENTIAL // sequential, not random access
3219 | OPEN_SHARE_DENYWRITE // deny write mode
3220 | OPEN_ACCESS_WRITEONLY, // write mode
3221 NULL))) // no EAs
3222 {
3223 if (!(arc = DosSetFilePtr(hFile,
3224 0L,
3225 FILE_BEGIN,
3226 &ulLocal)))
3227 if (!(arc = DosWrite(hFile,
3228 (PVOID)pszContent,
3229 ulSize,
3230 &ulWritten)))
3231 arc = DosSetFileSize(hFile, ulSize);
3232
3233 DosClose(hFile);
3234 }
3235 } // end if ((pszFile) && (pszContent))
3236
3237 if (pulWritten)
3238 *pulWritten = ulWritten;
3239
3240 return arc;
3241}
3242
3243/*
3244 *@@category: Helpers\Control program helpers\Directory management
3245 * directory helpers (querying, creating, deleting etc.).
3246 */
3247
3248/* ******************************************************************
3249 *
3250 * Directory helpers
3251 *
3252 ********************************************************************/
3253
3254/*
3255 *@@ doshQueryDirExist:
3256 * returns TRUE if the given directory
3257 * exists and really is a directory.
3258 */
3259
3260BOOL doshQueryDirExist(PCSZ pcszDir)
3261{
3262 FILESTATUS3 fs3;
3263 APIRET arc;
3264
3265 if (!(arc = DosQueryPathInfo((PSZ)pcszDir,
3266 FIL_STANDARD,
3267 &fs3,
3268 sizeof(fs3))))
3269 // file found:
3270 return ((fs3.attrFile & FILE_DIRECTORY) != 0);
3271 else
3272 return FALSE;
3273}
3274
3275/*
3276 *@@ doshCreatePath:
3277 * this creates the specified directory.
3278 * As opposed to DosCreateDir, this
3279 * function can create several directories
3280 * at the same time, if the parent
3281 * directories do not exist yet.
3282 */
3283
3284APIRET doshCreatePath(PCSZ pcszPath,
3285 BOOL fHidden) // in: if TRUE, the new directories will get FILE_HIDDEN
3286{
3287 APIRET arc0 = NO_ERROR;
3288 CHAR path[CCHMAXPATH];
3289 CHAR *cp, c;
3290 ULONG cbPath;
3291
3292 strcpy(path, pcszPath);
3293 cbPath = strlen(path);
3294
3295 if (path[cbPath] != '\\')
3296 {
3297 path[cbPath] = '\\';
3298 path[cbPath + 1] = 0;
3299 }
3300
3301 cp = path;
3302 // advance past the drive letter only if we have one
3303 if (*(cp+1) == ':')
3304 cp += 3;
3305
3306 // go!
3307 while (*cp != 0)
3308 {
3309 if (*cp == '\\')
3310 {
3311 c = *cp;
3312 *cp = 0;
3313 if (!doshQueryDirExist(path))
3314 {
3315 APIRET arc = DosCreateDir(path,
3316 0); // no EAs
3317 if (arc != NO_ERROR)
3318 {
3319 arc0 = arc;
3320 break;
3321 }
3322 else
3323 if (fHidden)
3324 {
3325 // hide the directory we just created
3326 doshSetPathAttr(path, FILE_HIDDEN);
3327 }
3328 }
3329 *cp = c;
3330 }
3331 cp++;
3332 }
3333
3334 return (arc0);
3335}
3336
3337/*
3338 *@@ doshQueryCurrentDir:
3339 * writes the current directory into
3340 * the specified buffer, which should be
3341 * CCHMAXPATH in size.
3342 *
3343 * As opposed to DosQueryCurrentDir, this
3344 * includes the drive letter.
3345 *
3346 *@@added V0.9.4 (2000-07-22) [umoeller]
3347 */
3348
3349APIRET doshQueryCurrentDir(PSZ pszBuf)
3350{
3351 APIRET arc = NO_ERROR;
3352 ULONG ulCurDisk = 0;
3353 ULONG ulMap = 0;
3354 if (!(arc = DosQueryCurrentDisk(&ulCurDisk, &ulMap)))
3355 {
3356 ULONG cbBuf = CCHMAXPATH - 3;
3357 pszBuf[0] = ulCurDisk + 'A' - 1;
3358 pszBuf[1] = ':';
3359 pszBuf[2] = '\\';
3360 pszBuf[3] = '\0';
3361 arc = DosQueryCurrentDir(0, pszBuf + 3, &cbBuf);
3362 }
3363
3364 return arc;
3365}
3366
3367/*
3368 *@@ doshDeleteDir:
3369 * deletes a directory. As opposed to DosDeleteDir,
3370 * this removes empty subdirectories and/or files
3371 * as well.
3372 *
3373 * flFlags can be any combination of the following:
3374 *
3375 * -- DOSHDELDIR_RECURSE: recurse into subdirectories.
3376 *
3377 * -- DOSHDELDIR_DELETEFILES: delete all regular files
3378 * which are found on the way.
3379 *
3380 * THIS IS NOT IMPLEMENTED YET.
3381 *
3382 * If 0 is specified, this effectively behaves just as
3383 * DosDeleteDir.
3384 *
3385 * If you specify DOSHDELDIR_RECURSE only, only empty
3386 * subdirectories are deleted as well.
3387 *
3388 * If you specify DOSHDELDIR_RECURSE | DOSHDELDIR_DELETEFILES,
3389 * this removes an entire directory tree, including all
3390 * subdirectories and files.
3391 *
3392 *@@added V0.9.4 (2000-07-01) [umoeller]
3393 */
3394
3395APIRET doshDeleteDir(PCSZ pcszDir,
3396 ULONG flFlags,
3397 PULONG pulDirs, // out: directories found
3398 PULONG pulFiles) // out: files found
3399{
3400 APIRET arc = NO_ERROR,
3401 arcReturn = NO_ERROR;
3402
3403 HDIR hdirFindHandle = HDIR_CREATE;
3404 FILEFINDBUF3 ffb3 = {0}; // returned from FindFirst/Next
3405 ULONG ulResultBufLen = sizeof(FILEFINDBUF3);
3406 ULONG ulFindCount = 1; // look for 1 file at a time
3407
3408 CHAR szFileMask[2*CCHMAXPATH];
3409 sprintf(szFileMask, "%s\\*", pcszDir);
3410
3411 // find files
3412 arc = DosFindFirst(szFileMask,
3413 &hdirFindHandle, // directory search handle
3414 FILE_ARCHIVED | FILE_DIRECTORY | FILE_SYSTEM
3415 | FILE_HIDDEN | FILE_READONLY,
3416 // search attributes; include all, even dirs
3417 &ffb3, // result buffer
3418 ulResultBufLen, // result buffer length
3419 &ulFindCount, // number of entries to find
3420 FIL_STANDARD); // return level 1 file info
3421
3422 if (arc == NO_ERROR)
3423 {
3424 // keep finding the next file until there are no more files
3425 while (arc == NO_ERROR) // != ERROR_NO_MORE_FILES
3426 {
3427 if (ffb3.attrFile & FILE_DIRECTORY)
3428 {
3429 // we found a directory:
3430
3431 // ignore the pseudo-directories
3432 if ( (strcmp(ffb3.achName, ".") != 0)
3433 && (strcmp(ffb3.achName, "..") != 0)
3434 )
3435 {
3436 // real directory:
3437 if (flFlags & DOSHDELDIR_RECURSE)
3438 {
3439 // recurse!
3440 CHAR szSubDir[2*CCHMAXPATH];
3441 sprintf(szSubDir, "%s\\%s", pcszDir, ffb3.achName);
3442 arcReturn = doshDeleteDir(szSubDir,
3443 flFlags,
3444 pulDirs,
3445 pulFiles);
3446 // this removes ffb3.achName as well
3447 }
3448 else
3449 {
3450 // directory, but no recursion:
3451 // report "access denied"
3452 // (this is what OS/2 reports with DosDeleteDir as well)
3453 arcReturn = ERROR_ACCESS_DENIED; // 5
3454 (*pulDirs)++;
3455 }
3456 }
3457 }
3458 else
3459 {
3460 // it's a file:
3461 arcReturn = ERROR_ACCESS_DENIED;
3462 (*pulFiles)++;
3463 }
3464
3465 if (arc == NO_ERROR)
3466 {
3467 ulFindCount = 1; // reset find count
3468 arc = DosFindNext(hdirFindHandle, // directory handle
3469 &ffb3, // result buffer
3470 ulResultBufLen, // result buffer length
3471 &ulFindCount); // number of entries to find
3472 }
3473 } // endwhile
3474
3475 DosFindClose(hdirFindHandle); // close our find handle
3476 }
3477
3478 if (arcReturn == NO_ERROR)
3479 // success so far:
3480 // delete our directory now
3481 arcReturn = DosDeleteDir((PSZ)pcszDir);
3482
3483 return (arcReturn);
3484}
3485
3486/*
3487 *@@ doshCanonicalize:
3488 * simplifies path specifications to remove '.'
3489 * and '..' entries and generates a fully
3490 * qualified path name where possible.
3491 * File specifications are left unchanged.
3492 *
3493 * This returns:
3494 *
3495 * -- NO_ERROR: the buffers were valid.
3496 *
3497 * -- ERROR_INVALID_PARAMETER: the buffers
3498 * were invalid.
3499 *
3500 *@@added V0.9.19 (2002-04-22) [pr]
3501 */
3502
3503APIRET doshCanonicalize(PCSZ pcszFileIn, // in: path to canonicalize
3504 PSZ pszFileOut, // out: canonicalized path if NO_ERROR
3505 ULONG cbFileOut) // in: size of pszFileOut buffer
3506{
3507 APIRET ulrc = NO_ERROR;
3508 CHAR szFileTemp[CCHMAXPATH];
3509
3510 if (pcszFileIn && pszFileOut && cbFileOut)
3511 {
3512 strncpy(szFileTemp, pcszFileIn, sizeof(szFileTemp) - 1);
3513 szFileTemp[sizeof(szFileTemp) - 1] = 0;
3514 if ( strchr(szFileTemp, '\\')
3515 || strchr(szFileTemp, ':')
3516 )
3517 {
3518 ULONG cbFileTemp = strlen(szFileTemp);
3519
3520 if ( (cbFileTemp > 3)
3521 && (szFileTemp[cbFileTemp - 1] == '\\')
3522 )
3523 {
3524 szFileTemp[cbFileTemp - 1] = 0;
3525 }
3526
3527 if (DosQueryPathInfo(szFileTemp,
3528 FIL_QUERYFULLNAME,
3529 pszFileOut,
3530 cbFileOut))
3531 {
3532 pszFileOut[0] = 0;
3533 }
3534 }
3535 else
3536 {
3537 strncpy(pszFileOut, pcszFileIn, cbFileOut - 1);
3538 pszFileOut[cbFileOut - 1] = 0;
3539 }
3540 }
3541 else
3542 ulrc = ERROR_INVALID_PARAMETER;
3543
3544 return(ulrc);
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.