source: trunk/dll/flesh.c@ 1911

Last change on this file since 1911 was 1901, checked in by Gregg Young, 17 months ago

Fix ticket #583 tree corruption on startup or rescan and ticket #584 spurious "no drop" on first subdirectory in tree.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.6 KB
Line 
1
2/***********************************************************************
3
4 $Id: flesh.c 1901 2024-05-29 20:05:35Z gyoung $
5
6 Drive tree container management
7
8 Copyright (c) 1993-1998 M. Kimes
9 Copyright (c) 2005-2020 Steven H. Levine
10
11 24 May 05 SHL Rework Win_Error usage
12 25 May 05 SHL Rework for ProcessDirectory
13 28 May 05 SHL Clean while reading code
14 24 Oct 05 SHL Delete obsolete code
15 22 Jul 06 SHL Check more run time errors
16 19 Oct 06 SHL Stubby - correct . and .. detect
17 22 Mar 07 GKY Use QWL_USER
18 01 Aug 07 SHL Sync with CNRITEM mods
19 06 Aug 07 GKY Reduce DosSleep times (ticket 148)
20 20 Aug 07 GKY Move #pragma alloc_text to end for OpenWatcom compat
21 29 Feb 08 GKY Use xfree where appropriate
22 24 Nov 08 GKY remove redundant code and minor speed up of Stubby
23 25 Dec 08 GKY Add ProcessDirectoryThread to allow optional recursive drive scan at startup.
24 25 Dec 08 GKY Add DRIVE_RSCANNED flag to monitor for the first recursive drive scan per session
25 to prevent duplicate directory names in tree following a copy before initial scan.
26 08 Mar 09 GKY Additional strings move to PCSZs in init.c
27 13 Dec 09 GKY Fixed separate paramenters. Please note that appname should be used in
28 profile calls for user settings that work and are setable in more than one
29 miniapp; FM3Str should be used for setting only relavent to FM/2 or that
30 aren't user settable; realappname should be used for setting applicable to
31 one or more miniapp but not to FM/2
32 17 Jan 10 GKY Changes to get working with Watcom 1.9 Beta (1/16/10). Mostly cast CHAR CONSTANT * as CHAR *.
33 04 Aug 12 GKY Fix trap on close during drive scan
34 02 Aug 15 GKY Fix trap in Stubby
35 03 Aug 15 SHL Document Stubby a bit better
36 07 Aug 15 SHL Rework to use AddFleshWorkRequest rather than direct calls to Stubby/Flesh/Unflesh
37 19 Aug 15 SHL Allow WaitFleshWorkListEmpty to wait for dependent items
38 23 Aug 15 GKY Fixed code to notify on drive with no subdirectories in first 64 entries
39 20 Sep 15 GKY Add code for Flesh to skip the directory entry added by Stubby (eliminate
40 use of NULL/Nullstr pszFileNames by Stubby). Add code in Stubby to insert a
41 complete container item. Add a flag to indicate when a directory needed to be
42 Fleshed
43 26 Sep 15 GKY Changes to speed up ExpandAll
44 26 Sep 15 GKY WaitFleshWorkListEmpty now gives error message and returns if semaphore request
45 fails more than 5 consecutive times.
46 27 Sep 15 GKY DosSleep times in WaitFleshWorkListEmpty set by caller
47 10 Oct 15 GKY Don't use Flesh thread for floppy drive scans fix them getting mistakenly identified
48 as directories and add nonexistent subdirectories.
49 25 Jan 20 SHL Complain if AddFleshWorkRequest returns empty string
50 27 Jan 20 SHL Avoid more traps if passed bogus pciParent
51
52***********************************************************************/
53
54#include <stdlib.h>
55#include <string.h>
56#include <ctype.h>
57
58#define INCL_DOS
59#define INCL_DOSERRORS
60#define INCL_WIN
61#define INCL_LONGLONG // dircnrs.h
62
63#include "fm3dll.h"
64#include "draglist.h" // Data declaration(s)
65#include "notebook.h" // Data declaration(s)
66#include "info.h" // Data declaration(s)
67#include "init.h" // Data declaration(s)
68#include "mainwnd.h" // Data declaration(s)
69#include "fm3str.h"
70#include "filldir.h" // FileAttrToString...
71#include "errutil.h" // Dos_Error...
72#include "strutil.h" // GetPString
73#include "flesh.h"
74#include "valid.h" // IsValidDir
75#include "misc.h" // LoadLibPath GetTidForThread
76#include "findrec.h" // FindCnrRecord
77#include "notify.h" // Notify
78#include "wrappers.h" // xfree
79#include "excputil.h" // xbeginthread
80#include "listutil.h" // List...
81#include "common.h" // IncrThreadUsage DecrThreadUsage
82#include "pathutil.h"
83
84#ifdef PMPRINTF
85#define _PMPRINTF_ // Enable debug macros
86#include "PMPRINTF.H"
87#endif
88
89// Data definitions
90#pragma data_seg(DATA1)
91
92static PSZ pszSrcFile = __FILE__;
93
94static INT tidFleshWorkListThread = -1; // 2015-08-08 SHL
95
96static PCSZ pszFleshFocusPath; // 2015-08-20 SHL
97
98#pragma data_seg(GLOBAL1)
99ULONG NoBrokenNotify;
100BOOL fFilesInTree;
101
102BOOL Stubby(HWND hwndCnr, PCNRITEM pciParent);
103BOOL FleshEnv(HWND hwndCnr, PCNRITEM pciParent);
104
105/**
106 * Insert CNRITEMs for members of PATH-like environment variable
107 * @param hwndCnr is the container to be populated
108 * @param pciParent is CNRITEM defining PATH-like environment variable
109 * @return TRUE if OK, FALSE is error detected
110 */
111
112BOOL FleshEnv(HWND hwndCnr, PCNRITEM pciParent)
113{
114 PCNRITEM pciL;
115 DIRCNRDATA *dcd;
116 CHAR path[CCHMAXPATH + 12],
117 fullpath[CCHMAXPATH + 12], *env, *p, *pp, *var = NULL;
118
119 if (!pciParent || (INT)pciParent == -1 || !hwndCnr)
120 return FALSE;
121
122 dcd = (DIRCNRDATA *) WinQueryWindowPtr(hwndCnr, QWL_USER);
123 if (!dcd)
124 return FALSE;
125
126 strcpy(path, pciParent->pszFileName + 1);
127 if (stricmp(path, GetPString(IDS_ENVVARSTEXT) + 1))
128 UnFlesh(hwndCnr, pciParent);
129 if (*path) {
130 path[strlen(path) - 1] = 0;
131 if (!stricmp(path, PCSZ_LIBPATH)) {
132 var = xmalloc(65536, pszSrcFile, __LINE__);
133 if (var)
134 LoadLibPath(var, 65536);
135 env = var;
136 }
137 else
138 env = getenv(path);
139 if (env && *env) {
140 p = env;
141 while (*p) {
142 pp = path;
143 while (*p == ';')
144 p++;
145 while (*p && *p != ';') {
146 *pp = *p;
147 p++;
148 pp++;
149 }
150 *pp = 0;
151 if (*path &&
152 strcmp(path, ".") &&
153 strcmp(path, ".\\") &&
154 strcmp(path, "..") &&
155 strcmp(path, "..\\") &&
156 strncmp(path, ".\\", 2) && strncmp(path, "..\\", 3)) {
157 if (!DosQueryPathInfo(path,
158 FIL_QUERYFULLNAME,
159 fullpath,
160 sizeof(fullpath)) && IsValidDir(fullpath)) {
161 pciL = FindCnrRecord(hwndCnr,
162 fullpath, pciParent, FALSE, FALSE, FALSE);
163 if (pciL) {
164 while (pciL && pciL != (PCNRITEM)-1 && pciL != pciParent)
165 pciL = WinSendMsg(hwndCnr,
166 CM_QUERYRECORD,
167 MPFROMP(pciL),
168 MPFROM2SHORT(CMA_PARENT, CMA_ITEMORDER));
169 }
170 if (!pciL) {
171
172 RECORDINSERT ri;
173
174 pciL = WinSendMsg(hwndCnr,
175 CM_ALLOCRECORD,
176 MPFROMLONG(EXTRA_RECORD_BYTES),
177 MPFROMLONG(1));
178 if (pciL) {
179 pciL->pszFileName = xstrdup(fullpath, pszSrcFile, __LINE__);
180 pciL->rc.pszIcon = pciL->pszFileName;
181 if (!fNoIconsDirs &&
182 (!isalpha(*fullpath) ||
183 !(driveflags[toupper(*fullpath) - 'A'] &
184 DRIVE_NOLOADICONS)))
185 pciL->rc.hptrIcon = WinLoadFileIcon(fullpath, FALSE);
186 if (!pciL->rc.hptrIcon)
187 pciL->rc.hptrIcon = hptrDir;
188 pciL->attrFile = FILE_DIRECTORY;
189 pciL->pszDispAttr = FileAttrToString(pciL->attrFile);
190 memset(&ri, 0, sizeof(ri));
191 ri.cb = sizeof(ri);
192 ri.pRecordOrder = (PRECORDCORE)CMA_END;
193 ri.pRecordParent = (PRECORDCORE)pciParent;
194 ri.zOrder = (ULONG)CMA_TOP;
195 ri.cRecordsInsert = 1;
196 ri.fInvalidateRecord = FALSE;
197 if (!WinSendMsg(hwndCnr,
198 CM_INSERTRECORD, MPFROMP(pciL), MPFROMP(&ri)))
199 FreeCnrItem(hwndCnr, pciL);
200 }
201 }
202 }
203 }
204 }
205 }
206 xfree(var, pszSrcFile, __LINE__);
207 pciL = (PCNRITEM)WinSendMsg(hwndCnr,
208 CM_QUERYRECORD,
209 MPFROMP(pciParent),
210 MPFROM2SHORT(CMA_FIRSTCHILD, CMA_ITEMORDER));
211 while (pciL && (INT)pciL != -1) {
212 pciL->flags |= (RECFLAGS_NODRAG | RECFLAGS_UNDERENV);
213 WinSendMsg(hwndCnr,
214 CM_INVALIDATERECORD, MPFROMP(&pciL), MPFROM2SHORT(1, 0));
215 pciL = WinSendMsg(hwndCnr,
216 CM_QUERYRECORD,
217 MPFROMP(pciL), MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
218 }
219 }
220 return TRUE;
221}
222
223/**
224 * Insert CNRITEMs for all children of pciParent
225 * @param hwnCnr is container to receive CNRITEMs
226 * @param pciParent is CNRITEM to have children inserted
227 * @return TRUE if OK, FALSE if error detected
228 */
229
230BOOL Flesh(HWND hwndCnr, PCNRITEM pciParent)
231{
232 PCNRITEM pciL;
233 DIRCNRDATA *dcd;
234 BOOL includefiles;
235
236 if (!pciParent || (INT)pciParent == -1 || !hwndCnr)
237 return FALSE;
238
239 // 2015-08-13 SHL
240 if (fAmQuitting)
241 return FALSE;
242
243 if (!pciParent->fleshed) {
244 pciL = (PCNRITEM)WinSendMsg(hwndCnr,
245 CM_QUERYRECORD,
246 MPFROMP(pciParent),
247 MPFROM2SHORT(CMA_FIRSTCHILD, CMA_ITEMORDER));
248 // Added by Stubby to create plus sign run Stubby on it here and skip it in ProcessDirectory
249 if (pciL && (INT)pciL != -1) {
250 AddFleshWorkRequest(hwndCnr, pciL, eStubby);
251 /* Under some condtions AddFleshWorkRequest will return before pciL is populated
252 2015-08-06 SHL FIXME to ensure pciL->szFileName not empty string
253 2020-01-25 SHL FIXME to ensure pciL->szFileName not null
254 */
255 if (!pciL->pszFileName || !*pciL->pszFileName || !strcmp(pciL->pszFileName, NullStr))
256 DosSleep(250);
257 if (!pciL->pszFileName || !*pciL->pszFileName || !strcmp(pciL->pszFileName, NullStr)) {
258 Runtime_Error(pszSrcFile, __LINE__, "Flesh called with pci %p pszFileName %p = %s",
259 pciL,
260 pciL ? pciL->pszFileName : 0,
261 pciL ? pciL->pszFileName ? pciL->pszFileName : "(null)" : "(null)");
262 return FALSE;
263 }
264
265 }
266 dcd = INSTDATA(hwndCnr);
267 if (dcd && dcd->size != sizeof(DIRCNRDATA))
268 dcd = NULL;
269
270 includefiles =
271 driveflags[toupper(*pciParent->pszFileName) - 'A'] & DRIVE_INCLUDEFILES ?
272 TRUE : fFilesInTree;
273
274 ProcessDirectory(hwndCnr,
275 pciParent,
276 pciParent->pszFileName,
277 includefiles, // filestoo
278 TRUE, // recurse
279 TRUE, // partial
280 NULL, // stop flag
281 dcd,
282 NULL, // total files
283 NULL, // total bytes
284 (pciL && (INT)pciL != -1) && pciL->pszDisplayName ? pciL->pszDisplayName : 0);
285 pciParent->fleshed = TRUE;
286 return TRUE;
287 }
288
289 return FALSE;
290}
291
292/**
293 * Remove children from container
294 * @param pciParent is parent of children to be removed
295 */
296
297VOID UnFlesh(HWND hwndCnr, PCNRITEM pciParent)
298{
299 BOOL removed = FALSE;
300 PCNRITEM pciL;
301
302 if (!pciParent || !hwndCnr)
303 return;
304 for (;;) {
305 pciL = (PCNRITEM)WinSendMsg(hwndCnr,
306 CM_QUERYRECORD,
307 MPFROMP(pciParent),
308 MPFROM2SHORT(CMA_FIRSTCHILD, CMA_ITEMORDER));
309 if (!pciL || (INT)pciL == -1)
310 break;
311 RemoveCnrItems(hwndCnr, pciL, 1, CMA_FREE);
312 removed = TRUE;
313 } // for
314
315 if (removed) {
316 WinSendMsg(hwndCnr,
317 CM_INVALIDATERECORD,
318 MPFROMP(&pciParent),
319 MPFROM2SHORT(1, CMA_ERASE | CMA_REPOSITION));
320 pciParent->fleshed = FALSE;
321 DosSleep(1); // Let container items go away
322 }
323 return;
324}
325
326#define DDEPTH 64
327
328/**
329 * Insert CNRITEM for 1st subdirectory [or file] of pciParent
330 * @param hwdCnr is container to be filled
331 * @param pciParent is CNRITEM to receive child record
332 * @return TRUE if record inserted, else FALSE
333 * Ensures that expand/collapse button displays if directory has children
334 * Secondary purpose is to detect broken LANs and inaccesible mapped drives
335 */
336
337BOOL Stubby(HWND hwndCnr, PCNRITEM pciParent)
338{
339 /**
340 * this code is full of workarounds for screwed up LANs.
341 * let's hope all the current LAN programmers fall into
342 * a black hole and make way for people who can get it right...
343 */
344
345 BOOL ok = FALSE;
346 FILEFINDBUF3 ffb[DDEPTH];
347 PFILEFINDBUF3 pffb;
348 HDIR hDir = HDIR_CREATE;
349 ULONG nm, ulM = 1, total = 0, fl;
350 CHAR wildcard[CCHMAXPATH];
351 register INT len;
352 APIRET rc, prc;
353 BOOL isadir = FALSE;
354 BOOL isremote;
355 BOOL includefiles;
356 ULONG ddepth = DDEPTH;
357 ULONG drvNum;
358 ULONG flags;
359 static BOOL brokenlan = FALSE, isbroken = FALSE;
360
361 // 2020-01-27 SHL FIXME to doc how can get here with unitialized pciParent
362 if (!pciParent || (INT)pciParent == -1||
363 !pciParent->pszFileName ||
364 !*pciParent->pszFileName ||
365 pciParent->pszFileName == NullStr ||
366 !hwndCnr) {
367 return FALSE;
368 }
369
370 // Build wildcard
371 len = strlen(pciParent->pszFileName);
372 memcpy(wildcard, pciParent->pszFileName, len + 1);
373 if (wildcard[len - 1] != '\\')
374 wildcard[len++] = '\\';
375 wildcard[len++] = '*';
376 wildcard[len] = 0;
377
378 // 2015-08-19 SHL FIXME to know how this can happen
379 if (!isalpha(*wildcard) || wildcard[1] != ':' || wildcard[2] != '\\') {
380 MakeFullName(wildcard);
381 }
382 drvNum = toupper(*pciParent->pszFileName) - 'A';
383 flags = driveflags[drvNum];
384 if (!isalpha(*wildcard) ||
385 wildcard[1] != ':' ||
386 wildcard[2] != '\\' || ((flags & DRIVE_IGNORE)))
387 return FALSE; // Not a directory or ignore requested
388
389 includefiles = flags & DRIVE_INCLUDEFILES ? TRUE : fFilesInTree;
390
391 isremote = flags & DRIVE_REMOTE ? TRUE : FALSE;
392
393 if (isremote) {
394 if (fRemoteBug) {
395 if (brokenlan) {
396 ddepth = (ULONG)-1;
397 ddepth--;
398 }
399 ulM = 1;
400 }
401 }
402 else if (isbroken)
403 ddepth = 14;
404
405 if (!fRemoteBug)
406 ulM = ddepth <= DDEPTH ? ddepth : 1;
407
408 nm = ulM;
409
410 DosError(FERR_DISABLEHARDERR);
411
412 fl = includefiles ? FILE_DIRECTORY : MUST_HAVE_DIRECTORY;
413 rc = DosFindFirst(wildcard,
414 &hDir,
415 FILE_NORMAL | fl |
416 FILE_READONLY | FILE_ARCHIVED |
417 FILE_SYSTEM | FILE_HIDDEN,
418 &ffb, ulM * sizeof(FILEFINDBUF3), &nm, FIL_STANDARD);
419 if (ulM == 1 && !rc) {
420 // Loop looking for 1st directory (or file)
421 do {
422 pffb = &ffb[0];
423 if (!includefiles && !(pffb->attrFile & FILE_DIRECTORY) && !brokenlan) {
424 // Find returned file when only directories requested
425 brokenlan = TRUE;
426 ddepth = (ULONG)-1;
427 ddepth--;
428 if (!NoBrokenNotify) {
429 prc = saymsg(MB_YESNO | MB_ICONEXCLAMATION,
430 HWND_DESKTOP,
431 GetPString(IDS_LANERRORTITLETEXT),
432 GetPString(IDS_LANERRORTEXT));
433 if (prc == MBID_NO) {
434 saymsg(MB_ENTER,
435 HWND_DESKTOP,
436 GetPString(IDS_LANERROR2TITLETEXT),
437 GetPString(IDS_LANERROR2TEXT));
438 NoBrokenNotify = 255;
439 PrfWriteProfileData(fmprof, FM3Str, "NoBrokenNotify",
440 &NoBrokenNotify, sizeof(ULONG));
441 }
442 }
443 else {
444 NoBrokenNotify--;
445 PrfWriteProfileData(fmprof, FM3Str, "NoBrokenNotify",
446 &NoBrokenNotify, sizeof(ULONG));
447 }
448 }
449
450 if (*pffb->achName &&
451 (includefiles || (pffb->attrFile & FILE_DIRECTORY)) &&
452 (pffb->achName[0] != '.' ||
453 (pffb->achName[1] &&
454 (pffb->achName[1] != '.' || pffb->achName[2])))) {
455 // Got directory other than . or .. (or a file)
456 DosFindClose(hDir);
457 isadir = TRUE;
458 goto Interruptus;
459 }
460 nm = 1;
461 DosError(FERR_DISABLEHARDERR);
462 } while (++total < ddepth && !(rc = (DosFindNext(hDir,
463 &ffb,
464 sizeof(FILEFINDBUF3),
465 &nm))));
466 DosFindClose(hDir);
467
468 if (toupper(*pciParent->pszFileName) > 'B' &&
469 (*(pciParent->pszFileName + 1)) == ':' &&
470 (*(pciParent->pszFileName + 2)) == '\\' && !(*(pciParent->pszFileName + 3))) {
471
472 // Searching root of hard or remote drive and find reported error
473 CHAR s[132];
474 sprintf(s,
475 GetPString(IDS_NOSUBDIRSTEXT),
476 total, toupper(*pciParent->pszFileName));
477 if (rc && rc != ERROR_NO_MORE_FILES)
478 sprintf(&s[strlen(s)], GetPString(IDS_SEARCHERRORTEXT), rc, wildcard);
479 else if (ddepth < 16)
480 brokenlan = TRUE;
481 Notify(s);
482 }
483 goto None; // Done
484 }
485 if (!rc) {
486 DosFindClose(hDir);
487 if (nm) {
488 PBYTE fb = (PBYTE)&ffb[0];
489 for (len = 0; len < nm; len++) {
490 pffb = (PFILEFINDBUF3) fb;
491 if (!includefiles && !(pffb->attrFile & FILE_DIRECTORY)) {
492 // Got file(s), but did not ask for files
493 if (!isbroken) {
494 isbroken = TRUE;
495 if (!NoBrokenNotify) {
496 prc = saymsg(MB_YESNO | MB_ICONEXCLAMATION,
497 HWND_DESKTOP,
498 GetPString(IDS_FSDERRORTITLETEXT),
499 GetPString(IDS_FSDERRORTEXT),
500 isremote ? GetPString(IDS_REMOTETEXT) :
501 GetPString(IDS_LOCALTEXT),
502 *wildcard);
503 if (prc == MBID_NO) {
504 saymsg(MB_ENTER,
505 HWND_DESKTOP,
506 GetPString(IDS_FSDERROR2TITLETEXT),
507 GetPString(IDS_FSDERROR2TEXT));
508 NoBrokenNotify = 255;
509 PrfWriteProfileData(fmprof, FM3Str, "NoBrokenNotify",
510 &NoBrokenNotify, sizeof(ULONG));
511 }
512 }
513 else {
514 NoBrokenNotify--;
515 PrfWriteProfileData(fmprof, FM3Str, "NoBrokenNotify",
516 &NoBrokenNotify, sizeof(ULONG));
517 }
518 } // if !broken
519 } // if !directory
520
521 if (*pffb->achName &&
522 (includefiles || (pffb->attrFile & FILE_DIRECTORY)) &&
523 ((pffb->achName[0] && pffb->achName[0] != '.') ||
524 (pffb->achName[1] &&
525 (pffb->achName[1] != '.' || pffb->achName[2]))))
526 {
527 // Got directory other than . or .. (or a file)
528 isadir = TRUE;
529 break;
530 }
531 fb += pffb->oNextEntryOffset;
532 } // for
533
534 Interruptus:
535
536 if (isadir) {
537
538 // Insert CNRITEM for selected directory (or file)
539 PCNRITEM pci;
540
541 if (WinIsWindow((HAB)0, hwndCnr)) {
542 pci = WinSendMsg(hwndCnr,
543 CM_ALLOCRECORD,
544 MPFROMLONG(EXTRA_RECORD_BYTES), MPFROMLONG(1));
545 if (!pci) {
546 Win_Error(hwndCnr, HWND_DESKTOP, __FILE__, __LINE__,
547 GetPString(IDS_RECORDALLOCFAILEDTEXT));
548 }
549 else {
550 RECORDINSERT ri;
551 CHAR szBuffer[CCHMAXPATH + 14];
552 CHAR *p;
553 HPOINTER hptr;
554
555 p = strchr(wildcard, '*');
556 *p = 0;;
557 BldFullPathName(szBuffer, wildcard, pffb->achName);
558 pci->pszFileName = xstrdup(szBuffer, pszSrcFile, __LINE__);
559 p = strrchr(pci->pszFileName, '\\');
560 p++;
561 pci->pszDisplayName = p;
562 pci->rc.pszIcon = pci->pszDisplayName;
563 if (fForceUpper)
564 strupr(pci->pszFileName);
565 else if (fForceLower)
566 strlwr(pci->pszFileName);
567
568 flags = driveflags[toupper(*pci->pszFileName) - 'A'];
569
570 // get an icon to use with it
571 if (pffb->attrFile & FILE_DIRECTORY) {
572 // is directory
573 if (fNoIconsDirs ||
574 (flags & DRIVE_NOLOADICONS) ||
575 !isalpha(*pci->pszFileName)) {
576 hptr = (HPOINTER) 0;
577 }
578 else
579 hptr = WinLoadFileIcon(pci->pszFileName, FALSE);
580 }
581 else {
582 // is file
583 if (fNoIconsFiles ||
584 (flags & DRIVE_NOLOADICONS) ||
585 !isalpha(*pci->pszFileName)) {
586 hptr = (HPOINTER) 0;
587 }
588 else
589 hptr = WinLoadFileIcon(pci->pszFileName, FALSE);
590
591 if (!hptr || IsDefaultIcon(hptr))
592 hptr = IDFile(pci->pszFileName);
593 }
594
595 if (!hptr) {
596 hptr = pffb->attrFile & FILE_DIRECTORY ?
597 hptrDir : pffb->attrFile & FILE_SYSTEM ?
598 hptrSystem : pffb->attrFile & FILE_HIDDEN ?
599 hptrHidden : pffb->attrFile & FILE_READONLY ?
600 hptrReadonly : hptrFile;
601 }
602 pci->rc.hptrIcon = hptr;
603 pci->attrFile = pffb->attrFile;
604 memset(&ri, 0, sizeof(RECORDINSERT));
605 ri.cb = sizeof(RECORDINSERT);
606 ri.pRecordOrder = (PRECORDCORE)CMA_END;
607 ri.pRecordParent = (PRECORDCORE)pciParent;
608 ri.zOrder = (ULONG)CMA_TOP;
609 ri.cRecordsInsert = 1;
610 ri.fInvalidateRecord = TRUE;
611 if (!WinSendMsg(hwndCnr,
612 CM_INSERTRECORD, MPFROMP(pci), MPFROMP(&ri))) {
613 // Assume busy and try again
614 DosSleep(50);
615 WinSetFocus(HWND_DESKTOP, hwndCnr);
616 if (WinIsWindow((HAB)0, hwndCnr)) {
617 if (!WinSendMsg(hwndCnr,
618 CM_INSERTRECORD, MPFROMP(pci), MPFROMP(&ri))) {
619 Win_Error(hwndCnr, HWND_DESKTOP, __FILE__, __LINE__,
620 GetPString(IDS_RECORDINSERTFAILEDTEXT));
621 FreeCnrItem(hwndCnr, pci);
622 }
623 else
624 ok = TRUE;
625 }
626 }
627 else
628 ok = TRUE;
629 }
630 }
631 } // if isadir
632 }
633 } // if !rc
634 else if (toupper(*wildcard) > 'B' && wildcard[1] == ':' && wildcard[2] == '\\' &&
635 wildcard[3] == '*' && !wildcard[4]) {
636 // Is root and no subdirectories
637 CHAR s[162];
638
639 sprintf(s,
640 GetPString(IDS_NOSUBDIRS2TEXT),
641 nm,
642 toupper(*pciParent->pszFileName),
643 isremote ? GetPString(IDS_NOSUBDIRS3TEXT) : NullStr);
644 Notify(s);
645 }
646 else if (toupper(*wildcard) > 'B' && rc != ERROR_NO_MORE_FILES) {
647 // Find for remote or hard drive failed with error
648 CHAR s[CCHMAXPATH + 80];
649 sprintf(s, GetPString(IDS_SEARCHERRORTEXT), rc, wildcard);
650 Notify(s);
651 }
652
653None:
654
655 DosError(FERR_DISABLEHARDERR);
656 return ok;
657} // Stubby
658
659// Stubby/Flesh/Unflesh work list item
660
661typedef struct {
662 LIST2 list;
663 HWND hwndCnr;
664 PCNRITEM pci;
665 FLESHWORKACTION action;
666} FLESHWORKITEM;
667typedef FLESHWORKITEM *PFLESHWORKITEM;
668
669// Stubby/Flesh/Unflesh work list
670LIST2 FleshWorkList;
671
672HMTX hmtxFleshWork;
673HEV hevFleshWorkListChanged;
674
675/**
676 * Check work list item pci matches passed pci
677 */
678
679BOOL WorkListItemMatches(PLIST2 item, PVOID data)
680{
681 return ((PFLESHWORKITEM)data)->pci == ((PFLESHWORKITEM)item)->pci;
682}
683
684/**
685 * Delete stale items from flesh queue
686 */
687
688VOID DeleteStaleFleshWorkListItems(PCNRITEM pci)
689{
690 FLESHWORKITEM match;
691 PLIST2 item;
692
693 match.pci = pci;
694
695 for (;;) {
696 item = List2Search(&FleshWorkList, WorkListItemMatches, &match);
697 if (!item)
698 break;
699 List2Delete(&FleshWorkList, item);
700 xfree(item, pszSrcFile, __LINE__);
701 }
702}
703
704/**
705 * Add item to flesh work list
706 * eUnFlesh requests get special handling
707 * eFlesh etc. items for the same CNRITEM are considered stale and are
708 * deleted because eUnFlesh will free the CNRITEM associated with the item
709 * before the work list item is processed
710 */
711
712BOOL AddFleshWorkRequest(HWND hwndCnr, PCNRITEM pci, FLESHWORKACTION action)
713{
714 PFLESHWORKITEM item = xmallocz(sizeof(FLESHWORKITEM), pszSrcFile, __LINE__);
715 item->hwndCnr = hwndCnr;
716 item->pci = pci;
717 item->action= action;
718
719 if (fAmQuitting)
720 return FALSE;
721
722 xDosRequestMutexSem(hmtxFleshWork, SEM_INDEFINITE_WAIT);
723
724 // Delete stale requests
725 if (item->action == eUnFlesh)
726 DeleteStaleFleshWorkListItems(pci);
727
728 List2Append(&FleshWorkList, (PLIST2)item);
729
730 xDosReleaseMutexSem(hmtxFleshWork);
731 xDosPostEventSem(hevFleshWorkListChanged);
732
733 return TRUE;
734}
735
736/**
737 * Return TRUE if work list empty
738 * Advisory only
739 */
740
741BOOL IsFleshWorkListEmpty(VOID)
742{
743 return FleshWorkList.next == NULL;
744}
745
746/**
747 * Check if pci pathname is parent of child path name
748 * @param data is child path name
749 * @return TRUE if is work item path is parent of given path
750 */
751
752BOOL IsParentOfChildPath(PLIST2 item, PVOID data)
753{
754 UINT c;
755 if (!((PFLESHWORKITEM)item)->pci->pszFileName) {
756 Runtime_Error(pszSrcFile, __LINE__, "IsParentOfChildPath called with pci %p pszFileName (null)", ((PFLESHWORKITEM)item)->pci);
757 return FALSE;
758 }
759 c = strlen(((PFLESHWORKITEM)item)->pci->pszFileName);
760 return strncmp(((PFLESHWORKITEM)item)->pci->pszFileName, (PCSZ)data, c) == 0;
761}
762
763/**
764 * Wait until work list empty or until dependent items removed from list
765 * Advisory only
766 * @parse pszFileName is dependent pathName
767 */
768
769VOID WaitFleshWorkListEmpty(PCSZ pszDirName, ULONG ulSleep)
770{
771 APIRET rc;
772 PFLESHWORKITEM item;
773 INT tid = GetTidForThread();
774 BOOL pathSaved = FALSE;
775 BOOL waited;
776 PCSZ pszSavedFleshFocusPath;
777 INT rcCount = 0;
778
779 // 11 Oct 15 GKY FIXME did we intend to keep this
780 if (tid == 1 || tid == tidFleshWorkListThread) {
781 Runtime_Error(pszSrcFile, __LINE__, "WaitFleshWorkListEmpty called by tid %u", tid);
782 return; // Avoid hang
783 }
784 // Can not wait if call from thread 1 or FleshWorkListThread
785 for (waited = FALSE; !IsFleshWorkListEmpty(); waited = TRUE) {
786 // 2015-08-13 SHL
787 if (fAmQuitting)
788 return;
789
790 // Just wait for dependents to be gone if path name given
791 if (pszDirName) {
792 rc = xDosRequestMutexSem(hmtxFleshWork, SEM_INDEFINITE_WAIT);
793 if (rc) {
794 rcCount++;
795 if (rcCount < 6)
796 continue; // Maybe should return ???
797 else {
798 Dos_Error(MB_CANCEL, rc, HWND_DESKTOP, pszSrcFile, __LINE__,
799 PCSZ_DOSREQUESTMUTEXSEM);
800 return;
801 }
802
803 }
804
805 if (!pathSaved) {
806 // Give priority to work items for parents of this path
807 pathSaved = TRUE;
808 pszSavedFleshFocusPath = pszFleshFocusPath;
809 pszFleshFocusPath = pszDirName;
810 }
811
812 item = (PFLESHWORKITEM)List2Search(&FleshWorkList, IsParentOfChildPath, (PVOID)pszDirName);
813
814 xDosReleaseMutexSem(hmtxFleshWork);
815 rcCount = 0;
816
817 if (!item) {
818 if (waited)
819 DosSleep(ulSleep); // Let PM do some work
820 break; // Dependents gone from work list
821 }
822 } // if pszDirName
823 DosSleep(ulSleep);
824 } // for
825
826 if (pathSaved) {
827 xDosRequestMutexSem(hmtxFleshWork, SEM_INDEFINITE_WAIT);
828 pszFleshFocusPath = pszSavedFleshFocusPath;
829 xDosReleaseMutexSem(hmtxFleshWork);
830 }
831
832}
833
834/**
835 * Set focus drive to optimize work list processing
836 * @param chDriveLetter is upper case drive letter (A-Z)
837 */
838
839VOID SetFleshFocusPath(PCSZ pszPath) {
840 PCSZ pszOld;
841 PCSZ pszNew = strdup(pszPath);
842 xDosRequestMutexSem(hmtxFleshWork, SEM_INDEFINITE_WAIT);
843 pszOld = pszFleshFocusPath;
844 pszFleshFocusPath = pszNew;
845 xDosReleaseMutexSem(hmtxFleshWork);
846 if (pszOld)
847 xfree((PVOID)pszOld, pszSrcFile, __LINE__);
848}
849
850/**
851 * Run Flesh, UnFlesh, FleshEnv, Stubby for directory for items in work list
852 */
853
854VOID FleshWorkThread(PVOID arg)
855{
856 HAB thab;
857 HMQ hmq = (HMQ)0;
858
859 // 2015-08-07 SHL FIXME to be gone
860 static INT ProcessDirCount = 0;
861
862 DosError(FERR_DISABLEHARDERR);
863
864# ifdef FORTIFY
865 Fortify_EnterScope();
866# endif
867
868 thab = WinInitialize(0);
869 if (thab) {
870 hmq = WinCreateMsgQueue(thab, 0);
871 if (hmq) {
872 IncrThreadUsage();
873 priority_normal();
874
875 // process list entries until time to die
876 for (;!fAmQuitting;) {
877
878 PFLESHWORKITEM item;
879
880 // 2015-08-07 SHL FIXME to use SMPSafe...
881 xDosRequestMutexSem(hmtxFleshWork, SEM_INDEFINITE_WAIT);
882
883 // 2015-08-14 SHL
884 // Get next work list item and remove from list
885 // If focus path set, process parents of focus path first
886 if (pszFleshFocusPath) {
887 item = (PFLESHWORKITEM)List2Search(&FleshWorkList, IsParentOfChildPath, (PVOID)pszFleshFocusPath);
888 if (!item) {
889 xfree((PSZ)pszFleshFocusPath, pszSrcFile, __LINE__);
890 pszFleshFocusPath = NULL; // Revert to normal
891 }
892 else
893 List2Delete(&FleshWorkList, (PLIST2)item);
894 }
895 else
896 item = NULL;
897
898 if (!item)
899 item = (PFLESHWORKITEM)List2DeleteFirst(&FleshWorkList);
900
901 xDosReleaseMutexSem(hmtxFleshWork);
902
903 // Wait for new items to be added to list
904 if (!item) {
905 ULONG ul;
906 xDosWaitEventSem(hevFleshWorkListChanged, SEM_INDEFINITE_WAIT);
907 xDosResetEventSem(hevFleshWorkListChanged, &ul);
908 continue;
909 }
910
911 if (WinIsWindow((HAB)0, item->hwndCnr)) {
912
913 switch (item->action) {
914 case eUnFlesh:
915 UnFlesh(item->hwndCnr, item->pci);
916 break;
917 case eFleshEnv:
918 FleshEnv(item->hwndCnr, item->pci);
919 break;
920 case eStubby:
921 priority_bumped();
922 Stubby(item->hwndCnr, item->pci);
923 priority_normal();
924 break;
925 case eFlesh:
926 if (Flesh(item->hwndCnr, item->pci)) {
927 // 2015-08-06 SHL FIXME to report?
928 }
929 break;
930 default:
931 Runtime_Error(pszSrcFile, __LINE__, "item %u unexpected", item->action);
932 } // switch
933
934 } // if window
935
936 xfree(item, pszSrcFile, __LINE__);
937
938 } // for
939
940 WinDestroyMsgQueue(hmq);
941 }
942 DecrThreadUsage();
943 WinTerminate(thab);
944 }
945
946 ProcessDirCount++;
947 if (ProcessDirCount >= FixedVolume) {
948 ProcessDirCount = 0;
949 FixedVolume = 0;
950 }
951
952# ifdef FORTIFY
953 Fortify_LeaveScope();
954# endif
955
956}
957
958/**
959 * Allocate resources and start FleshWorkThread
960 * @return TRUE if OK
961 */
962
963BOOL StartFleshWorkThread(VOID)
964{
965 APIRET rc = DosCreateMutexSem(NULL, &hmtxFleshWork, 0L /* Not shared */, FALSE /* Not owned */);
966 if (rc) {
967 Dos_Error(MB_CANCEL, rc, HWND_DESKTOP, pszSrcFile, __LINE__,
968 PCSZ_DOSCREATEMUTEXSEM);
969 return FALSE;
970 }
971
972 rc = xDosCreateEventSem(NULL, &hevFleshWorkListChanged, 0 /* Not shared */, FALSE /* Reset */);
973 if (rc)
974 return FALSE; // Give up
975 tidFleshWorkListThread = xbeginthread(FleshWorkThread,
976 65536,
977 NULL,
978 pszSrcFile, __LINE__);
979 return tidFleshWorkListThread != -1;
980
981}
982
983#pragma alloc_text(FLESH,Flesh,FleshEnv,UnFlesh,Stubby,FleshWorkThread,StartFleshWorkThread,AddFleshWorkRequest)
Note: See TracBrowser for help on using the repository browser.