source: trunk/dll/dirsize.c

Last change on this file was 1916, checked in by Gregg Young, 7 days ago

Fix easize so that EAs larger than 32767 show their actual size instead of 32767

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.6 KB
Line 
1
2/***********************************************************************
3
4 $Id: dirsize.c 1916 2025-11-01 18:30:47Z gyoung $
5
6 Directory sizes
7
8 Copyright (c) 1993-98 M. Kimes
9 Copyright (c) 2001, 2015 Steven H. Levine
10
11 16 Oct 02 SHL Handle large partitions
12 12 Feb 03 SHL Use CBLIST_TO_EASIZE
13 21 Nov 03 SHL Avoid VAC \ after // bug (wierd)
14 21 Nov 03 SHL Correct minor typos
15 21 Nov 03 SHL Total drives >4GB better
16 24 May 05 SHL Rework for CNRITEM.szSubject
17 25 May 05 SHL Use ULONGLONG and CommaFmtULL
18 26 May 05 SHL More large file formatting updates
19 06 Jun 05 SHL Drop obsoletes
20 19 Jun 05 SHL More 64-bit math fixes
21 08 Aug 05 SHL Avoid Expand/Collapse hangs while working
22 17 Jul 06 SHL Use Runtime_Error
23 19 Oct 06 SHL Correct . and .. detect
24 18 Feb 07 GKY Add new drive type icons
25 22 Mar 07 GKY Use QWL_USER
26 23 Jul 07 SHL Sync with naming standards
27 03 Aug 07 GKY Enlarged and made setable everywhere Findbuf (speed file loading)
28 03 Aug 07 SHL DirSizeProc; correct sizing and positioning to be deterministic
29 06 Aug 07 GKY Reduce DosSleep times (ticket 148)
30 13 Aug 07 SHL ProcessDir: remove unneeded reallocs. Sanitize code
31 13 Aug 07 SHL Move #pragma alloc_text to end for OpenWatcom compat
32 26 Aug 07 GKY DosSleep(1) in loops changed to (0)
33 29 Feb 08 GKY Use xfree where appropriate
34 29 Feb 08 GKY Add presparams & update appearence of "Sizes" dialog
35 07 Jul 08 GKY Fixed trap in PMCTLS (strlen) inadequate memory allocation
36 07 Jul 08 GKY Fixed trap by no longer allocating pci->pszLongName as flag but pointing isroot
37 version to NullStr and all others to NULL.
38 19 Jul 08 GKY Replace save_dir2(dir) with pFM2SaveDirectory; use pTmpDir for temp files
39 03 Aug 08 GKY Reworked FillInRecSizes to use pci->pszDisplayName for display names and
40 created a more consitent string for passing to DRAWITEM. Finally (I hope) fixed
41 the strlen trap.
42 23 Aug 08 GKY Fix memory leak (failure to free cnritems)
43 10 Dec 08 SHL Integrate exception handler support
44 07 Feb 09 GKY Allow user to turn off alert and/or error beeps in settings notebook.
45 08 Mar 09 GKY Renamed commafmt.h i18nutil.h
46 08 Mar 09 GKY Additional strings move to PCSZs in init.c
47 28 Jun 09 GKY Added AddBackslashToPath() to remove repeatative code.
48 13 Dec 09 GKY Fixed separate paramenters. Please note that appname should be used in
49 profile calls for user settings that work and are setable in more than one
50 miniapp; FM3Str should be used for setting only relavent to FM/2 or that
51 aren't user settable; realappname should be used for setting applicable to
52 one or more miniapp but not to FM/2
53 17 JAN 10 GKY Changes to get working with Watcom 1.9 Beta (1/16/10).
54 Mostly cast CHAR CONSTANT * as CHAR *.
55 20 Nov 10 GKY Check that pTmpDir IsValid and recreate if not found; Fixes hangs caused
56 by temp file creation failures.
57 12 Jun 11 GKY Added SleepIfNeeded in the container fill loop
58 20 Sep 15 GKY Move directory expansion to a thread. Split ExpandAll into ExpandAll and CollapseAll
59 to try to speed proccessing else where.
60
61***********************************************************************/
62
63#include <stdlib.h>
64#include <string.h>
65#include <ctype.h>
66// #include <process.h> // _beginthread
67
68#define INCL_DOS
69#define INCL_DOSERRORS
70#define INCL_WIN
71#define INCL_GPI
72#define INCL_LONGLONG
73
74#include "fm3dll.h"
75#include "fm3dll2.h" // #define's for UM_*, control id's, etc.
76#include "draglist.h" // Data declaration(s)
77#include "init.h" // Data declaration(s)
78#include "notebook.h" // Data declaration(s)
79#include "mainwnd.h" // Data declaration(s)
80#include "newview.h" // Data declarations
81#include "fm3dlg.h"
82#include "fm3str.h"
83#include "dircnrs.h"
84#include "errutil.h" // Dos_Error...
85#include "strutil.h" // GetPString
86#include "filldir.h" // EmptyCnr...
87#include "dirsize.h"
88#include "select.h" // ExpandAll
89#include "valid.h" // CheckDrive
90#include "common.h" // OpenDirCnr
91#include "shadow.h" // OpenObject
92#include "presparm.h" // PresParamChanged
93#include "i18nutil.h" // commafmt
94#include "getnames.h" // export_filename
95#include "wrappers.h" // xDosFindNext
96#include "dirs.h" // save_dir2
97#include "misc.h" // PostMsg
98#include "fortify.h"
99#include "excputil.h" // xbeginthread
100#include "pathutil.h" // AddBackslashToPath
101#include "tmrsvcs.h"
102#include "eas.h" // GetLargeEASize
103
104typedef struct
105{
106 CHAR *pszFileName;
107 HWND hwndCnr;
108 CHAR *pchStopFlag;
109 DIRCNRDATA *pDCD;
110} DIRSIZE;
111
112typedef struct
113{
114 HWND hwndCnr;
115 PCNRITEM pci;
116} EXPANDSIZE;
117
118typedef struct
119{
120 CHAR szDirName[CCHMAXPATH];
121 CHAR chStopFlag;
122 BOOL dying;
123 BOOL working;
124 HPOINTER hptr;
125} tState;
126
127static PSZ pszSrcFile = __FILE__;
128
129static SHORT APIENTRY SortSizeCnr(PMINIRECORDCORE p1,
130 PMINIRECORDCORE p2,
131 PVOID SortFlags)
132{
133 ULONGLONG size1;
134 ULONGLONG size2;
135
136 size1 = ((PCNRITEM) p1)->cbFile + ((PCNRITEM) p1)->easize;
137 size2 = ((PCNRITEM) p2)->cbFile + ((PCNRITEM) p2)->easize;
138 return (size1 < size2) ? 1 : (size1 == size2) ? 0 : -1;
139}
140
141static BOOL ProcessDir(HWND hwndCnr,
142 CHAR *pszFileName,
143 PCNRITEM pciParent,
144 CHAR *pchStopFlag,
145 BOOL top,
146 PULONGLONG pullTotalBytes)
147{
148 CHAR maskstr[CCHMAXPATH];
149 CHAR szBuf[CCHMAXPATH];
150 CHAR FileSystem[CCHMAXPATH];
151 CHAR *pEndMask;
152 register char *p;
153 register char *sp;
154 register char *pp;
155 ULONG ulFindCnt;
156 ULONGLONG ullCurDirBytes = 0;
157 ULONGLONG ullSubDirBytes = 0;
158 ULONGLONG ull;
159 HDIR hdir;
160 PFILEFINDBUF4L pffbArray;
161 APIRET rc;
162 RECORDINSERT ri;
163 PCNRITEM pci;
164 ULONG ulBufBytes;
165
166 *pullTotalBytes = 0; // In case we fail
167
168 CheckDrive(toupper(*pszFileName), FileSystem, NULL);
169 ulBufBytes = sizeof(FILEFINDBUF4L) * FilesToGet;
170 pffbArray = xmalloc(ulBufBytes, pszSrcFile, __LINE__);
171 if (!pffbArray)
172 return FALSE; // Error already reported
173
174 strcpy(maskstr, pszFileName);
175 AddBackslashToPath(maskstr);
176 pEndMask = &maskstr[strlen(maskstr)]; // Point after last backslash
177 strcat(maskstr, "*");
178
179 hdir = HDIR_CREATE;
180 ulFindCnt = 1;
181 DosError(FERR_DISABLEHARDERR);
182 // Check directory exists
183 rc = xDosFindFirst(pszFileName, &hdir,
184 FILE_NORMAL | FILE_READONLY | FILE_ARCHIVED |
185 FILE_SYSTEM | FILE_HIDDEN | MUST_HAVE_DIRECTORY,
186 pffbArray, ulBufBytes, &ulFindCnt, FIL_QUERYEASIZEL);
187
188 if (!rc)
189 DosFindClose(hdir);
190
191 /**
192 * the "|| strlen(pszFileName) < 4 below works around an OS/2 bug
193 * that prevents FAT root directories from being found when
194 * requesting EASIZE. sheesh.
195 */
196 if (((!rc || rc == ERROR_NO_MORE_FILES) && (pffbArray->attrFile & FILE_DIRECTORY)) ||
197 strlen(pszFileName) < 4) {
198 if (*pchStopFlag) {
199 free(pffbArray);
200 return FALSE;
201 }
202 pci = WinSendMsg(hwndCnr, CM_ALLOCRECORD, MPFROMLONG(EXTRA_RECORD_BYTES),
203 MPFROMLONG(1));
204 if (!pci) {
205 Win_Error(hwndCnr, HWND_DESKTOP, pszSrcFile, __LINE__, PCSZ_CM_ALLOCRECORD);
206 xfree(pffbArray, pszSrcFile, __LINE__);
207 return FALSE;
208 }
209 if (!rc) {
210 ullCurDirBytes = pffbArray->cbFile;
211 ullCurDirBytes += pffbArray->cbList == 65535 ? GetLargeEASize(pffbArray->achName) : CBLIST_TO_EASIZE(pffbArray->cbList);
212 }
213 else
214 DosError(FERR_DISABLEHARDERR);
215 pci->rc.hptrIcon = hptrDir;
216 pci->attrFile = 0;
217 pci->pszDispAttr = NULL;
218 pci->pszSubject = NULL;
219 } // if got something
220 else {
221 // No match
222 xfree(pffbArray, pszSrcFile, __LINE__);
223 Dos_Error(MB_ENTER, rc, HWND_DESKTOP, pszSrcFile, __LINE__,
224 GetPString(IDS_CANTFINDDIRTEXT), pszFileName);
225 return FALSE;
226 }
227
228 if (strlen(pszFileName) < 4 || top)
229 pci->pszFileName = xstrdup(pszFileName, pszSrcFile, __LINE__);
230 else {
231 p = strrchr(pszFileName, '\\');
232 if (!p)
233 p = pszFileName;
234 else
235 p++; // After last backslash
236 // Handle quoted names
237 // fixme to understand this - why lose path prefix?
238 sp = strchr(pszFileName, ' ') != NULL ? "\"" : NullStr;
239 pp = szBuf;
240 if (*sp)
241 *pp++ = *sp; // Need quotes
242 strcpy(pp, p);
243 if (*sp)
244 strcat(pp, sp);
245 pci->pszFileName = xstrdup(szBuf, pszSrcFile, __LINE__);
246 }
247 // Use pszDisplayname for display so no need to save length of pszFileName 03 Aug 08 GKY
248 pci->pszDisplayName = pci->pszFileName;
249 pci->rc.pszIcon = pci->pszFileName;
250 pci->rc.flRecordAttr |= CRA_RECORDREADONLY;
251 if (fForceUpper)
252 strupr(pci->pszFileName);
253 else if (fForceLower)
254 strlwr(pci->pszFileName);
255 memset(&ri, 0, sizeof(RECORDINSERT));
256 ri.cb = sizeof(RECORDINSERT);
257 ri.pRecordOrder = (PRECORDCORE) CMA_END;
258 ri.pRecordParent = (PRECORDCORE) pciParent;
259 ri.zOrder = (USHORT) CMA_TOP;
260 ri.cRecordsInsert = 1;
261 ri.fInvalidateRecord = TRUE;
262 if (!WinSendMsg(hwndCnr, CM_INSERTRECORD, MPFROMP(pci), MPFROMP(&ri))) {
263 xfree(pffbArray, pszSrcFile, __LINE__);
264 return FALSE;
265 }
266
267 // Find files and directories in this directory
268 hdir = HDIR_CREATE;
269 // 13 Aug 07 SHL fixme to know if need to support fRemoteBug here like objcnr.c?
270 ulFindCnt = FilesToGet;
271 rc = xDosFindFirst(maskstr, &hdir,
272 FILE_NORMAL | FILE_READONLY | FILE_ARCHIVED |
273 FILE_SYSTEM | FILE_HIDDEN | FILE_DIRECTORY,
274 pffbArray, ulBufBytes, &ulFindCnt, FIL_QUERYEASIZEL);
275 if (!rc) {
276 PFILEFINDBUF4L pffbFile;
277 ULONG x;
278 ITIMER_DESC itdSleep = { 0 }; // 30 May 11 GKY
279
280 InitITimer(&itdSleep, 500);
281 while (!rc) {
282
283 priority_normal();
284 pffbFile = pffbArray;
285 for (x = 0; x < ulFindCnt; x++) {
286 // Total size skipping . and ..
287 if ((~pffbFile->attrFile & FILE_DIRECTORY) ||
288 (pffbFile->achName[0] != '.' ||
289 (pffbFile->achName[1] &&
290 (pffbFile->achName[1] != '.' || pffbFile->achName[2])))) {
291 ullCurDirBytes += pffbFile->cbFile;
292 ullCurDirBytes += (pffbFile->cbList == 65535 ? GetLargeEASize(pffbFile->achName) : CBLIST_TO_EASIZE(pffbFile->cbList)) & 0x3ff;
293
294 if (*pchStopFlag)
295 break;
296 if (~pffbFile->attrFile & FILE_DIRECTORY)
297 pci->attrFile++; // Bump file count
298 else {
299 // Recurse into subdir
300 strcpy(pEndMask, pffbFile->achName); // Append dirname to base dirname
301 ProcessDir(hwndCnr, maskstr, pci, pchStopFlag, FALSE, &ull);
302 ullSubDirBytes += ull;
303 }
304 }
305 if (!pffbFile->oNextEntryOffset)
306 break;
307 pffbFile = (PFILEFINDBUF4L)((PBYTE)pffbFile + pffbFile->oNextEntryOffset);
308
309 } // for matches
310 if (*pchStopFlag)
311 break;
312 DosSleep(0);
313 ulFindCnt = FilesToGet;
314 DosError(FERR_DISABLEHARDERR);
315 rc = xDosFindNext(hdir, pffbArray, ulBufBytes, &ulFindCnt, FIL_QUERYEASIZEL);
316 SleepIfNeeded(&itdSleep, 1);
317 } // while more found
318
319 DosFindClose(hdir);
320 priority_normal();
321 } // if got files or directories
322
323 if (rc && rc != ERROR_NO_MORE_FILES) {
324 Dos_Error(MB_ENTER, rc, HWND_DESKTOP, pszSrcFile, __LINE__,
325 GetPString(IDS_CANTFINDDIRTEXT), pszFileName);
326 }
327
328 xfree(pffbArray, pszSrcFile, __LINE__);
329
330 pci->cbFile = ullCurDirBytes;
331 pci->easize = ullSubDirBytes; // hack cough
332 WinSendMsg(hwndCnr, CM_INVALIDATERECORD, MPFROMP(&pci),
333 MPFROM2SHORT(1, CMA_ERASE | CMA_TEXTCHANGED));
334
335 *pullTotalBytes = ullCurDirBytes + ullSubDirBytes;
336 return TRUE;
337}
338
339static VOID FillInRecSizes(HWND hwndCnr, PCNRITEM pciParent,
340 ULONGLONG ullTotalBytes, CHAR * pchStopFlag,
341 BOOL isroot)
342{
343 PCNRITEM pci = pciParent;
344 SHORT attrib = CMA_FIRSTCHILD;
345
346 if (pci) {
347
348 float fltPct = (float) 0.0;
349 CHAR szCurDir[80];
350 CHAR szSubDir[80];
351 CHAR szAllDir[80];
352 CHAR szBar[101];
353 CHAR szBuf[CCHMAXPATH + 341];
354
355 // cbFile = currect directory usage in bytes
356 // easize = subdirectory usage in bytes
357 CommaFmtULL(szCurDir, sizeof(szCurDir), pci->cbFile, 'K');
358 *szBar = 0;
359 pci->pszLongName = NULL;
360 memset(szBuf, 0, sizeof(szBuf));
361 if (ullTotalBytes) {
362 UINT cBar;
363
364 if (isroot) {
365 FSALLOCATE fsa;
366 APIRET rc;
367
368
369 memset(&fsa, 0, sizeof(fsa));
370 rc = DosQueryFSInfo(toupper(*pci->pszFileName) - '@', FSIL_ALLOC, &fsa,
371 sizeof(FSALLOCATE));
372 if (!rc) {
373 fltPct = (float) (ullTotalBytes * 100.0) /
374 ((float)fsa.cUnit * (fsa.cSectorUnit * fsa.cbSector));
375 }
376 // Need unique buffer 23 Jul 07 SHL
377 pci->pszLongName = NullStr;
378 }
379 else
380 fltPct = (float) (((float)pci->cbFile + pci->easize) * 100.0) / ullTotalBytes;
381
382 //Second line for graph reworked 03 AUG 08 GKY
383 memset(szBar, ' ', sizeof(szBar));
384 cBar = (UINT) fltPct / 2;
385 if (cBar && cBar * 2 != (UINT) fltPct)
386 szBar[cBar] = '=';
387 szBar[100] = 0;
388 }
389
390 pci->flags = (ULONG) fltPct;
391 CommaFmtULL(szSubDir, sizeof(szSubDir), pci->easize, 'K');
392 CommaFmtULL(szAllDir, sizeof(szAllDir), pci->cbFile + pci->easize, 'K');
393 sprintf(szBuf,
394 "%s %s + %s = %s (%.02lf%%%s)\r%s",
395 pci->pszFileName,
396 szCurDir,
397 szSubDir,
398 szAllDir,
399 fltPct,
400 isroot ? GetPString(IDS_OFDRIVETEXT) : NullStr,
401 szBar);
402 pci->pszDisplayName = xstrdup(szBuf, pszSrcFile, __LINE__);
403 // use DisplayName for display hopefully fixes "strlen" trap 02 AUG 08 GKY
404 if (pci->pszDisplayName) {
405# ifdef FORTIFY
406 Fortify_ChangeScope(pci->pszDisplayName, -1);
407 Fortify_ChangeScope(pci->pszFileName, -1);
408# endif
409 WinSendMsg(hwndCnr, CM_INVALIDATERECORD, MPFROMP(&pci), MPFROM2SHORT(1, 0));
410 }
411 isroot = FALSE;
412 }
413 else
414 attrib = CMA_FIRST;
415 pci = (PCNRITEM) WinSendMsg(hwndCnr, CM_QUERYRECORD, MPFROMP(pci),
416 MPFROM2SHORT(attrib, CMA_ITEMORDER));
417 while (pci && (INT) pci != -1) {
418 if (*pchStopFlag)
419 break;
420 FillInRecSizes(hwndCnr, pci, ullTotalBytes, pchStopFlag, isroot);
421 isroot = FALSE;
422 pci = (PCNRITEM) WinSendMsg(hwndCnr, CM_QUERYRECORD, MPFROMP(pci),
423 MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
424 }
425}
426
427static VOID PrintToFile(HWND hwndCnr, ULONG indent, PCNRITEM pciParent,
428 FILE * fp)
429{
430 PCNRITEM pci;
431 CHAR *p;
432
433 if (!pciParent) {
434 pciParent = WinSendMsg(hwndCnr, CM_QUERYRECORD, MPFROMP(NULL),
435 MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
436 indent = 0;
437 }
438 if (pciParent) {
439 p = strchr(pciParent->pszDisplayName, '\r'); // GKY use display name for display
440 if (p)
441 *p = 0;
442 fprintf(fp, "%*.*s%s %lu %s%s\n",
443 indent * 2, indent * 2, " ",
444 pciParent->pszDisplayName,
445 pciParent->attrFile,
446 GetPString(IDS_FILETEXT), &"s"[pciParent->attrFile == 1]);
447 if (p)
448 *p = '\r';
449 if (pciParent->rc.flRecordAttr & CRA_EXPANDED) {
450 pci = (PCNRITEM) WinSendMsg(hwndCnr, CM_QUERYRECORD, MPFROMP(pciParent),
451 MPFROM2SHORT(CMA_FIRSTCHILD,
452 CMA_ITEMORDER));
453 while (pci && (INT) pci != -1) {
454 DosSleep(0);
455 PrintToFile(hwndCnr, indent + 1, pci, fp);
456 pci = (PCNRITEM) WinSendMsg(hwndCnr, CM_QUERYRECORD, MPFROMP(pci),
457 MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
458 } //while
459 }
460 }
461}
462
463static VOID FillCnrThread(VOID *args)
464{
465 HAB hab;
466 HMQ hmq;
467 DIRSIZE *dirsize = (DIRSIZE *)args;
468 HWND hwndCnr;
469 ULONGLONG ull;
470
471 if (!dirsize) {
472 Runtime_Error(pszSrcFile, __LINE__, NULL);
473 return;
474 }
475# ifdef FORTIFY
476 Fortify_EnterScope();
477 Fortify_BecomeOwner(dirsize); // We free dirsize
478# endif
479
480 hwndCnr = dirsize->hwndCnr;
481
482 DosError(FERR_DISABLEHARDERR);
483
484 // priority_normal();
485 hab = WinInitialize(0);
486 if (hab) {
487 hmq = WinCreateMsgQueue(hab, 0);
488 if (hmq) {
489 WinCancelShutdown(hmq, TRUE);
490 ProcessDir(hwndCnr, dirsize->pszFileName,
491 (PCNRITEM) NULL, dirsize->pchStopFlag, TRUE, &ull);
492 DosPostEventSem(CompactSem);
493 WinEnableWindowUpdate(hwndCnr, FALSE);
494 FillInRecSizes(hwndCnr, NULL, ull, dirsize->pchStopFlag, TRUE);
495 WinEnableWindowUpdate(hwndCnr, TRUE);
496 WinSendMsg(hwndCnr, CM_INVALIDATERECORD, MPVOID,
497 MPFROM2SHORT(0, CMA_ERASE | CMA_TEXTCHANGED));
498 WinDestroyMsgQueue(hmq);
499 }
500 WinTerminate(hab);
501 }
502
503 xfree(dirsize, pszSrcFile, __LINE__);
504 PostMsg(WinQueryWindow(hwndCnr, QW_PARENT),
505 UM_CONTAINER_FILLED, MPVOID, MPVOID);
506# ifdef FORTIFY
507 Fortify_LeaveScope();
508# endif
509}
510
511static VOID ExpandCnrThread(VOID *args)
512{
513 HAB hab;
514 HMQ hmq;
515 INT x = 0;
516 EXPANDSIZE *expandsize = (EXPANDSIZE *)args;
517
518 if (!expandsize) {
519 Runtime_Error(pszSrcFile, __LINE__, NULL);
520 return;
521 }
522# ifdef FORTIFY
523 Fortify_EnterScope();
524 Fortify_BecomeOwner(expandsize); // We free dirsize
525# endif
526
527 DosError(FERR_DISABLEHARDERR);
528
529 priority_idle();
530 hab = WinInitialize(0);
531 if (hab) {
532 hmq = WinCreateMsgQueue(hab, 0);
533 if (hmq) {
534 WinCancelShutdown(hmq, TRUE);
535 ExpandAll(expandsize->hwndCnr, x, expandsize->pci);
536 WinDestroyMsgQueue(hmq);
537 }
538 WinTerminate(hab);
539 }
540
541 xfree(expandsize, pszSrcFile, __LINE__);
542# ifdef FORTIFY
543 Fortify_LeaveScope();
544# endif
545}
546
547MRESULT EXPENTRY DirSizeProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
548{
549 tState *pState;
550 PCNRITEM pci;
551 CHAR szBytes[44];
552 CHAR sz[66];
553
554 switch (msg) {
555 case WM_INITDLG:
556 if (!mp2) {
557 WinDismissDlg(hwnd, 0);
558 break;
559 }
560# ifdef FORTIFY
561 Fortify_EnterScope();
562# endif
563 pState = xmallocz(sizeof(tState), pszSrcFile, __LINE__);
564 if (!pState) {
565 WinDismissDlg(hwnd, 0);
566 break;
567 }
568 strcpy(pState->szDirName, (CHAR *)mp2);
569 WinSetWindowPtr(hwnd, QWL_USER, (PVOID) pState);
570 pState->hptr = WinLoadPointer(HWND_DESKTOP, FM3ModHandle, DIRSIZE_ICON);
571 WinDefDlgProc(hwnd, WM_SETICON, MPFROMLONG(pState->hptr), MPVOID);
572 {
573 CHAR s[CCHMAXPATH + 81];
574 RestorePresParams(hwnd, PCSZ_DIRSIZES);
575 sprintf(s, GetPString(IDS_DIRSIZETITLETEXT), pState->szDirName);
576 WinSetWindowText(hwnd, s);
577 }
578 {
579 SWP swp;
580 ULONG size = sizeof(SWP);
581
582 PrfQueryProfileData(fmprof, FM3Str, "DirSizes.Position", (PVOID) &swp, &size);
583 swp.fl &= ~SWP_SIZE; // 04 Feb 09 SHL ignore saved size
584 WinSetWindowPos(hwnd,
585 HWND_TOP,
586 swp.x,
587 swp.y,
588 swp.cx,
589 swp.cy,
590 swp.fl);
591 }
592 {
593 DIRSIZE *dirsize;
594
595 dirsize = xmalloc(sizeof(DIRSIZE), pszSrcFile, __LINE__);
596 if (!dirsize) {
597 WinDismissDlg(hwnd, 0);
598 break;
599 }
600 dirsize->pchStopFlag = (CHAR *)&pState->chStopFlag;
601 dirsize->pszFileName = pState->szDirName;
602 dirsize->hwndCnr = WinWindowFromID(hwnd, DSZ_CNR);
603 if (xbeginthread(FillCnrThread,
604 122880 * 5,
605 dirsize,
606 pszSrcFile,
607 __LINE__) == -1)
608 {
609 xfree(dirsize, pszSrcFile, __LINE__);
610 WinDismissDlg(hwnd, 0);
611 break;
612 }
613 pState->working = TRUE;
614 WinEnableWindow(WinWindowFromID(hwnd, DSZ_COLLAPSE), FALSE);
615 WinEnableWindow(WinWindowFromID(hwnd, DSZ_EXPAND), FALSE);
616 WinEnableWindow(WinWindowFromID(hwnd, DSZ_PRINT), FALSE);
617 }
618 PostMsg(hwnd, UM_SETUP, MPVOID, MPVOID);
619 break;
620
621 case UM_SETUP:
622 {
623 CNRINFO cnri;
624 FSALLOCATE fsa;
625 APIRET rc;
626
627 memset(&cnri, 0, sizeof(CNRINFO));
628 cnri.cb = sizeof(CNRINFO);
629 WinSendDlgItemMsg(hwnd, DSZ_CNR, CM_QUERYCNRINFO,
630 MPFROMP(&cnri), MPFROMLONG(sizeof(CNRINFO)));
631 cnri.cyLineSpacing = 0;
632 cnri.cxTreeIndent = 12;
633 cnri.flWindowAttr = CV_TREE | CV_FLOW | CA_TREELINE | CA_OWNERDRAW;
634 WinSendDlgItemMsg(hwnd, DSZ_CNR, CM_SETCNRINFO, MPFROMP(&cnri),
635 MPFROMLONG(CMA_FLWINDOWATTR | CMA_TREEICON |
636 CMA_LINESPACING | CMA_CXTREEINDENT));
637 pState = INSTDATA(hwnd);
638 if (pState && isalpha(*pState->szDirName)) {
639 memset(&fsa, 0, sizeof(fsa));
640 rc =
641 DosQueryFSInfo(toupper(*pState->szDirName) - '@', FSIL_ALLOC, &fsa,
642 sizeof(FSALLOCATE));
643 if (!rc) {
644
645 CHAR s[132], tf[80], tb[80], tu[80];
646
647 CommaFmtULL(tf, sizeof(tf),
648 (ULONGLONG) fsa.cUnitAvail *
649 (fsa.cSectorUnit * fsa.cbSector), 'M');
650 CommaFmtULL(tb, sizeof(tb),
651 (ULONGLONG) fsa.cUnit *
652 (fsa.cSectorUnit * fsa.cbSector), 'M');
653 CommaFmtULL(tu, sizeof(tu),
654 (ULONGLONG) (fsa.cUnit - fsa.cUnitAvail) *
655 (fsa.cSectorUnit * fsa.cbSector), 'M');
656 sprintf(s, GetPString(IDS_FREESPACETEXT), tf, tb, tu);
657 WinSetDlgItemText(hwnd, DSZ_FREESPACE, s);
658 }
659 else
660 WinSetDlgItemText(hwnd, DSZ_FREESPACE, (CHAR *) GetPString(IDS_FREESPACEUTEXT));
661 }
662 }
663 return 0;
664
665 case UM_CONTAINER_FILLED:
666 pState = INSTDATA(hwnd);
667 if (!pState || pState->dying) {
668 if (pState)
669 pState->working = FALSE;
670 WinDismissDlg(hwnd, 0);
671 return 0;
672 }
673 pState->working = FALSE;
674 WinEnableWindow(WinWindowFromID(hwnd, DSZ_COLLAPSE), TRUE);
675 WinEnableWindow(WinWindowFromID(hwnd, DSZ_EXPAND), TRUE);
676 WinEnableWindow(WinWindowFromID(hwnd, DSZ_PRINT), TRUE);
677
678 pci = WinSendDlgItemMsg(hwnd, DSZ_CNR, CM_QUERYRECORD, MPVOID,
679 MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
680 if (pci && (INT) pci != -1)
681 WinSendDlgItemMsg(hwnd, DSZ_CNR, CM_EXPANDTREE, MPFROMP(pci), MPVOID);
682 *sz = 0;
683 pci = WinSendDlgItemMsg(hwnd, DSZ_CNR, CM_QUERYRECORDEMPHASIS,
684 MPFROMLONG(CMA_FIRST), MPFROMSHORT(CRA_CURSORED));
685 if (pci && (INT) pci != -1) {
686 commafmt(szBytes, sizeof(szBytes), pci->attrFile);
687 sprintf(sz,
688 "%s %s%s",
689 szBytes, GetPString(IDS_FILETEXT), &"s"[pci->attrFile == 1]);
690 }
691 WinSetDlgItemText(hwnd, DSZ_NUMFILES, sz);
692
693 WinSendDlgItemMsg(hwnd, DSZ_CNR, CM_SORTRECORD, MPFROMP(SortSizeCnr),
694 MPVOID);
695 if (!fAlertBeepOff)
696 DosBeep(500, 25); // Wake up user
697 return 0;
698
699 case WM_ADJUSTWINDOWPOS:
700 PostMsg(hwnd, UM_STRETCH, MPVOID, MPVOID);
701 break;
702
703 case UM_STRETCH:
704 {
705 SWP swpC, swp;
706
707 WinQueryWindowPos(hwnd, &swp);
708 if (!(swp.fl & (SWP_HIDE | SWP_MINIMIZE))) {
709 WinQueryWindowPos(WinWindowFromID(hwnd, DSZ_CNR), &swpC);
710 WinSetWindowPos(WinWindowFromID(hwnd, DSZ_CNR), HWND_TOP,
711 SysVal(SV_CXSIZEBORDER),
712 swpC.y,
713 swp.cx - (SysVal(SV_CXSIZEBORDER) * 2),
714 (swp.cy - swpC.y) - (SysVal(SV_CYTITLEBAR) +
715 SysVal(SV_CYSIZEBORDER)),
716 SWP_MOVE | SWP_SIZE);
717 }
718 }
719 return 0;
720
721 case WM_PRESPARAMCHANGED:
722 PresParamChanged(hwnd, PCSZ_DIRSIZES, mp1, mp2);
723 break;
724
725 case WM_DRAWITEM:
726 if (mp2) {
727
728 OWNERITEM *oi = mp2;
729 CNRDRAWITEMINFO *cnd;
730 PCNRITEM pci;
731
732 if (oi->idItem == CMA_TEXT) {
733
734 cnd = (CNRDRAWITEMINFO *)oi->hItem;
735
736 if (cnd) {
737 pci = (PCNRITEM)cnd->pRecord;
738
739 if (pci) {
740 POINTL aptl[TXTBOX_COUNT];
741 POINTL ptl;
742 PSZ p;
743 LONG clr;
744 LONG x;
745 LONG yBottom;
746 INT boxHeight;
747 p = strchr(pci->pszDisplayName, '\r');
748 if (p) {
749 // draw text
750 if (pci->pszLongName == NullStr) // is root record
751 GpiSetColor(oi->hps, CLR_DARKRED);
752 else if (!pci->cbFile) // no size
753 GpiSetColor(oi->hps, CLR_DARKGRAY);
754 else if (!pci->easize) // no size below
755 GpiSetColor(oi->hps, CLR_DARKBLUE);
756 else
757 GpiSetColor(oi->hps, CLR_BLACK);
758 GpiSetBackMix(oi->hps, BM_LEAVEALONE);
759 GpiSetMix(oi->hps, FM_OVERPAINT);
760
761 *p = 0; // Make 1 line high
762
763 // Calculate nominal graph box height based on font size
764 GpiQueryTextBox(oi->hps, p - pci->pszDisplayName,
765 pci->pszDisplayName, TXTBOX_COUNT, aptl);
766 boxHeight = aptl[TXTBOX_TOPRIGHT].y - aptl[TXTBOX_BOTTOMRIGHT].y;
767 boxHeight -= 4;
768
769 // Calculate nominal baseline of graph box
770 // rclItem.yBottom is at center of icon because it is
771 yBottom = oi->rclItem.yBottom - boxHeight + 3;
772
773 // Place text above graph box with a bit of whitespace between
774 ptl.x = oi->rclItem.xLeft;
775 ptl.y = yBottom + boxHeight + 6;
776 GpiCharStringAt(oi->hps, &ptl, p - pci->pszDisplayName,
777 pci->pszDisplayName);
778
779 *p = '\r'; // Restore
780
781 // draw the graph box
782 // draw black outline
783 GpiSetColor(oi->hps, CLR_BLACK);
784 ptl.x = oi->rclItem.xLeft;
785 ptl.y = yBottom + 2;
786 GpiMove(oi->hps, &ptl);
787 ptl.x = oi->rclItem.xLeft + 201;
788 ptl.y = yBottom + boxHeight;
789 GpiBox(oi->hps, DRO_OUTLINE, &ptl, 0, 0);
790 // fill with gray
791 GpiSetColor(oi->hps, CLR_PALEGRAY);
792 ptl.x = oi->rclItem.xLeft + 1;
793 ptl.y = yBottom + 3;
794 GpiMove(oi->hps, &ptl);
795 ptl.x = oi->rclItem.xLeft + 200;
796 ptl.y = yBottom + boxHeight - 1;
797 GpiBox(oi->hps, DRO_OUTLINEFILL, &ptl, 0, 0);
798
799 // draw shadow at bottom & right sides
800 GpiSetColor(oi->hps, CLR_DARKGRAY);
801 ptl.x = oi->rclItem.xLeft + 1;
802 ptl.y = yBottom + 3;
803 GpiMove(oi->hps, &ptl);
804 ptl.x = oi->rclItem.xLeft + 200;
805 GpiLine(oi->hps, &ptl);
806 ptl.y = yBottom + boxHeight - 1;
807 GpiLine(oi->hps, &ptl);
808
809 // draw highlight at top and left sides
810 GpiSetColor(oi->hps, CLR_WHITE);
811 ptl.x = oi->rclItem.xLeft + 1;
812 GpiLine(oi->hps, &ptl);
813 ptl.y = yBottom + 3;
814 GpiLine(oi->hps, &ptl);
815
816 // draw shadow of box
817 GpiSetColor(oi->hps, CLR_DARKGRAY);
818 ptl.x = oi->rclItem.xLeft + 2;
819 ptl.y = yBottom + boxHeight;
820 GpiMove(oi->hps, &ptl);
821 ptl.x = oi->rclItem.xLeft + 201;
822 GpiLine(oi->hps, &ptl);
823 ptl.y = yBottom + boxHeight - 2;
824 GpiLine(oi->hps, &ptl);
825 ptl.x--;
826 GpiMove(oi->hps, &ptl);
827 ptl.y = yBottom + 1;
828 GpiLine(oi->hps, &ptl);
829 ptl.x = oi->rclItem.xLeft + 2;
830 GpiLine(oi->hps, &ptl);
831
832 // fill box with graph bar, flags is integer %
833 if (pci->flags) {
834 if (pci->pszLongName == NullStr) // is root record
835 GpiSetColor(oi->hps, CLR_DARKGREEN);
836 else
837 GpiSetColor(oi->hps, CLR_RED);
838 ptl.x = oi->rclItem.xLeft + 2;
839 ptl.y = yBottom + 3;
840 GpiMove(oi->hps, &ptl);
841 ptl.x = oi->rclItem.xLeft + pci->flags * 2;
842 ptl.y = yBottom + boxHeight - 1;
843 GpiBox(oi->hps, DRO_OUTLINEFILL, &ptl, 0, 0);
844
845 // draw highlights and shadows on graph
846 if (pci->pszLongName == NullStr)
847 GpiSetColor(oi->hps, CLR_GREEN);
848 else
849 GpiSetColor(oi->hps, CLR_PALEGRAY);
850 if (pci->flags > 5) {
851 ptl.x = oi->rclItem.xLeft + 1;
852 ptl.y = yBottom + 3;
853 GpiMove(oi->hps, &ptl);
854 ptl.y = yBottom + boxHeight - 1;
855 GpiLine(oi->hps, &ptl);
856 }
857 else {
858 ptl.y = yBottom + boxHeight - 1;
859 GpiMove(oi->hps, &ptl);
860 }
861 ptl.x = oi->rclItem.xLeft + pci->flags * 2;
862 GpiLine(oi->hps, &ptl);
863 if (pci->pszLongName == NULL) {
864 GpiSetColor(oi->hps, CLR_DARKRED);
865 ptl.x = oi->rclItem.xLeft + 2;
866 ptl.y = yBottom + 3;
867 GpiMove(oi->hps, &ptl);
868 ptl.x = oi->rclItem.xLeft + pci->flags * 2;
869 GpiLine(oi->hps, &ptl);
870 }
871 }
872
873 // draw hash marks in box
874 GpiSetColor(oi->hps, CLR_WHITE);
875 clr = CLR_WHITE;
876 for (x = 1; x < 20; x++) {
877 if (clr == CLR_WHITE && x * 10 > pci->flags * 2) {
878 clr = CLR_BLACK;
879 GpiSetColor(oi->hps, CLR_BLACK);
880 }
881 ptl.x = oi->rclItem.xLeft + 1 + x * 10;
882 ptl.y = yBottom + boxHeight - 1;
883 GpiMove(oi->hps, &ptl);
884 switch (x) {
885 case 1:
886 case 3:
887 case 5:
888 case 7:
889 case 9:
890 case 11:
891 case 13:
892 case 15:
893 case 17:
894 case 19:
895 ptl.y -= 1;
896 break;
897 case 10:
898 ptl.y -= 4;
899 break;
900 case 2:
901 case 4:
902 case 6:
903 case 8:
904 case 12:
905 case 14:
906 case 16:
907 case 18:
908 ptl.y -= 2;
909 break;
910 }
911 GpiLine(oi->hps, &ptl);
912 } // for x
913 return MRFROMLONG(TRUE);
914 }
915 }
916 }
917 }
918 }
919 return FALSE; // Let PM draw
920
921 case WM_CONTROL:
922 switch (SHORT2FROMMP(mp1)) {
923 case CN_ENTER:
924 if (mp2) {
925 PCNRITEM pci = (PCNRITEM)((PNOTIFYRECORDENTER)mp2)->pRecord;
926 CHAR szFileName[CCHMAXPATH]; // 23 Jul 07 SHL
927 CHAR szTemp[CCHMAXPATH];
928
929 if (pci) {
930 *szFileName = 0;
931 while (pci && (INT) pci != -1) {
932 memset(szTemp, 0, sizeof(szTemp));
933 strcpy(szTemp, pci->pszFileName);
934 strrev(szTemp);
935 AddBackslashToPath(szFileName);
936 strcat(szFileName, szTemp);
937 pci = WinSendDlgItemMsg(hwnd, DSZ_CNR, CM_QUERYRECORD,
938 MPFROMP(pci),
939 MPFROM2SHORT(CMA_PARENT, CMA_ITEMORDER));
940 }
941 strrev(szFileName);
942 if (!fVTreeOpensWPS)
943 OpenDirCnr((HWND)0,
944 hwndMain ? hwndMain : HWND_DESKTOP,
945 hwnd,
946 FALSE,
947 szFileName);
948 else {
949
950 ULONG size = sizeof(ULONG);
951 ULONG flWindowAttr = CV_ICON;
952 CHAR s[33];
953
954 strcpy(s, PCSZ_ICON);
955 PrfQueryProfileData(fmprof, appname, "DirflWindowAttr",
956 (PVOID) &flWindowAttr, &size);
957 if (flWindowAttr & CV_DETAIL) {
958 if (IsRoot(szFileName))
959 strcpy(s, PCSZ_TREE);
960 else
961 strcpy(s, Details);
962 }
963 OpenObject(szFileName, s, hwnd);
964 }
965 }
966 }
967 break;
968 case CN_EMPHASIS:
969 pState = INSTDATA(hwnd);
970 if (pState && !pState->working && mp2) {
971
972 PNOTIFYRECORDEMPHASIS pre = mp2;
973
974 pci = (PCNRITEM) ((pre) ? pre->pRecord : NULL);
975 if (pci && (pre->fEmphasisMask & CRA_SELECTED) &&
976 (pci->rc.flRecordAttr & CRA_SELECTED)) {
977 commafmt(szBytes, sizeof(szBytes), pci->attrFile);
978 sprintf(sz,
979 "%s %s%s",
980 szBytes,
981 GetPString(IDS_FILETEXT), &"s"[pci->attrFile == 1]);
982 WinSetDlgItemText(hwnd, DSZ_NUMFILES, sz);
983 }
984 }
985 break;
986 }
987 return 0;
988
989 case WM_COMMAND:
990 switch (SHORT1FROMMP(mp1)) {
991 case IDM_HELP:
992 if (hwndHelp)
993 WinSendMsg(hwndHelp, HM_DISPLAY_HELP,
994 MPFROM2SHORT(HELP_DIRSIZE, 0), MPFROMSHORT(HM_RESOURCEID));
995 break;
996
997 case DSZ_PRINT:
998 // Save button
999 pState = INSTDATA(hwnd);
1000 if (!pState)
1001 Runtime_Error(pszSrcFile, __LINE__, NULL);
1002 else {
1003
1004 CHAR szFileName[CCHMAXPATH];
1005 FILE *fp;
1006 CHAR *modea = "a+";
1007
1008 if (pTmpDir && !IsValidDir(pTmpDir))
1009 DosCreateDir(pTmpDir, 0);
1010 //if (pTmpDir)
1011 // strcpy(szFileName, pTmpDir);
1012 else if (!pTmpDir)
1013 strcpy(szFileName, pFM2SaveDirectory);
1014 sprintf(&szFileName[strlen(szFileName)], "%s%csizes.Rpt", PCSZ_BACKSLASH,
1015 (pState) ? toupper(*pState->szDirName) : '+');
1016 if (export_filename(hwnd, szFileName, FALSE) && *szFileName) {
1017 if (stricmp(szFileName, "PRN") &&
1018 strnicmp(szFileName, "\\DEV\\LPT", 8) &&
1019 !strchr(szFileName, '.'))
1020 strcat(szFileName, ".RPT");
1021 fp = xfopen(szFileName, modea, pszSrcFile, __LINE__, TRUE);
1022 if (!fp) {
1023 saymsg(MB_CANCEL,
1024 hwnd,
1025 GetPString(IDS_ERRORTEXT),
1026 GetPString(IDS_COMPCANTOPENTEXT), szFileName);
1027 }
1028 else {
1029 WinSetPointer(HWND_DESKTOP, hptrBusy);
1030 PrintToFile(WinWindowFromID(hwnd, DSZ_CNR), 0, NULL, fp);
1031 fclose(fp);
1032 WinSetPointer(HWND_DESKTOP, hptrArrow);
1033 }
1034 }
1035 }
1036 break;
1037
1038 case DSZ_EXPAND:
1039 case DSZ_COLLAPSE:
1040 pState = INSTDATA(hwnd);
1041 if (pState) {
1042 pci = (PCNRITEM) WinSendDlgItemMsg(hwnd, DSZ_CNR,
1043 CM_QUERYRECORDEMPHASIS,
1044 MPFROMLONG(CMA_FIRST),
1045 MPFROMSHORT(CRA_CURSORED));
1046 if (pci) {
1047 WinEnableWindow(WinWindowFromID(hwnd, DID_OK), FALSE);
1048 WinEnableWindow(WinWindowFromID(hwnd, IDM_HELP), FALSE);
1049 WinEnableWindow(WinWindowFromID(hwnd, DSZ_COLLAPSE), FALSE);
1050 WinEnableWindow(WinWindowFromID(hwnd, DSZ_EXPAND), FALSE);
1051 WinEnableWindow(WinWindowFromID(hwnd, DSZ_PRINT), FALSE);
1052 WinEnableWindow(WinWindowFromID(hwnd, DID_CANCEL), FALSE);
1053 if (SHORT1FROMMP(mp1) == DSZ_EXPAND) {
1054 EXPANDSIZE *expandsize;
1055
1056 expandsize = xmalloc(sizeof(EXPANDSIZE), pszSrcFile, __LINE__);
1057 if (expandsize) {
1058
1059 expandsize->pci = pci;
1060 expandsize->hwndCnr = WinWindowFromID(hwnd, DSZ_CNR);
1061 if (xbeginthread(ExpandCnrThread,
1062 122880 * 5,
1063 expandsize,
1064 pszSrcFile,
1065 __LINE__) == -1)
1066 xfree(expandsize, pszSrcFile, __LINE__);
1067 }
1068 }
1069 else
1070 CollapseAll(WinWindowFromID(hwnd, DSZ_CNR), pci);
1071 WinEnableWindow(WinWindowFromID(hwnd, DID_OK), TRUE);
1072 WinEnableWindow(WinWindowFromID(hwnd, IDM_HELP), TRUE);
1073 WinEnableWindow(WinWindowFromID(hwnd, DSZ_COLLAPSE), TRUE);
1074 WinEnableWindow(WinWindowFromID(hwnd, DSZ_EXPAND), TRUE);
1075 WinEnableWindow(WinWindowFromID(hwnd, DSZ_PRINT), TRUE);
1076 WinEnableWindow(WinWindowFromID(hwnd, DID_CANCEL), TRUE);
1077 }
1078 }
1079 break;
1080
1081 case DID_OK:
1082 case DID_CANCEL:
1083 {
1084 SWP swp;
1085 ULONG size = sizeof(SWP);
1086
1087 WinQueryWindowPos(hwnd, &swp);
1088 PrfWriteProfileData(fmprof, FM3Str, "DirSizes.Position", (PVOID) &swp, size);
1089 }
1090 pState = INSTDATA(hwnd);
1091 if (!pState)
1092 Runtime_Error(pszSrcFile, __LINE__, NULL);
1093 else {
1094 if (pState->working) {
1095 pState->dying = TRUE;
1096 pState->chStopFlag = (CHAR)0xff;
1097 if (!fAlertBeepOff)
1098 DosBeep(1000, 100); // Complain?
1099 }
1100 else
1101 WinDismissDlg(hwnd, 0);
1102 }
1103 break;
1104 } // switch mp1
1105 return 0;
1106
1107 case WM_CLOSE:
1108 pState = INSTDATA(hwnd);
1109 if (pState)
1110 pState->chStopFlag = (CHAR)0xff;
1111 DosSleep(1);
1112 break;
1113
1114 case WM_DESTROY:
1115 pState = INSTDATA(hwnd);
1116 EmptyCnr(hwnd);
1117 if (pState) {
1118 pState->chStopFlag = (CHAR)0xff;
1119 if (pState->hptr)
1120 WinDestroyPointer(pState->hptr);
1121 DosSleep(16);
1122 xfree(pState, pszSrcFile, __LINE__); // Let's hope no one is still looking
1123# ifdef FORTIFY
1124 Fortify_LeaveScope();
1125# endif
1126 }
1127 DosPostEventSem(CompactSem);
1128 break;
1129 }
1130 return WinDefDlgProc(hwnd, msg, mp1, mp2);
1131}
1132
1133#pragma alloc_text(DIRSIZE,ProcessDir,FillCnrThread,DirSizeProc)
1134#pragma alloc_text(DIRSIZE2,PrintToFile,FillInRecSizes,SortSizeCnr)
1135
Note: See TracBrowser for help on using the repository browser.