source: trunk/dll/filldir.c@ 775

Last change on this file since 775 was 775, checked in by Gregg Young, 18 years ago

Minor clean up add comments re recent changes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.3 KB
Line 
1
2/***********************************************************************
3
4 $Id: filldir.c 775 2007-08-11 21:07:07Z gyoung $
5
6 Fill Directory Tree Containers
7
8 Copyright (c) 1993-98 M. Kimes
9 Copyright (c) 2001, 2007 Steven H. Levine
10
11 10 Jan 04 SHL ProcessDirectory: avoid most large drive failures
12 24 May 05 SHL Rework Win_Error usage
13 24 May 05 SHL Rework for CNRITEM.szSubject
14 25 May 05 SHL Rework for ULONGLONG
15 25 May 05 SHL Rework FillInRecordFromFFB
16 25 May 05 SHL Rework FillTreeCnr
17 28 May 05 SHL Drop stale debug code
18 05 Jun 05 SHL Comments
19 09 Jun 05 SHL Rework WinLoadFileIcon enables
20 09 Jun 05 SHL Rework IDFile
21 13 Aug 05 SHL Renames
22 24 Oct 05 SHL FillInRecordFromFFB: correct longname display enable
23 24 Oct 05 SHL FillInRecordFromFSA: correct longname display enable
24 24 Oct 05 SHL Drop obsolete code
25 22 Jul 06 SHL Check more run time errors
26 20 Oct 06 SHL Sync . .. check code
27 22 Oct 06 GKY Add NDFS32 support
28 17 Feb 07 GKY Additional archive and image file tyoes identifed by extension
29 17 Feb 07 GKY Add more drive types
30 09 Mar 07 GKY Use SelectDriveIcon
31 20 Mar 07 GKY Increase extention check to 4 letters for icon selections
32 23 Jun 07 GKY Fixed ram disk without a directory not appearing on states drive list
33 23 Jul 07 SHL Sync with CNRITEM updates (ticket#24)
34 29 Jul 07 SHL Add CNRITEM free and remove support (ticket#24)
35 02 Aug 07 SHL Add FileAttrToString
36 03 Aug 07 GKY Enlarged and made setable everywhere Findbuf (speed file loading)
37 04 Aug 07 SHL Update #pragma alloc_test for new functions
38 06 Aug 07 GKY Reduce DosSleep times (ticket 148)
39
40***********************************************************************/
41
42#define INCL_DOS
43#define INCL_WIN
44#define INCL_LONGLONG
45#include <os2.h>
46
47#include <stdarg.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51#include <ctype.h>
52#include <time.h>
53#include <time.h>
54
55#if 1 // fixme to disable or to be configurable
56#include <malloc.h> // _heapchk
57#endif
58
59#include "fm3dll.h"
60#include "fm3str.h"
61
62static PSZ pszSrcFile = __FILE__;
63
64#pragma alloc_text(FILLDIR,FillInRecordFromFFB,FillInRecordFromFSA,IDFile)
65#pragma alloc_text(FILLDIR1,ProcessDirectory,FillDirCnr,FillTreeCnr,FileAttrToString)
66#pragma alloc_text(EMPTYCNR,EmptyCnr,FreeCnrItemData,FreeCnrItem,FreeCnrItemList,RemoveCnrItems)
67
68/**
69 * Return display string given standard file attribute mask
70 * @param fileAttr attribute mask in FILEFINDBUF format
71 * @return fixed length string for display
72 */
73
74const PSZ FileAttrToString(ULONG fileAttr)
75{
76 // From os2win.h
77 // FILE_ATTRIBUTE_READONLY 0x00000001
78 // FILE_ATTRIBUTE_HIDDEN 0x00000002
79 // FILE_ATTRIBUTE_SYSTEM 0x00000004
80 // 0x00000008
81 // FILE_ATTRIBUTE_DIRECTORY 0x00000010
82 // FILE_ATTRIBUTE_ARCHIVE 0x00000020
83
84 static CHAR *apszAttrString[] = {
85 // RHSDA
86 "-----",
87 "R----",
88 "-H---",
89 "RH---",
90 "--S--",
91 "R-S--",
92 "-HS--",
93 "RHS--",
94 "---D-",
95 "R--D-",
96 "-H-D-",
97 "RH-D-",
98 "--SD-",
99 "R-SD-",
100 "-HSD-",
101 "RHSD-",
102 "----A",
103 "R---A",
104 "-H--A",
105 "RH--A",
106 "--S-A",
107 "R-S-A",
108 "-HS-A",
109 "RHS-A",
110 "---DA",
111 "R--DA",
112 "-H-DA",
113 "RH-DA",
114 "--SDA",
115 "R-SDA",
116 "-HSDA",
117 "RHSDA"
118 };
119
120 fileAttr = ((fileAttr & 0x30) >> 1) | (fileAttr & 7); // Drop don't care bit from index
121
122 return apszAttrString[fileAttr];
123
124}
125
126static HPOINTER IDFile(PSZ p)
127{
128 HPOINTER hptr;
129 ULONG cmp;
130 CHAR cmps[5];
131
132 p = strrchr(p, '.');
133 if (p && !p[5]) {
134 cmps[0] = '.';
135 cmps[1] = toupper(p[1]);
136 cmps[2] = toupper(p[2]);
137 cmps[3] = toupper(p[3]);
138 cmps[4] = toupper(p[4]);
139
140 cmp = *(ULONG *) cmps;
141
142 if (cmp == *(ULONG *) ".EXE" || cmp == *(ULONG *) ".CMD" ||
143 cmp == *(ULONG *) ".BAT" || cmp == *(ULONG *) ".COM")
144 hptr = hptrApp;
145 else if (cmp == *(ULONG *) ".ZIP" || cmp == *(ULONG *) ".LZH" ||
146 cmp == *(ULONG *) ".ARJ" || cmp == *(ULONG *) ".ARC" ||
147 cmp == *(ULONG *) ".ZOO" || cmp == *(ULONG *) ".RAR" ||
148 cmp == *(ULONG *) ".TAR" || cmp == *(ULONG *) ".TGZ" ||
149 cmp == *(ULONG *) ".GZ" || cmp == *(ULONG *) ".Z" ||
150 cmp == *(ULONG *) ".CAB" || cmp == *(ULONG *) ".BZ2")
151 hptr = hptrArc;
152 else if (cmp == *(ULONG *) ".BMP" || cmp == *(ULONG *) ".ICO" ||
153 cmp == *(ULONG *) ".PTR" || cmp == *(ULONG *) ".GIF" ||
154 cmp == *(ULONG *) ".TIF" || cmp == *(ULONG *) ".PCX" ||
155 cmp == *(ULONG *) ".TGA" || cmp == *(ULONG *) ".XBM" ||
156 cmp == *(ULONG *) ".JPEG" || cmp == *(ULONG *) ".JPG" ||
157 cmp == *(ULONG *) ".PNG" || cmp == *(ULONG *) ".PSD" ||
158 cmp == *(ULONG *) ".LGO" || cmp == *(ULONG *) ".EPS" ||
159 cmp == *(ULONG *) ".RLE" || cmp == *(ULONG *) ".RAS" ||
160 cmp == *(ULONG *) ".PLC" || cmp == *(ULONG *) ".MSP" ||
161 cmp == *(ULONG *) ".IFF" || cmp == *(ULONG *) ".FIT" ||
162 cmp == *(ULONG *) ".DCX" || cmp == *(ULONG *) ".MAC" ||
163 cmp == *(ULONG *) ".SFF" || cmp == *(ULONG *) ".SGI" ||
164 cmp == *(ULONG *) ".XWD" || cmp == *(ULONG *) ".XPM" ||
165 cmp == *(ULONG *) ".WPG" || cmp == *(ULONG *) ".CUR" ||
166 cmp == *(ULONG *) ".PNM" || cmp == *(ULONG *) ".PPM" ||
167 cmp == *(ULONG *) ".PGM" || cmp == *(ULONG *) ".PBM")
168 hptr = hptrArt;
169 else
170 hptr = (HPOINTER) 0;
171 }
172 else
173 hptr = (HPOINTER) 0;
174
175 return hptr;
176}
177
178static BOOL IsDefaultIcon(HPOINTER hptr)
179{
180 HPOINTER hptr2;
181 HPOINTER hptr3;
182 UINT u;
183
184 static HPOINTER hptrPMFile;
185 static HPOINTER hptrWPSFile;
186
187 if (!hptrPMFile) {
188 hptrPMFile = WinQuerySysPointer(HWND_DESKTOP, SPTR_FILE, FALSE);
189 }
190
191 // try to guess WPS default file icon
192 hptr2 = (HPOINTER) 0;
193 for (u = 0; !hptrWPSFile && u < 10; u++) {
194 char szFileName[CCHMAXPATH];
195 char *psz;
196
197 psz = getenv("TMP");
198 if (!psz && *psz)
199 psz = getenv("TEMP");
200 if (psz && *psz) {
201 strcpy(szFileName, psz);
202 psz = szFileName + strlen(szFileName) - 1;
203 if (*psz != '\\') {
204 psz++;
205 *psz++ = '\\';
206 }
207 }
208 else
209 psz = szFileName;
210
211 sprintf(psz, "%08x.%03x", rand() & 0xffffffff, rand() & 0xfff);
212 if (IsFile(szFileName) != 1) {
213 FILE *fp = fopen(szFileName, "w");
214
215 if (fp) {
216 fclose(fp);
217 hptr3 = WinLoadFileIcon(szFileName, FALSE);
218 unlinkf("%s", szFileName);
219 if (!hptr2)
220 hptr2 = hptr3;
221 else if (hptr3 == hptr3) {
222 hptrWPSFile = hptr3; // Got same icon twice
223 break;
224 }
225 }
226 }
227 DosSleep(rand() % 100);
228
229 } // for
230
231 return hptr == hptrPMFile || hptr == hptrWPSFile;
232
233} // IsDefaultIcon
234
235ULONGLONG FillInRecordFromFFB(HWND hwndCnr,
236 PCNRITEM pci,
237 const PSZ pszDirectory,
238 const PFILEFINDBUF4 pffb,
239 const BOOL partial,
240 DIRCNRDATA *dcd)
241{
242 /* fill in a container record from a FILEFINDBUF4 structure */
243
244 CHAR *p;
245 HPOINTER hptr;
246
247 pci->hwndCnr = hwndCnr;
248
249 /* note that we cheat below, and accept the full pathname in pszDirectory
250 if !*pffb->achName. This speeds up and simplifies processing elsewhere
251 (like in update.c)
252 */
253 if (!*pffb->achName) {
254 pci->pszFileName = xstrdup(pszDirectory, pszSrcFile, __LINE__);
255 strcpy(pci->pszFileName, pszDirectory);
256 }
257 else {
258 INT c = strlen(pszDirectory);
259 INT c2 = pffb->cchName + 1;
260 if (pszDirectory[c - 1] != '\\')
261 c2++;
262 pci->pszFileName = xmalloc(c + c2, pszSrcFile, __LINE__);
263 memcpy(pci->pszFileName, pszDirectory, c + 1);
264 p = pci->pszFileName + c - 1;
265 if (*p != '\\') {
266 p++;
267 *p = '\\';
268 }
269 p++;
270 memcpy(p, pffb->achName, pffb->cchName + 1);
271 }
272
273 /* load the object's Subject, if required */
274 pci->pszSubject = NullStr;
275 if (pffb->cbList > 4L &&
276 dcd && fLoadSubject &&
277 (isalpha(*pci->pszFileName) &&
278 !(driveflags[toupper(*pci->pszFileName) - 'A'] & DRIVE_NOLOADSUBJS)))
279 {
280 APIRET rc;
281 EAOP2 eaop;
282 PGEA2LIST pgealist;
283 PFEA2LIST pfealist;
284 PGEA2 pgea;
285 PFEA2 pfea;
286 CHAR *value;
287
288 pgealist = xmallocz(sizeof(GEA2LIST) + 32, pszSrcFile, __LINE__);
289 if (pgealist) {
290 pgea = &pgealist->list[0];
291 strcpy(pgea->szName, SUBJECT);
292 pgea->cbName = strlen(pgea->szName);
293 pgea->oNextEntryOffset = 0;
294 pgealist->cbList = (sizeof(GEA2LIST) + pgea->cbName);
295 pfealist = xmallocz(1532, pszSrcFile, __LINE__);
296 if (pfealist) {
297 pfealist->cbList = 1024;
298 eaop.fpGEA2List = pgealist;
299 eaop.fpFEA2List = pfealist;
300 eaop.oError = 0;
301 rc = DosQueryPathInfo(pci->pszFileName, FIL_QUERYEASFROMLIST,
302 (PVOID) & eaop, (ULONG) sizeof(EAOP2));
303 if (!rc) {
304 pfea = &eaop.fpFEA2List->list[0];
305 value = pfea->szName + pfea->cbName + 1;
306 value[pfea->cbValue] = 0;
307 if (*(USHORT *) value == EAT_ASCII)
308 pci->pszSubject = xstrdup(value + (sizeof(USHORT) * 2), pszSrcFile, __LINE__);
309 }
310 free(pfealist);
311 }
312 free(pgealist);
313 }
314 }
315 if (!pci->pszSubject)
316 pci->pszSubject = NullStr;
317
318 /* load the object's longname */
319 pci->pszLongName = 0;
320 if (fLoadLongnames &&
321 dcd &&
322 pffb->cbList > 4L &&
323 isalpha(*pci->pszFileName) &&
324 ~driveflags[toupper(*pci->pszFileName) - 'A'] & DRIVE_NOLONGNAMES &&
325 ~driveflags[toupper(*pci->pszFileName) - 'A'] & DRIVE_NOLOADLONGS)
326 {
327 APIRET rc;
328 EAOP2 eaop;
329 PGEA2LIST pgealist;
330 PFEA2LIST pfealist;
331 PGEA2 pgea;
332 PFEA2 pfea;
333 CHAR *value;
334
335 pgealist = xmallocz(sizeof(GEA2LIST) + 32, pszSrcFile, __LINE__);
336 if (pgealist) {
337 pgea = &pgealist->list[0];
338 strcpy(pgea->szName, LONGNAME);
339 pgea->cbName = strlen(pgea->szName);
340 pgea->oNextEntryOffset = 0;
341 pgealist->cbList = (sizeof(GEA2LIST) + pgea->cbName);
342 pfealist = xmallocz(1532, pszSrcFile, __LINE__);
343 if (pfealist) {
344 pfealist->cbList = 1024;
345 eaop.fpGEA2List = pgealist;
346 eaop.fpFEA2List = pfealist;
347 eaop.oError = 0;
348 rc = DosQueryPathInfo(pci->pszFileName, FIL_QUERYEASFROMLIST,
349 (PVOID) & eaop, (ULONG) sizeof(EAOP2));
350 if (!rc) {
351 pfea = &eaop.fpFEA2List->list[0];
352 value = pfea->szName + pfea->cbName + 1;
353 value[pfea->cbValue] = 0;
354 if (*(USHORT *) value == EAT_ASCII)
355 pci->pszLongName = xstrdup(value + (sizeof(USHORT) * 2), pszSrcFile, __LINE__);
356 }
357 free(pfealist);
358 }
359 free(pgealist);
360 }
361 }
362 if (!pci->pszLongName)
363 pci->pszLongName = NullStr;
364
365 /* do anything required to case of filename */
366 if (fForceUpper)
367 strupr(pci->pszFileName);
368 else if (fForceLower)
369 strlwr(pci->pszFileName);
370
371 /* get an icon to use with it */
372 if (pffb->attrFile & FILE_DIRECTORY) {
373 // is directory
374 if (fNoIconsDirs ||
375 (driveflags[toupper(*pci->pszFileName) - 'A'] & DRIVE_NOLOADICONS) ||
376 !isalpha(*pci->pszFileName)) {
377 hptr = (HPOINTER) 0;
378 }
379 else
380 hptr = WinLoadFileIcon(pci->pszFileName, FALSE);
381 }
382 else {
383 // is file
384 if (fNoIconsFiles ||
385 (driveflags[toupper(*pci->pszFileName) - 'A'] & DRIVE_NOLOADICONS) ||
386 !isalpha(*pci->pszFileName)) {
387 hptr = (HPOINTER) 0;
388 }
389 else
390 hptr = WinLoadFileIcon(pci->pszFileName, FALSE);
391
392 if (!hptr || IsDefaultIcon(hptr))
393 hptr = IDFile(pci->pszFileName);
394 }
395
396 if (!hptr) {
397 hptr = pffb->attrFile & FILE_DIRECTORY ?
398 hptrDir : pffb->attrFile & FILE_SYSTEM ?
399 hptrSystem :
400 pffb->attrFile & FILE_HIDDEN ?
401 hptrHidden :
402 pffb->attrFile & FILE_READONLY ?
403 hptrReadonly : hptrFile;
404 }
405
406 // Tell container what part of pathname to display
407 if (partial) {
408 p = strrchr(pci->pszFileName, '\\');
409 if (!p) {
410 p = strrchr(pci->pszFileName, ':');
411 if (!p)
412 p = pci->pszFileName;
413 else
414 p++;
415 }
416 else if ((dcd && dcd->type == TREE_FRAME) ||
417 (!(pffb->attrFile & FILE_DIRECTORY) || !*(p + 1))) {
418 p++;
419 }
420 if (!*p)
421 p = pci->pszFileName;
422 }
423 else
424 p = pci->pszFileName;
425 pci->pszDisplayName = p;
426
427 /* now fill the darned thing in... */
428 pci->date.day = pffb->fdateLastWrite.day;
429 pci->date.month = pffb->fdateLastWrite.month;
430 pci->date.year = pffb->fdateLastWrite.year + 1980;
431 pci->time.seconds = pffb->ftimeLastWrite.twosecs * 2;
432 pci->time.minutes = pffb->ftimeLastWrite.minutes;
433 pci->time.hours = pffb->ftimeLastWrite.hours;
434 pci->ladate.day = pffb->fdateLastAccess.day;
435 pci->ladate.month = pffb->fdateLastAccess.month;
436 pci->ladate.year = pffb->fdateLastAccess.year + 1980;
437 pci->latime.seconds = pffb->ftimeLastAccess.twosecs * 2;
438 pci->latime.minutes = pffb->ftimeLastAccess.minutes;
439 pci->latime.hours = pffb->ftimeLastAccess.hours;
440 pci->crdate.day = pffb->fdateCreation.day;
441 pci->crdate.month = pffb->fdateCreation.month;
442 pci->crdate.year = pffb->fdateCreation.year + 1980;
443 pci->crtime.seconds = pffb->ftimeCreation.twosecs * 2;
444 pci->crtime.minutes = pffb->ftimeCreation.minutes;
445 pci->crtime.hours = pffb->ftimeCreation.hours;
446 pci->easize = CBLIST_TO_EASIZE(pffb->cbList);
447 pci->cbFile = pffb->cbFile;
448 pci->attrFile = pffb->attrFile;
449 pci->pszDispAttr = FileAttrToString(pci->attrFile);
450 pci->rc.pszIcon = pci->pszDisplayName;
451 pci->rc.hptrIcon = hptr;
452
453 /* check to see if record should be visible */
454 if (dcd && (*dcd->mask.szMask || dcd->mask.antiattr ||
455 ((dcd->mask.attrFile &
456 (FILE_HIDDEN | FILE_SYSTEM | FILE_READONLY | FILE_ARCHIVED))
457 !=
458 (FILE_HIDDEN | FILE_SYSTEM | FILE_READONLY | FILE_ARCHIVED))))
459 {
460 if (*dcd->mask.szMask || dcd->mask.antiattr) {
461 if (!Filter((PMINIRECORDCORE) pci, (PVOID) & dcd->mask))
462 pci->rc.flRecordAttr |= CRA_FILTERED;
463 }
464 else if ((!(dcd->mask.attrFile & FILE_HIDDEN) &&
465 (pci->attrFile & FILE_HIDDEN)) ||
466 (!(dcd->mask.attrFile & FILE_SYSTEM) &&
467 (pci->attrFile & FILE_SYSTEM)) ||
468 (!(dcd->mask.attrFile & FILE_READONLY) &&
469 (pci->attrFile & FILE_READONLY)) ||
470 (!(dcd->mask.attrFile & FILE_ARCHIVED) &&
471 (pci->attrFile & FILE_ARCHIVED))) {
472 pci->rc.flRecordAttr |= CRA_FILTERED;
473 }
474 }
475
476 return pffb->cbFile + pci->easize;
477
478} // FillInRecordFromFFB
479
480ULONGLONG FillInRecordFromFSA(HWND hwndCnr, PCNRITEM pci, const PSZ pszFileName, const PFILESTATUS4 pfsa4, const BOOL partial, DIRCNRDATA * dcd) // Optional
481{
482 HPOINTER hptr;
483 CHAR *p;
484
485 /* fill in a container record from a FILESTATUS4 structure */
486
487 pci->hwndCnr = hwndCnr;
488 pci->pszFileName = xstrdup(pszFileName, pszSrcFile, __LINE__);
489 strcpy(pci->pszFileName, pszFileName);
490
491 /* load the object's Subject, if required */
492 pci->pszSubject = NullStr;
493 if (pfsa4->cbList > 4L &&
494 dcd &&
495 fLoadSubject &&
496 (!isalpha(*pci->pszFileName) ||
497 !(driveflags[toupper(*pci->pszFileName) - 'A'] & DRIVE_NOLOADSUBJS)))
498 {
499 APIRET rc;
500 EAOP2 eaop;
501 PGEA2LIST pgealist;
502 PFEA2LIST pfealist;
503 PGEA2 pgea;
504 PFEA2 pfea;
505 CHAR *value;
506
507 pgealist = xmallocz(sizeof(GEA2LIST) + 32, pszSrcFile, __LINE__);
508 if (pgealist) {
509 pgea = &pgealist->list[0];
510 strcpy(pgea->szName, SUBJECT);
511 pgea->cbName = strlen(pgea->szName);
512 pgea->oNextEntryOffset = 0;
513 pgealist->cbList = (sizeof(GEA2LIST) + pgea->cbName);
514 pfealist = xmallocz(1532, pszSrcFile, __LINE__);
515 if (pfealist) {
516 pfealist->cbList = 1024;
517 eaop.fpGEA2List = pgealist;
518 eaop.fpFEA2List = pfealist;
519 eaop.oError = 0;
520 rc = DosQueryPathInfo(pci->pszFileName, FIL_QUERYEASFROMLIST,
521 (PVOID) & eaop, (ULONG) sizeof(EAOP2));
522 if (!rc) {
523 pfea = &eaop.fpFEA2List->list[0];
524 value = pfea->szName + pfea->cbName + 1;
525 value[pfea->cbValue] = 0;
526 if (*(USHORT *) value == EAT_ASCII)
527 pci->pszSubject = xstrdup(value + (sizeof(USHORT) * 2), pszSrcFile, __LINE__);
528 }
529 free(pfealist);
530 }
531 free(pgealist);
532 }
533 }
534 if (!pci->pszSubject)
535 pci->pszSubject = NullStr;
536
537 pci->pszLongName = 0;
538 if (fLoadLongnames &&
539 dcd &&
540 pfsa4->cbList > 4L &&
541 isalpha(*pci->pszFileName) &&
542 ~driveflags[toupper(*pci->pszFileName) - 'A'] & DRIVE_NOLONGNAMES &&
543 ~driveflags[toupper(*pci->pszFileName) - 'A'] & DRIVE_NOLOADLONGS)
544 {
545 APIRET rc;
546 EAOP2 eaop;
547 PGEA2LIST pgealist;
548 PFEA2LIST pfealist;
549 PGEA2 pgea;
550 PFEA2 pfea;
551 CHAR *value;
552
553 pgealist = xmallocz(sizeof(GEA2LIST) + 32, pszSrcFile, __LINE__);
554 if (pgealist) {
555 pgea = &pgealist->list[0];
556 strcpy(pgea->szName, LONGNAME);
557 pgea->cbName = strlen(pgea->szName);
558 pgea->oNextEntryOffset = 0;
559 pgealist->cbList = (sizeof(GEA2LIST) + pgea->cbName);
560 pfealist = xmallocz(1532, pszSrcFile, __LINE__);
561 if (pfealist) {
562 pfealist->cbList = 1024;
563 eaop.fpGEA2List = pgealist;
564 eaop.fpFEA2List = pfealist;
565 eaop.oError = 0;
566 rc = DosQueryPathInfo(pci->pszFileName, FIL_QUERYEASFROMLIST,
567 (PVOID) & eaop, (ULONG) sizeof(EAOP2));
568 if (!rc) {
569 pfea = &eaop.fpFEA2List->list[0];
570 value = pfea->szName + pfea->cbName + 1; // Point at EA value
571 value[pfea->cbValue] = 0; // Terminate
572 if (*(USHORT *) value == EAT_ASCII) {
573 p = value + sizeof(USHORT) * 2; // Point at value string
574 pci->pszLongName = xstrdup(p, pszSrcFile, __LINE__);
575 }
576 }
577 free(pfealist);
578 }
579 free(pgealist);
580 }
581 }
582 if (!pci->pszLongName)
583 pci->pszLongName = NullStr;
584
585 if (fForceUpper)
586 strupr(pci->pszFileName);
587 else if (fForceLower)
588 strlwr(pci->pszFileName);
589
590 if (pfsa4->attrFile & FILE_DIRECTORY) {
591 if (fNoIconsDirs ||
592 (driveflags[toupper(*pci->pszFileName) - 'A'] & DRIVE_NOLOADICONS) ||
593 !isalpha(*pci->pszFileName)) {
594 hptr = (HPOINTER) 0;
595 }
596 else
597 hptr = WinLoadFileIcon(pci->pszFileName, FALSE);
598 }
599 else {
600 if (fNoIconsFiles ||
601 (driveflags[toupper(*pci->pszFileName) - 'A'] & DRIVE_NOLOADICONS) ||
602 !isalpha(*pci->pszFileName)) {
603 hptr = IDFile(pci->pszFileName);
604 }
605 else
606 hptr = WinLoadFileIcon(pci->pszFileName, FALSE);
607 }
608 if (!hptr) {
609 hptr = pfsa4->attrFile & FILE_DIRECTORY ?
610 hptrDir :
611 pfsa4->attrFile & FILE_SYSTEM ?
612 hptrSystem :
613 pfsa4->attrFile & FILE_HIDDEN ?
614 hptrHidden : pfsa4->attrFile & FILE_READONLY ? hptrReadonly : hptrFile;
615 }
616
617 // Tell container what part of pathname to display
618 if (partial) {
619 p = strrchr(pci->pszFileName, '\\');
620 if (!p) {
621 p = strrchr(pci->pszFileName, ':');
622 if (!p)
623 p = pci->pszFileName;
624 else
625 p++;
626 }
627 else if ((dcd && dcd->type == TREE_FRAME) ||
628 !(pfsa4->attrFile & FILE_DIRECTORY) || !*(p + 1))
629 p++;
630 if (!*p)
631 p = pci->pszFileName;
632 }
633 else
634 p = pci->pszFileName;
635 pci->pszDisplayName = p;
636
637 pci->date.day = pfsa4->fdateLastWrite.day;
638 pci->date.month = pfsa4->fdateLastWrite.month;
639 pci->date.year = pfsa4->fdateLastWrite.year + 1980;
640 pci->time.seconds = pfsa4->ftimeLastWrite.twosecs * 2;
641 pci->time.minutes = pfsa4->ftimeLastWrite.minutes;
642 pci->time.hours = pfsa4->ftimeLastWrite.hours;
643 pci->ladate.day = pfsa4->fdateLastAccess.day;
644 pci->ladate.month = pfsa4->fdateLastAccess.month;
645 pci->ladate.year = pfsa4->fdateLastAccess.year + 1980;
646 pci->latime.seconds = pfsa4->ftimeLastAccess.twosecs * 2;
647 pci->latime.minutes = pfsa4->ftimeLastAccess.minutes;
648 pci->latime.hours = pfsa4->ftimeLastAccess.hours;
649 pci->crdate.day = pfsa4->fdateCreation.day;
650 pci->crdate.month = pfsa4->fdateCreation.month;
651 pci->crdate.year = pfsa4->fdateCreation.year + 1980;
652 pci->crtime.seconds = pfsa4->ftimeCreation.twosecs * 2;
653 pci->crtime.minutes = pfsa4->ftimeCreation.minutes;
654 pci->crtime.hours = pfsa4->ftimeCreation.hours;
655 pci->easize = CBLIST_TO_EASIZE(pfsa4->cbList);
656 pci->cbFile = pfsa4->cbFile;
657 pci->attrFile = pfsa4->attrFile;
658 pci->pszDispAttr = FileAttrToString(pci->attrFile);
659 pci->rc.pszIcon = pci->pszDisplayName;
660 pci->rc.hptrIcon = hptr;
661
662 if (dcd &&
663 (*dcd->mask.szMask || dcd->mask.antiattr ||
664 ((dcd->mask.attrFile &
665 (FILE_HIDDEN | FILE_SYSTEM | FILE_READONLY | FILE_ARCHIVED)) !=
666 (FILE_HIDDEN | FILE_SYSTEM | FILE_READONLY | FILE_ARCHIVED)))) {
667 if (*dcd->mask.szMask || dcd->mask.antiattr) {
668 if (!Filter((PMINIRECORDCORE) pci, (PVOID) & dcd->mask))
669 pci->rc.flRecordAttr |= CRA_FILTERED;
670 }
671 else if ((!(dcd->mask.attrFile & FILE_HIDDEN) &&
672 (pci->attrFile & FILE_HIDDEN)) ||
673 (!(dcd->mask.attrFile & FILE_SYSTEM) &&
674 (pci->attrFile & FILE_SYSTEM)) ||
675 (!(dcd->mask.attrFile & FILE_READONLY) &&
676 (pci->attrFile & FILE_READONLY)) ||
677 (!(dcd->mask.attrFile & FILE_ARCHIVED) &&
678 (pci->attrFile & FILE_ARCHIVED)))
679 pci->rc.flRecordAttr |= CRA_FILTERED;
680 }
681
682 return pfsa4->cbFile + pci->easize;
683
684} // FillInRecordFromFSA
685
686VOID ProcessDirectory(const HWND hwndCnr,
687 const PCNRITEM pciParent,
688 const CHAR *szDirBase,
689 const BOOL filestoo,
690 const BOOL recurse,
691 const BOOL partial,
692 CHAR *stopflag,
693 DIRCNRDATA *dcd, // Optional
694 ULONG *pulTotalFiles, // Optional
695 PULONGLONG pullTotalBytes) // Optional
696{
697 /* put all the directories (and files if filestoo is TRUE) from a
698 * directory into the container. recurse through subdirectories if
699 * recurse is TRUE.
700 */
701
702 PSZ pszFileSpec;
703 INT t;
704 PFILEFINDBUF4 paffbFound;
705 PFILEFINDBUF4 *papffbSelected;
706 PFILEFINDBUF4 pffbFile;
707 PFILEFINDBUF4 paffbTotal = NULL;
708 PFILEFINDBUF4 paffbTemp;
709 HDIR hdir = HDIR_CREATE;
710 ULONG ulFileCnt;
711 ULONG ulExtraBytes;
712 ULONG ulM = 1;
713 ULONG ulTotal = 0;
714 ULONGLONG ullBytes;
715 ULONGLONG ullTotalBytes;
716 ULONG ulReturnFiles = 0;
717 ULONGLONG ullReturnBytes = 0;
718 PCH pchEndPath;
719 APIRET rc;
720 PCNRITEM pci;
721 PCNRITEM pciFirst;
722 RECORDINSERT ri;
723 PBYTE pByte;
724 PBYTE pByte2;
725 BOOL ok = TRUE;
726
727 if (isalpha(*szDirBase) && szDirBase[1] == ':' && szDirBase[2] == '\\') {
728 ulExtraBytes = EXTRA_RECORD_BYTES;
729 if ((driveflags[toupper(*szDirBase) - 'A'] & DRIVE_REMOTE) && fRemoteBug)
730 ulM = 1; /* file system gets confused */
731 else if (driveflags[toupper(*szDirBase) - 'A'] & DRIVE_ZIPSTREAM)
732 ulM = min(FilesToGet, 225); /* anything more is wasted */
733 else
734 ulM = FilesToGet; /* full-out */
735 }
736 else {
737 ulExtraBytes = EXTRA_RECORD_BYTES;
738 ulM = FilesToGet;
739 }
740 if (OS2ver[0] == 20 && OS2ver[1] < 30)
741 ulM = min(ulM, (65535 / sizeof(FILEFINDBUF4)));
742
743 ulFileCnt = ulM;
744 pszFileSpec = xmalloc(CCHMAXPATH + 2, pszSrcFile, __LINE__);
745 paffbFound =
746 xmalloc((ulM + 1) * sizeof(FILEFINDBUF4), pszSrcFile, __LINE__);
747 papffbSelected =
748 xmalloc((ulM + 1) * sizeof(PFILEFINDBUF4), pszSrcFile, __LINE__);
749 if (paffbFound && papffbSelected && pszFileSpec) {
750 t = strlen(szDirBase);
751 memcpy(pszFileSpec, szDirBase, t + 1);
752 pchEndPath = pszFileSpec + t;
753 if (*(pchEndPath - 1) != '\\') {
754 memcpy(pchEndPath, "\\", 2);
755 pchEndPath++;
756 }
757 memcpy(pchEndPath, "*", 2);
758 DosError(FERR_DISABLEHARDERR);
759 rc = DosFindFirst(pszFileSpec, &hdir,
760 FILE_NORMAL | ((filestoo) ? FILE_DIRECTORY :
761 MUST_HAVE_DIRECTORY) | FILE_READONLY |
762 FILE_ARCHIVED | FILE_SYSTEM | FILE_HIDDEN,
763 paffbFound, ulM * sizeof(FILEFINDBUF4),
764 &ulFileCnt, FIL_QUERYEASIZE);
765 priority_normal();
766 *pchEndPath = 0;
767 if (!rc) {
768 while (!rc) {
769 /*
770 * remove . and .. from list if present
771 * also counter file system bugs that sometimes
772 * allows normal files to slip through when
773 * only directories should appear (only a few
774 * network file systems exhibit such a problem).
775 */
776 register ULONG x;
777
778 if (stopflag && *stopflag)
779 goto Abort;
780 pByte = (PBYTE) paffbFound;
781 for (x = 0; x < ulFileCnt;) {
782 pffbFile = (PFILEFINDBUF4) pByte;
783 if (!*pffbFile->achName ||
784 (!filestoo && !(pffbFile->attrFile & FILE_DIRECTORY)) ||
785 ((pffbFile->attrFile & FILE_DIRECTORY) &&
786 pffbFile->achName[0] == '.' &&
787 (!pffbFile->achName[1] ||
788 (pffbFile->achName[1] == '.' && !pffbFile->achName[2])))) {
789 ulFileCnt--; // Got . or ..
790 }
791 else
792 papffbSelected[x++] = pffbFile; // Count file
793 if (!pffbFile->oNextEntryOffset) {
794 ulFileCnt = x; // Adjust count
795 break;
796 }
797 pByte += pffbFile->oNextEntryOffset;
798 } // for
799 if (ulFileCnt) {
800 if (stopflag && *stopflag)
801 goto Abort;
802 if (fSyncUpdates) {
803 pciFirst = WinSendMsg(hwndCnr, CM_ALLOCRECORD,
804 MPFROMLONG(ulExtraBytes),
805 MPFROMLONG(ulFileCnt));
806 if (!pciFirst) {
807 Win_Error2(hwndCnr, HWND_DESKTOP, pszSrcFile, __LINE__,
808 IDS_CMALLOCRECERRTEXT);
809 ok = FALSE;
810 ullTotalBytes = 0;
811 }
812 else {
813 register INT i;
814
815 pci = pciFirst;
816 ullTotalBytes = 0;
817 for (i = 0; i < ulFileCnt; i++) {
818 pffbFile = papffbSelected[i];
819 ullBytes = FillInRecordFromFFB(hwndCnr, pci, pszFileSpec,
820 pffbFile, partial, dcd);
821 pci = (PCNRITEM) pci->rc.preccNextRecord;
822 ullTotalBytes += ullBytes;
823 } // for
824 if (ulFileCnt) {
825 memset(&ri, 0, sizeof(RECORDINSERT));
826 ri.cb = sizeof(RECORDINSERT);
827 ri.pRecordOrder = (PRECORDCORE) CMA_END;
828 ri.pRecordParent = (PRECORDCORE) pciParent;
829 ri.zOrder = (ULONG) CMA_TOP;
830 ri.cRecordsInsert = ulFileCnt;
831 ri.fInvalidateRecord = (!fSyncUpdates && dcd &&
832 dcd->type == DIR_FRAME) ?
833 FALSE : TRUE;
834 if (!WinSendMsg(hwndCnr,
835 CM_INSERTRECORD,
836 MPFROMP(pciFirst), MPFROMP(&ri))) {
837 DosSleep(10);
838 WinSetFocus(HWND_DESKTOP, hwndCnr);
839 if (!WinSendMsg(hwndCnr,
840 CM_INSERTRECORD,
841 MPFROMP(pciFirst), MPFROMP(&ri))) {
842 Win_Error2(hwndCnr, HWND_DESKTOP, pszSrcFile, __LINE__,
843 IDS_CMINSERTERRTEXT);
844 ok = FALSE;
845 ullTotalBytes = 0;
846 if (WinIsWindow((HAB) 0, hwndCnr))
847 FreeCnrItemList(hwndCnr, pciFirst);
848 }
849 }
850 }
851 }
852 if (ok) {
853 ullReturnBytes += ullTotalBytes;
854 ulReturnFiles += ulFileCnt;
855 }
856 }
857 else {
858 paffbTemp = xrealloc(paffbTotal,
859 sizeof(FILEFINDBUF4) * (ulFileCnt + ulTotal),
860 pszSrcFile, __LINE__);
861 if (paffbTemp) {
862 paffbTotal = paffbTemp;
863 for (x = 0; x < ulFileCnt; x++)
864 paffbTotal[x + ulTotal] = *papffbSelected[x];
865 ulTotal += ulFileCnt;
866 }
867 else {
868 saymsg(MB_ENTER,
869 HWND_DESKTOP,
870 GetPString(IDS_ERRORTEXT), GetPString(IDS_OUTOFMEMORY));
871 break;
872 }
873 }
874 }
875 if (stopflag && *stopflag)
876 goto Abort;
877 ulFileCnt = ulM;
878 DosError(FERR_DISABLEHARDERR);
879 rc = DosFindNext(hdir, paffbFound, ulM * sizeof(FILEFINDBUF4),
880 &ulFileCnt);
881 priority_normal();
882 if (rc)
883 DosError(FERR_DISABLEHARDERR);
884 }
885 DosFindClose(hdir);
886
887 if (paffbFound || papffbSelected) {
888 if (paffbFound)
889 free(paffbFound);
890 if (papffbSelected)
891 free(papffbSelected);
892 papffbSelected = NULL;
893 paffbFound = NULL;
894 }
895
896 if (ulTotal && paffbTotal) {
897
898 if (stopflag && *stopflag)
899 goto Abort;
900
901 pciFirst = WinSendMsg(hwndCnr, CM_ALLOCRECORD,
902 MPFROMLONG(ulExtraBytes), MPFROMLONG(ulTotal));
903 if (!pciFirst) {
904 Win_Error2(hwndCnr, HWND_DESKTOP, pszSrcFile, __LINE__,
905 IDS_CMALLOCRECERRTEXT);
906 ok = FALSE;
907 ullTotalBytes = 0;
908 }
909 else {
910 register INT i;
911
912 pci = pciFirst;
913 ullTotalBytes = 0;
914 pByte2 = (PBYTE) paffbTotal;
915 for (i = 0; i < ulTotal; i++) {
916 pffbFile = (PFILEFINDBUF4) pByte2;
917 ullBytes = FillInRecordFromFFB(hwndCnr, pci, pszFileSpec,
918 pffbFile, partial, dcd);
919 pci = (PCNRITEM) pci->rc.preccNextRecord;
920 ullTotalBytes += ullBytes;
921
922 pByte2 += sizeof(FILEFINDBUF4);
923 }
924 if (ulTotal) {
925 memset(&ri, 0, sizeof(RECORDINSERT));
926 ri.cb = sizeof(RECORDINSERT);
927 ri.pRecordOrder = (PRECORDCORE) CMA_END;
928 ri.pRecordParent = (PRECORDCORE) pciParent;
929 ri.zOrder = (ULONG) CMA_TOP;
930 ri.cRecordsInsert = ulTotal;
931 ri.fInvalidateRecord = (!fSyncUpdates && dcd &&
932 dcd->type == DIR_FRAME) ? FALSE : TRUE;
933 if (!WinSendMsg(hwndCnr, CM_INSERTRECORD,
934 MPFROMP(pciFirst), MPFROMP(&ri))) {
935 DosSleep(10);
936 WinSetFocus(HWND_DESKTOP, hwndCnr);
937 if (!WinSendMsg(hwndCnr, CM_INSERTRECORD,
938 MPFROMP(pciFirst), MPFROMP(&ri))) {
939 Win_Error2(hwndCnr, HWND_DESKTOP, pszSrcFile, __LINE__,
940 IDS_CMINSERTERRTEXT);
941 ok = FALSE;
942 ullTotalBytes = 0;
943 if (WinIsWindow((HAB) 0, hwndCnr))
944 FreeCnrItemList(hwndCnr, pciFirst);
945 }
946 }
947 }
948 }
949 if (ok) {
950 ullReturnBytes += ullTotalBytes;
951 ulReturnFiles += ulFileCnt;
952 }
953 }
954 }
955
956 if (!fSyncUpdates && dcd && dcd->type == DIR_FRAME)
957 WinSendMsg(hwndCnr, CM_INVALIDATERECORD, MPVOID,
958 MPFROM2SHORT(0, CMA_ERASE));
959 }
960Abort:
961 if (paffbTotal || papffbSelected || paffbFound || pszFileSpec) {
962 if (paffbTotal)
963 free(paffbTotal);
964 if (pszFileSpec)
965 free(pszFileSpec);
966 if (paffbFound)
967 free(paffbFound);
968 if (papffbSelected)
969 free(papffbSelected);
970 }
971 if (recurse) {
972 pci = WinSendMsg(hwndCnr, CM_QUERYRECORD, MPFROMP(pciParent),
973 MPFROM2SHORT(CMA_FIRSTCHILD, CMA_ITEMORDER));
974 while (pci && (INT)pci != -1) {
975 if (pci->attrFile & FILE_DIRECTORY)
976 Stubby(hwndCnr, pci);
977 pci = WinSendMsg(hwndCnr, CM_QUERYRECORD, MPFROMP(pci),
978 MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
979 }
980 }
981
982 if (pulTotalFiles)
983 *pulTotalFiles = ulReturnFiles;
984
985 if (pullTotalBytes)
986 *pullTotalBytes = ullReturnBytes;
987
988} // ProcessDirectory
989
990VOID FillDirCnr(HWND hwndCnr,
991 CHAR * pszDirectory,
992 DIRCNRDATA * dcd, PULONGLONG pullTotalBytes)
993{
994 ProcessDirectory(hwndCnr,
995 (PCNRITEM) NULL,
996 pszDirectory,
997 TRUE, // filestoo
998 FALSE, // recurse
999 TRUE, // partial
1000 dcd ? &dcd->stopflag : NULL,
1001 dcd,
1002 NULL,
1003 pullTotalBytes);
1004 DosPostEventSem(CompactSem);
1005
1006#if 0 // fixme to be gone or to be configurable
1007 {
1008 int state = _heapchk();
1009 if (state != _HEAPOK)
1010 Runtime_Error(pszSrcFile, __LINE__, "heap corrupted %d", state);
1011 else
1012 DbgMsg(pszSrcFile, __LINE__, "_memavl %u", _memavl());
1013 }
1014#endif
1015
1016} // FillDirCnr
1017
1018VOID FillTreeCnr(HWND hwndCnr, HWND hwndParent)
1019{
1020 ULONG ulCurDriveNum, ulDriveMap, numtoinsert = 0, drvtype;
1021 PCNRITEM pci, pciFirst = NULL, pciNext, pciParent = NULL;
1022 INT x, removable;
1023 CHAR suggest[32];
1024 CHAR szDrive[] = " :\\";
1025 CHAR szFileSystem[CCHMAXPATH];
1026 FILESTATUS4 fsa4;
1027 APIRET rc;
1028 BOOL drivesbuilt = FALSE;
1029 ULONG startdrive = 3;
1030
1031 static BOOL didonce = FALSE;
1032
1033 fDummy = TRUE;
1034 *suggest = 0;
1035 for (x = 0; x < 26; x++) {
1036 driveflags[x] &= (DRIVE_IGNORE | DRIVE_NOPRESCAN | DRIVE_NOLOADICONS |
1037 DRIVE_NOLOADSUBJS | DRIVE_NOLOADLONGS |
1038 DRIVE_INCLUDEFILES | DRIVE_SLOW | DRIVE_NOSTATS);
1039 }
1040 memset(driveserial, -1, sizeof(driveserial));
1041
1042 DosError(FERR_DISABLEHARDERR);
1043 if (!DosQuerySysInfo(QSV_BOOT_DRIVE,
1044 QSV_BOOT_DRIVE,
1045 (PVOID) &startdrive,
1046 (ULONG) sizeof(ULONG)) &&
1047 startdrive)
1048 {
1049 driveflags[startdrive - 1] |= DRIVE_BOOT;
1050 }
1051
1052 DosError(FERR_DISABLEHARDERR);
1053 rc = DosQCurDisk(&ulCurDriveNum, &ulDriveMap);
1054 if (rc) {
1055 Dos_Error(MB_CANCEL,
1056 rc,
1057 HWND_DESKTOP,
1058 pszSrcFile, __LINE__, GetPString(IDS_FILLDIRQCURERRTEXT));
1059 exit(0);
1060 }
1061
1062 // Calc number of drive items to create
1063 for (x = 0; x < 26; x++) {
1064 if ((ulDriveMap & (1L << x)) && !(driveflags[x] & DRIVE_IGNORE))
1065 numtoinsert++;
1066 }
1067
1068 if (numtoinsert) {
1069 pciFirst = WinSendMsg(hwndCnr,
1070 CM_ALLOCRECORD,
1071 MPFROMLONG(EXTRA_RECORD_BYTES),
1072 MPFROMLONG((ULONG) numtoinsert));
1073 }
1074
1075 if (!pciFirst) {
1076 Win_Error2(hwndCnr, HWND_DESKTOP, pszSrcFile, __LINE__, IDS_CMALLOCRECERRTEXT);
1077 exit(0);
1078 }
1079
1080 pci = pciFirst;
1081 for (x = 0; x < 26; x++) {
1082 if ((ulDriveMap & (1L << x)) && !(driveflags[x] & DRIVE_IGNORE)) {
1083
1084 CHAR s[80];
1085 ULONG flags = 0;
1086 ULONG size = sizeof(ULONG);
1087
1088 *szDrive = (CHAR)x + 'A'; // Build path spec
1089
1090 sprintf(s, "%c.DriveFlags", toupper(*szDrive));
1091 if (PrfQueryProfileData(fmprof, appname, s, &flags, &size) &&
1092 size == sizeof(ULONG)) {
1093 driveflags[toupper(*szDrive) - 'A'] |= flags;
1094 }
1095
1096 if (x > 1) {
1097 // Hard drive (2..N)
1098 if (!(driveflags[x] & DRIVE_NOPRESCAN)) {
1099 *szFileSystem = 0;
1100 drvtype = 0;
1101 removable = CheckDrive(*szDrive, szFileSystem, &drvtype);
1102 driveserial[x] = -1;
1103 if (removable != -1) {
1104 struct {
1105 ULONG serial;
1106 CHAR volumelength;
1107 CHAR volumelabel[CCHMAXPATH];
1108 } volser;
1109
1110 DosError(FERR_DISABLEHARDERR);
1111 if (!DosQueryFSInfo((ULONG) x,
1112 FSIL_VOLSER, &volser, sizeof(volser))) {
1113 driveserial[x] = volser.serial;
1114 }
1115 }
1116 else
1117 driveflags[x] |= DRIVE_INVALID;
1118
1119 memset(&fsa4, 0, sizeof(FILESTATUS4));
1120 driveflags[x] |= removable == -1 || removable == 1 ?
1121 DRIVE_REMOVABLE : 0;
1122 if (drvtype & DRIVE_REMOTE)
1123 driveflags[x] |= DRIVE_REMOTE;
1124 if (!stricmp(szFileSystem,RAMFS)) {
1125 driveflags[x] |= DRIVE_RAMDISK;
1126 driveflags[x] &= ~DRIVE_REMOTE;
1127 }
1128 if (!stricmp(szFileSystem,NDFS32)) {
1129 driveflags[x] |= DRIVE_VIRTUAL;
1130 driveflags[x] &= ~DRIVE_REMOTE;
1131 }
1132 if (!stricmp(szFileSystem,NTFS))
1133 driveflags[x] |= DRIVE_NOTWRITEABLE;
1134 if (strcmp(szFileSystem, HPFS) &&
1135 strcmp(szFileSystem, JFS) &&
1136 strcmp(szFileSystem, ISOFS) &&
1137 strcmp(szFileSystem, CDFS) &&
1138 strcmp(szFileSystem, FAT32) &&
1139 strcmp(szFileSystem, NDFS32) &&
1140 strcmp(szFileSystem, RAMFS) &&
1141 strcmp(szFileSystem, NTFS) &&
1142 strcmp(szFileSystem, HPFS386)) {
1143 driveflags[x] |= DRIVE_NOLONGNAMES;
1144 }
1145
1146 if (!strcmp(szFileSystem, CDFS) || !strcmp(szFileSystem,ISOFS)) {
1147 removable = 1;
1148 driveflags[x] |= DRIVE_REMOVABLE | DRIVE_NOTWRITEABLE |
1149 DRIVE_CDROM;
1150 }
1151 else if (!stricmp(szFileSystem, CBSIFS)) {
1152 driveflags[x] |= DRIVE_ZIPSTREAM;
1153 driveflags[x] &= ~DRIVE_REMOTE;
1154 if (drvtype & DRIVE_REMOVABLE)
1155 driveflags[x] |= DRIVE_REMOVABLE;
1156 if (!(drvtype & DRIVE_NOLONGNAMES))
1157 driveflags[x] &= ~DRIVE_NOLONGNAMES;
1158 }
1159
1160 pci->rc.flRecordAttr |= CRA_RECORDREADONLY;
1161 // if ((ULONG) (toupper(*pci->pszFileName) - '@') == ulCurDriveNum) // 23 Jul 07 SHL
1162 if ((ULONG)(toupper(*szDrive) - '@') == ulCurDriveNum)
1163 pci->rc.flRecordAttr |= (CRA_CURSORED | CRA_SELECTED);
1164
1165 if (removable == 0) {
1166 // Fixed volume
1167 pci->attrFile |= FILE_DIRECTORY;
1168 DosError(FERR_DISABLEHARDERR);
1169 rc = DosQueryPathInfo(szDrive,
1170 FIL_QUERYEASIZE,
1171 &fsa4, (ULONG) sizeof(FILESTATUS4));
1172 // ERROR_BAD_NET_RSP = 58
1173 if (rc == 58) {
1174 DosError(FERR_DISABLEHARDERR);
1175 rc = DosQueryPathInfo(szDrive,
1176 FIL_STANDARD,
1177 &fsa4, (ULONG) sizeof(FILESTATUS3));
1178 fsa4.cbList = 0;
1179 }
1180 if (rc && !didonce) {
1181 // Guess drive letter
1182 if (!*suggest) {
1183 *suggest = '/';
1184 suggest[1] = 0;
1185 }
1186 sprintf(suggest + strlen(suggest), "%c" , toupper(*szDrive));
1187 pci->pszFileName = xstrdup(szDrive, pszSrcFile, __LINE__);
1188 strcpy(pci->pszFileName, szDrive);
1189 pci->pszDisplayName = pci->pszFileName;
1190 pci->rc.pszIcon = pci->pszDisplayName;
1191 pci->attrFile = FILE_DIRECTORY;
1192 pci->pszDispAttr = FileAttrToString(pci->attrFile);
1193 driveserial[x] = -1;
1194 }
1195 else
1196 FillInRecordFromFSA(hwndCnr, pci, szDrive, &fsa4, TRUE, NULL);
1197 }
1198 else {
1199 // Removable volume
1200 pci->pszFileName = xstrdup(szDrive, pszSrcFile, __LINE__);
1201 strcpy(pci->pszFileName, szDrive);
1202 pci->pszDisplayName = pci->pszFileName;
1203 pci->rc.pszIcon = pci->pszDisplayName;
1204 pci->attrFile = FILE_DIRECTORY;
1205 pci->pszDispAttr = FileAttrToString(pci->attrFile);
1206 }
1207 SelectDriveIcon(pci);
1208 }
1209 else {
1210 pci->rc.hptrIcon = hptrDunno;
1211 pci->pszFileName = xstrdup(szDrive, pszSrcFile, __LINE__);
1212 strcpy(pci->pszFileName, szDrive);
1213 pci->pszDisplayName = pci->pszFileName;
1214 pci->rc.pszIcon = pci->pszFileName;
1215 pci->attrFile = FILE_DIRECTORY;
1216 pci->pszDispAttr = FileAttrToString(pci->attrFile);
1217 driveserial[x] = -1;
1218 }
1219 }
1220 else {
1221 // diskette drive (A or B)
1222 pci->rc.hptrIcon = hptrFloppy;
1223 pci->pszFileName = xstrdup(szDrive, pszSrcFile, __LINE__);
1224 strcpy(pci->pszFileName, szDrive);
1225 pci->pszDisplayName = pci->pszFileName;
1226 pci->rc.pszIcon = pci->pszDisplayName;
1227 pci->attrFile = FILE_DIRECTORY;
1228 pci->pszDispAttr = FileAttrToString(pci->attrFile);
1229 driveflags[x] |= (DRIVE_REMOVABLE | DRIVE_NOLONGNAMES);
1230 driveserial[x] = -1;
1231 }
1232 pci->rc.flRecordAttr |= CRA_RECORDREADONLY;
1233 pci = (PCNRITEM) pci->rc.preccNextRecord; /* next rec */
1234 }
1235 else if (!(ulDriveMap & (1L << x)))
1236 driveflags[x] |= DRIVE_INVALID;
1237 } // for drives
1238
1239 PostMsg(hwndMain, UM_BUILDDRIVEBAR, MPVOID, MPVOID);
1240 drivesbuilt = TRUE;
1241
1242 /* insert the drives */
1243 if (numtoinsert && pciFirst) {
1244 RECORDINSERT ri;
1245
1246 memset(&ri, 0, sizeof(RECORDINSERT));
1247 ri.cb = sizeof(RECORDINSERT);
1248 ri.pRecordOrder = (PRECORDCORE) CMA_END;
1249 ri.pRecordParent = (PRECORDCORE) NULL;
1250 ri.zOrder = (ULONG) CMA_TOP;
1251 ri.cRecordsInsert = numtoinsert;
1252 ri.fInvalidateRecord = FALSE;
1253 if (!WinSendMsg(hwndCnr,
1254 CM_INSERTRECORD, MPFROMP(pciFirst), MPFROMP(&ri)))
1255 {
1256 Win_Error2(hwndCnr, HWND_DESKTOP, pszSrcFile, __LINE__,
1257 IDS_CMINSERTERRTEXT);
1258 }
1259 }
1260
1261 /* move cursor onto the default drive rather than the first drive */
1262 if (!fSwitchTree) {
1263 pci = (PCNRITEM) WinSendMsg(hwndCnr,
1264 CM_QUERYRECORD,
1265 MPVOID,
1266 MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
1267 while (pci && (INT)pci != -1) {
1268 if ((ULONG) (toupper(*pci->pszFileName) - '@') == ulCurDriveNum) {
1269 WinSendMsg(hwndCnr,
1270 CM_SETRECORDEMPHASIS,
1271 MPFROMP(pci), MPFROM2SHORT(TRUE, CRA_CURSORED));
1272 break;
1273 }
1274 pci = (PCNRITEM) WinSendMsg(hwndCnr,
1275 CM_QUERYRECORD,
1276 MPFROMP(pci),
1277 MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
1278 }
1279 }
1280
1281 if (hwndParent) {
1282 WinSendMsg(WinWindowFromID(WinQueryWindow(hwndParent, QW_PARENT),
1283 MAIN_DRIVELIST),
1284 LM_DELETEALL, MPVOID, MPVOID);
1285 }
1286
1287 if (fShowEnv) {
1288 RECORDINSERT ri;
1289
1290 pciParent = WinSendMsg(hwndCnr,
1291 CM_ALLOCRECORD,
1292 MPFROMLONG(EXTRA_RECORD_BYTES), MPFROMLONG(1));
1293 if (pciParent) {
1294 pciParent->flags |= RECFLAGS_ENV;
1295 pciParent->pszFileName = xstrdup(GetPString(IDS_ENVVARSTEXT), pszSrcFile, __LINE__);
1296 strcpy(pciParent->pszFileName, GetPString(IDS_ENVVARSTEXT));
1297 pciParent->pszDisplayName = pciParent->pszFileName; // 03 Aug 07 SHL
1298 pciParent->rc.hptrIcon = hptrEnv;
1299 pciParent->rc.pszIcon = pciParent->pszFileName;
1300 pciParent->pszDispAttr = FileAttrToString(0);
1301 memset(&ri, 0, sizeof(RECORDINSERT));
1302 ri.cb = sizeof(RECORDINSERT);
1303 ri.pRecordOrder = (PRECORDCORE) CMA_END;
1304 ri.pRecordParent = (PRECORDCORE) NULL;
1305 ri.zOrder = (ULONG) CMA_TOP;
1306 ri.cRecordsInsert = 1;
1307 ri.fInvalidateRecord = FALSE;
1308 if (WinSendMsg(hwndCnr,
1309 CM_INSERTRECORD, MPFROMP(pciParent), MPFROMP(&ri))) {
1310
1311 char *p, *pp;
1312
1313 p = GetPString(IDS_ENVVARNAMES);
1314 while (*p == ' ')
1315 p++;
1316 while (*p) {
1317 *szFileSystem = 0;
1318 pp = szFileSystem;
1319 while (*p && *p != ' ')
1320 *pp++ = *p++;
1321 *pp = 0;
1322 while (*p == ' ')
1323 p++;
1324 if (*szFileSystem &&
1325 (!stricmp(szFileSystem, "LIBPATH") || getenv(szFileSystem))) {
1326 pci = WinSendMsg(hwndCnr,
1327 CM_ALLOCRECORD,
1328 MPFROMLONG(EXTRA_RECORD_BYTES),
1329 MPFROMLONG(1));
1330 if (pci) {
1331 CHAR fname[CCHMAXPATH];
1332 pci->flags |= RECFLAGS_ENV;
1333 sprintf(fname, "%%%s%%", szFileSystem);
1334 pci->pszFileName = xstrdup(fname, pszSrcFile, __LINE__);
1335 pci->rc.hptrIcon = hptrEnv;
1336 pci->rc.pszIcon = pci->pszFileName;
1337 pci->pszDispAttr = FileAttrToString(0);
1338 memset(&ri, 0, sizeof(RECORDINSERT));
1339 ri.cb = sizeof(RECORDINSERT);
1340 ri.pRecordOrder = (PRECORDCORE) CMA_END;
1341 ri.pRecordParent = (PRECORDCORE) pciParent;
1342 ri.zOrder = (ULONG) CMA_TOP;
1343 ri.cRecordsInsert = 1;
1344 ri.fInvalidateRecord = FALSE;
1345 if (!WinSendMsg(hwndCnr,
1346 CM_INSERTRECORD,
1347 MPFROMP(pci), MPFROMP(&ri))) {
1348 Win_Error2(hwndCnr, HWND_DESKTOP, pszSrcFile, __LINE__,
1349 IDS_CMINSERTERRTEXT);
1350 FreeCnrItem(hwndCnr, pci);
1351 }
1352 }
1353 }
1354 }
1355 WinSendMsg(hwndCnr,
1356 CM_INVALIDATERECORD,
1357 MPFROMP(&pciParent),
1358 MPFROM2SHORT(1, CMA_ERASE | CMA_REPOSITION));
1359 }
1360 else
1361 FreeCnrItem(hwndCnr, pciParent);
1362 }
1363 } // if show env
1364
1365 x = 0;
1366 pci = (PCNRITEM) WinSendMsg(hwndCnr,
1367 CM_QUERYRECORD,
1368 MPVOID,
1369 MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
1370 while (pci && (INT)pci != -1) {
1371 pciNext = (PCNRITEM) WinSendMsg(hwndCnr,
1372 CM_QUERYRECORD,
1373 MPFROMP(pci),
1374 MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
1375 if (!(pci->flags & RECFLAGS_ENV)) {
1376 if ((ULONG) (toupper(*pci->pszFileName) - '@') == ulCurDriveNum ||
1377 toupper(*pci->pszFileName) > 'B')
1378 {
1379 if (!(driveflags[toupper(*pci->pszFileName) - 'A'] & DRIVE_INVALID) &&
1380 !(driveflags[toupper(*pci->pszFileName) - 'A'] & DRIVE_NOPRESCAN) &&
1381 (!fNoRemovableScan ||
1382 !(driveflags[toupper(*pci->pszFileName) - 'A'] & DRIVE_REMOVABLE)))
1383 {
1384 if (!Stubby(hwndCnr, pci) && !DRIVE_RAMDISK) {
1385 WinSendMsg(hwndCnr,
1386 CM_INVALIDATERECORD,
1387 MPFROMP(&pci),
1388 MPFROM2SHORT(1, CMA_ERASE | CMA_REPOSITION));
1389 goto SkipBadRec;
1390 }
1391 }
1392 }
1393 else {
1394 WinSendMsg(hwndCnr,
1395 CM_INVALIDATERECORD,
1396 MPFROMP(&pci),
1397 MPFROM2SHORT(1, CMA_ERASE | CMA_REPOSITION));
1398 }
1399
1400 WinSendMsg(WinWindowFromID(WinQueryWindow(hwndParent, QW_PARENT),
1401 MAIN_DRIVELIST),
1402 LM_INSERTITEM,
1403 MPFROM2SHORT(LIT_SORTASCENDING, 0),
1404 MPFROMP(pci->pszFileName));
1405 }
1406 SkipBadRec:
1407 x++;
1408 pci = pciNext;
1409 }
1410 if (hwndParent)
1411 WinSendMsg(WinWindowFromID(WinQueryWindow(hwndParent, QW_PARENT),
1412 MAIN_DRIVELIST), LM_SELECTITEM,
1413 MPFROM2SHORT(0, 0), MPFROMLONG(TRUE));
1414
1415 pci = (PCNRITEM) WinSendMsg(hwndCnr,
1416 CM_QUERYRECORD,
1417 MPVOID,
1418 MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
1419 while (pci && (INT)pci != -1) {
1420 pciNext = (PCNRITEM) WinSendMsg(hwndCnr,
1421 CM_QUERYRECORD,
1422 MPFROMP(pci),
1423 MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
1424 if (pci->flags & RECFLAGS_ENV) {
1425 pci = (PCNRITEM) WinSendMsg(hwndCnr,
1426 CM_QUERYRECORD,
1427 MPFROMP(pci),
1428 MPFROM2SHORT(CMA_FIRSTCHILD,
1429 CMA_ITEMORDER));
1430 while (pci && (INT)pci != -1) {
1431 if (pci->flags & RECFLAGS_ENV)
1432 FleshEnv(hwndCnr, pci);
1433 pci = (PCNRITEM) WinSendMsg(hwndCnr,
1434 CM_QUERYRECORD,
1435 MPFROMP(pci),
1436 MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
1437 }
1438 break;
1439 }
1440 pci = (PCNRITEM) WinSendMsg(hwndCnr,
1441 CM_QUERYRECORD,
1442 MPFROMP(pci),
1443 MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
1444 }
1445
1446 if (!drivesbuilt && hwndMain)
1447 PostMsg(hwndMain, UM_BUILDDRIVEBAR, MPVOID, MPVOID);
1448 DosSleep(16);//05 Aug 07 GKY 33
1449 fDummy = FALSE;
1450 DosPostEventSem(CompactSem);
1451
1452 {
1453 BYTE info;
1454 BOOL includesyours = FALSE;
1455
1456 if (*suggest || (!(driveflags[1] & DRIVE_IGNORE) && fFirstTime)) {
1457 if (!DosDevConfig(&info, DEVINFO_FLOPPY) && info == 1) {
1458 if (!*suggest) {
1459 *suggest = '/';
1460 suggest[1] = 0;
1461 }
1462 else
1463 memmove(suggest + 2, suggest + 1, strlen(suggest));
1464 suggest[1] = 'B';
1465 }
1466 }
1467 if (*suggest) {
1468 for (x = 2; x < 26; x++) {
1469 if (driveflags[x] & DRIVE_IGNORE) {
1470 includesyours = TRUE;
1471 sprintf(suggest + strlen(suggest), "%c", (char)(x + 'A'));
1472 }
1473 }
1474 strcat(suggest, " %*");
1475 if (saymsg(MB_YESNO | MB_ICONEXCLAMATION,
1476 (hwndParent) ? hwndParent : hwndCnr,
1477 GetPString(IDS_SUGGESTTITLETEXT),
1478 GetPString(IDS_SUGGEST1TEXT),
1479 (includesyours) ? GetPString(IDS_SUGGEST2TEXT) : NullStr,
1480 suggest) == MBID_YES) {
1481 char s[64];
1482
1483 sprintf(s, "PARAMETERS=%s", suggest);
1484 WinCreateObject(WPProgram, "FM/2", s, FM3Folder, CO_UPDATEIFEXISTS);
1485 WinCreateObject(WPProgram,
1486 "FM/2 Lite", s, FM3Folder, CO_UPDATEIFEXISTS);
1487 WinCreateObject(WPProgram,
1488 "Archive Viewer/2", s, FM3Tools, CO_UPDATEIFEXISTS);
1489 WinCreateObject(WPProgram,
1490 "Dir Sizes", s, FM3Tools, CO_UPDATEIFEXISTS);
1491 WinCreateObject(WPProgram,
1492 "Visual Tree", s, FM3Tools, CO_UPDATEIFEXISTS);
1493 WinCreateObject(WPProgram,
1494 "Visual Directory", s, FM3Tools, CO_UPDATEIFEXISTS);
1495 WinCreateObject(WPProgram,
1496 "Global File Viewer", s, FM3Tools, CO_UPDATEIFEXISTS);
1497 WinCreateObject(WPProgram, "Databar", s, FM3Tools, CO_UPDATEIFEXISTS);
1498 }
1499 }
1500 }
1501
1502 didonce = TRUE;
1503
1504} // FillTreeCnr
1505
1506
1507/**
1508 * Empty all records from a container and free associated storage and
1509 * Free up field infos
1510 */
1511
1512VOID EmptyCnr(HWND hwnd)
1513{
1514 PFIELDINFO pfi;
1515
1516#if 0 // fixme to be gone or to be configurable
1517 {
1518 int state = _heapchk();
1519 if (state != _HEAPOK)
1520 Runtime_Error(pszSrcFile, __LINE__, "heap corrupted %d", state);
1521 }
1522#endif
1523
1524 // Remove all records
1525 RemoveCnrItems(hwnd, NULL, 0, CMA_FREE);
1526
1527 // Remove field info descriptors
1528 pfi = (PFIELDINFO) WinSendMsg(hwnd, CM_QUERYDETAILFIELDINFO, MPVOID,
1529 MPFROMSHORT(CMA_FIRST));
1530 if (pfi &&
1531 (INT)WinSendMsg(hwnd, CM_REMOVEDETAILFIELDINFO, MPVOID,
1532 MPFROM2SHORT(0, CMA_FREE)) == -1) {
1533 Win_Error(hwnd, HWND_DESKTOP, pszSrcFile, __LINE__,"CM_REMOVEDETAILFIELDINFO hwnd %x", hwnd);
1534 }
1535}
1536
1537/**
1538 * Free storage associated with container item
1539 */
1540
1541VOID FreeCnrItemData(PCNRITEM pci)
1542{
1543 PSZ psz;
1544 // DbgMsg(pszSrcFile, __LINE__, "FreeCnrItemData %p", pci);
1545
1546 if (pci->pszSubject && pci->pszSubject != NullStr) {
1547
1548 psz = pci->pszSubject;
1549 pci->pszSubject = NullStr;
1550 free(psz);
1551 }
1552
1553 // +1 in case long name pointing after last backslash
1554 if (pci->pszLongName &&
1555 pci->pszLongName != NullStr &&
1556 pci->pszLongName != pci->pszFileName &&
1557 pci->pszLongName != pci->pszDisplayName &&
1558 pci->pszLongName != pci->pszDisplayName + 1) {
1559 psz = pci->pszLongName;
1560 pci->pszLongName = NullStr;
1561 free(psz);
1562 }
1563
1564 if (pci->pszFileName && pci->pszFileName != NullStr) {
1565 psz = pci->pszFileName;
1566 pci->pszFileName = NullStr;
1567 free(psz);
1568 }
1569}
1570
1571/**
1572 * Free single container item and associated storage
1573 */
1574
1575VOID FreeCnrItem(HWND hwnd, PCNRITEM pci)
1576{
1577 // DbgMsg(pszSrcFile, __LINE__, "FreeCnrItem hwnd %x pci %p", hwnd, pci);
1578
1579 FreeCnrItemData(pci);
1580
1581 if (!WinSendMsg(hwnd, CM_FREERECORD, MPFROMP(&pci), MPFROMSHORT(1))) {
1582 // Win_Error2(hwnd, HWND_DESKTOP, pszSrcFile, __LINE__,IDS_CMFREEERRTEXT);
1583 Win_Error(hwnd, HWND_DESKTOP, pszSrcFile, __LINE__,
1584 "CM_FREERECORD hwnd %x pci %p file %s",
1585 hwnd, pci,
1586 pci && pci->pszFileName ? pci->pszFileName : "n/a");
1587 }
1588}
1589
1590/**
1591 * Free container item list and associated storage
1592 */
1593
1594VOID FreeCnrItemList(HWND hwnd, PCNRITEM pciFirst)
1595{
1596 PCNRITEM pci = pciFirst;
1597 PCNRITEM pciNext;
1598 USHORT usCount;
1599
1600 for (usCount = 0; pci; usCount++) {
1601 pciNext = (PCNRITEM) pci->rc.preccNextRecord;
1602 FreeCnrItemData(pci);
1603 pci = pciNext;
1604 }
1605
1606 if (usCount) {
1607 if (!WinSendMsg(hwnd, CM_FREERECORD, MPFROMP(&pci), MPFROMSHORT(usCount))) {
1608 // Win_Error2(hwnd, HWND_DESKTOP, pszSrcFile, __LINE__,IDS_CMFREEERRTEXT);
1609 Win_Error(hwnd, HWND_DESKTOP, pszSrcFile, __LINE__,"CM_FREERECORD hwnd %x pci %p cnt %u", hwnd, pci, usCount);
1610 }
1611 }
1612}
1613
1614/**
1615 * Remove item(s) from container and free associated storage if requested
1616 * @param pciFirst points to first item to remove or NULL to remove all
1617 * @param usCnt is remove count or 0 to remove all
1618 * @returns count of items remaining in container or -1 if error
1619 */
1620
1621INT RemoveCnrItems(HWND hwnd, PCNRITEM pciFirst, USHORT usCnt, USHORT usFlags)
1622{
1623 INT remaining = usCnt;
1624 PCNRITEM pci;
1625
1626 if ((usCnt && !pciFirst) || (!usCnt && pciFirst)) {
1627 Runtime_Error(pszSrcFile, __LINE__, "pciFirst %p usCnt %u mismatch", pciFirst, usCnt);
1628 remaining = -1;
1629 }
1630 else {
1631 // Free our buffers if free requested
1632 if (usFlags & CMA_FREE) {
1633 if (pciFirst)
1634 pci = pciFirst;
1635 else {
1636 pci = (PCNRITEM)WinSendMsg(hwnd, CM_QUERYRECORD, MPVOID,
1637 MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
1638 if ((INT)pci == -1) {
1639 Win_Error(hwnd, HWND_DESKTOP, pszSrcFile, __LINE__,"CM_QUERYRECORD");
1640 remaining = -1;
1641 pci = NULL;
1642 }
1643 }
1644 while (pci) {
1645 FreeCnrItemData(pci);
1646 pci = (PCNRITEM)pci->rc.preccNextRecord;
1647 if (remaining && --remaining == 0)
1648 break;
1649 }
1650 }
1651 }
1652
1653 // DbgMsg(pszSrcFile, __LINE__, "RemoveCnrItems %p %u %s", pci, usCnt, pci->pszFileName);
1654
1655 if (remaining != - 1) {
1656 remaining = (INT)WinSendMsg(hwnd, CM_REMOVERECORD, MPFROMP(&pciFirst), MPFROM2SHORT(usCnt, usFlags));
1657 if (remaining == -1) {
1658 // Win_Error2(hwnd, HWND_DESKTOP, pszSrcFile, __LINE__,IDS_CMREMOVEERRTEXT);
1659 Win_Error(hwnd, HWND_DESKTOP, pszSrcFile, __LINE__,"CM_REMOVERECORD hwnd %x pci %p cnt %u", hwnd, pciFirst, usCnt);
1660 }
1661 }
1662
1663 return remaining;
1664}
1665
Note: See TracBrowser for help on using the repository browser.