1 // File management class with progress dialog
2
3 #include "config.h"
4 #include "i18n.h"
5
6 #include <fcntl.h>
7 #include <utime.h>
8 #if defined(linux)
9 #include <sys/statfs.h>
10 #endif
11
12 // For Sun compatibility
13 #ifdef __sun
14 #include <alloca.h>
15 #endif
16
17
18 #include <fx.h>
19 #include <fxkeys.h>
20 #include <FXPNGIcon.h>
21 #include <FXUTF8Codec.h>
22
23 #include "xfedefs.h"
24 #include "icons.h"
25 #include "xfeutils.h"
26 #include "OverwriteBox.h"
27 #include "MessageBox.h"
28 #include "CommandWindow.h"
29 #include "File.h"
30
31
32 // Delay before the progress bar should be shown (ms)
33 #define SHOW_PROGRESSBAR_DELAY 1000
34
35 // Progress dialog width
36 #define PROGRESSDIALOG_WIDTH 200
37
38
39
40 // Message Map
41 FXDEFMAP(File) FileMap[] =
42 {
43 FXMAPFUNC(SEL_COMMAND, File::ID_CANCEL_BUTTON, File::onCmdCancel),
44 FXMAPFUNC(SEL_TIMEOUT, File::ID_TIMEOUT, File::onTimeout),
45 };
46
47 // Object implementation
FXIMPLEMENT(File,DialogBox,FileMap,ARRAYNUMBER (FileMap))48 FXIMPLEMENT(File, DialogBox, FileMap, ARRAYNUMBER(FileMap))
49
50 // Construct object
51 File::File(FXWindow* owner, FXString title, const FXuint operation, const FXuint num) : DialogBox(owner, title, DECOR_TITLE|DECOR_BORDER|DECOR_STRETCHABLE)
52 {
53 // Progress window
54 FXPacker* buttons = new FXPacker(this, LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X, 0, 0, 10, 10, PROGRESSDIALOG_WIDTH, PROGRESSDIALOG_WIDTH, 5, 5);
55 FXVerticalFrame* contents = new FXVerticalFrame(this, LAYOUT_SIDE_TOP|FRAME_NONE|LAYOUT_FILL_X|LAYOUT_FILL_Y);
56
57 // Cancel Button
58 cancelButton = new FXButton(buttons, _("&Cancel"), NULL, this, File::ID_CANCEL_BUTTON, FRAME_RAISED|FRAME_THICK|LAYOUT_CENTER_X, 0, 0, 0, 0, 20, 20);
59 cancelButton->setFocus();
60 cancelButton->addHotKey(KEY_Escape);
61 cancelled = false;
62
63 // Progress bar
64 progressbar = NULL;
65
66 // Progress bar colors (foreground and background)
67 FXuint r, g, b, l;
68 FXColor textcolor, textaltcolor;
69 FXColor fgcolor = getApp()->reg().readColorEntry("SETTINGS", "pbarcolor", FXRGB(0, 0, 255));
70 FXColor bgcolor = getApp()->reg().readColorEntry("SETTINGS", "backcolor", FXRGB(255, 255, 255));
71
72 // Text color is white or black depending on the background luminance
73 r = FXREDVAL(bgcolor);
74 g = FXGREENVAL(bgcolor);
75 b = FXBLUEVAL(bgcolor);
76 l = (FXuint)(0.3*r+0.59*g+0.11*b);
77 if (l < 150)
78 {
79 textcolor = FXRGB(255, 255, 255);
80 }
81 else
82 {
83 textcolor = FXRGB(0, 0, 0);
84 }
85
86 // Alternate text color is white or black depending on the foreground luminance
87 r = FXREDVAL(fgcolor);
88 g = FXGREENVAL(fgcolor);
89 b = FXBLUEVAL(fgcolor);
90 l = (FXuint)(0.3*r+0.59*g+0.11*b);
91 if (l < 150)
92 {
93 textaltcolor = FXRGB(255, 255, 255);
94 }
95 else
96 {
97 textaltcolor = FXRGB(0, 0, 0);
98 }
99
100 // Progress dialog depends on the file operation
101 switch (operation)
102 {
103 case COPY:
104 // Labels and progress bar
105 uplabel = new FXLabel(contents, _("Source:"), NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
106 downlabel = new FXLabel(contents, _("Target:"), NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
107 progressbar = new FXProgressBar(contents, NULL, 0, LAYOUT_FILL_X|FRAME_SUNKEN|FRAME_THICK|PROGRESSBAR_PERCENTAGE, 0, 0, 0, 0, PROGRESSDIALOG_WIDTH);
108 progressbar->setBarColor(fgcolor);
109 progressbar->setTextColor(textcolor);
110 progressbar->setTextAltColor(textaltcolor);
111 datatext = _("Copied data:");
112 datalabel = new FXLabel(contents, datatext, NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
113
114 // Timer on
115 getApp()->addTimeout(this, File::ID_TIMEOUT, SHOW_PROGRESSBAR_DELAY);
116 break;
117
118 case MOVE:
119 // Labels and progress bar
120 uplabel = new FXLabel(contents, _("Source:"), NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
121 downlabel = new FXLabel(contents, _("Target:"), NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
122 progressbar = new FXProgressBar(contents, NULL, 0, LAYOUT_FILL_X|FRAME_SUNKEN|FRAME_THICK|PROGRESSBAR_PERCENTAGE, 0, 0, 0, 0, PROGRESSDIALOG_WIDTH);
123 progressbar->setBarColor(fgcolor);
124 progressbar->setTextColor(textcolor);
125 progressbar->setTextAltColor(textaltcolor);
126 datatext = _("Moved data:");
127 datalabel = new FXLabel(contents, datatext, NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
128
129 // Timer on
130 getApp()->addTimeout(this, File::ID_TIMEOUT, SHOW_PROGRESSBAR_DELAY);
131 break;
132
133 case DELETE:
134 // Labels
135 uplabel = new FXLabel(contents, _("Delete:"), NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
136 downlabel = new FXLabel(contents, _("From:"), NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
137 datalabel = NULL;
138
139 // Timer on
140 getApp()->addTimeout(this, File::ID_TIMEOUT, SHOW_PROGRESSBAR_DELAY);
141 break;
142
143 case CHMOD:
144 // Labels
145 uplabel = new FXLabel(contents, _("Changing permissions..."), NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
146 downlabel = new FXLabel(contents, _("File:"), NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
147 datalabel = NULL;
148
149 // Timer on
150 getApp()->addTimeout(this, File::ID_TIMEOUT, SHOW_PROGRESSBAR_DELAY);
151 break;
152
153 case CHOWN:
154 // Labels
155 uplabel = new FXLabel(contents, _("Changing owner..."), NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
156 downlabel = new FXLabel(contents, _("File:"), NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
157 datalabel = NULL;
158
159 // Timer on
160 getApp()->addTimeout(this, File::ID_TIMEOUT, SHOW_PROGRESSBAR_DELAY);
161 break;
162
163 #if defined(linux)
164 case MOUNT:
165 // Labels
166 uplabel = new FXLabel(contents, _("Mount file system..."), NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
167 downlabel = new FXLabel(contents, _("Mount the folder:"), NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
168 datalabel = NULL;
169 break;
170
171 case UNMOUNT:
172 // Labels
173 uplabel = new FXLabel(contents, _("Unmount file system..."), NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
174 downlabel = new FXLabel(contents, _("Unmount the folder:"), NULL, JUSTIFY_LEFT|LAYOUT_FILL_X);
175 datalabel = NULL;
176 break;
177 #endif
178
179 default: // Other : RENAME, SYMLINK, ARCHIVE, EXTRACT, PKG_INSTALL, PKG_UNINSTALL
180 // Progress dialog not used
181 uplabel = NULL;
182 downlabel = NULL;
183 datalabel = NULL;
184 }
185
186 FXbool confirm_overwrite = getApp()->reg().readUnsignedEntry("OPTIONS", "confirm_overwrite", true);
187
188 // Initialize the overwrite flags
189 if (confirm_overwrite)
190 {
191 overwrite = false;
192 overwrite_all = false;
193 skip_all = false;
194 }
195 else
196 {
197 overwrite = true;
198 overwrite_all = true;
199 skip_all = false;
200 }
201
202 // Total data read
203 totaldata = 0;
204
205 // Owner window
206 ownerwin = owner;
207
208 // Number of selected items
209 numsel = num;
210
211 // Error message box
212 mbox = new MessageBox(ownerwin, _("Error"), "", errorbigicon, BOX_OK_CANCEL|DECOR_TITLE|DECOR_BORDER);
213 }
214
215
216 // Destructor
~File()217 File::~File()
218 {
219 getApp()->removeTimeout(this, File::ID_TIMEOUT);
220 delete progressbar;
221 delete mbox;
222 }
223
224
225 // Create and initialize
create()226 void File::create()
227 {
228 DialogBox::create();
229 }
230
231
232 // Force timeout for progress dialog (used before opening confirmation or error dialogs)
forceTimeout(void)233 void File::forceTimeout(void)
234 {
235 getApp()->removeTimeout(this, File::ID_TIMEOUT);
236 hide();
237 getApp()->forceRefresh();
238 getApp()->flush();
239 }
240
241
242 // Restart timeout for progress dialog (used after closing confirmation or error dialogs)
restartTimeout(void)243 void File::restartTimeout(void)
244 {
245 getApp()->addTimeout(this, File::ID_TIMEOUT, SHOW_PROGRESSBAR_DELAY);
246 }
247
248
249 // Read bytes
fullread(int fd,FXuchar * ptr,FXlong len)250 FXlong File::fullread(int fd, FXuchar* ptr, FXlong len)
251 {
252 FXlong nread;
253
254 #ifdef EINTR
255 do
256 {
257 nread = read(fd, ptr, len);
258 } while (nread < 0 && errno == EINTR);
259 #else
260 nread = read(fd, ptr, len);
261 #endif
262 return(nread);
263 }
264
265
266 // Write bytes
fullwrite(int fd,const FXuchar * ptr,FXlong len)267 FXlong File::fullwrite(int fd, const FXuchar* ptr, FXlong len)
268 {
269 FXlong nwritten, ntotalwritten = 0;
270
271 while (len > 0)
272 {
273 nwritten = write(fd, ptr, len);
274 if (nwritten < 0)
275 {
276 #ifdef EINTR
277 if (errno == EINTR)
278 {
279 continue;
280 }
281 #endif
282 return(-1);
283 }
284 ntotalwritten += nwritten;
285 ptr += nwritten;
286 len -= nwritten;
287 }
288 return(ntotalwritten);
289 }
290
291
292 // Construct overwrite dialog and get user answer
getOverwriteAnswer(FXString srcpath,FXString tgtpath)293 FXuint File::getOverwriteAnswer(FXString srcpath, FXString tgtpath)
294 {
295 // Message string
296 FXString msg;
297
298 if (::isDirectory(tgtpath))
299 {
300 msg.format(_("Folder %s already exists.\nOverwrite?\n=> Caution, files within this folder could be overwritten!"), tgtpath.text());
301 }
302 else
303 {
304 msg.format(_("File %s already exists.\nOverwrite?"), tgtpath.text());
305 }
306
307 // Read time format
308 FXString timeformat = getApp()->reg().readStringEntry("SETTINGS", "time_format", DEFAULT_TIME_FORMAT);
309
310 // Get the size and mtime of the source and target
311 struct stat linfo;
312 FXString srcsize, srcmtime, tgtsize, tgtmtime;
313 FXbool statsrc = false, stattgt = false;
314 if (lstatrep(srcpath.text(), &linfo) == 0)
315 {
316 statsrc = true;
317 srcmtime = FXSystem::time(timeformat.text(), linfo.st_mtime);
318 char buf[MAXPATHLEN];
319 if (S_ISDIR(linfo.st_mode)) // Folder
320 {
321 FXulong dirsize = 0;
322 FXuint nbfiles = 0, nbsubfolders = 0;
323 FXulong totalsize=0;
324 strlcpy(buf, srcpath.text(), srcpath.length()+1);
325 dirsize = pathsize(buf, &nbfiles, &nbsubfolders,&totalsize);
326 #if __WORDSIZE == 64
327 snprintf(buf, sizeof(buf), "%lu", dirsize);
328 #else
329 snprintf(buf, sizeof(buf), "%llu", dirsize);
330 #endif
331 }
332 else // File
333 #if __WORDSIZE == 64
334 {
335 snprintf(buf, sizeof(buf), "%lu", (FXulong)linfo.st_size);
336 }
337 #else
338 {
339 snprintf(buf, sizeof(buf), "%llu", (FXulong)linfo.st_size);
340 }
341 #endif
342 srcsize = ::hSize(buf);
343 }
344 if (lstatrep(tgtpath.text(), &linfo) == 0)
345 {
346 stattgt = true;
347 tgtmtime = FXSystem::time(timeformat.text(), linfo.st_mtime);
348 char buf[64];
349 if (S_ISDIR(linfo.st_mode)) // Folder
350 {
351 FXulong dirsize = 0;
352 FXuint nbfiles = 0, nbsubfolders = 0;
353 FXulong totalsize=0;
354
355 strlcpy(buf, tgtpath.text(), tgtpath.length()+1);
356 dirsize = pathsize(buf, &nbfiles, &nbsubfolders,&totalsize);
357 #if __WORDSIZE == 64
358 snprintf(buf, sizeof(buf), "%lu", dirsize);
359 #else
360 snprintf(buf, sizeof(buf), "%llu", dirsize);
361 #endif
362 }
363 else // File
364 #if __WORDSIZE == 64
365 {
366 snprintf(buf, sizeof(buf), "%lu", (FXulong)linfo.st_size);
367 }
368 #else
369 {
370 snprintf(buf, sizeof(buf), "%llu", (FXulong)linfo.st_size);
371 }
372 #endif
373 tgtsize = ::hSize(buf);
374 }
375
376 // Overwrite dialog
377 OverwriteBox* dlg;
378 if (statsrc && stattgt)
379 {
380 if (numsel == 1)
381 {
382 dlg = new OverwriteBox(ownerwin, _("Confirm Overwrite"), msg, srcsize, srcmtime, tgtsize, tgtmtime, OVWBOX_SINGLE_FILE);
383 }
384 else
385 {
386 dlg = new OverwriteBox(ownerwin, _("Confirm Overwrite"), msg, srcsize, srcmtime, tgtsize, tgtmtime);
387 }
388 }
389 else
390 {
391 if (numsel == 1)
392 {
393 dlg = new OverwriteBox(ownerwin, _("Confirm Overwrite"), msg, OVWBOX_SINGLE_FILE);
394 }
395 else
396 {
397 dlg = new OverwriteBox(ownerwin, _("Confirm Overwrite"), msg);
398 }
399 }
400
401 FXuint answer = dlg->execute(PLACEMENT_OWNER);
402 delete dlg;
403 restartTimeout();
404
405 return(answer);
406 }
407
408
409 // Copy ordinary file
410 // Return 0 to allow displaying an error message in the calling function
411 // Return -1 to prevent displaying an error message in the calling function
412 // Return -2 when an error has occurred during the copy
copyfile(const FXString & source,const FXString & target,const FXbool preserve_date)413 int File::copyfile(const FXString& source, const FXString& target, const FXbool preserve_date)
414 {
415 FXString destfile;
416 FXuchar buffer[32768];
417 struct stat info;
418 struct utimbuf timbuf;
419 FXlong nread, nwritten;
420 FXlong size, dataread = 0;
421 int src, dst;
422 int ok = false;
423
424 FXbool warn = getApp()->reg().readUnsignedEntry("OPTIONS", "preserve_date_warn", true);
425
426 if ((src = ::open(source.text(), O_RDONLY)) >= 0)
427 {
428 if (statrep(source.text(), &info) == 0)
429 {
430 // If destination is a directory
431 if (::isDirectory(target))
432 {
433 destfile = target+PATHSEPSTRING+FXPath::name(source);
434 }
435 else
436 {
437 destfile = target;
438 }
439
440 // Copy file block by block
441 size = info.st_size;
442 if ((dst = ::open(destfile.text(), O_WRONLY|O_CREAT|O_TRUNC, info.st_mode)) >= 0)
443 {
444 int error = false;
445
446 while (1)
447 {
448 errno = 0;
449 nread = File::fullread(src, buffer, sizeof(buffer));
450 int errcode = errno;
451 if (nread < 0)
452 {
453 forceTimeout();
454
455 FXString str;
456 if (errcode)
457 {
458 str.format(_("Can't copy file %s: %s"), target.text(), strerror(errcode));
459 }
460 else
461 {
462 str.format(_("Can't copy file %s"), target.text());
463 }
464 mbox->setText(str);
465 FXuint answer = mbox->execute(PLACEMENT_OWNER);
466
467 restartTimeout();
468 if (answer == BOX_CLICKED_CANCEL)
469 {
470 ::close(dst);
471 ::close(src);
472 cancelled = true;
473 return(false);
474 }
475 else
476 {
477 error = true; // An error has occurred
478 }
479 }
480 if (nread == 0)
481 {
482 break;
483 }
484
485 // Force timeout checking for progress dialog
486 checkTimeout();
487
488 // Set percentage value for progress dialog
489 dataread += nread;
490 totaldata += nread;
491
492 if (progressbar)
493 {
494 // Percentage
495 int pct = (100.0*dataread)/size;
496 progressbar->setProgress(pct);
497
498 // Total data copied
499 FXString hsize;
500 char size[64];
501
502 #if __WORDSIZE == 64
503 snprintf(size, sizeof(size)-1, "%ld", totaldata);
504 #else
505 snprintf(size, sizeof(size)-1, "%lld", totaldata);
506 #endif
507 hsize = ::hSize(size);
508 #if __WORDSIZE == 64
509 snprintf(size, sizeof(size)-1, "%s %s", datatext.text(), hsize.text());
510 #else
511 snprintf(size, sizeof(size)-1, "%s %s", datatext.text(), hsize.text());
512 #endif
513
514 datalabel->setText(size);
515 }
516
517 // Give cancel button an opportunity to be clicked
518 if (cancelButton)
519 {
520 getApp()->runModalWhileEvents(cancelButton);
521 }
522
523 // Set labels for progress dialog
524 FXString label = _("Source: ")+source;
525 if (uplabel)
526 {
527 uplabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
528 }
529 label = _("Target: ")+target;
530 if (downlabel)
531 {
532 downlabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
533 }
534 getApp()->repaint();
535
536 // If cancel button was clicked, close files and return
537 if (cancelled)
538 {
539 ::close(dst);
540 ::close(src);
541 return(false);
542 }
543 errno = 0;
544 nwritten = File::fullwrite(dst, buffer, nread);
545 errcode = errno;
546 if (nwritten < 0)
547 {
548 forceTimeout();
549
550 FXString str;
551 if (errcode)
552 {
553 str.format(_("Can't copy file %s: %s"), target.text(), strerror(errcode));
554 }
555 else
556 {
557 str.format(_("Can't copy file %s"), target.text());
558 }
559 mbox->setText(str);
560 FXuint answer = mbox->execute(PLACEMENT_OWNER);
561
562 restartTimeout();
563 if (answer == BOX_CLICKED_CANCEL)
564 {
565 ::close(dst);
566 ::close(src);
567 cancelled = true;
568 return(false);
569 }
570 else
571 {
572 error = true; // An error has occurred
573 }
574 }
575 }
576
577 // An error has occurred during the copy
578 if (error)
579 {
580 ok = -2;
581 }
582 else
583 {
584 ok = true;
585 }
586
587 ::close(dst);
588
589 // Keep original date if asked
590 if (preserve_date)
591 {
592 timbuf.actime = info.st_atime;
593 timbuf.modtime = info.st_mtime;
594 errno = 0;
595 int rc = utime(destfile.text(), &timbuf);
596 int errcode = errno;
597 if (warn && rc)
598 {
599 forceTimeout();
600
601 FXString str;
602 if (errcode)
603 {
604 str.format(_("Can't preserve date when copying file %s : %s"), target.text(), strerror(errcode));
605 }
606 else
607 {
608 str.format(_("Can't preserve date when copying file %s"), target.text());
609 }
610 mbox->setText(str);
611 FXuint answer = mbox->execute(PLACEMENT_OWNER);
612
613 restartTimeout();
614 if (answer == BOX_CLICKED_CANCEL)
615 {
616 ::close(src);
617 cancelled = true;
618 return(false);
619 }
620 }
621 }
622 }
623
624 #if defined(linux)
625 // If source file is on a ISO9660 file system (CD or DVD, thus read-only)
626 // then add to the target the write permission for the user
627 if (ok)
628 {
629 struct statfs fs;
630 if ((statfs(source.text(), &fs) == 0) && (fs.f_type == 0x9660))
631 {
632 ::chmod(target.text(), info.st_mode|S_IWUSR);
633 }
634 }
635 #endif
636 }
637 ::close(src);
638 }
639
640 // File cannot be opened
641 else
642 {
643 forceTimeout();
644 int errcode = errno;
645
646 FXString str;
647 if (errcode)
648 {
649 str.format(_("Can't copy file %s: %s"), target.text(), strerror(errcode));
650 }
651 else
652 {
653 str.format(_("Can't copy file %s"), target.text());
654 }
655 mbox->setText(str);
656 FXuint answer = mbox->execute(PLACEMENT_OWNER);
657
658 restartTimeout();
659 if (answer == BOX_CLICKED_CANCEL)
660 {
661 cancelled = true;
662 return(false);
663 }
664 ok = -1; // Prevent displaying an error message
665 // in the calling function
666 }
667 return(ok);
668 }
669
670
671 // Copy directory
copydir(const FXString & source,const FXString & target,struct stat & parentinfo,inodelist * inodes,const FXbool preserve_date)672 int File::copydir(const FXString& source, const FXString& target, struct stat& parentinfo, inodelist* inodes, const FXbool preserve_date)
673 {
674 DIR* dirp;
675 struct dirent* dp;
676 struct stat linfo;
677 struct utimbuf timbuf;
678 inodelist* in, inode;
679 FXString destfile, oldchild, newchild;
680
681 FXbool warn = getApp()->reg().readUnsignedEntry("OPTIONS", "preserve_date_warn", true);
682
683 // Destination file
684 destfile = target;
685
686 // See if visited this inode already
687 for (in = inodes; in; in = in->next)
688 {
689 if (in->st_ino == parentinfo.st_ino)
690 {
691 return(true);
692 }
693 }
694
695 // Try make directory, if none exists yet
696 if ((mkdir(destfile.text(), parentinfo.st_mode|S_IWUSR) != 0) && (errno != EEXIST))
697 {
698 return(false);
699 }
700
701 // Can we stat it
702 if ((lstatrep(destfile.text(), &linfo) != 0) || !S_ISDIR(linfo.st_mode))
703 {
704 return(false);
705 }
706
707 // Try open directory to copy
708 dirp = opendir(source.text());
709 if (!dirp)
710 {
711 return(false);
712 }
713
714 // Add this to the list
715 inode.st_ino = linfo.st_ino;
716 inode.next = inodes;
717
718 // Copy stuff
719 while ((dp = readdir(dirp)) != NULL)
720 {
721 if ((dp->d_name[0] != '.') || ((dp->d_name[1] != '\0') && ((dp->d_name[1] != '.') || (dp->d_name[2] != '\0'))))
722 {
723 oldchild = source;
724 if (!ISPATHSEP(oldchild[oldchild.length()-1]))
725 {
726 oldchild.append(PATHSEP);
727 }
728 oldchild.append(dp->d_name);
729 newchild = destfile;
730 if (!ISPATHSEP(newchild[newchild.length()-1]))
731 {
732 newchild.append(PATHSEP);
733 }
734 newchild.append(dp->d_name);
735 if (!copyrec(oldchild, newchild, &inode, preserve_date))
736 {
737 // If the cancel button was pressed
738 if (cancelled)
739 {
740 closedir(dirp);
741 return(false);
742 }
743
744 // Or a permission problem occured
745 else
746 {
747 FXString str;
748 if (::isDirectory(oldchild))
749 {
750 str.format(_("Can't copy folder %s : Permission denied"), oldchild.text());
751 }
752 else
753 {
754 str.format(_("Can't copy file %s : Permission denied"), oldchild.text());
755 }
756 forceTimeout();
757 mbox->setText(str);
758 FXuint answer = mbox->execute(PLACEMENT_OWNER);
759
760 restartTimeout();
761 if (answer == BOX_CLICKED_CANCEL)
762 {
763 closedir(dirp);
764 cancelled = true;
765 return(false);
766 }
767 }
768 }
769 }
770 }
771
772 // Close directory
773 closedir(dirp);
774
775 // Keep original date if asked
776 if (preserve_date)
777 {
778 if (lstatrep(source.text(), &linfo) == 0)
779 {
780 timbuf.actime = linfo.st_atime;
781 timbuf.modtime = linfo.st_mtime;
782 errno = 0;
783 int rc = utime(destfile.text(), &timbuf);
784 int errcode = errno;
785 if (warn && rc)
786 {
787 forceTimeout();
788 FXString str;
789 if (errcode)
790 {
791 str.format(_("Can't preserve date when copying folder %s: %s"), target.text(), strerror(errcode));
792 }
793 else
794 {
795 str.format(_("Can't preserve date when copying folder %s"), target.text());
796 }
797 mbox->setText(str);
798 FXuint answer = mbox->execute(PLACEMENT_OWNER);
799
800 restartTimeout();
801 if (answer == BOX_CLICKED_CANCEL)
802 {
803 cancelled = true;
804 return(false);
805 }
806 }
807 }
808 }
809
810 // Success
811 return(true);
812 }
813
814
815 // Recursive copy
copyrec(const FXString & source,const FXString & target,inodelist * inodes,const FXbool preserve_date)816 int File::copyrec(const FXString& source, const FXString& target, inodelist* inodes, const FXbool preserve_date)
817 {
818 struct stat linfo1, linfo2;
819
820 // Source file or directory does not exist
821 if (lstatrep(source.text(), &linfo1) != 0)
822 {
823 return(false);
824 }
825
826 // If target is not a directory, remove it if allowed
827 if (lstatrep(target.text(), &linfo2) == 0)
828 {
829 if (!S_ISDIR(linfo2.st_mode))
830 {
831 if (!(overwrite|overwrite_all))
832 {
833 return(false);
834 }
835 if (::unlink(target.text()) != 0)
836 {
837 return(false);
838 }
839 }
840 }
841
842 // Source is directory: copy recursively
843 if (S_ISDIR(linfo1.st_mode))
844 {
845 return(File::copydir(source, target, linfo1, inodes, preserve_date));
846 }
847
848 // Source is regular file: copy block by block
849 if (S_ISREG(linfo1.st_mode))
850 {
851 return(File::copyfile(source, target, preserve_date));
852 }
853
854 // Remove target if it already exists
855 if (existFile(target))
856 {
857 int ret = File::remove(target);
858 if (!ret)
859 {
860 return(false);
861 }
862 }
863
864 // Source is fifo: make a new one
865 if (S_ISFIFO(linfo1.st_mode))
866 {
867 return(mkfifo(target.text(), linfo1.st_mode));
868 }
869
870 // Source is device: make a new one
871 if (S_ISBLK(linfo1.st_mode) || S_ISCHR(linfo1.st_mode) || S_ISSOCK(linfo1.st_mode))
872 {
873 return(mknod(target.text(), linfo1.st_mode, linfo1.st_rdev) == 0);
874 }
875
876 // Source is symbolic link: make a new one
877 if (S_ISLNK(linfo1.st_mode))
878 {
879 FXString lnkfile = ::readLink(source);
880 return(::symlink(lnkfile.text(), target.text()) == 0);
881 }
882
883 // This shouldn't happen
884 return(false);
885 }
886
887
888 // Copy file (with progress dialog)
889 // Return 0 to allow displaying an error message in the calling function
890 // Return -1 to prevent displaying an error message in the calling function
copy(const FXString & source,const FXString & target,const FXbool confirm_dialog,const FXbool preserve_date)891 int File::copy(const FXString& source, const FXString& target, const FXbool confirm_dialog, const FXbool preserve_date)
892 {
893 FXString targetfile;
894
895 // Source doesn't exist
896 if (!existFile(source))
897 {
898 forceTimeout();
899 MessageBox::error(this, BOX_OK, _("Error"), _("Source %s doesn't exist"), source.text());
900 return(-1);
901 }
902
903 // Source and target are identical
904 if (::identical(target, source))
905 {
906 forceTimeout();
907 MessageBox::error(this, BOX_OK, _("Error"), _("Destination %s is identical to source"), target.text());
908 return(-1);
909 }
910
911 // Source path is included into target path
912 FXString str = source + PATHSEPSTRING;
913 if (target.left(str.length()) == str)
914 {
915 forceTimeout();
916 MessageBox::error(this, BOX_OK, _("Error"), _("Target %s is a sub-folder of source"), target.text());
917 return(-1);
918 }
919
920 // Target is an existing directory
921 if (::isDirectory(target))
922 {
923 targetfile = target+PATHSEPSTRING+FXPath::name(source);
924 }
925 else
926 {
927 targetfile = target;
928 }
929
930 // Source and target are identical => add a suffix to the name
931 if (::identical(source, targetfile))
932 {
933 FXString pathname = ::cleanPath(targetfile);
934 targetfile = ::buildCopyName(pathname, ::isDirectory(pathname)); // Remove trailing / if any
935 }
936 // Source and target file are identical
937 if (::identical(targetfile, source))
938 {
939 forceTimeout();
940 MessageBox::error(this, BOX_OK, _("Error"), _("Destination %s is identical to source"), targetfile.text());
941 return(-1);
942 }
943
944 // Target already exists
945 if (existFile(targetfile))
946 {
947 // Overwrite dialog if necessary
948 if ( (!(overwrite_all | skip_all)) & confirm_dialog )
949 {
950 FXString label = _("Source: ")+source;
951 if (uplabel)
952 {
953 uplabel->setText(::multiLines(label, MAX_MESSAGE_LENGTH));
954 }
955 label = _("Target: ")+targetfile;
956 if (downlabel)
957 {
958 downlabel->setText(::multiLines(label, MAX_MESSAGE_LENGTH));
959 }
960 getApp()->repaint();
961 forceTimeout();
962 FXuint answer = getOverwriteAnswer(source, targetfile);
963 switch (answer)
964 {
965 // Cancel
966 case 0:
967 forceTimeout();
968 cancelled = true;
969 return(false);
970
971 break;
972
973 // Overwrite
974 case 1:
975 overwrite = true;
976 break;
977
978 // Overwrite all
979 case 2:
980 overwrite_all = true;
981 break;
982
983 // Skip
984 case 3:
985 overwrite = false;
986 break;
987
988 // Skip all
989 case 4:
990 skip_all = true;
991 break;
992 }
993 }
994 if ( (!(overwrite | overwrite_all)) | skip_all )
995 {
996 return(true);
997 }
998
999 // Remove targetfile if source is not a directory
1000 if (!::isDirectory(source))
1001 {
1002 if (File::remove(targetfile) == false)
1003 {
1004 forceTimeout();
1005 return(false);
1006 }
1007 }
1008 }
1009
1010 // Copy file or directory
1011 return(File::copyrec(source, targetfile, NULL, preserve_date));
1012 }
1013
1014
1015 // Remove file or directory (with progress dialog)
1016 // Return 0 to allow displaying an error message in the calling function
1017 // Return -1 to prevent displaying an error message in the calling function
remove(const FXString & file)1018 int File::remove(const FXString& file)
1019 {
1020 FXString dirname;
1021 struct stat linfo;
1022 static FXbool ISDIR = false; // Caution! ISDIR is common to all File instances, is that we want?
1023
1024 if (lstatrep(file.text(), &linfo) == 0)
1025 {
1026 // It is a directory
1027 if (S_ISDIR(linfo.st_mode))
1028 {
1029 DIR* dirp = opendir(file.text());
1030 if (dirp)
1031 {
1032 struct dirent* dp;
1033 FXString child;
1034
1035 // Used to display only one progress dialog when deleting a directory
1036 ISDIR = true;
1037
1038 // Force timeout checking for progress dialog
1039 checkTimeout();
1040
1041 // Give cancel button an opportunity to be clicked
1042 if (cancelButton)
1043 {
1044 getApp()->runModalWhileEvents(cancelButton);
1045 }
1046
1047 // Set labels for progress dialog
1048 FXString label = _("Delete folder: ")+file;
1049 if (uplabel)
1050 {
1051 uplabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1052 }
1053 dirname = FXPath::directory(FXPath::absolute(file));
1054 label = _("From: ")+dirname;
1055 if (downlabel)
1056 {
1057 downlabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1058 }
1059 getApp()->repaint();
1060
1061 // If cancel button was clicked, return
1062 if (cancelled)
1063 {
1064 closedir(dirp);
1065 return(false);
1066 }
1067
1068 while ((dp = readdir(dirp)) != NULL)
1069 {
1070 if ((dp->d_name[0] != '.') || ((dp->d_name[1] != '\0') && ((dp->d_name[1] != '.') || (dp->d_name[2] != '\0'))))
1071 {
1072 child = file;
1073 if (!ISPATHSEP(child[child.length()-1]))
1074 {
1075 child.append(PATHSEP);
1076 }
1077 child.append(dp->d_name);
1078 if (!File::remove(child))
1079 {
1080 closedir(dirp);
1081 return(false);
1082 }
1083 }
1084 }
1085 closedir(dirp);
1086 }
1087 if (rmdir(file.text()) == -1)
1088 {
1089 int errcode = errno;
1090 forceTimeout();
1091
1092 FXString str;
1093 if (errcode)
1094 {
1095 str.format(_("Can't delete folder %s: %s"), file.text(), strerror(errcode));
1096 }
1097 else
1098 {
1099 str.format(_("Can't delete folder %s"), file.text());
1100 }
1101 mbox->setText(str);
1102 FXuint answer = mbox->execute(PLACEMENT_OWNER);
1103
1104 restartTimeout();
1105 if (answer == BOX_CLICKED_CANCEL)
1106 {
1107 cancelled = true;
1108 return(false);
1109 }
1110 return(-1); // To prevent displaying an error message
1111 // in the calling function
1112 }
1113 else
1114 {
1115 return(true);
1116 }
1117 }
1118 else
1119 {
1120 // If it was not a directory
1121 if (!ISDIR)
1122 {
1123 // Force timeout checking for progress dialog
1124 checkTimeout();
1125
1126 // Give cancel button an opportunity to be clicked
1127 if (cancelButton)
1128 {
1129 getApp()->runModalWhileEvents(cancelButton);
1130 }
1131
1132 // Set labels for progress dialog
1133 FXString label = _("Delete:")+file;
1134 if (uplabel)
1135 {
1136 uplabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1137 }
1138 dirname = FXPath::directory(FXPath::absolute(file));
1139 label = _("From: ")+dirname;
1140 if (downlabel)
1141 {
1142 downlabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1143 }
1144 getApp()->repaint();
1145
1146 // If cancel button was clicked, return
1147 if (cancelled)
1148 {
1149 return(false);
1150 }
1151 }
1152 if (::unlink(file.text()) == -1)
1153 {
1154 int errcode = errno;
1155 forceTimeout();
1156
1157 FXString str;
1158 if (errcode)
1159 {
1160 str.format(_("Can't delete file %s: %s"), file.text(), strerror(errcode));
1161 }
1162 else
1163 {
1164 str.format(_("Can't delete file %s"), file.text());
1165 }
1166 mbox->setText(str);
1167 FXuint answer = mbox->execute(PLACEMENT_OWNER);
1168
1169 restartTimeout();
1170 if (answer == BOX_CLICKED_CANCEL)
1171 {
1172 cancelled = true;
1173 return(false);
1174 }
1175 return(-1); // To prevent displaying an error message
1176 // in the calling function
1177 }
1178 else
1179 {
1180 return(true);
1181 }
1182 }
1183 }
1184 return(-1);
1185 }
1186
1187
1188 // Rename a file or a directory (no progress dialog)
1189 // Return 0 to allow displaying an error message in the calling function
1190 // Return -1 to prevent displaying an error message in the calling function
rename(const FXString & source,const FXString & target)1191 int File::rename(const FXString& source, const FXString& target)
1192 {
1193 // Source doesn't exist
1194 if (!existFile(source))
1195 {
1196 MessageBox::error(this, BOX_OK, _("Error"), _("Source %s doesn't exist"), source.text());
1197 return(-1);
1198 }
1199
1200 // Source and target are identical
1201 if (::identical(target, source))
1202 {
1203 MessageBox::error(this, BOX_OK, _("Error"), _("Destination %s is identical to source"), target.text());
1204 return(-1);
1205 }
1206
1207 // Target already exists => only allow overwriting destination if both source and target are files
1208 if (existFile(target))
1209 {
1210 // Source or target are a directory
1211 if (::isDirectory(source) || ::isDirectory(target))
1212 {
1213 MessageBox::error(this, BOX_OK, _("Error"), _("Destination %s already exists"), target.text());
1214 return(-1);
1215 }
1216
1217 // Source and target are files
1218 else
1219 {
1220 FXuint answer = getOverwriteAnswer(source, target);
1221 if (answer == 0)
1222 {
1223 return(-1);
1224 }
1225 }
1226 }
1227
1228 // Rename file using the standard C function
1229 // This should only work for files that are on the same file system
1230 if (::rename(source.text(), target.text()) == 0)
1231 {
1232 return(true);
1233 }
1234
1235 int errcode = errno;
1236 if ((errcode != EXDEV) && (errcode != ENOTEMPTY))
1237 {
1238 forceTimeout();
1239 MessageBox::error(this, BOX_OK, _("Error"), _("Can't rename to target %s: %s"), target.text(), strerror(errcode));
1240 return(-1);
1241 }
1242
1243 // If files are on different file systems, use the copy/delete scheme and preserve the original date
1244 int ret = this->copy(source, target, false, true);
1245 if (ret == true)
1246 {
1247 return(remove(source.text()) == true);
1248 }
1249 else
1250 {
1251 return(false);
1252 }
1253 }
1254
1255
1256 // Move files
1257 // Return 0 to allow displaying an error message in the calling function
1258 // Return -1 to prevent displaying an error message in the calling function
move(const FXString & source,const FXString & target,const FXbool restore)1259 int File::move(const FXString& source, const FXString& target, const FXbool restore)
1260 {
1261 // Source doesn't exist
1262 if (!existFile(source))
1263 {
1264 forceTimeout();
1265 MessageBox::error(this, BOX_OK, _("Error"), _("Source %s doesn't exist"), source.text());
1266 return(-1);
1267 }
1268
1269 // Source and target are identical
1270 if (identical(target, source))
1271 {
1272 forceTimeout();
1273 MessageBox::error(this, BOX_OK, _("Error"), _("Destination %s is identical to source"), target.text());
1274 return(-1);
1275 }
1276
1277 // Source path is included into target path
1278 FXString str = source + PATHSEPSTRING;
1279 if (target.left(str.length()) == str)
1280 {
1281 forceTimeout();
1282 MessageBox::error(this, BOX_OK, _("Error"), _("Target %s is a sub-folder of source"), target.text());
1283 return(-1);
1284 }
1285
1286 // Target is an existing directory (don't do this in the restore case)
1287 FXString targetfile;
1288 if (!restore && ::isDirectory(target))
1289 {
1290 targetfile = target+PATHSEPSTRING+FXPath::name(source);
1291 }
1292 else
1293 {
1294 targetfile = target;
1295 }
1296
1297 // Source and target file are identical
1298 if (::identical(targetfile, source))
1299 {
1300 forceTimeout();
1301 MessageBox::error(this, BOX_OK, _("Error"), _("Destination %s is identical to source"), targetfile.text());
1302 return(-1);
1303 }
1304
1305 // Force timeout checking for progress dialog
1306 checkTimeout();
1307
1308 // Give cancel button an opportunity to be clicked
1309 if (cancelButton)
1310 {
1311 getApp()->runModalWhileEvents(cancelButton);
1312 }
1313
1314 // Set labels for progress dialog
1315 FXString label = _("Source: ")+source;
1316 if (uplabel)
1317 {
1318 uplabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1319 }
1320 label = _("Target: ")+target;
1321 if (downlabel)
1322 {
1323 downlabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1324 }
1325 getApp()->repaint();
1326
1327 // Target file already exists
1328 if (existFile(targetfile))
1329 {
1330 // Overwrite dialog if necessary
1331 if (!overwrite_all & !skip_all)
1332 {
1333 forceTimeout();
1334 FXuint answer = getOverwriteAnswer(source, targetfile);
1335 restartTimeout();
1336 switch (answer)
1337 {
1338 // Cancel
1339 case 0:
1340 forceTimeout();
1341 cancelled = true;
1342 return(false);
1343
1344 break;
1345
1346 // Overwrite
1347 case 1:
1348 overwrite = true;
1349 break;
1350
1351 // Overwrite all
1352 case 2:
1353 overwrite_all = true;
1354 break;
1355
1356 // Skip
1357 case 3:
1358 overwrite = false;
1359 break;
1360
1361 // Skip all
1362 case 4:
1363 skip_all = true;
1364 break;
1365 }
1366 }
1367 if ( (!(overwrite | overwrite_all)) | skip_all )
1368 {
1369 // Hide progress dialog and restart timer
1370 forceTimeout();
1371 restartTimeout();
1372
1373 return(true);
1374 }
1375 }
1376
1377 // Get the size of the source
1378 FXulong srcsize = 0;
1379 struct stat linfo;
1380 if (lstatrep(source.text(), &linfo) == 0)
1381 {
1382 char buf[MAXPATHLEN];
1383 if (S_ISDIR(linfo.st_mode)) // Folder
1384 {
1385 FXuint nbfiles = 0, nbsubfolders = 0;
1386 FXulong totalsize=0;
1387 strlcpy(buf, source.text(), source.length()+1);
1388 srcsize = pathsize(buf, &nbfiles, &nbsubfolders,&totalsize);
1389 totaldata += srcsize;
1390 }
1391 else // File
1392 {
1393 srcsize = (FXulong)linfo.st_size;
1394 totaldata += srcsize;
1395 }
1396 }
1397
1398 if (progressbar)
1399 {
1400 // Trick to display a percentage
1401 int pct = (100.0*rand())/RAND_MAX+50;
1402 progressbar->setProgress((int)pct);
1403
1404 // Total data moved
1405 FXString hsize;
1406 char size[64];
1407
1408 #if __WORDSIZE == 64
1409 snprintf(size, sizeof(size)-1, "%ld", totaldata);
1410 #else
1411 snprintf(size, sizeof(size)-1, "%lld", totaldata);
1412 #endif
1413 hsize = ::hSize(size);
1414 #if __WORDSIZE == 64
1415 snprintf(size, sizeof(size)-1, "%s %s", datatext.text(), hsize.text());
1416 #else
1417 snprintf(size, sizeof(size)-1, "%s %s", datatext.text(), hsize.text());
1418 #endif
1419
1420 datalabel->setText(size);
1421 }
1422
1423 // Rename file using the standard C function
1424 // This should only work for files that are on the same file system
1425 if (::rename(source.text(), targetfile.text()) == 0)
1426 {
1427 return(true);
1428 }
1429
1430 int errcode = errno;
1431 if ((errcode != EXDEV) && (errcode != ENOTEMPTY))
1432 {
1433 forceTimeout();
1434 MessageBox::error(this, BOX_OK, _("Error"), _("Can't rename to target %s: %s"), targetfile.text(), strerror(errcode));
1435 return(-1);
1436 }
1437
1438 // If files are on different file systems, use the copy/delete scheme and preserve the original date
1439 totaldata -= srcsize; // Avoid counting data twice
1440
1441 int ret = this->copy(source, target, false, true);
1442
1443 // Success
1444 if (ret == true)
1445 {
1446 return(remove(source.text()) == true);
1447 }
1448
1449 // Error during copy
1450 else if (ret == -2)
1451 {
1452 return true;
1453 }
1454
1455 // Operation cancelled
1456 else
1457 {
1458 return(false);
1459 }
1460 }
1461
1462
1463 // Symbolic Link file (no progress dialog)
1464 // Return 0 to allow displaying an error message in the calling function
1465 // Return -1 to prevent displaying an error message in the calling function
symlink(const FXString & source,const FXString & target)1466 int File::symlink(const FXString& source, const FXString& target)
1467 {
1468 // Source doesn't exist
1469 if (!existFile(source))
1470 {
1471 forceTimeout();
1472 MessageBox::error(this, BOX_OK, _("Error"), _("Source %s doesn't exist"), source.text());
1473 return(-1);
1474 }
1475
1476 // Source and target are identical
1477 if (::identical(target, source))
1478 {
1479 forceTimeout();
1480 MessageBox::error(this, BOX_OK, _("Error"), _("Destination %s is identical to source"), target.text());
1481 return(-1);
1482 }
1483
1484 // Target is an existing directory
1485 FXString targetfile;
1486 if (::isDirectory(target))
1487 {
1488 targetfile = target+PATHSEPSTRING+FXPath::name(source);
1489 }
1490 else
1491 {
1492 targetfile = target;
1493 }
1494
1495 // Source and target are identical
1496 if (::identical(targetfile, source))
1497 {
1498 forceTimeout();
1499 MessageBox::error(this, BOX_OK, _("Error"), _("Destination %s is identical to source"), targetfile.text());
1500 return(-1);
1501 }
1502
1503 // Target already exists
1504 if (existFile(targetfile))
1505 {
1506 // Overwrite dialog if necessary
1507 if (!(overwrite_all | skip_all))
1508 {
1509 FXuint answer = getOverwriteAnswer(source, targetfile);
1510 switch (answer)
1511 {
1512 // Cancel
1513 case 0:
1514 forceTimeout();
1515 return(false);
1516
1517 break;
1518
1519 // Overwrite
1520 case 1:
1521 overwrite = true;
1522 break;
1523
1524 // Overwrite all
1525 case 2:
1526 overwrite_all = true;
1527 break;
1528
1529 // Skip
1530 case 3:
1531 overwrite = false;
1532 break;
1533
1534 // Skip all
1535 case 4:
1536 skip_all = true;
1537 break;
1538 }
1539 }
1540 if ( (!(overwrite | overwrite_all)) | skip_all )
1541 {
1542 return(true);
1543 }
1544 }
1545
1546 // Create symbolic link using the standard C function
1547 errno = 0;
1548
1549 // Use the relative path for the symbolic link
1550 FXString relativepath;
1551 if (existFile(target) && ::isDirectory(target))
1552 {
1553 relativepath = FXPath::relative(target, source);
1554 }
1555 else
1556 {
1557 relativepath = FXPath::relative(FXPath::directory(target), source);
1558 }
1559
1560 int ret = ::symlink(relativepath.text(), targetfile.text());
1561
1562 int errcode = errno;
1563 if (ret == 0)
1564 {
1565 return(true);
1566 }
1567 else
1568 {
1569 forceTimeout();
1570 if (errcode)
1571 {
1572 MessageBox::error(this, BOX_OK, _("Error"), _("Can't symlink %s: %s"), target.text(), strerror(errcode));
1573 }
1574 else
1575 {
1576 MessageBox::error(this, BOX_OK, _("Error"), _("Can't symlink %s"), target.text());
1577 }
1578 return(-1);
1579 }
1580 }
1581
1582
1583 // Chmod a file or directory, recursively or not
1584 // We don't process symbolic links (since their permissions cannot be changed)
1585 //
1586 // Note : the variable file returns the last processed file
1587 // It can be different from the initial path, if recursive chmod is used
1588 // (Used to fill an error message, if needed)
chmod(char * path,char * file,mode_t mode,FXbool rec,const FXbool dironly,const FXbool fileonly)1589 int File::chmod(char* path, char* file, mode_t mode, FXbool rec, const FXbool dironly, const FXbool fileonly)
1590 {
1591 struct stat linfo;
1592
1593 // Initialise the file variable with the initial path
1594 strlcpy(file, path, strlen(path)+1);
1595
1596 // If it doesn't exist
1597 if (lstatrep(path, &linfo))
1598 {
1599 return(-1);
1600 }
1601
1602 // If it's a symbolic link
1603 if (S_ISLNK(linfo.st_mode))
1604 {
1605 return(0);
1606 }
1607
1608 if (!S_ISDIR(linfo.st_mode)) // File
1609 {
1610 if (dironly)
1611 {
1612 return(0);
1613 }
1614
1615 // Force timeout checking for progress dialog
1616 checkTimeout();
1617
1618 // Give cancel button an opportunity to be clicked
1619 if (cancelButton)
1620 {
1621 getApp()->runModalWhileEvents(cancelButton);
1622 }
1623
1624 // Set labels for progress dialog
1625 FXString label = _("Changing permissions...");
1626 uplabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1627 label = _("File:")+FXString(path);
1628 downlabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1629 getApp()->repaint();
1630
1631 // If cancel button was clicked, return
1632 if (cancelled)
1633 {
1634 return(-1);
1635 }
1636
1637 return(::chmod(path, mode));
1638 }
1639 else // Directory
1640 {
1641 if ((rec == false) && !fileonly)
1642 {
1643 // Force timeout checking for progress dialog
1644 checkTimeout();
1645
1646 // Give cancel button an opportunity to be clicked
1647 if (cancelButton)
1648 {
1649 getApp()->runModalWhileEvents(cancelButton);
1650 }
1651
1652 // Set labels for progress dialog
1653 FXString label = _("Changing permissions...");
1654 uplabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1655 label = _("Folder: ")+FXString(path);
1656 downlabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1657 getApp()->repaint();
1658
1659 // If cancel button was clicked, return
1660 if (cancelled)
1661 {
1662 return(-1);
1663 }
1664
1665 if (::chmod(path, mode)) // Do not change recursively
1666 {
1667 return(-1);
1668 }
1669 }
1670 else
1671 {
1672 return(rchmod(path, file, mode, dironly, fileonly)); // Recursive change
1673 }
1674 }
1675 return(0);
1676 }
1677
1678
1679 // Recursive chmod for a directory
1680 // We don't process symbolic links (since their permissions cannot be changed)
rchmod(char * path,char * file,mode_t mode,const FXbool dironly,const FXbool fileonly)1681 int File::rchmod(char* path, char* file, mode_t mode, const FXbool dironly, const FXbool fileonly)
1682 {
1683 struct stat linfo;
1684
1685 // Initialize the file variable with the initial path
1686 strlcpy(file, path, strlen(path)+1);
1687
1688 // If it doesn't exist
1689 if (lstatrep(path, &linfo))
1690 {
1691 return(-1);
1692 }
1693
1694 // If it's a symbolic link
1695 if (S_ISLNK(linfo.st_mode))
1696 {
1697 return(0);
1698 }
1699
1700 if (!S_ISDIR(linfo.st_mode)) // File
1701 {
1702 if (dironly)
1703 {
1704 return(0);
1705 }
1706
1707 // Force timeout checking for progress dialog
1708 checkTimeout();
1709
1710 // Give cancel button an opportunity to be clicked
1711 if (cancelButton)
1712 {
1713 getApp()->runModalWhileEvents(cancelButton);
1714 }
1715
1716 // Set labels for progress dialog
1717 FXString label = _("Changing permissions...");
1718 uplabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1719 label = _("File:")+FXString(path);
1720 downlabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1721 getApp()->repaint();
1722
1723 // If cancel button was clicked, return
1724 if (cancelled)
1725 {
1726 return(-1);
1727 }
1728
1729 return(::chmod(path, mode));
1730 }
1731
1732 DIR* dir;
1733 struct dirent* entry;
1734 int i, pl = strlen(path);
1735
1736 if (!(dir = opendir(path)))
1737 {
1738 return(-1);
1739 }
1740
1741 for (i = 0; (entry = readdir(dir)); i++)
1742 {
1743 if ((entry->d_name[0] != '.') || ((entry->d_name[1] != '\0') &&
1744 ((entry->d_name[1] != '.') ||
1745 (entry->d_name[2] != '\0'))))
1746 {
1747 int pl1 = pl, l = strlen(entry->d_name);
1748 char* path1 = (char*)alloca(pl1+l+2);
1749
1750 strlcpy(path1, path, strlen(path)+1);
1751 if (path1[pl1-1] != '/')
1752 {
1753 path1[pl1++] = '/';
1754 }
1755 strlcpy(path1+pl1, entry->d_name, strlen(entry->d_name)+1);
1756
1757 // Modify the file variable with the new path
1758 strlcpy(file, path1, strlen(path1)+1);
1759 if (rchmod(path1, file, mode, dironly, fileonly))
1760 {
1761 closedir(dir);
1762 return(-1);
1763 }
1764 }
1765 }
1766
1767 if (closedir(dir))
1768 {
1769 return(-1);
1770 }
1771
1772 if (fileonly)
1773 {
1774 return(0);
1775 }
1776 else
1777 {
1778 return(::chmod(path, mode));
1779 }
1780 }
1781
1782
1783 // Chown a file or directory, recursively or not
1784 // We don't follow symbolic links
1785 //
1786 // Note : the variable file returns the last processed file
1787 // It can be different from the initial path, if recursive chmod is used
1788 // (Used to fill an error message, if needed)
chown(char * path,char * file,uid_t uid,gid_t gid,const FXbool rec,const FXbool dironly,const FXbool fileonly)1789 int File::chown(char* path, char* file, uid_t uid, gid_t gid, const FXbool rec, const FXbool dironly, const FXbool fileonly)
1790 {
1791 struct stat linfo;
1792
1793 // Initialise the file variable with the initial path
1794 strlcpy(file, path, strlen(path)+1);
1795
1796 // If it doesn't exist
1797 if (lstatrep(path, &linfo))
1798 {
1799 return(-1);
1800 }
1801
1802 if (!S_ISDIR(linfo.st_mode)) // File
1803 {
1804 if (dironly)
1805 {
1806 return(0);
1807 }
1808
1809 // Force timeout checking for progress dialog
1810 checkTimeout();
1811
1812 // Give cancel button an opportunity to be clicked
1813 if (cancelButton)
1814 {
1815 getApp()->runModalWhileEvents(cancelButton);
1816 }
1817
1818 // Set labels for progress dialog
1819 FXString label = _("Changing owner...");
1820 uplabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1821 label = _("File:")+FXString(path);
1822 downlabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1823 getApp()->repaint();
1824
1825 // If cancel button was clicked, return
1826 if (cancelled)
1827 {
1828 return(-1);
1829 }
1830
1831 if (::lchown(path, uid, gid))
1832 {
1833 return(-1);
1834 }
1835 }
1836 else // Directory
1837 {
1838 if ((rec == false) && !fileonly)
1839 {
1840 // Force timeout checking for progress dialog
1841 checkTimeout();
1842
1843 // Give cancel button an opportunity to be clicked
1844 if (cancelButton)
1845 {
1846 getApp()->runModalWhileEvents(cancelButton);
1847 }
1848
1849 // Set labels for progress dialog
1850 FXString label = _("Changing owner...");
1851 uplabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1852 label = _("Folder: ")+FXString(path);
1853 downlabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1854 getApp()->repaint();
1855
1856 // If cancel button was clicked, return
1857 if (cancelled)
1858 {
1859 return(-1);
1860 }
1861
1862 if (::lchown(path, uid, gid)) // Do not change recursively
1863 {
1864 return(-1);
1865 }
1866 }
1867 else if (rchown(path, file, uid, gid, dironly, fileonly)) // Recursive change
1868 {
1869 return(-1);
1870 }
1871 }
1872 return(0);
1873 }
1874
1875
1876 // Recursive chown for a directory
1877 // We don't follow symbolic links
rchown(char * path,char * file,uid_t uid,gid_t gid,const FXbool dironly,const FXbool fileonly)1878 int File::rchown(char* path, char* file, uid_t uid, gid_t gid, const FXbool dironly, const FXbool fileonly)
1879 {
1880 struct stat linfo;
1881
1882 // Initialise the file variable with the initial path
1883 strlcpy(file, path, strlen(path)+1);
1884
1885 // If it doesn't exist
1886 if (lstatrep(path, &linfo))
1887 {
1888 return(-1);
1889 }
1890
1891 if (!S_ISDIR(linfo.st_mode)) // file
1892 {
1893 if (dironly)
1894 {
1895 return(0);
1896 }
1897
1898 // Force timeout checking for progress dialog
1899 checkTimeout();
1900
1901 // Give cancel button an opportunity to be clicked
1902 if (cancelButton)
1903 {
1904 getApp()->runModalWhileEvents(cancelButton);
1905 }
1906
1907 // Set labels for progress dialog
1908 FXString label = _("Changing owner...");
1909 uplabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1910 label = _("File:")+FXString(path);
1911 downlabel->setText(::truncLine(label, MAX_MESSAGE_LENGTH));
1912 getApp()->repaint();
1913
1914 // If cancel button was clicked, return
1915 if (cancelled)
1916 {
1917 return(-1);
1918 }
1919
1920 return(::lchown(path, uid, gid));
1921 }
1922
1923 DIR* dir;
1924 struct dirent* entry;
1925 int i, pl = strlen(path);
1926
1927 if (!(dir = opendir(path)))
1928 {
1929 return(-1);
1930 }
1931
1932 for (i = 0; (entry = readdir(dir)); i++)
1933 {
1934 if ((entry->d_name[0] != '.') || ((entry->d_name[1] != '\0') &&
1935 ((entry->d_name[1] != '.') ||
1936 (entry->d_name[2] != '\0'))))
1937 {
1938 int pl1 = pl, l = strlen(entry->d_name);
1939 char* path1 = (char*)alloca(pl1+l+2);
1940
1941 strlcpy(path1, path, strlen(path)+1);
1942 if (path1[pl1-1] != '/')
1943 {
1944 path1[pl1++] = '/';
1945 }
1946 strlcpy(path1+pl1, entry->d_name, strlen(entry->d_name)+1);
1947 strlcpy(file, path1, strlen(path1)+1);
1948 if (rchown(path1, file, uid, gid, dironly, fileonly))
1949 {
1950 closedir(dir);
1951 return(-1);
1952 }
1953 }
1954 }
1955
1956 if (closedir(dir))
1957 {
1958 return(-1);
1959 }
1960
1961 if (fileonly)
1962 {
1963 return(0);
1964 }
1965 else
1966 {
1967 return(::lchown(path, uid, gid));
1968 }
1969 }
1970
1971
1972 // Extract an archive in a specified directory
extract(const FXString name,const FXString dir,const FXString cmd)1973 int File::extract(const FXString name, const FXString dir, const FXString cmd)
1974 {
1975 int ret;
1976
1977 // Change to the specified directory
1978 FXString currentdir = FXSystem::getCurrentDirectory();
1979
1980 ret = chdir(dir.text());
1981 if (ret < 0)
1982 {
1983 int errcode = errno;
1984 if (errcode)
1985 {
1986 MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), dir.text(), strerror(errcode));
1987 }
1988 else
1989 {
1990 MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), dir.text());
1991 }
1992
1993 return(0);
1994 }
1995
1996 // Make and show command window
1997 CommandWindow* cmdwin = new CommandWindow(getApp(), _("Extract archive"), cmd, 30, 80);
1998 cmdwin->create();
1999 cmdwin->setIcon(archexticon);
2000
2001 // The command window object deletes itself after closing the window!
2002
2003 // Return to initial directory
2004 ret = chdir(currentdir.text());
2005 if (ret < 0)
2006 {
2007 int errcode = errno;
2008 if (errcode)
2009 {
2010 MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), currentdir.text(), strerror(errcode));
2011 }
2012 else
2013 {
2014 MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), currentdir.text());
2015 }
2016
2017 return(0);
2018 }
2019
2020 return(1);
2021 }
2022
2023
2024 // Create an archive
archive(const FXString name,const FXString cmd)2025 int File::archive(const FXString name, const FXString cmd)
2026 {
2027 // Target file already exists
2028 if (existFile(FXPath::dequote(name)))
2029 {
2030 FXString msg;
2031 msg.format(_("File %s already exists.\nOverwrite?"), name.text());
2032 OverwriteBox* dlg = new OverwriteBox(ownerwin, _("Confirm Overwrite"), msg, OVWBOX_SINGLE_FILE);
2033 FXuint answer = dlg->execute(PLACEMENT_OWNER);
2034 delete dlg;
2035 if (answer == 0)
2036 {
2037 return(false);
2038 }
2039 }
2040
2041 // Make and show command window
2042 CommandWindow* cmdwin = new CommandWindow(getApp(), _("Add to archive"), cmd, 30, 80);
2043 cmdwin->create();
2044 cmdwin->setIcon(archaddicon);
2045
2046 // The command window object deletes itself after closing the window!
2047
2048 return(1);
2049 }
2050
2051
2052 #if defined(linux)
mount(const FXString dir,const FXString msg,const FXString cmd,const FXuint op)2053 int File::mount(const FXString dir, const FXString msg, const FXString cmd, const FXuint op)
2054 {
2055 FXbool mount_messages = getApp()->reg().readUnsignedEntry("OPTIONS", "mount_messages", true);
2056
2057 // Show progress dialog (no timer here)
2058 show(PLACEMENT_OWNER);
2059 getApp()->forceRefresh();
2060 getApp()->flush();
2061
2062 // Set labels for progress dialog
2063 uplabel->setText(msg);
2064 downlabel->setText(dir.text());
2065 getApp()->repaint();
2066
2067 // Give cancel button an opportunity to be clicked
2068 if (cancelButton)
2069 {
2070 getApp()->runModalWhileEvents(cancelButton);
2071 }
2072
2073 // If cancel button was clicked, return
2074 if (cancelled)
2075 {
2076 return(-1);
2077 }
2078
2079 // Perform the mount/unmount command
2080 FILE* pcmd = popen(cmd.text(), "r");
2081 if (!pcmd)
2082 {
2083 MessageBox::error(this, BOX_OK, _("Error"), _("Failed command: %s"), cmd.text());
2084 return(-1);
2085 }
2086
2087 // Get error message if any
2088 char text[10000] = { 0 };
2089 FXString buf;
2090 while (fgets(text, sizeof(text), pcmd))
2091 {
2092 buf += text;
2093 }
2094 snprintf(text, sizeof(text)-1, "%s", buf.text());
2095
2096 // Close the stream
2097 if ((pclose(pcmd) == -1) && (strcmp(text, "") != 0))
2098 {
2099 MessageBox::error(this, BOX_OK, _("Error"), "%s", text);
2100 return(-1);
2101 }
2102
2103 // Hide progress dialog
2104 hide();
2105
2106 // Success message, eventually
2107 if (mount_messages)
2108 {
2109 if (op == MOUNT)
2110 {
2111 MessageBox::information(this, BOX_OK, _("Success"), _("Folder %s was successfully mounted."), dir.text());
2112 }
2113 else
2114 {
2115 MessageBox::information(this, BOX_OK, _("Success"), _("Folder %s was successfully unmounted."), dir.text());
2116 }
2117 }
2118 return(1);
2119 }
2120
2121
2122 // Install / Upgrade package
pkgInstall(const FXString name,const FXString cmd)2123 int File::pkgInstall(const FXString name, const FXString cmd)
2124 {
2125 // Make and show command window
2126 CommandWindow* cmdwin = new CommandWindow(getApp(), _("Install/Upgrade package"), cmd, 10, 80);
2127
2128 cmdwin->create();
2129
2130 FXString msg;
2131 msg.format(_("Installing package: %s \n"), name.text());
2132 cmdwin->appendText(msg.text());
2133
2134 // The command window object deletes itself after closing the window!
2135
2136 return(1);
2137 }
2138
2139
2140 // Uninstall package
pkgUninstall(const FXString name,const FXString cmd)2141 int File::pkgUninstall(const FXString name, const FXString cmd)
2142 {
2143 // Make and show command window
2144 CommandWindow* cmdwin = new CommandWindow(getApp(), _("Uninstall package"), cmd, 10, 80);
2145
2146 cmdwin->create();
2147
2148 FXString msg;
2149 msg.format(_("Uninstalling package: %s \n"), name.text());
2150 cmdwin->appendText(msg.text());
2151
2152 // The command window object deletes itself after closing the window!
2153
2154 return(1);
2155 }
2156
2157
2158 #endif
2159
2160
2161 // Handle cancel button in progress bar dialog
onCmdCancel(FXObject *,FXSelector,void *)2162 long File::onCmdCancel(FXObject*, FXSelector, void*)
2163 {
2164 cancelled = true;
2165 return(1);
2166 }
2167
2168
2169 // Handle timeout for progress bar
onTimeout(FXObject *,FXSelector,void *)2170 long File::onTimeout(FXObject*, FXSelector, void*)
2171 {
2172 show(PLACEMENT_OWNER);
2173 getApp()->forceRefresh();
2174 getApp()->flush();
2175 return(1);
2176 }
2177