source: trunk/src/helpers/dosh.c

Last change on this file was 413, checked in by pr, 13 years ago

Fix buffer overwrite. Bug 1206.

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