source: trunk/dll/worker.c@ 1739

Last change on this file since 1739 was 1739, checked in by Gregg Young, 12 years ago

Cleanup of readonly check code suppress spurious error on blocked directory delete and eliminated the check on additional temp file deletes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.8 KB
Line 
1
2/***********************************************************************
3
4 $Id: worker.c 1739 2014-02-22 21:06:09Z gyoung $
5
6 Worker thread
7
8 Copyright (c) 1993-98 M. Kimes
9 Copyright (c) 2001, 2014 Steven H. Levine
10
11 16 Oct 02 SHL Comments
12 18 Oct 02 SHL MassAction:Archive - force extension so file found
13 06 Jun 05 SHL Indent -i2
14 06 Jun 05 SHL Rework Action for VAC3.65 compat
15 27 Jul 05 SHL IDM_DOITYOURSELF - avoid need to strip in ExecOnList
16 22 Jul 06 SHL Comments
17 22 Jul 06 SHL Check more run time errors
18 03 Nov 06 SHL Renames
19 03 Nov 06 SHL Count thread usage
20 21 Apr 07 GKY Find FM2Utils by path or utils directory
21 16 Jun 07 SHL Update for OpenWatcom
22 06 Aug 07 GKY Reduce DosSleep times (ticket 148)
23 07 Aug 07 SHL Use BldQuotedFileName
24 20 Aug 07 GKY Move #pragma alloc_text to end for OpenWatcom compat
25 26 Aug 07 SHL Revert to DosSleep(0)
26 29 Feb 08 GKY Refactor global command line variables to notebook.h
27 22 Jun 08 GKY Made Felete move to xworkplace trash can on systems that have it
28 16 JUL 08 GKY Use TMP directory for temp files
29 20 Jul 08 GKY Add save/append filename to clipboard.
30 02 Aug 08 GKY Limit use of "trash can" to local writable fixed drives or to trash can supported
31 drives list if it exists. Fix ability to deselect use of trash can.
32 01 Sep 08 GKY Add code to retry on Netdrives "pipe error"
33 04 Dec 08 GKY Add a DosSleep to allow file extract to complete before rescan
34 04 Dec 08 GKY Add mutex semaphore and disable fSyncUpdates for file deletes to prevent the creation
35 on dead CNRITEMS.
36 10 Dec 08 SHL Integrate exception handler support
37 25 Dec 08 GKY Add code to allow write verify to be turned off on a per drive basis
38 25 Dec 08 GKY Add DRIVE_RSCANNED flag to monitor for the first recursive drive scan per session
39 to prevent duplicate directory names in tree following a copy before initial scan.
40 07 Feb 09 GKY Allow user to turn off alert and/or error beeps in settings notebook.
41 08 Mar 09 GKY Removed variable aurguments from docopyf and unlinkf (not used)
42 08 Mar 09 GKY Additional strings move to PCSZs
43 28 Jun 09 GKY Added AddBackslashToPath() to remove repeatative code.
44 26 Jul 09 GKY Fix failure of containers to update when Tree container isn't open in FM2 lite
45 13 Dec 09 GKY Attempt to fix container update issues with FM/2 lite
46 17 JAN 10 GKY Changes to get working with Watcom 1.9 Beta (1/16/10).
47 Mostly cast CHAR CONSTANT * as CHAR *.
48 20 Nov 10 GKY Check that pTmpDir IsValid and recreate if not found; Fixes hangs caused
49 by temp file creation failures.
50 12 Nov 11 GKY Fixed extract failure caused by spaces in the arc file name.
51 04 Aug 12 GKY Changes to use Unlock to unlock files if Unlock.exe is in path both from menu/toolbar and as part of
52 copy, move and delete operations
53 04 Aug 12 GKY Changes to allow copy and move over readonly files with a warning dialog; also added a warning dialog
54 for delete of readonly files
55 10 Mar 13 GKY Improvrd readonly check on delete to allow cancel and don't ask again options
56 Added saymsg2 for this purpose
57 09 Feb 14 GKY Modified wipeallf to allow suppression of the readonly warning on delete
58 of temporary files
59 16 Feb 14 GKY Fixed move to trashcan to work when the trashcan::drives key hadn't been
60 written to OS2.INI. INI check is now only if fTrashCan is TRUE.
61 16 Feb 14 GKY Rework readonly check on delete code so it actually works in a logical way
62 and so it works with move to trashcan inabled.
63 22 Feb 14 GKY Cleanup of readonly check code suppress spurious error on blocked directory
64 delete and eliminated the check on additional temp file deletes
65
66***********************************************************************/
67
68#include <stdlib.h>
69#include <string.h>
70#include <ctype.h>
71#include <share.h>
72#include <process.h> // _beginthread // 10 Dec 08 SHL
73#include <time.h>
74
75#define INCL_DOS
76#define INCL_DOSERRORS
77// #define INCL_WINPROGRAMLIST // 13 Jul 09 SHL dropped
78// #define INCL_WINHELP // 13 Jul 09 SHL dropped
79#define INCL_LONGLONG
80#define INCL_WINPOINTERS // WinSetFileIcon
81// #define INCL_WINWORKPLACE // 13 Jul 09 SHL dropped
82#define INCL_WINSHELLDATA // PrfQueryProfileData
83
84#include "fm3dll.h"
85#include "fm3dll2.h" // #define's for UM_*, control id's, etc.
86#include "mainwnd2.h" // Data declaration(s)
87#include "arccnrs.h" // Data declaration(s)
88#include "init.h" // Data declaration(s)
89#include "defview.h" // Data declaration(s)
90#include "newview.h" // Data declarations
91#include "fm3dlg.h"
92#include "fm3str.h"
93#include "comp.h" // FCOMPARE
94#include "pathutil.h" // BldQuotedFileName
95#include "makelist.h" // AddToList
96#include "errutil.h" // Dos_Error...
97#include "strutil.h" // GetPString
98#include "notebook.h" // External viewers
99#include "worker.h" // Action
100#include "notify.h" // AddNote
101#include "copyf.h" // AdjustWildcardName, make_deleteable
102#include "attribs.h" // AttrListDlgProc
103#include "chklist.h" // CheckListProc
104#include "info.h" // DrvInfoProc
105#include "extract.h" // ExtractDlgProc
106#include "info.h" // FileInfoProc
107#include "valid.h" // GetDesktopName, IsNewer
108#include "saveclip.h" // ListToClipboardHab
109#include "shadow.h" // MakeShadows
110#include "mkdir.h" // MassMkdir
111#include "uudecode.h" // MergeDlgProc
112#include "objcnr.h" // ObjCnrDlgProc
113#include "printer.h" // PrintDlgProc, PrintListThread
114#include "rename.h" // RenameProc
115#include "srchpath.h" // RunFM2Util
116#include "mainwnd.h" // TopWindowName
117#include "uudecode.h" // UUD
118#include "walkem.h" // WalkCopyDlgProc, WalkDlgProc, WalkMoveDlgProc
119#include "archive.h" // ArchiveDlgProc
120#include "misc.h" // Broadcast
121#include "common.h" // DecrThreadUsage, IncrThreadUsage
122#include "eas.h" // DisplayEAsProc
123#include "systemf.h" // ExecOnList
124#include "avl.h" // SBoxDlgProc
125#include "subj.h" // Subject
126#include "stristr.h" // stristr
127#include "wrappers.h" // xfopen
128#include "fortify.h"
129#include "excputil.h" // 06 May 08 SHL added
130
131// Data definitions
132#pragma data_seg(GLOBAL2)
133FILE *LogFileHandle;
134BOOL fUnlock;
135
136#pragma data_seg(DATA2)
137
138static PSZ pszSrcFile = __FILE__;
139
140#ifdef UNDO
141
142static VOID LINFO undo;
143
144VOID FreeUndo(VOID)
145{
146 if (undo->list)
147 FreeList(undo->list);
148 memset(&undo, 0, sizeof(undo));
149}
150
151VOID Undo(HWND hwndCnr, HWND hwndFrame, HWND hwndClient, HWND hwndParent)
152{
153 LISTINFO *li;
154 WORKER *wk;
155
156 if (undo->type && undo->list && undo->list[0]) {
157 switch (undo->type) {
158 case IDM_MOVE:
159 case IDM_COPY:
160 case IDM_EXTRACT:
161 {
162# ifdef FORTIFY
163 Fortify_EnterScope();
164# endif
165 li = xmallocz(sizeof(LISTINFO), pszSrcFile, __LINE__);
166 if (li) {
167 wk = xmallocz(sizeof(WORKER), pszSrcFile, __LINE__);
168 if (wk) {
169 wk->size = sizeof(WORKER);
170 wk->hwndCnr = hwndCnr;
171 wk->hwndParent = hwndParent;
172 wk->hwndFrame = hwndFrame;
173 wk->hwndClient = hwndClient;
174 wk->li = li;
175 *wk->li = *undo;
176 switch (undo->type) {
177 case IDM_COPY:
178 case IDM_EXTRACT:
179 li->type = IDM_PERMDELETE;
180 break;
181 }
182 if (xbeginthread(MassAction,
183 122880,
184 wk,
185 pszSrcFile,
186 __LINE__) == -1)
187 {
188 FreeListInfo(wk->li);
189 free(wk);
190# ifdef FORTIFY
191 Fortify_LeaveScope();
192# endif
193 }
194 }
195 else
196 FreeListInfo(li);
197 }
198 }
199 break;
200 } // switch
201 }
202 FreeUndo();
203# ifdef FORTIFY
204 Fortify_LeaveScope();
205# endif
206}
207
208#endif // defined(UNDO)
209
210/**
211 * Apply action to file list
212 * Action repeated for each item in list
213 */
214
215VOID Action(VOID * args)
216{
217 WORKER *wk = (WORKER *)args;
218 HAB hab2;
219 HMQ hmq2;
220 CHAR **files = NULL;
221 INT plen = 0;
222 CHAR *p, *pp;
223 CHAR szQuotedDirName[CCHMAXPATH];
224 CHAR szQuotedFileName[CCHMAXPATH];
225
226
227 if (wk) {
228# ifdef FORTIFY
229 Fortify_EnterScope();
230# endif
231 if (wk->li && wk->li->list && wk->li->list[0]) {
232 hab2 = WinInitialize(0);
233 if (hab2) {
234 hmq2 = WinCreateMsgQueue(hab2, 0);
235 if (hmq2) {
236 CHAR message[(CCHMAXPATH * 2) + 80], wildname[CCHMAXPATH];
237 UINT x;
238 BOOL dontask = FALSE, wildcarding = FALSE, overold = FALSE;
239 BOOL overnew = FALSE, usedtarget;
240 BOOL noreadonlywarn = FALSE;
241
242 WinCancelShutdown(hmq2, TRUE);
243 IncrThreadUsage();
244 *wildname = 0;
245 // Do action specific preprocessing
246 switch (wk->li->type) {
247 case IDM_MERGE:
248 if (wk->li->type == IDM_MERGE) {
249 if (TestBinary(wk->li->list[0]))
250 wk->li->type = IDM_MERGEBINARY;
251 else
252 wk->li->type = IDM_MERGETEXT;
253 }
254 strcpy(wk->li->targetpath, wk->li->list[0]);
255 p = strrchr(wk->li->targetpath, '\\');
256 if (p) {
257 p++;
258 *p = 0;
259 }
260 else
261 strcat(wk->li->targetpath, PCSZ_BACKSLASH);
262 sprintf(wk->li->targetpath + strlen(wk->li->targetpath),
263 "MERGE.%03x", (clock() & 4095L));
264 if (!WinDlgBox(HWND_DESKTOP,
265 wk->hwndFrame,
266 MergeDlgProc, FM3ModHandle, MRG_FRAME, (PVOID) wk))
267 goto Abort;
268 if (!wk->li->type ||
269 !*wk->li->targetpath || !wk->li->list || !wk->li->list[0])
270 goto Abort;
271 if (IsFile(wk->li->targetpath) != 1 && !wk->li->list[1]) {
272 saymsg(MB_CANCEL | MB_ICONEXCLAMATION,
273 wk->hwndFrame,
274 GetPString(IDS_AHEMTEXT),
275 GetPString(IDS_SILLYMERGETEXT));
276 goto Abort;
277 }
278 break;
279 case IDM_WILDMOVE:
280 wildcarding = TRUE;
281 wk->li->type = IDM_MOVE;
282 break;
283 case IDM_WILDRENAME:
284 wildcarding = TRUE;
285 wk->li->type = IDM_RENAME;
286 break;
287 case IDM_WILDCOPY:
288 wildcarding = TRUE;
289 wk->li->type = IDM_COPY;
290 break;
291 case IDM_MOVEPRESERVE:
292 {
293 CHAR preserve[CCHMAXPATH], *end;
294
295 wk->li->type = IDM_MOVE;
296 strcpy(preserve, wk->li->list[0] + 2);
297 end = strrchr(preserve, '\\');
298 if (end) {
299 end++;
300 for (x = 1; wk->li->list[x]; x++) {
301 p = preserve;
302 pp = wk->li->list[x] + 2;
303 while (p < end && toupper(*p) == toupper(*pp)) {
304 p++;
305 pp++;
306 }
307 if (*p == '\\')
308 p++;
309 if (p < end)
310 end = p;
311 }
312 *end = 0;
313 }
314 else
315 *preserve = 0;
316 plen = strlen(preserve);
317 if (plen)
318 plen += 2;
319 }
320 break;
321 case IDM_COPYPRESERVE:
322 {
323 CHAR preserve[CCHMAXPATH], *end;
324
325 wk->li->type = IDM_COPY;
326 strcpy(preserve, wk->li->list[0] + 2);
327 end = strrchr(preserve, '\\');
328 if (end) {
329 end++;
330 for (x = 1; wk->li->list[x]; x++) {
331 p = preserve;
332 pp = wk->li->list[x] + 2;
333 while (p < end && toupper(*p) == toupper(*pp)) {
334 p++;
335 pp++;
336 }
337 if (*p == '\\')
338 p++;
339 if (p < end)
340 end = p;
341 }
342 *end = 0;
343 }
344 else
345 *preserve = 0;
346 plen = strlen(preserve);
347 if (plen)
348 plen += 2;
349 }
350 break;
351 }
352 // Process each list item
353 if (wk->li && wk->li->list && wk->li->list[0]) {
354 UINT cFilesModified = 0; // Required for AddToList
355 UINT cItemsAllocated = 0; // Required for AddToList
356 UINT cItemsInList;
357 for (cItemsInList = 0; wk->li->list[cItemsInList]; cItemsInList++); // Count
358 for (x = 0; wk->li->list[x]; x++) {
359 switch (wk->li->type) {
360 case IDM_COLLECTFROMFILE:
361 if (Collector) {
362
363 CHAR *temp = xstrdup(wk->li->list[x], pszSrcFile, __LINE__);
364
365 if (temp) {
366 if (!PostMsg(Collector,
367 UM_COLLECTFROMFILE, MPFROMP(temp), MPVOID))
368 free(temp);
369 }
370 }
371 break;
372
373 case IDM_MERGEBINARY:
374 case IDM_MERGETEXT:
375 case IDM_MERGEBINARYAPPEND:
376 case IDM_MERGETEXTAPPEND:
377 {
378 FILE *in, *out;
379 CHAR *moder, *modew;
380 int c;
381
382 switch (wk->li->type) {
383 case IDM_MERGEBINARY:
384 moder = "rb";
385 modew = "wb";
386 break;
387 case IDM_MERGEBINARYAPPEND:
388 moder = "rb";
389 modew = "a+b";
390 break;
391 case IDM_MERGETEXTAPPEND:
392 moder = "r";
393 modew = "a+";
394 break;
395 default:
396 moder = "r";
397 modew = "w";
398 break;
399 }
400 in = xfsopen(wk->li->list[x], moder, SH_DENYWR, pszSrcFile, __LINE__, TRUE);
401 if (!in) {
402 if (saymsg(MB_ENTERCANCEL,
403 HWND_DESKTOP,
404 GetPString(IDS_MERGEERRORTEXT),
405 GetPString(IDS_CANTOPENINPUTTEXT),
406 wk->li->list[x]) == MBID_CANCEL)
407 goto Abort;
408 }
409 else {
410 out = xfsopen(wk->li->targetpath, modew, SH_DENYWR,
411 pszSrcFile, __LINE__, TRUE);
412 if (out) {
413 fseek(out, 0L, SEEK_END);
414 switch (wk->li->type) {
415 case IDM_MERGEBINARY:
416 wk->li->type = IDM_MERGEBINARYAPPEND;
417 break;
418 default:
419 wk->li->type = IDM_MERGETEXTAPPEND;
420 break;
421 }
422 sprintf(message,
423 GetPString(IDS_MERGINGTEXT),
424 wk->li->list[x], wk->li->targetpath);
425 AddNote(message);
426 while ((c = fgetc(in)) != EOF)
427 fputc(c, out);
428 fclose(out);
429 sprintf(message,
430 GetPString(IDS_MERGECOMPLETETEXT),
431 wk->li->list[x], wk->li->targetpath);
432 AddNote(message);
433 }
434 else {
435 saymsg(MB_CANCEL,
436 HWND_DESKTOP,
437 GetPString(IDS_MERGEERRORTEXT),
438 GetPString(IDS_CANTOPENOUTPUTTEXT),
439 wk->li->targetpath);
440 fclose(in);
441 goto Abort;
442 }
443 fclose(in);
444 }
445 }
446 break;
447
448 case IDM_UUDECODE:
449 {
450 CHAR outname[CCHMAXPATH + 2];
451
452 sprintf(message,
453 GetPString(IDS_UUDECODINGTEXT), wk->li->list[x]);
454 AddNote(message);
455 if (UUD(wk->li->list[x], outname) && *outname) {
456 sprintf(message,
457 GetPString(IDS_UUDECODECOMPLETETEXT),
458 wk->li->list[x]);
459 AddNote(message);
460 if (//fSyncUpdates ||
461 AddToList(outname, &files, &cFilesModified, &cItemsAllocated))
462 Broadcast(hab2,
463 wk->hwndCnr,
464 UM_UPDATERECORD, MPFROMP(outname), MPVOID);
465 }
466 else {
467 sprintf(message,
468 GetPString(IDS_UUDECODEABORTEDTEXT),
469 wk->li->list[x]);
470 AddNote(message);
471 }
472 }
473 break;
474
475 case IDM_UNLOCKFILE:
476 if (IsFile(wk->li->list[x]) > 0 && fUnlock) {
477 runemf2(SEPARATE | INVISIBLE | BACKGROUND | WAIT,
478 HWND_DESKTOP, pszSrcFile, __LINE__,
479 NULL, NULL, "%s %s", PCSZ_UNLOCKEXE, wk->li->list[x]);
480 }
481 break;
482
483 case IDM_VIEWARCHIVE:
484 if (IsFile(wk->li->list[x]) > 0) {
485
486 ARC_TYPE *info = NULL; // Say calling for editing - fixme to know why?
487
488 if (WinDlgBox(HWND_DESKTOP,
489 wk->hwndFrame,
490 SBoxDlgProc,
491 FM3ModHandle,
492 ASEL_FRAME, (PVOID) & info) && info) {
493 WinSendMsg(wk->hwndCnr,
494 UM_OPENWINDOWFORME,
495 MPFROMP(wk->li->list[x]), MPFROMP(info));
496 }
497 }
498 break;
499
500 case IDM_EXTRACT:
501 {
502 EXTRDATA ex;
503 BOOL maskspaces = FALSE;
504 CHAR arcname[CCHMAXPATH];
505
506 memset(&ex, 0, sizeof(EXTRDATA));
507 ex.info = find_type(wk->li->list[x], NULL);
508 if (!ex.info || (!ex.info->extract && !ex.info->exwdirs))
509 break;
510 ex.size = sizeof(EXTRDATA);
511 BldQuotedFileName(arcname, wk->li->list[x]);
512 ex.arcname = arcname;
513 if (!*ex.masks)
514 strcpy(ex.masks, "*");
515 strcpy(ex.extractdir, wk->li->targetpath);
516 if (!WinDlgBox(HWND_DESKTOP,
517 wk->hwndFrame,
518 ExtractDlgProc,
519 FM3ModHandle,
520 EXT_FRAME,
521 (PVOID) & ex) ||
522 !ex.ret ||
523 !*ex.command || !*ex.arcname || !*ex.extractdir)
524 goto Abort;
525 {
526 FILESTATUS3 fsa;
527
528 DosError(FERR_DISABLEHARDERR);
529 if (DosQueryPathInfo(ex.extractdir,
530 FIL_STANDARD,
531 &fsa,
532 (ULONG) sizeof(FILESTATUS3)) ||
533 !(fsa.attrFile & FILE_DIRECTORY))
534 goto Abort;
535 }
536 if (needs_quoting(ex.masks) && !strchr(ex.masks, '\"'))
537 maskspaces = TRUE;
538 if (!runemf2(SEPARATE | WINDOWED | WAIT |
539 fArcStuffVisible ? 0 : (BACKGROUND | MINIMIZED),
540 HWND_DESKTOP, pszSrcFile, __LINE__, ex.extractdir, NULL,
541 "%s %s %s%s%s",
542 ex.command,
543 ex.arcname,
544 maskspaces ? "\"" : NullStr,
545 *ex.masks ? ex.masks : "\"*\"",
546 maskspaces ? "\"" : NullStr) &&
547 !stricmp(ex.extractdir, wk->directory)) {
548 DosSleep(100); // wait for runemf2 to complete so rescan will actually show something
549 if (WinIsWindow((HAB) 0, wk->hwndCnr))
550 WinSendMsg(wk->hwndCnr,
551 WM_COMMAND,
552 MPFROM2SHORT(IDM_RESCAN, 0), MPVOID);
553 }
554 }
555 break;
556
557 case IDM_SUBJECT:
558 {
559 INT ret;
560
561 ret = Subject(wk->hwndFrame, wk->li->list[x]);
562 if (!ret)
563 goto Abort;
564 if (ret == 1) {
565 if (//fSyncUpdates ||
566 AddToList(wk->li->list[x],
567 &files, &cFilesModified, &cItemsAllocated))
568 Broadcast(hab2,
569 wk->hwndCnr,
570 UM_UPDATERECORD,
571 MPFROMP(wk->li->list[x]), MPVOID);
572 }
573 }
574 break;
575
576 case IDM_INFO:
577 if (IsFullName(wk->li->list[x]) &&
578 !(driveflags[toupper(*wk->li->list[x]) - 'A'] &
579 DRIVE_INVALID)) {
580 if (!IsRoot(wk->li->list[x])) {
581
582 CHAR *list[2];
583
584 list[0] = wk->li->list[x];
585 list[1] = NULL;
586 if (!WinDlgBox(HWND_DESKTOP,
587 HWND_DESKTOP,
588 FileInfoProc,
589 FM3ModHandle, FLE_FRAME, (PVOID) list)) {
590 goto Abort;
591 }
592 }
593 else {
594 if (!WinDlgBox(HWND_DESKTOP,
595 HWND_DESKTOP,
596 DrvInfoProc,
597 FM3ModHandle,
598 INFO_FRAME, (PVOID) wk->li->list[x]))
599 goto Abort;
600 }
601 }
602 break;
603
604 case IDM_OPENWINDOW:
605 if (!IsFile(wk->li->list[x]) &&
606 WinIsWindow(hab2, wk->hwndCnr))
607 WinSendMsg(wk->hwndCnr,
608 UM_OPENWINDOWFORME,
609 MPFROMP(wk->li->list[x]), MPVOID);
610 break;
611
612 case IDM_OPENICON:
613 case IDM_OPENDETAILS:
614 case IDM_OPENTREE:
615 {
616 FILESTATUS3 fsa;
617
618 DosError(FERR_DISABLEHARDERR);
619 if (DosQueryPathInfo(wk->li->list[x],
620 FIL_STANDARD,
621 &fsa,
622 (ULONG) sizeof(FILESTATUS3)) ||
623 !(fsa.attrFile & FILE_DIRECTORY))
624 break;
625 }
626 // else intentional fallthru
627 case IDM_OPENDEFAULT:
628 case IDM_OPENSETTINGS:
629 {
630 CHAR *s;
631
632 switch (wk->li->type) {
633 case IDM_OPENICON:
634 s = (PSZ) PCSZ_ICON;
635 break;
636 case IDM_OPENDETAILS:
637 s = (PSZ) Details;
638 break;
639 case IDM_OPENTREE:
640 s = (PSZ) PCSZ_TREE;
641 break;
642 case IDM_OPENSETTINGS:
643 s = (PSZ) Settings;
644 break;
645 default:
646 s = (PSZ) Default;
647 break;
648 }
649 OpenObject(wk->li->list[x], s, wk->hwndFrame);
650 }
651 break;
652
653 case IDM_WPSMOVE:
654 case IDM_WPSCOPY:
655 case IDM_MOVE:
656 case IDM_COPY:
657 case IDM_RENAME:
658 {
659
660 // Select target
661 if (!*wk->li->targetpath && (wk->li->type == IDM_MOVE ||
662 wk->li->type == IDM_COPY ||
663 wk->li->type == IDM_WPSMOVE ||
664 wk->li->type == IDM_WPSCOPY)) {
665
666 APIRET rc = 1;
667 usedtarget = FALSE;
668 if (hwndMain) {
669 if (!*targetdir)
670 TopWindowName(hwndMain,
671 wk->hwndFrame, wk->li->targetpath);
672 else {
673 strcpy(wk->li->targetpath, targetdir);
674 usedtarget = TRUE;
675 }
676 }
677 if (!*wk->li->targetpath)
678 strcpy(wk->li->targetpath, wk->directory);
679 if (!*wk->li->targetpath) {
680 strcpy(wk->li->targetpath, wk->li->list[0]);
681 p = strrchr(wk->li->targetpath, '\\');
682 if (p) {
683 if (*(p - 1) == ':')
684 p++;
685 *p = 0;
686 }
687 }
688 MakeValidDir(wk->li->targetpath);
689 if (fConfirmTarget ||
690 (!*targetdir && strcmp(realappname, "FM/4"))) {
691 RetryPath:
692 // Confirm target
693 usedtarget = FALSE;
694 if (wk->li->type == IDM_MOVE ||
695 wk->li->type == IDM_WPSMOVE) {
696 rc = WinDlgBox(HWND_DESKTOP,
697 wk->hwndFrame,
698 WalkMoveDlgProc,
699 FM3ModHandle,
700 WALK_FRAME, MPFROMP(wk->li->targetpath));
701 }
702 else if (wk->li->type == IDM_COPY ||
703 wk->li->type == IDM_WPSCOPY) {
704 rc = WinDlgBox(HWND_DESKTOP,
705 wk->hwndFrame,
706 WalkCopyDlgProc,
707 FM3ModHandle,
708 WALK_FRAME, MPFROMP(wk->li->targetpath));
709 }
710 else
711 rc = WinDlgBox(HWND_DESKTOP,
712 wk->hwndFrame,
713 WalkDlgProc,
714 FM3ModHandle,
715 WALK_FRAME, MPFROMP(wk->li->targetpath));
716 }
717 if (!rc || !*wk->li->targetpath)
718 goto Abort;
719 // Check target OK
720 if (driveflags[toupper(*wk->li->targetpath) - 'A'] &
721 DRIVE_NOTWRITEABLE) {
722 saymsg(MB_CANCEL,
723 wk->hwndFrame,
724 GetPString(IDS_ERRORTEXT),
725 GetPString(IDS_NOTWRITENOTARGETTEXT));
726 goto RetryPath;
727 }
728 }
729 Retry:
730 {
731 // Target OK so far
732 CHAR newname[CCHMAXPATH];
733 APIRET rc;
734 INT ret;
735 FILESTATUS4L fs4;
736 BOOL isnewer;
737 BOOL existed;
738 BOOL fResetVerify = FALSE;
739 INT type = wk->li->type == IDM_RENAME ?
740 MOVE :
741 wk->li->type == IDM_MOVE ?
742 MOVE : (wk->li->type == IDM_WPSMOVE) ?
743 WPSMOVE : wk->li->type == IDM_WPSCOPY ?
744 WPSCOPY : COPY;
745 PCSZ moving = wk->li->type == IDM_RENAME ?
746 GetPString(IDS_RENAMINGTEXT) :
747 wk->li->type == IDM_MOVE || wk->li->type == IDM_WPSMOVE ?
748 GetPString(IDS_MOVINGTEXT) : GetPString(IDS_COPYINGTEXT);
749 PCSZ move = wk->li->type == IDM_RENAME ?
750 GetPString(IDS_RENAMETEXT) :
751 wk->li->type == IDM_MOVE ||
752 wk->li->type == IDM_WPSMOVE ?
753 GetPString(IDS_MOVETEXT) : GetPString(IDS_COPYTEXT);
754 PCSZ moved = wk->li->type == IDM_RENAME ?
755 GetPString(IDS_RENAMEDTEXT) :
756 wk->li->type == IDM_MOVE || wk->li->type == IDM_WPSMOVE ?
757 GetPString(IDS_MOVEDTEXT) : GetPString(IDS_COPIEDTEXT);
758
759 if (*wk->li->targetpath) {
760 strcpy(newname, wk->li->targetpath);
761 AddBackslashToPath(newname);
762 if (plen)
763 p = wk->li->list[x] + plen;
764 else {
765 p = strrchr(wk->li->list[x], '\\');
766 if (p)
767 p++;
768 else
769 p = wk->li->list[x];
770 }
771 strcat(newname, p);
772 }
773 else
774 strcpy(newname, wk->li->list[x]);
775 if ((wildcarding || wk->li->type == IDM_RENAME) &&
776 *wildname)
777 {
778 CHAR testname[CCHMAXPATH];
779 strcpy(testname, wildname);
780 if (AdjustWildcardName(newname, testname))
781 strcpy(newname, testname);
782 }
783 existed = IsFile(newname) != -1;
784 isnewer = IsNewer(wk->li->list[x], newname);
785 if (existed && wk->li->type != IDM_RENAME && dontask) {
786 if (!overold && !overnew)
787 break;
788 if (!overold && !isnewer)
789 break;
790 if (!overnew && isnewer)
791 break;
792 }
793 // Confirm overwrite unless bypassed
794 if ((wk->li->type == IDM_RENAME &&
795 (!dontask || !*wildname)) ||
796 (!dontask && existed) ||
797 (!dontask && wildcarding) ||
798 (IsFile(newname) == 0 && IsFile(wk->li->list[x]) > 0))
799 {
800 MOVEIT mv;
801 memset(&mv, 0, sizeof(MOVEIT));
802 mv.rename = (wk->li->type == IDM_RENAME);
803 mv.source = wk->li->list[x];
804 strcpy(mv.target, newname);
805 rc = WinDlgBox(HWND_DESKTOP,
806 wk->hwndFrame,
807 RenameProc,
808 FM3ModHandle, REN_FRAME, (PVOID) & mv);
809 if (!rc)
810 goto Abort;
811
812 DosSleep(1);
813 if (mv.skip || !*mv.target)
814 break;
815 if (mv.dontask)
816 dontask = TRUE;
817 if (mv.overold)
818 overold = TRUE;
819 if (mv.overnew)
820 overnew = TRUE;
821 if (mv.noreadonlywarn)
822 noreadonlywarn = TRUE;
823 if (wildcarding || wk->li->type == IDM_RENAME) {
824 p = strrchr(mv.target, '\\');
825 if (p && (strchr(p, '*') || strchr(p, '?'))) {
826 strcpy(wildname, mv.target);
827 AdjustWildcardName(wk->li->list[x], mv.target);
828 }
829 else
830 *wildname = 0;
831 }
832 strcpy(newname, mv.target);
833 existed = (IsFile(newname) != -1);
834 isnewer = IsNewer(wk->li->list[x], newname);
835 if (!mv.overwrite) {
836 if (existed && wk->li->type != IDM_RENAME && dontask) {
837 if (!overold && !overnew)
838 break;
839 if (!overold && !isnewer)
840 break;
841 if (!overnew && isnewer)
842 break;
843 }
844 }
845 }
846 if (!strcmp(wk->li->list[x], newname) ||
847 (wk->li->type == IDM_COPY &&
848 !stricmp(wk->li->list[x], newname)))
849 break;
850 sprintf(message,
851 " %s \"%s\" %s\"%s\"%s [%u %s%u]",
852 moving,
853 wk->li->list[x],
854 GetPString(IDS_TOTEXT), // Has trailing space
855 newname,
856 usedtarget ?
857 GetPString(IDS_TOTARGETTEXT) :
858 NullStr,
859 x + 1,
860 GetPString(IDS_OFTEXT), // Has trailing space
861 cItemsInList);
862 AddNote(message);
863 if (fVerify && (driveflags[toupper(*wk->li->targetpath) - 'A'] & DRIVE_WRITEVERIFYOFF) |
864 (driveflags[toupper(*wk->li->list[x]) - 'A'] & DRIVE_WRITEVERIFYOFF)) {
865 DosSetVerify(FALSE);
866 fResetVerify = TRUE;
867 }
868 if (plen) {
869 // make directory/ies, if required
870
871 CHAR dirpart[CCHMAXPATH];
872
873 strcpy(dirpart, newname);
874 p = strrchr(dirpart, '\\');
875 if (p) {
876 *p = 0;
877 if (p > dirpart + 3)
878 MassMkdir((hwndMain) ? hwndMain : wk->hwndCnr,
879 dirpart);
880 }
881 }
882 if (fRealIdle)
883 priority_idle();
884
885 rc = docopyf(type, wk->li->list[x], newname);
886 if (rc == ERROR_ACCESS_DENIED || rc == ERROR_SHARING_VIOLATION) {
887 ret = make_deleteable(newname, rc, noreadonlywarn);
888 if (ret == SM2_CANCEL)
889 break;
890 if (ret == SM2_DONTASK)
891 noreadonlywarn = TRUE;
892 if (ret == SM2_NO)
893 continue;
894 rc = docopyf(type, wk->li->list[x], newname);
895 }
896 if (rc == ERROR_ACCESS_DENIED || (rc == ERROR_SHARING_VIOLATION && fUnlock))
897 rc = NO_ERROR;
898 if (fResetVerify) {
899 DosSetVerify(fVerify);
900 fResetVerify = FALSE;
901 }
902 priority_normal();
903 if (rc) {
904 if ((rc == ERROR_DISK_FULL ||
905 rc == ERROR_HANDLE_DISK_FULL) &&
906 isalpha(*newname) &&
907 (driveflags[toupper(*newname) - 'A'] &
908 DRIVE_REMOVABLE)
909 && !(driveflags[toupper(*newname) - 'A'] &
910 DRIVE_NOTWRITEABLE)
911 && toupper(*newname) != toupper(*wk->li->list[x])
912 && !DosQueryPathInfo(wk->li->list[x], FIL_QUERYEASIZEL,
913 &fs4, sizeof(fs4))
914 && !(fs4.attrFile & FILE_DIRECTORY)) {
915
916 FSALLOCATE fsa;
917 ULONGLONG ullFreeBytes;
918 CHAR *ptr;
919 INT cntr;
920
921 Notify(GetPString(IDS_FITTINGTEXT));
922 DosError(FERR_DISABLEHARDERR);
923 if (!DosQueryFSInfo(toupper(*newname) - '@',
924 FSIL_ALLOC,
925 &fsa, sizeof(FSALLOCATE))) {
926 // Assume large file support
927 ullFreeBytes = (ULONGLONG) fsa.cUnitAvail * fsa.cSectorUnit *
928 fsa.cbSector;
929 if (ullFreeBytes) {
930 // Find item that will fit in available space
931 for (cntr = x + 1; wk->li->list[cntr]; cntr++) {
932 DosError(FERR_DISABLEHARDERR);
933 if (!DosQueryPathInfo(wk->li->list[cntr],
934 FIL_QUERYEASIZEL,
935 &fs4,
936 sizeof(fs4)) &&
937 !(fs4.attrFile & FILE_DIRECTORY) &&
938 // fixme to use CBLIST_TO_EASIZE?
939 fs4.cbFile + fs4.cbList <= ullFreeBytes) {
940 // Swap with failing item
941 ptr = wk->li->list[x];
942 wk->li->list[x] = wk->li->list[cntr];
943 wk->li->list[cntr] = ptr;
944 goto Retry;
945 }
946 }
947 Notify(GetPString(IDS_COULDNTFITTEXT));
948 }
949 }
950 rc = saymsg(MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION,
951 wk->hwndFrame,
952 GetPString(IDS_DISKFULLTEXT),
953 GetPString(IDS_ANOTHERDISKTEXT));
954 if (rc == MBID_RETRY)
955 goto Retry;
956 if (rc == MBID_ABORT)
957 goto Abort;
958 }
959 else {
960 if (LogFileHandle) {
961 fprintf(LogFileHandle,
962 GetPString(IDS_LOGTOFAILEDTEXT),
963 move, wk->li->list[x], newname, rc);
964 }
965 rc = Dos_Error(MB_ENTERCANCEL,
966 rc,
967 wk->hwndFrame,
968 pszSrcFile,
969 __LINE__,
970 "%s %s\"%s\" %s\"%s\" %s.",
971 move, // move, copy, rename etc.
972 GetPString(IDS_OFTEXT), // "of " - note trailing space
973 wk->li->list[x],
974 GetPString(IDS_TOTEXT), // "to "
975 newname, GetPString(IDS_FAILEDTEXT)); // "failed"
976 if (rc == MBID_CANCEL)
977 goto Abort;
978 }
979 }
980 else {
981 if (LogFileHandle) {
982 fprintf(LogFileHandle,
983 "%s \"%s\" %s\"%s\"\n",
984 moved,
985 wk->li->list[x],
986 GetPString(IDS_TOTEXT), newname);
987 }
988 if (!strcmp(realappname, "FM/4") ||
989 ((driveflags[*wk->li->targetpath - 'A'] & DRIVE_RSCANNED) &&
990 AddToList(wk->li->list[x],
991 &files, &cFilesModified, &cItemsAllocated)))
992 Broadcast(hab2,
993 wk->hwndCnr,
994 UM_UPDATERECORD,
995 MPFROMP(wk->li->list[x]), MPVOID);
996 if (!strcmp(realappname, "FM/4") ||
997 (driveflags[*wk->li->targetpath - 'A'] & DRIVE_RSCANNED) &&
998 AddToList(newname, &files, &cFilesModified, &cItemsAllocated))
999 Broadcast(hab2,
1000 wk->hwndCnr,
1001 UM_UPDATERECORD, MPFROMP(newname), MPVOID);
1002 }
1003 }
1004 break;
1005 }
1006
1007 case IDM_COMPARE:
1008 if ((!IsFile(wk->li->targetpath) ||
1009 IsRoot(wk->li->targetpath)) &&
1010 (!IsFile(wk->li->list[x]) || IsRoot(wk->li->list[x]))) {
1011 if (!*dircompare && WinIsWindow(hab2, wk->hwndCnr))
1012 WinSendMsg(wk->hwndCnr,
1013 UM_COMPARE,
1014 MPFROMP(wk->li->targetpath),
1015 MPFROMP(wk->li->list[x]));
1016 else {
1017 runemf2(SEPARATE,
1018 HWND_DESKTOP, pszSrcFile, __LINE__,
1019 NULL, NULL,
1020 "%s %s %s",
1021 dircompare,
1022 BldQuotedFileName(szQuotedDirName, wk->li->targetpath),
1023 BldQuotedFileName(szQuotedFileName, wk->li->list[x]));
1024 }
1025 }
1026 else if (*compare) {
1027 CHAR *fakelist[3];
1028
1029 fakelist[0] = wk->li->list[x];
1030 fakelist[1] = wk->li->targetpath;
1031 fakelist[2] = NULL;
1032 ExecOnList(wk->hwndFrame,
1033 compare,
1034 WINDOWED | SEPARATEKEEP, NULL, NULL, fakelist, NULL,
1035 pszSrcFile, __LINE__);
1036 }
1037 else {
1038 FCOMPARE fc;
1039
1040 memset(&fc, 0, sizeof(fc));
1041 fc.size = sizeof(fc);
1042 fc.hwndParent = wk->hwndParent;
1043 strcpy(fc.file1, wk->li->list[x]);
1044 strcpy(fc.file2, wk->li->targetpath);
1045 if (WinDlgBox(HWND_DESKTOP,
1046 wk->hwndFrame,
1047 CFileDlgProc,
1048 FM3ModHandle, FCMP_FRAME, (PVOID) & fc))
1049 goto Abort;
1050 }
1051 break;
1052 } // switch
1053 DosSleep(0);
1054 } // for list
1055
1056 // Do action specific post-processing
1057 switch (wk->li->type) {
1058 case IDM_MOVE:
1059 case IDM_COPY:
1060 case IDM_WPSMOVE:
1061 case IDM_WPSCOPY:
1062 case IDM_RENAME:
1063 sprintf(message,
1064 GetPString(IDS_OPSCOMPLETETEXT),
1065 wk->li->type == IDM_MOVE ?
1066 GetPString(IDS_MOVETEXT) :
1067 wk->li->type == IDM_COPY ?
1068 GetPString(IDS_COPYTEXT) :
1069 wk->li->type == IDM_WPSMOVE ?
1070 GetPString(IDS_WPSMOVETEXT) :
1071 wk->li->type == IDM_WPSCOPY ?
1072 GetPString(IDS_WPSCOPYTEXT) :
1073 GetPString(IDS_RENAMETEXT),
1074 &"s"[x == 1], // s or nul
1075 (wk->li->type == IDM_MOVE ||
1076 wk->li->type == IDM_COPY ||
1077 wk->li->type == IDM_WPSMOVE ||
1078 wk->li->type == IDM_WPSCOPY) ?
1079 GetPString(IDS_TOTEXT) : NullStr,
1080 (wk->li->type == IDM_MOVE ||
1081 wk->li->type == IDM_COPY ||
1082 wk->li->type == IDM_WPSMOVE ||
1083 wk->li->type == IDM_WPSCOPY) ?
1084 wk->li->targetpath : NullStr,
1085 GetPString(x != 1 ? IDS_ARETEXT : IDS_ISTEXT));
1086 Notify(message);
1087 if (toupper(*wk->li->targetpath) < 'C' && !fAlertBeepOff)
1088 DosBeep(1000, 25); // Wake up user
1089 DosSleep(16);//05 Aug 07 GKY 33
1090 if (wk->li->type == IDM_WPSMOVE || wk->li->type == IDM_WPSCOPY)
1091 DosSleep(48);//05 Aug 07 GKY 96
1092 break;
1093 default:
1094 break;
1095 }
1096 } // if have non-empty list
1097
1098 Abort:
1099
1100 if (files) {
1101 if (!strcmp(realappname, "FM/4") || !hwndTree ||
1102 (driveflags[*wk->li->targetpath - 'A'] & DRIVE_RSCANNED))
1103 Broadcast(hab2,
1104 wk->hwndCnr,
1105 UM_UPDATERECORDLIST, MPFROMP(files), MPVOID);
1106 FreeList(files);
1107 }
1108
1109 if (WinIsWindow(hab2, wk->hwndCnr))
1110 PostMsg(wk->hwndCnr, UM_RESCAN, MPVOID, MPVOID);
1111
1112 WinDestroyMsgQueue(hmq2);
1113 } // if queue
1114 DecrThreadUsage();
1115 WinTerminate(hab2);
1116 } // if hab2
1117 } // if list not empty
1118
1119 if (wk->li)
1120 FreeListInfo(wk->li);
1121 free(wk);
1122# ifdef FORTIFY
1123 Fortify_LeaveScope();
1124# endif
1125 DosPostEventSem(CompactSem);
1126 }
1127}
1128
1129/**
1130 * Apply file list to action
1131 * All items in list processed as a group, if possible
1132 */
1133
1134VOID MassAction(VOID * args)
1135{
1136 WORKER *wk = (WORKER *) args;
1137 HAB hab2;
1138 HMQ hmq2;
1139 CHAR **files = NULL;
1140 CHAR *p, *pp;
1141 UINT numfiles = 0, numalloc = 0;
1142
1143
1144 if (wk) {
1145# ifdef FORTIFY
1146 // Fortify_BecomeOwner(wk);
1147 Fortify_EnterScope();
1148# endif
1149 if (wk->li && wk->li->list && wk->li->list[0]) {
1150 hab2 = WinInitialize(0);
1151 if (hab2) {
1152 hmq2 = WinCreateMsgQueue(hab2, 0);
1153 if (hmq2) {
1154 WinCancelShutdown(hmq2, TRUE);
1155 IncrThreadUsage();
1156 DosError(FERR_DISABLEHARDERR);
1157 if (IsRoot(wk->li->list[0]) || !IsFile(wk->li->list[0])) {
1158 if (wk->li->type == IDM_VIEW)
1159 wk->li->type = IDM_INFO;
1160 if (wk->li->type == IDM_EDIT)
1161 wk->li->type = IDM_EAS;
1162 }
1163 switch (wk->li->type) {
1164 case IDM_INFO:
1165 if (WinDlgBox(HWND_DESKTOP,
1166 wk->hwndFrame,
1167 FileInfoProc,
1168 FM3ModHandle, FLE_FRAME, (PVOID) wk->li->list) != 2)
1169 {
1170 break;
1171 }
1172 // else intentional fallthru
1173 case IDM_UPDATE:
1174 Broadcast(hab2,
1175 wk->hwndCnr,
1176 UM_UPDATERECORDLIST, MPFROMP(wk->li->list), MPVOID);
1177 break;
1178
1179 case IDM_EAS:
1180 if (WinDlgBox(HWND_DESKTOP,
1181 wk->hwndFrame,
1182 DisplayEAsProc,
1183 FM3ModHandle, EA_FRAME, (PVOID) wk->li->list))
1184 Broadcast(hab2,
1185 wk->hwndCnr,
1186 UM_UPDATERECORDLIST, MPFROMP(wk->li->list), MPVOID);
1187 break;
1188
1189 case IDM_DOITYOURSELF:
1190 ExecOnList(wk->hwndFrame,
1191 "%a",
1192 WINDOWED | SEPARATE | PROMPT, NULL,
1193 NULL, wk->li->list, GetPString(IDS_DOITYOURSELFTEXT),
1194 pszSrcFile, __LINE__);
1195 break;
1196
1197 case IDM_MCIPLAY:
1198 {
1199 UINT x;
1200 UINT MaxFM2playStrLen = 24;
1201 ULONG total;
1202 CHAR fbuf[CCHMAXPATH];
1203
1204 if (DosSearchPath(SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT |
1205 SEARCH_CUR_DIRECTORY,
1206 (CHAR *) PCSZ_PATH, (CHAR *) PCSZ_FM2PLAYEXE, (PBYTE)fbuf, CCHMAXPATH - 1))
1207 total += MaxFM2playStrLen;
1208 else
1209 total = strlen(fbuf);
1210 for (x = 0; wk->li->list[x]; x++)
1211 total += (strlen(wk->li->list[x]) + 1 +
1212 (needs_quoting(wk->li->list[x]) * 2));
1213 if (total > 1000) {
1214
1215 FILE *fp;
1216 CHAR szTempFile[CCHMAXPATH];
1217 CHAR *modew = "w";
1218
1219 if (pTmpDir && !IsValidDir(pTmpDir))
1220 DosCreateDir(pTmpDir, 0);
1221 BldFullPathName(szTempFile, pTmpDir, PCSZ_FM2PLAYTEMP);
1222 fp = xfopen(szTempFile, modew, pszSrcFile, __LINE__, FALSE);
1223 if (fp) {
1224 fprintf(fp, "%s", ";AV/2-built FM2Play listfile\n");
1225 for (x = 0; wk->li->list[x]; x++)
1226 fprintf(fp, "%s\n", wk->li->list[x]);
1227 fprintf(fp, ";end\n");
1228 fclose(fp);
1229 strrev(szTempFile);
1230 strcat(szTempFile, "@/");
1231 strrev(szTempFile);
1232 RunFM2Util(PCSZ_FM2PLAYEXE, szTempFile);
1233 }
1234 }
1235 }
1236 // intentional fallthru
1237 case IDM_FAKEEXTRACT:
1238 case IDM_FAKEEXTRACTM:
1239 if (wk->li->type == IDM_MCIPLAY ||
1240 (*wk->li->arcname && wk->li->info &&
1241 wk->li->info->extract && *wk->li->targetpath)) {
1242
1243 CHAR szBuffer[1025];
1244 CHAR fbuf[CCHMAXPATH];
1245 UINT x;
1246
1247 if (wk->li->type == IDM_FAKEEXTRACT ||
1248 wk->li->type == IDM_FAKEEXTRACTM) {
1249 strcpy(szBuffer,
1250 (wk->li->info->exwdirs) ?
1251 wk->li->info->exwdirs : wk->li->info->extract);
1252 strcat(szBuffer, " ");
1253 BldQuotedFileName(szBuffer + strlen(szBuffer), wk->li->arcname);
1254 }
1255 else {
1256 if (DosSearchPath(SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT |
1257 SEARCH_CUR_DIRECTORY,
1258 (CHAR *) PCSZ_PATH, (CHAR *) PCSZ_FM2PLAYEXE, (PBYTE)fbuf, CCHMAXPATH - 1))
1259 strcpy(szBuffer, "UTILS\\FM2PLAY.EXE");
1260 else
1261 strcpy(szBuffer, PCSZ_FM2PLAYEXE);
1262 }
1263 p = &szBuffer[strlen(szBuffer)];
1264 strcat(szBuffer, " ");
1265 x = 0;
1266 while (wk->li->list[x]) {
1267 pp = wk->li->list[x];
1268 while (*pp) {
1269 if (*pp == '/')
1270 *pp = '\\';
1271 pp++;
1272 }
1273 BldQuotedFileName(szBuffer + strlen(szBuffer), wk->li->list[x]);
1274 x++;
1275 if (!wk->li->list[x] || strlen(szBuffer) +
1276 strlen(wk->li->list[x]) + 5 > 1024) {
1277 runemf2(SEPARATE | WINDOWED | BACKGROUND | MINIMIZED | WAIT,
1278 HWND_DESKTOP, pszSrcFile, __LINE__,
1279 (wk->li->type == IDM_FAKEEXTRACT ||
1280 wk->li->type == IDM_FAKEEXTRACTM) ?
1281 wk->li->targetpath : NULL,
1282 NULL,
1283 "%s", szBuffer);
1284 DosSleep(1);
1285 *p = 0;
1286 }
1287 strcat(szBuffer, " ");
1288 }
1289 if (wk->li->type == IDM_MCIPLAY)
1290 break;
1291 strcpy(szBuffer, wk->li->targetpath);
1292 AddBackslashToPath(wk->li->targetpath);
1293 //if (wk->li->targetpath[strlen(wk->li->targetpath) - 1] != '\\')
1294 // strcat(szBuffer, "\\");
1295 p = szBuffer + strlen(szBuffer);
1296 for (x = 0; wk->li->list[x]; x++) {
1297 strcpy(p, wk->li->list[x]);
1298 free(wk->li->list[x]);
1299 wk->li->list[x] = xstrdup(szBuffer, pszSrcFile, __LINE__);
1300 }
1301 if (wk->li->list[0])
1302 Broadcast(hab2,
1303 wk->hwndCnr,
1304 UM_UPDATERECORDLIST, MPFROMP(wk->li->list), MPVOID);
1305 }
1306 break;
1307
1308 case IDM_SETICON:
1309 if (*wk->li->targetpath) {
1310
1311 ICONINFO ici;
1312
1313 memset(&ici, 0, sizeof(ICONINFO));
1314 ici.cb = sizeof(ICONINFO);
1315 ici.fFormat = ICON_FILE;
1316 ici.pszFileName = wk->li->list[0];
1317 if (!WinSetFileIcon((PSZ) wk->li->targetpath,
1318 (PICONINFO) & ici)) {
1319 ici.fFormat = ICON_CLEAR;
1320 WinSetFileIcon((PSZ) wk->li->targetpath, (PICONINFO) & ici);
1321 }
1322 Broadcast(hab2,
1323 wk->hwndCnr,
1324 UM_UPDATERECORD, MPFROMP(wk->li->targetpath), MPVOID);
1325 }
1326 break;
1327
1328 case IDM_APPENDTOCLIP:
1329 case IDM_SAVETOCLIP:
1330 case IDM_SAVETOCLIPFILENAME:
1331 case IDM_APPENDTOCLIPFILENAME:
1332 ListToClipboardHab(hab2,
1333 wk->li->list,
1334 wk->li->type);
1335 break;
1336
1337 case IDM_ARCHIVEM:
1338 case IDM_ARCHIVE:
1339 {
1340 DIRCNRDATA ad;
1341 CHAR szBuffer[1025];
1342 ARC_TYPE *info = NULL;
1343 char *pch;
1344 UINT x;
1345
1346 memset(&ad, 0, sizeof(DIRCNRDATA));
1347 strcpy(ad.arcname, wk->li->targetpath);
1348 if (*wk->li->targetpath && IsFile(wk->li->targetpath) > 0) {
1349 info = find_type(wk->li->targetpath, NULL);
1350 ad.namecanchange = 0;
1351 }
1352 else {
1353 if (*wk->li->targetpath && !IsFile(wk->li->targetpath))
1354 AddBackslashToPath(wk->li->targetpath);
1355 ad.namecanchange = 1;
1356 }
1357 strcpy(ad.arcname, wk->li->targetpath);
1358 if (wk->li->type == IDM_ARCHIVEM)
1359 ad.fmoving = TRUE;
1360 if (!info) {
1361 ad.info = arcsighead; // Hide dups
1362 if (!WinDlgBox(HWND_DESKTOP,
1363 wk->hwndFrame,
1364 SBoxDlgProc,
1365 FM3ModHandle,
1366 ASEL_FRAME, (PVOID) & ad.info) || !ad.info) {
1367 break; // we blew it
1368 }
1369 }
1370 else
1371 ad.info = info;
1372 if (!ad.info || (!ad.info->create &&
1373 !ad.info->move &&
1374 !ad.info->createwdirs &&
1375 !ad.info->movewdirs &&
1376 !ad.info->createrecurse))
1377 break;
1378 if (!*wk->li->targetpath && *wk->directory) {
1379 strcpy(ad.arcname, wk->directory);
1380 AddBackslashToPath(ad.arcname);
1381 }
1382 if (!WinDlgBox(HWND_DESKTOP, wk->hwndFrame, ArchiveDlgProc, FM3ModHandle,
1383 ARCH_FRAME, (PVOID) & ad) || !*ad.arcname || !*ad.command) // we blew it
1384 break;
1385 // Provide extension so containers work
1386 pch = strrchr(ad.arcname, '\\');
1387 if (pch)
1388 pch = strrchr(pch, '.');
1389 else
1390 pch = strrchr(ad.arcname, '.');
1391 if (!pch && ad.info->ext) {
1392 strcat(ad.arcname, ".");
1393 strcat(ad.arcname, ad.info->ext);
1394 }
1395 // build the sucker
1396 strcpy(szBuffer, ad.command);
1397 strcat(szBuffer, " ");
1398 BldQuotedFileName(szBuffer + strlen(szBuffer), ad.arcname);
1399 p = &szBuffer[strlen(szBuffer)];
1400 if (ad.mask.szMask) {
1401 strcat(szBuffer, " ");
1402 strcat(szBuffer, ad.mask.szMask);
1403 }
1404 strcat(szBuffer, " ");
1405 x = 0;
1406 while (wk->li->list[x]) {
1407 FILESTATUS3 fsa;
1408 memset(&fsa, 0, sizeof(FILESTATUS3));
1409 DosError(FERR_DISABLEHARDERR);
1410 DosQueryPathInfo(wk->li->list[x],
1411 FIL_STANDARD,
1412 &fsa, (ULONG) sizeof(FILESTATUS3));
1413 if (fsa.attrFile & FILE_DIRECTORY) {
1414 BldQuotedFullPathName(szBuffer + strlen(szBuffer), wk->li->list[x], "*");
1415 }
1416 else
1417 BldQuotedFileName(szBuffer + strlen(szBuffer), wk->li->list[x]);
1418 x++;
1419 if (!wk->li->list[x] ||
1420 strlen(szBuffer) + strlen(wk->li->list[x]) + 5 > 1024) {
1421 runemf2(SEPARATE | WINDOWED | WAIT |
1422 (fArcStuffVisible ? 0 : (BACKGROUND | MINIMIZED)),
1423 HWND_DESKTOP, pszSrcFile, __LINE__, NULL, NULL,
1424 "%s", szBuffer);
1425 DosSleep(1);
1426 *p = 0;
1427 }
1428 strcat(szBuffer, " ");
1429 }
1430 Broadcast(hab2,
1431 wk->hwndCnr,
1432 UM_UPDATERECORDLIST, MPFROMP(wk->li->list), MPVOID);
1433 Broadcast(hab2,
1434 wk->hwndCnr,
1435 UM_UPDATERECORD, MPFROMP(ad.arcname), MPVOID);
1436 }
1437 break;
1438
1439 case IDM_VIEW:
1440 if (!TestBinary(wk->li->list[0])) {
1441 wk->li->type = IDM_VIEWTEXT;
1442 goto SkipViewing;
1443 }
1444 else
1445 wk->li->type = IDM_VIEWBINARY;
1446 // intentional fallthru
1447 case IDM_VIEWBINARY:
1448 if (*binview) {
1449 ExecOnList((HWND) 0,
1450 binview,
1451 WINDOWED | SEPARATE, NULL, NULL, wk->li->list, NULL,
1452 pszSrcFile, __LINE__);
1453 break;
1454 }
1455 // else intentional fallthru
1456 case IDM_VIEWTEXT:
1457 SkipViewing:
1458 if (*viewer)
1459 ExecOnList((HWND) 0, viewer,
1460 WINDOWED | SEPARATE |
1461 ((fViewChild) ? CHILD : 0),
1462 NULL, NULL, wk->li->list, NULL,
1463 pszSrcFile, __LINE__);
1464 else {
1465
1466 CHAR *temp;
1467 UINT x;
1468 ULONG viewtype;
1469
1470 viewtype = (wk->li->type == IDM_VIEWTEXT) ? 8 :
1471 (wk->li->type == IDM_VIEWBINARY) ? 16 : 0;
1472 for (x = 0; wk->li->list[x]; x++) {
1473 temp = xstrdup(wk->li->list[x], pszSrcFile, __LINE__);
1474 if (temp && WinIsWindow(hab2, wk->hwndCnr)) {
1475 if (!PostMsg(wk->hwndCnr,
1476 UM_LOADFILE,
1477 MPFROMLONG(5 + viewtype), MPFROMP(temp)))
1478 free(temp);
1479 }
1480 DosSleep(1);
1481 }
1482 }
1483 break;
1484
1485 case IDM_EDIT:
1486 if (!TestBinary(wk->li->list[0])) {
1487 wk->li->type = IDM_EDITTEXT;
1488 goto SkipEditing;
1489 }
1490 else
1491 wk->li->type = IDM_EDITBINARY;
1492 // intentional fallthru
1493 case IDM_EDITBINARY:
1494 if (*bined) {
1495 ExecOnList((HWND) 0,
1496 bined,
1497 WINDOWED | SEPARATE, NULL, NULL, wk->li->list, NULL,
1498 pszSrcFile, __LINE__);
1499 break;
1500 }
1501 // else intentional fallthru
1502 case IDM_EDITTEXT:
1503 SkipEditing:
1504 if (*editor)
1505 ExecOnList((HWND) 0,
1506 editor,
1507 WINDOWED | SEPARATE, NULL, NULL, wk->li->list, NULL,
1508 pszSrcFile, __LINE__);
1509 else {
1510
1511 CHAR *temp;
1512 UINT x;
1513 ULONG viewtype;
1514
1515 viewtype = (wk->li->type == IDM_EDITTEXT) ? 8 :
1516 (wk->li->type == IDM_EDITBINARY) ? 16 : 0;
1517 for (x = 0; wk->li->list[x]; x++) {
1518 temp = xstrdup(wk->li->list[x], pszSrcFile, __LINE__);
1519 if (temp && WinIsWindow(hab2, wk->hwndCnr)) {
1520 if (!PostMsg(wk->hwndCnr,
1521 UM_LOADFILE,
1522 MPFROMLONG(4 + viewtype), MPFROMP(temp)))
1523 free(temp);
1524 }
1525 DosSleep(1);
1526 }
1527 }
1528 break;
1529
1530 case IDM_SHADOW2:
1531 case IDM_OBJECT:
1532 case IDM_SHADOW:
1533 {
1534 CHAR objectpath[CCHMAXPATH];
1535 APIRET rc;
1536
1537 if (!*wk->li->targetpath || IsFile(wk->li->targetpath)) {
1538 GetDesktopName(objectpath, sizeof(objectpath));
1539 rc = WinDlgBox(HWND_DESKTOP,
1540 wk->hwndFrame,
1541 ObjCnrDlgProc,
1542 FM3ModHandle,
1543 OBJCNR_FRAME, MPFROMP(objectpath));
1544 if (rc) {
1545 if (rc > 1)
1546 strcpy(objectpath, "<WP_DESKTOP>");
1547 }
1548 else
1549 break;
1550 }
1551 else
1552 strcpy(objectpath, wk->li->targetpath);
1553 AddNote(GetPString(IDS_MAKINGOBJSTEXT));
1554 MakeShadows(wk->hwndFrame,
1555 wk->li->list,
1556 (wk->li->type == IDM_SHADOW) +
1557 (wk->li->type == IDM_SHADOW2) * 2,
1558 objectpath, NULL);
1559 AddNote(GetPString(IDS_MADEOBJSTEXT));
1560 }
1561 break;
1562
1563 case IDM_PRINT:
1564 if (WinDlgBox(HWND_DESKTOP,
1565 wk->hwndFrame,
1566 PrintDlgProc,
1567 FM3ModHandle, PRN_FRAME, MPFROMP(wk->li))) {
1568 if (wk->li && wk->li->list && wk->li->list[0]) {
1569 strcpy(wk->li->targetpath, printer);
1570 if (xbeginthread(PrintListThread,
1571 65536,
1572 wk->li,
1573 pszSrcFile,
1574 __LINE__) != -1)
1575 {
1576 wk->li = NULL; // prevent LISTINFO li from being freed here
1577 }
1578 }
1579 }
1580 break;
1581
1582 case IDM_ATTRS:
1583 if (WinDlgBox(HWND_DESKTOP,
1584 wk->hwndFrame,
1585 AttrListDlgProc,
1586 FM3ModHandle, ATR_FRAME, MPFROMP(wk->li))) {
1587 if (wk->li && wk->li->list && wk->li->list[0])
1588 Broadcast(hab2,
1589 wk->hwndCnr,
1590 UM_UPDATERECORDLIST, MPFROMP(wk->li->list), MPVOID);
1591 }
1592 break;
1593
1594 case IDM_PERMDELETE:
1595 case IDM_DELETE:
1596 {
1597 CHECKLIST cl;
1598 INT isdir = 0, sysdir = 0, ro = 0, hs = 0;
1599 UINT x;
1600 FILESTATUS3 fsa;
1601 CHAR prompt[CCHMAXPATH * 3];
1602 APIRET error = 0, rc;
1603 HOBJECT hObjectdest, hObjectofObject;
1604 BYTE G_abSupportedDrives[24] = {0};
1605 ULONG cbSupportedDrives = sizeof(G_abSupportedDrives);
1606 BOOL ignorereadonly = FALSE;
1607 INT retrn = 0;
1608
1609 for (x = 0; wk->li->list[x]; x++) {
1610 if (IsRoot(wk->li->list[x])) {
1611 wk->li->list = RemoveFromList(wk->li->list,
1612 wk->li->list[x]);
1613 if (!wk->li->list)
1614 break;
1615 x--;
1616 continue;
1617 }
1618 DosError(FERR_DISABLEHARDERR);
1619 if (DosQueryPathInfo(wk->li->list[x],
1620 FIL_STANDARD, &fsa,
1621 (ULONG) sizeof(FILESTATUS3))) {
1622 wk->li->list = RemoveFromList(wk->li->list,
1623 wk->li->list[x]);
1624 if (!wk->li->list)
1625 break;
1626 x--;
1627 continue;
1628 }
1629 if (fsa.attrFile & FILE_DIRECTORY) {
1630 isdir++;
1631 if (stristr(wk->li->list[x], ":\\OS2\\") ||
1632 !stricmp(wk->li->list[x] + 1, ":\\OS2"))
1633 sysdir++;
1634 }
1635 else {
1636 if (fsa.attrFile & (FILE_HIDDEN | FILE_SYSTEM))
1637 hs++;
1638 if (fsa.attrFile & FILE_READONLY)
1639 ro++;
1640 }
1641 }
1642 if (!wk->li->list)
1643 break;
1644 if (fConfirmDelete || isdir || hs || ro) {
1645 memset(&cl, 0, sizeof(cl));
1646 cl.size = sizeof(cl);
1647 cl.list = wk->li->list;
1648 cl.prompt = prompt;
1649 cl.flags |= CHECK_FILES;
1650 cl.cmd = wk->li->type;
1651 sprintf(prompt,
1652 GetPString(IDS_DELPROMPT1TEXT),
1653 (wk->li->type == IDM_DELETE) ?
1654 NullStr :
1655 GetPString(IDS_PERMANENTLYTEXT),
1656 &"s"[wk->li->list[1] == NULL]);
1657 if (isdir) {
1658 sprintf(&prompt[strlen(prompt)],
1659 GetPString(IDS_DELPROMPT2TEXT),
1660 isdir,
1661 (isdir > 1) ?
1662 GetPString(IDS_ARETEXT) :
1663 GetPString(IDS_ISTEXT),
1664 (isdir == 1) ?
1665 GetPString(IDS_ATEXT) :
1666 NullStr,
1667 (isdir > 1) ?
1668 GetPString(IDS_IESTEXT) : GetPString(IDS_YTEXT));
1669 if (sysdir)
1670 sprintf(&prompt[strlen(prompt)],
1671 GetPString(IDS_DELPROMPT3TEXT),
1672 sysdir,
1673 (sysdir == 1) ?
1674 GetPString(IDS_YTEXT) : GetPString(IDS_IESTEXT));
1675 }
1676 if (ro)
1677 sprintf(&prompt[strlen(prompt)],
1678 GetPString(IDS_DELPROMPT4TEXT),
1679 ro,
1680 &"s"[ro == 1],
1681 (ro > 1) ?
1682 GetPString(IDS_ARETEXT) : GetPString(IDS_ISTEXT));
1683 if (hs)
1684 sprintf(&prompt[strlen(prompt)],
1685 GetPString(IDS_DELPROMPT5TEXT),
1686 hs,
1687 &"s"[hs == 1],
1688 (hs > 1) ?
1689 GetPString(IDS_ARETEXT) : GetPString(IDS_ISTEXT));
1690 if ((ro || hs || sysdir) && !fAlertBeepOff)
1691 DosBeep(300, 100); // Wake up user
1692 strcat(prompt, GetPString(IDS_DELPROMPT6TEXT));
1693 error = WinDlgBox(HWND_DESKTOP,
1694 wk->hwndFrame,
1695 CheckListProc,
1696 FM3ModHandle, CHECK_FRAME, MPFROMP(&cl));
1697 if (!error || error == 65535)
1698 break;
1699 wk->li->list = cl.list;
1700 if (!wk->li->list || !wk->li->list[0])
1701 break;
1702 }
1703 if (fVerify && driveflags[toupper(*wk->li->list[0]) - 'A'] & DRIVE_WRITEVERIFYOFF)
1704 DosSetVerify(FALSE);
1705 DosRequestMutexSem(hmtxFM2Delete, SEM_INDEFINITE_WAIT); // Prevent race 12-3-08 GKY
1706 for (x = 0; wk->li->list[x]; x++) {
1707 fsa.attrFile = 0;
1708 DosError(FERR_DISABLEHARDERR);
1709 DosQueryPathInfo(wk->li->list[x],
1710 FIL_STANDARD,
1711 &fsa, (ULONG) sizeof(FILESTATUS3));
1712 if (fsa.attrFile & FILE_DIRECTORY) {
1713 error = (APIRET) wipeallf(FALSE, "%s%s*",
1714 wk->li->list[x],
1715 (*wk->li->list[x] &&
1716 wk->li->
1717 list[x][strlen(wk->li->list[x]) - 1]
1718 != '\\') ? PCSZ_BACKSLASH : NullStr);
1719 DosError(FERR_DISABLEHARDERR);
1720 if (!error)
1721 error = DosDeleteDir(wk->li->list[x]);
1722 else
1723 DosDeleteDir(wk->li->list[x]);
1724 }
1725 else {
1726
1727 DosError(FERR_DISABLEHARDERR);
1728 if (wk->li->type == IDM_DELETE) {
1729 if (fTrashCan) {
1730 hObjectdest = WinQueryObject("<XWP_TRASHCAN>");
1731 rc = PrfQueryProfileData(HINI_USER,
1732 "XWorkplace",
1733 "TrashCan::Drives",
1734 G_abSupportedDrives,
1735 &cbSupportedDrives);
1736 if (hObjectdest != NULLHANDLE &&
1737 (rc ? (G_abSupportedDrives[toupper(*wk->li->list[x]) - 'C'] & 1)
1738 :((driveflags[toupper(*wk->li->list[x]) - 'A'] &
1739 DRIVE_LOCALHD )))) {
1740 hObjectofObject = WinQueryObject(wk->li->list[x]);
1741 retrn = make_deleteable(wk->li->list[x], error, ignorereadonly);
1742 if (retrn == SM2_CANCEL)
1743 break;
1744 if (retrn == SM2_DONTASK)
1745 ignorereadonly = TRUE;
1746 if (retrn == SM2_NO)
1747 continue;
1748 error = WinMoveObject(hObjectofObject, hObjectdest, 0);
1749 }
1750 else
1751 error = DosDelete(wk->li->list[x]);
1752 }
1753 else
1754 error = DosDelete(wk->li->list[x]);
1755 }
1756 else
1757 error = DosForceDelete(wk->li->list[x]);
1758 if (error) {
1759
1760 DosError(FERR_DISABLEHARDERR);
1761 retrn = make_deleteable(wk->li->list[x], error, ignorereadonly);
1762 if (retrn == SM2_CANCEL)
1763 break;
1764 if (retrn == SM2_DONTASK)
1765 ignorereadonly = TRUE;
1766 if (retrn == SM2_NO)
1767 continue;
1768 if (wk->li->type == IDM_DELETE) {
1769 if (fTrashCan) {
1770 hObjectdest = WinQueryObject("<XWP_TRASHCAN>");
1771 rc = PrfQueryProfileData(HINI_USER,
1772 "XWorkplace",
1773 "TrashCan::Drives",
1774 G_abSupportedDrives,
1775 &cbSupportedDrives);
1776 if (hObjectdest != NULLHANDLE &&
1777 (rc ? (G_abSupportedDrives[toupper(*wk->li->list[x]) - 'C'] & 1)
1778 :((driveflags[toupper(*wk->li->list[x]) - 'A'] &
1779 DRIVE_LOCALHD )))) {
1780 hObjectofObject = WinQueryObject(wk->li->list[x]);
1781 error = WinMoveObject(hObjectofObject, hObjectdest, 0);
1782 }
1783 else
1784 error = DosDelete(wk->li->list[x]);
1785 }
1786 else
1787 error = DosDelete(wk->li->list[x]);
1788 }
1789 else
1790 error = DosForceDelete(wk->li->list[x]);
1791 }
1792 DosReleaseMutexSem(hmtxFM2Delete);
1793 }
1794 //DbgMsg(pszSrcFile, __LINE__, "error %i retrn %i", error, retrn);
1795 if (fWarnReadOnly && error == ERROR_FILE_EXISTS) {
1796 error = ERROR_ACCESS_DENIED;
1797 retrn = SM2_NO;
1798 }
1799 if (error && (error != ERROR_ACCESS_DENIED ||
1800 (error == ERROR_ACCESS_DENIED &&
1801 (retrn == SM2_YES || retrn == SM2_DONTASK || retrn == 0)))) {
1802 if (LogFileHandle)
1803 fprintf(LogFileHandle,
1804 GetPString(IDS_DELETEFAILED1TEXT),
1805 wk->li->list[x], error);
1806 if (Dos_Error(MB_ENTERCANCEL,
1807 error,
1808 wk->hwndFrame,
1809 pszSrcFile,
1810 __LINE__,
1811 GetPString(IDS_DELETEFAILED2TEXT),
1812 wk->li->list[x]) == MBID_CANCEL) {
1813 DosSetVerify(fVerify);
1814 break;
1815 }
1816 }
1817 else {
1818 if (LogFileHandle)
1819 fprintf(LogFileHandle,
1820 GetPString(IDS_DELETEDTEXT), wk->li->list[x]);
1821 sprintf(prompt,
1822 GetPString(IDS_DELETEDTEXT), wk->li->list[x]);
1823 AddNote(prompt);
1824 }
1825 if (//fSyncUpdates ||
1826 AddToList(wk->li->list[x], &files, &numfiles, &numalloc)) {
1827 Broadcast(hab2,
1828 wk->hwndCnr,
1829 UM_UPDATERECORD,
1830 MPFROMP(wk->li->list[x]), MPVOID);
1831 } ;
1832 } // for
1833 }
1834 if (fVerify)
1835 DosSetVerify(fVerify);
1836 break;
1837 } // switch
1838 if (files) {
1839 Broadcast(hab2,
1840 wk->hwndCnr,
1841 UM_UPDATERECORDLIST, MPFROMP(files), MPVOID);
1842 FreeList(files);
1843 }
1844 if (WinIsWindow(hab2, wk->hwndCnr))
1845 PostMsg(wk->hwndCnr, UM_RESCAN, MPVOID, MPVOID);
1846
1847 WinDestroyMsgQueue(hmq2);
1848 }
1849 DecrThreadUsage();
1850 WinTerminate(hab2);
1851 }
1852 }
1853 FreeListInfo(wk->li);
1854 free(wk);
1855# ifdef FORTIFY
1856 Fortify_LeaveScope();
1857# endif
1858 DosPostEventSem(CompactSem);
1859 }
1860}
1861
1862#pragma alloc_text(MASSACTION,MassAction)
1863#pragma alloc_text(ACTION,Action)
1864#pragma alloc_text(UNDO,FreeUndo,Undo)
Note: See TracBrowser for help on using the repository browser.