This source file includes following definitions.
- chattr_is_modifiable
- chattr_fill_str
- fileattrtext_fill
- fileattrtext_callback
- fileattrtext_new
- chattr_draw_select
- chattr_toggle_select
- chattrboxes_draw_scrollbar
- chattrboxes_draw
- chattrboxes_rename
- checkboxes_save_state
- chattrboxes_down
- chattrboxes_page_down
- chattrboxes_end
- chattrboxes_up
- chattrboxes_page_up
- chattrboxes_home
- chattrboxes_execute_cmd
- chattrboxes_key
- chattrboxes_callback
- chattrboxes_handle_mouse_event
- chattrboxes_mouse_callback
- chattrboxes_new
- chattr_init
- chattr_dlg_create
- chattr_done
- try_chattr
- do_chattr
- chattr_apply_mask
- chattr_cmd
- chattr_get_as_str
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 #include <config.h>
33
34 #include <errno.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37
38 #include <ext2fs/ext2_fs.h>
39
40 #include "lib/global.h"
41
42 #include "lib/tty/tty.h"
43 #include "lib/tty/color.h"
44 #include "lib/skin.h"
45 #include "lib/vfs/vfs.h"
46 #include "lib/widget.h"
47 #include "lib/util.h"
48
49 #include "src/keymap.h"
50
51 #include "cmd.h"
52
53
54
55
56
57 #define B_MARKED B_USER
58 #define B_SETALL (B_USER + 1)
59 #define B_SETMRK (B_USER + 2)
60 #define B_CLRMRK (B_USER + 3)
61
62 #define BUTTONS 6
63
64 #define CHATTRBOXES(x) ((WChattrBoxes *) (x))
65
66
67
68 typedef struct WFileAttrText WFileAttrText;
69
70 struct WFileAttrText
71 {
72 Widget widget;
73
74 char *filename;
75 int filename_width;
76 char attrs[32 + 1];
77 };
78
79 typedef struct WChattrBoxes WChattrBoxes;
80
81 struct WChattrBoxes
82 {
83 WGroup base;
84
85 int pos;
86 int top;
87 };
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133 static struct
134 {
135 unsigned long flags;
136 char attr;
137 const char *text;
138 gboolean selected;
139 gboolean state;
140 } check_attr[] = {
141 { EXT2_SECRM_FL, 's', N_ ("Secure deletion"), FALSE, FALSE },
142 { EXT2_UNRM_FL, 'u', N_ ("Undelete"), FALSE, FALSE },
143 { EXT2_SYNC_FL, 'S', N_ ("Synchronous updates"), FALSE, FALSE },
144 { EXT2_DIRSYNC_FL, 'D', N_ ("Synchronous directory updates"), FALSE, FALSE },
145 { EXT2_IMMUTABLE_FL, 'i', N_ ("Immutable"), FALSE, FALSE },
146 { EXT2_APPEND_FL, 'a', N_ ("Append only"), FALSE, FALSE },
147 { EXT2_NODUMP_FL, 'd', N_ ("No dump"), FALSE, FALSE },
148 { EXT2_NOATIME_FL, 'A', N_ ("No update atime"), FALSE, FALSE },
149 { EXT2_COMPR_FL, 'c', N_ ("Compress"), FALSE, FALSE },
150 #ifdef EXT2_COMPRBLK_FL
151
152
153 { EXT2_COMPRBLK_FL, 'B', N_ ("Compressed clusters"), FALSE, FALSE },
154 #endif
155 #ifdef EXT2_DIRTY_FL
156
157
158 { EXT2_DIRTY_FL, 'Z', N_ ("Compressed dirty file"), FALSE, FALSE },
159 #endif
160 #ifdef EXT2_NOCOMPR_FL
161
162
163 { EXT2_NOCOMPR_FL, 'X', N_ ("Compression raw access"), FALSE, FALSE },
164 #endif
165 #ifdef EXT4_ENCRYPT_FL
166 { EXT4_ENCRYPT_FL, 'E', N_ ("Encrypted inode"), FALSE, FALSE },
167 #endif
168 { EXT3_JOURNAL_DATA_FL, 'j', N_ ("Journaled data"), FALSE, FALSE },
169 { EXT2_INDEX_FL, 'I', N_ ("Indexed directory"), FALSE, FALSE },
170 { EXT2_NOTAIL_FL, 't', N_ ("No tail merging"), FALSE, FALSE },
171 { EXT2_TOPDIR_FL, 'T', N_ ("Top of directory hierarchies"), FALSE, FALSE },
172 { EXT4_EXTENTS_FL, 'e', N_ ("Inode uses extents"), FALSE, FALSE },
173 #ifdef EXT4_HUGE_FILE_FL
174
175
176 { EXT4_HUGE_FILE_FL, 'h', N_ ("Huge_file"), FALSE, FALSE },
177 #endif
178 { FS_NOCOW_FL, 'C', N_ ("No COW"), FALSE, FALSE },
179 #ifdef FS_DAX_FL
180
181
182 { FS_DAX_FL, 'x', N_ ("Direct access for files"), FALSE, FALSE },
183 #endif
184 #ifdef EXT4_CASEFOLD_FL
185
186
187 { EXT4_CASEFOLD_FL, 'F', N_ ("Casefolded file"), FALSE, FALSE },
188 #endif
189 #ifdef EXT4_INLINE_DATA_FL
190 { EXT4_INLINE_DATA_FL, 'N', N_ ("Inode has inline data"), FALSE, FALSE },
191 #endif
192 #ifdef EXT4_PROJINHERIT_FL
193
194
195
196 { EXT4_PROJINHERIT_FL, 'P', N_ ("Project hierarchy"), FALSE, FALSE },
197 #endif
198 #ifdef EXT4_VERITY_FL
199
200
201
202
203 { EXT4_VERITY_FL, 'V', N_ ("Verity protected inode"), FALSE, FALSE },
204 #endif
205 };
206
207
208 static const size_t check_attr_num = G_N_ELEMENTS (check_attr);
209
210
211 static int check_attr_mod[32];
212 static int check_attr_mod_num = 0;
213
214
215 static int check_attr_width = 0;
216
217 static struct
218 {
219 int ret_cmd;
220 button_flags_t flags;
221 int width;
222 const char *text;
223 Widget *button;
224 } chattr_but[BUTTONS] = {
225 { B_SETALL, NORMAL_BUTTON, 0, N_ ("Set &all"), NULL },
226 { B_MARKED, NORMAL_BUTTON, 0, N_ ("&Marked all"), NULL },
227 { B_SETMRK, NORMAL_BUTTON, 0, N_ ("S&et marked"), NULL },
228 { B_CLRMRK, NORMAL_BUTTON, 0, N_ ("C&lear marked"), NULL },
229 { B_ENTER, DEFPUSH_BUTTON, 0, N_ ("&Set"), NULL },
230 { B_CANCEL, NORMAL_BUTTON, 0, N_ ("&Cancel"), NULL },
231 };
232
233 static gboolean flags_changed;
234 static int current_file;
235 static gboolean ignore_all;
236
237 static unsigned long and_mask, or_mask, flags;
238
239 static WFileAttrText *file_attr;
240
241
242 static const int wx = 3;
243
244
245
246
247
248 static inline gboolean
249 chattr_is_modifiable (size_t i)
250 {
251 return ((check_attr[i].flags & EXT2_FL_USER_MODIFIABLE) != 0);
252 }
253
254
255
256 static void
257 chattr_fill_str (unsigned long attr, char *str)
258 {
259 size_t i;
260
261 for (i = 0; i < check_attr_num; i++)
262 str[i] = (attr & check_attr[i].flags) != 0 ? check_attr[i].attr : '-';
263
264 str[check_attr_num] = '\0';
265 }
266
267
268
269 static void
270 fileattrtext_fill (WFileAttrText *fat, unsigned long attr)
271 {
272 chattr_fill_str (attr, fat->attrs);
273 widget_draw (WIDGET (fat));
274 }
275
276
277
278 static cb_ret_t
279 fileattrtext_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
280 {
281 WFileAttrText *fat = (WFileAttrText *) w;
282
283 switch (msg)
284 {
285 case MSG_DRAW:
286 {
287 int color = COLOR_NORMAL;
288 size_t i;
289
290 tty_setcolor (color);
291
292 if (w->rect.cols > fat->filename_width)
293 {
294 widget_gotoyx (w, 0, (w->rect.cols - fat->filename_width) / 2);
295 tty_print_string (fat->filename);
296 }
297 else
298 {
299 widget_gotoyx (w, 0, 0);
300 tty_print_string (str_trunc (fat->filename, w->rect.cols));
301 }
302
303
304 widget_gotoyx (w, 1, (w->rect.cols - check_attr_num) / 2);
305 for (i = 0; i < check_attr_num; i++)
306 {
307
308 if (chattr_is_modifiable (i))
309 {
310 if (color == DISABLED_COLOR)
311 {
312 color = COLOR_NORMAL;
313 tty_setcolor (color);
314 }
315 }
316 else
317 {
318 if (color != DISABLED_COLOR)
319 {
320 color = DISABLED_COLOR;
321 tty_setcolor (color);
322 }
323 }
324
325 tty_print_char (fat->attrs[i]);
326 }
327 return MSG_HANDLED;
328 }
329
330 case MSG_RESIZE:
331 {
332 const WRect *wo = &CONST_WIDGET (w->owner)->rect;
333
334 widget_default_callback (w, sender, msg, parm, data);
335
336 if (fat->filename_width > wo->cols - wx * 2)
337 {
338 w->rect.x = wo->x + wx;
339 w->rect.cols = wo->cols - wx * 2;
340 }
341 return MSG_HANDLED;
342 }
343
344 case MSG_DESTROY:
345 g_free (fat->filename);
346 return MSG_HANDLED;
347
348 default:
349 return widget_default_callback (w, sender, msg, parm, data);
350 }
351 }
352
353
354
355 static WFileAttrText *
356 fileattrtext_new (int y, int x, const char *filename, unsigned long attr)
357 {
358 WRect r = { y, x, 2, 1 };
359 WFileAttrText *fat;
360 int width;
361
362 width = str_term_width1 (filename);
363 r.cols = MAX (width, (int) check_attr_num);
364
365 fat = g_new (WFileAttrText, 1);
366 widget_init (WIDGET (fat), &r, fileattrtext_callback, NULL);
367
368 fat->filename = g_strdup (filename);
369 fat->filename_width = width;
370 fileattrtext_fill (fat, attr);
371
372 return fat;
373 }
374
375
376
377 static void
378 chattr_draw_select (const Widget *w, gboolean selected)
379 {
380 widget_gotoyx (w, 0, -1);
381 tty_print_char (selected ? '*' : ' ');
382 widget_gotoyx (w, 0, 1);
383 }
384
385
386
387 static void
388 chattr_toggle_select (const WChattrBoxes *cb, int Id)
389 {
390 Widget *w;
391
392
393 w = WIDGET (g_list_nth_data (CONST_GROUP (cb)->widgets, Id - cb->top));
394
395 check_attr[Id].selected = !check_attr[Id].selected;
396
397 tty_setcolor (COLOR_NORMAL);
398 chattr_draw_select (w, check_attr[Id].selected);
399 }
400
401
402
403 static inline void
404 chattrboxes_draw_scrollbar (const WChattrBoxes *cb)
405 {
406 const Widget *w = CONST_WIDGET (cb);
407 int max_line;
408 int line;
409 int i;
410
411
412 widget_gotoyx (w, 0, w->rect.cols);
413 if (cb->top == 0)
414 tty_print_one_vline (TRUE);
415 else
416 tty_print_char ('^');
417
418 max_line = w->rect.lines - 1;
419
420
421 widget_gotoyx (w, max_line, w->rect.cols);
422 if (cb->top + w->rect.lines == check_attr_mod_num || w->rect.lines >= check_attr_mod_num)
423 tty_print_one_vline (TRUE);
424 else
425 tty_print_char ('v');
426
427
428 line = 1 + (cb->pos * (w->rect.lines - 2)) / check_attr_mod_num;
429
430 for (i = 1; i < max_line; i++)
431 {
432 widget_gotoyx (w, i, w->rect.cols);
433 if (i != line)
434 tty_print_one_vline (TRUE);
435 else
436 tty_print_char ('*');
437 }
438 }
439
440
441
442 static void
443 chattrboxes_draw (WChattrBoxes *cb)
444 {
445 Widget *w = WIDGET (cb);
446 int i;
447 GList *l;
448 const int *colors;
449
450 colors = widget_get_colors (w);
451 tty_setcolor (colors[DLG_COLOR_NORMAL]);
452 tty_fill_region (w->rect.y, w->rect.x - 1, w->rect.lines, w->rect.cols + 1, ' ');
453
454
455 group_default_callback (w, NULL, MSG_DRAW, 0, NULL);
456
457
458 tty_setcolor (colors[DLG_COLOR_NORMAL]);
459 if (!mc_global.tty.slow_terminal && check_attr_mod_num > w->rect.lines)
460 chattrboxes_draw_scrollbar (cb);
461
462
463 for (i = cb->top, l = GROUP (cb)->widgets; l != NULL; i++, l = g_list_next (l))
464 chattr_draw_select (WIDGET (l->data), check_attr[i].selected);
465 }
466
467
468
469 static void
470 chattrboxes_rename (WChattrBoxes *cb)
471 {
472 Widget *w = WIDGET (cb);
473 gboolean active;
474 int i;
475 GList *l;
476
477 active = widget_get_state (w, WST_ACTIVE);
478
479
480 if (active)
481 widget_set_state (w, WST_SUSPENDED, TRUE);
482
483 for (i = cb->top, l = GROUP (cb)->widgets; l != NULL; i++, l = g_list_next (l))
484 {
485 WCheck *c = CHECK (l->data);
486 int m = check_attr_mod[i];
487 char btext[BUF_SMALL];
488
489 g_snprintf (btext, sizeof (btext), "(%c) %s", check_attr[m].attr, check_attr[m].text);
490 check_set_text (c, btext);
491 c->state = check_attr[m].state;
492 }
493
494
495 if (active)
496 widget_set_state (w, WST_ACTIVE, TRUE);
497
498 widget_draw (w);
499 }
500
501
502
503 static void
504 checkboxes_save_state (const WChattrBoxes *cb)
505 {
506 int i;
507 GList *l;
508
509 for (i = cb->top, l = CONST_GROUP (cb)->widgets; l != NULL; i++, l = g_list_next (l))
510 {
511 int m = check_attr_mod[i];
512
513 check_attr[m].state = CHECK (l->data)->state;
514 }
515 }
516
517
518
519 static cb_ret_t
520 chattrboxes_down (WChattrBoxes *cb)
521 {
522 if (cb->pos == cb->top + WIDGET (cb)->rect.lines - 1)
523 {
524
525
526
527 if (cb->pos == check_attr_mod_num - 1)
528
529 return MSG_NOT_HANDLED;
530
531
532 checkboxes_save_state (cb);
533 cb->pos++;
534 cb->top++;
535 chattrboxes_rename (cb);
536 }
537 else
538 {
539 GList *l;
540
541
542 cb->pos++;
543 l = g_list_next (GROUP (cb)->current);
544 widget_select (WIDGET (l->data));
545 }
546
547 return MSG_HANDLED;
548 }
549
550
551
552 static cb_ret_t
553 chattrboxes_page_down (WChattrBoxes *cb)
554 {
555 WGroup *g = GROUP (cb);
556 GList *l;
557
558 if (cb->pos == check_attr_mod_num - 1)
559 {
560
561
562
563 l = g_list_last (g->widgets);
564 }
565 else
566 {
567 int i = WIDGET (cb)->rect.lines;
568
569 checkboxes_save_state (cb);
570
571 if (cb->top > check_attr_mod_num - 2 * i)
572 i = check_attr_mod_num - i - cb->top;
573 if (cb->top + i < 0)
574 i = -cb->top;
575 if (i == 0)
576 {
577 cb->pos = check_attr_mod_num - 1;
578 cb->top += i;
579 l = g_list_last (g->widgets);
580 }
581 else
582 {
583 cb->pos += i;
584 cb->top += i;
585 l = g_list_nth (g->widgets, cb->pos - cb->top);
586 }
587
588 chattrboxes_rename (cb);
589 }
590
591 widget_select (WIDGET (l->data));
592
593 return MSG_HANDLED;
594 }
595
596
597
598 static cb_ret_t
599 chattrboxes_end (WChattrBoxes *cb)
600 {
601 GList *l;
602
603 checkboxes_save_state (cb);
604 cb->pos = check_attr_mod_num - 1;
605 cb->top = cb->pos - WIDGET (cb)->rect.lines + 1;
606 l = g_list_last (GROUP (cb)->widgets);
607 chattrboxes_rename (cb);
608 widget_select (WIDGET (l->data));
609
610 return MSG_HANDLED;
611 }
612
613
614
615 static cb_ret_t
616 chattrboxes_up (WChattrBoxes *cb)
617 {
618 if (cb->pos == cb->top)
619 {
620
621
622
623 if (cb->top == 0)
624
625 return MSG_NOT_HANDLED;
626
627
628 checkboxes_save_state (cb);
629 cb->pos--;
630 cb->top--;
631 chattrboxes_rename (cb);
632 }
633 else
634 {
635 GList *l;
636
637
638 cb->pos--;
639 l = g_list_previous (GROUP (cb)->current);
640 widget_select (WIDGET (l->data));
641 }
642
643 return MSG_HANDLED;
644 }
645
646
647
648 static cb_ret_t
649 chattrboxes_page_up (WChattrBoxes *cb)
650 {
651 WGroup *g = GROUP (cb);
652 GList *l;
653
654 if (cb->pos == 0 && cb->top == 0)
655 {
656
657
658
659 l = g_list_first (g->widgets);
660 }
661 else
662 {
663 int i = WIDGET (cb)->rect.lines;
664
665 checkboxes_save_state (cb);
666
667 if (cb->top < i)
668 i = cb->top;
669 if (i == 0)
670 {
671 cb->pos = 0;
672 cb->top -= i;
673 l = g_list_first (g->widgets);
674 }
675 else
676 {
677 cb->pos -= i;
678 cb->top -= i;
679 l = g_list_nth (g->widgets, cb->pos - cb->top);
680 }
681
682 chattrboxes_rename (cb);
683 }
684
685 widget_select (WIDGET (l->data));
686
687 return MSG_HANDLED;
688 }
689
690
691
692 static cb_ret_t
693 chattrboxes_home (WChattrBoxes *cb)
694 {
695 GList *l;
696
697 checkboxes_save_state (cb);
698 cb->pos = 0;
699 cb->top = 0;
700 l = g_list_first (GROUP (cb)->widgets);
701 chattrboxes_rename (cb);
702 widget_select (WIDGET (l->data));
703
704 return MSG_HANDLED;
705 }
706
707
708
709 static cb_ret_t
710 chattrboxes_execute_cmd (WChattrBoxes *cb, long command)
711 {
712 switch (command)
713 {
714 case CK_Down:
715 return chattrboxes_down (cb);
716
717 case CK_PageDown:
718 return chattrboxes_page_down (cb);
719
720 case CK_Bottom:
721 return chattrboxes_end (cb);
722
723 case CK_Up:
724 return chattrboxes_up (cb);
725
726 case CK_PageUp:
727 return chattrboxes_page_up (cb);
728
729 case CK_Top:
730 return chattrboxes_home (cb);
731
732 case CK_Mark:
733 case CK_MarkAndDown:
734 {
735 chattr_toggle_select (cb, cb->pos);
736 if (command == CK_MarkAndDown)
737 chattrboxes_down (cb);
738
739 return MSG_HANDLED;
740 }
741
742 default:
743 return MSG_NOT_HANDLED;
744 }
745 }
746
747
748
749 static cb_ret_t
750 chattrboxes_key (WChattrBoxes *cb, int key)
751 {
752 long command;
753
754 command = widget_lookup_key (WIDGET (cb), key);
755 if (command == CK_IgnoreKey)
756 return MSG_NOT_HANDLED;
757 return chattrboxes_execute_cmd (cb, command);
758 }
759
760
761
762 static cb_ret_t
763 chattrboxes_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
764 {
765 WChattrBoxes *cb = CHATTRBOXES (w);
766 WGroup *g = GROUP (w);
767
768 switch (msg)
769 {
770 case MSG_DRAW:
771 chattrboxes_draw (cb);
772 return MSG_HANDLED;
773
774 case MSG_NOTIFY:
775 {
776
777 int i;
778
779 i = g_list_index (g->widgets, sender);
780 if (i >= 0)
781 {
782 int m;
783
784 i += cb->top;
785 m = check_attr_mod[i];
786 flags ^= check_attr[m].flags;
787 fileattrtext_fill (file_attr, flags);
788 chattr_toggle_select (cb, i);
789 flags_changed = TRUE;
790 return MSG_HANDLED;
791 }
792 }
793 return MSG_NOT_HANDLED;
794
795 case MSG_CHANGED_FOCUS:
796
797 if (widget_get_state (sender, WST_FOCUSED))
798 {
799 int i;
800
801 i = g_list_index (g->widgets, sender);
802 cb->pos = cb->top + i;
803 }
804 return MSG_HANDLED;
805
806 case MSG_KEY:
807 {
808 cb_ret_t ret;
809
810 ret = chattrboxes_key (cb, parm);
811 if (ret != MSG_HANDLED)
812 ret = group_default_callback (w, NULL, MSG_KEY, parm, NULL);
813
814 return ret;
815 }
816
817 case MSG_ACTION:
818 return chattrboxes_execute_cmd (cb, parm);
819
820 case MSG_DESTROY:
821
822 checkboxes_save_state (cb);
823 MC_FALLTHROUGH;
824
825 default:
826 return group_default_callback (w, sender, msg, parm, data);
827 }
828 }
829
830
831
832 static int
833 chattrboxes_handle_mouse_event (Widget *w, Gpm_Event *event)
834 {
835 int mou;
836
837 mou = mouse_handle_event (w, event);
838 if (mou == MOU_UNHANDLED)
839 mou = group_handle_mouse_event (w, event);
840
841 return mou;
842 }
843
844
845
846 static void
847 chattrboxes_mouse_callback (Widget *w, mouse_msg_t msg, mouse_event_t *event)
848 {
849 WChattrBoxes *cb = CHATTRBOXES (w);
850
851 (void) event;
852
853 switch (msg)
854 {
855 case MSG_MOUSE_SCROLL_UP:
856 chattrboxes_up (cb);
857 break;
858
859 case MSG_MOUSE_SCROLL_DOWN:
860 chattrboxes_down (cb);
861 break;
862
863 default:
864
865 event->result.abort = TRUE;
866 break;
867 }
868 }
869
870
871
872 static WChattrBoxes *
873 chattrboxes_new (const WRect *r)
874 {
875 WChattrBoxes *cb;
876 Widget *w;
877 WGroup *cbg;
878 int i;
879
880 cb = g_new0 (WChattrBoxes, 1);
881 w = WIDGET (cb);
882 cbg = GROUP (cb);
883 group_init (cbg, r, chattrboxes_callback, chattrboxes_mouse_callback);
884 w->options |= WOP_SELECTABLE | WOP_WANT_CURSOR;
885 w->mouse_handler = chattrboxes_handle_mouse_event;
886 w->keymap = chattr_map;
887
888
889 for (i = 0; i < r->lines; i++)
890 {
891 int m = check_attr_mod[i];
892 WCheck *check;
893
894 check = check_new (i, 0, check_attr[m].state, NULL);
895 group_add_widget (cbg, check);
896 }
897
898 chattrboxes_rename (cb);
899
900
901 cbg->current = cbg->widgets;
902
903 return cb;
904 }
905
906
907
908 static void
909 chattr_init (void)
910 {
911 static gboolean i18n = FALSE;
912 size_t i;
913
914 for (i = 0; i < check_attr_num; i++)
915 check_attr[i].selected = FALSE;
916
917 if (i18n)
918 return;
919
920 i18n = TRUE;
921
922 for (i = 0; i < check_attr_num; i++)
923 if (chattr_is_modifiable (i))
924 {
925 int width;
926
927 #ifdef ENABLE_NLS
928 check_attr[i].text = _ (check_attr[i].text);
929 #endif
930
931 check_attr_mod[check_attr_mod_num++] = i;
932
933 width = 4 + str_term_width1 (check_attr[i].text);
934 check_attr_width = MAX (check_attr_width, width);
935 }
936
937 check_attr_width += 1 + 3 + 1;
938
939 for (i = 0; i < BUTTONS; i++)
940 {
941 #ifdef ENABLE_NLS
942 chattr_but[i].text = _ (chattr_but[i].text);
943 #endif
944
945 chattr_but[i].width = str_term_width1 (chattr_but[i].text) + 3;
946 if (chattr_but[i].flags == DEFPUSH_BUTTON)
947 chattr_but[i].width += 2;
948 }
949 }
950
951
952
953 static WDialog *
954 chattr_dlg_create (WPanel *panel, const char *fname, unsigned long attr)
955 {
956 Widget *mw = WIDGET (WIDGET (panel)->owner);
957 gboolean single_set;
958 WDialog *ch_dlg;
959 int lines, cols;
960 int checkboxes_lines = check_attr_mod_num;
961 size_t i;
962 int y;
963 Widget *dw;
964 WGroup *dg;
965 WChattrBoxes *cb;
966 const int cb_scrollbar_width = 1;
967 WRect r;
968
969
970 for (i = 0; i < check_attr_num; i++)
971 check_attr[i].state = chattr_is_modifiable (i) && (attr & check_attr[i].flags) != 0;
972
973 cols = check_attr_width + cb_scrollbar_width;
974
975 single_set = (panel->marked < 2);
976
977 lines = 5 + checkboxes_lines + 4;
978 if (!single_set)
979 lines += 3;
980
981 if (lines >= mw->rect.lines - 2)
982 {
983 int dl;
984
985 dl = lines - (mw->rect.lines - 2);
986 lines -= dl;
987 checkboxes_lines -= dl;
988 }
989
990 ch_dlg = dlg_create (TRUE, 0, 0, lines, cols + wx * 2, WPOS_CENTER, FALSE, dialog_colors,
991 dlg_default_callback, NULL, "[Chattr]", _ ("Chattr command"));
992 dg = GROUP (ch_dlg);
993 dw = WIDGET (ch_dlg);
994
995 y = 2;
996 file_attr = fileattrtext_new (y, wx, fname, attr);
997 group_add_widget_autopos (dg, file_attr, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, NULL);
998 y += WIDGET (file_attr)->rect.lines;
999 group_add_widget (dg, hline_new (y++, -1, -1));
1000
1001 if (cols < WIDGET (file_attr)->rect.cols)
1002 {
1003 r = dw->rect;
1004 cols = WIDGET (file_attr)->rect.cols;
1005 cols = MIN (cols, mw->rect.cols - wx * 2);
1006 r.cols = cols + wx * 2;
1007 r.lines = lines;
1008 widget_set_size_rect (dw, &r);
1009 }
1010
1011 checkboxes_lines = MIN (check_attr_mod_num, checkboxes_lines);
1012 rect_init (&r, y++, wx, checkboxes_lines > 0 ? checkboxes_lines : 1, cols);
1013 cb = chattrboxes_new (&r);
1014 group_add_widget_autopos (dg, cb, WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
1015
1016 y += checkboxes_lines - 1;
1017 cols = 0;
1018
1019 for (i = single_set ? (BUTTONS - 2) : 0; i < BUTTONS; i++)
1020 {
1021 if (i == 0 || i == BUTTONS - 2)
1022 group_add_widget (dg, hline_new (y++, -1, -1));
1023
1024 chattr_but[i].button = WIDGET (button_new (y, dw->rect.cols / 2 + 1 - chattr_but[i].width,
1025 chattr_but[i].ret_cmd, chattr_but[i].flags,
1026 chattr_but[i].text, NULL));
1027 group_add_widget (dg, chattr_but[i].button);
1028
1029 i++;
1030 chattr_but[i].button =
1031 WIDGET (button_new (y++, dw->rect.cols / 2 + 2, chattr_but[i].ret_cmd,
1032 chattr_but[i].flags, chattr_but[i].text, NULL));
1033 group_add_widget (dg, chattr_but[i].button);
1034
1035
1036 cols =
1037 MAX (cols, chattr_but[i - 1].button->rect.cols + 1 + chattr_but[i].button->rect.cols);
1038 }
1039
1040
1041 cols += 6;
1042 if (cols > dw->rect.cols)
1043 {
1044 r = dw->rect;
1045 r.lines = lines;
1046 r.cols = cols;
1047 widget_set_size_rect (dw, &r);
1048
1049
1050 cols = dw->rect.x + dw->rect.cols / 2 + 1;
1051
1052 for (i = single_set ? (BUTTONS - 2) : 0; i < BUTTONS; i++)
1053 {
1054 Widget *b;
1055
1056 b = chattr_but[i++].button;
1057 r = b->rect;
1058 r.x = cols - r.cols;
1059 widget_set_size_rect (b, &r);
1060
1061 b = chattr_but[i].button;
1062 r = b->rect;
1063 r.x = cols + 1;
1064 widget_set_size_rect (b, &r);
1065 }
1066 }
1067
1068 widget_select (WIDGET (cb));
1069
1070 return ch_dlg;
1071 }
1072
1073
1074
1075 static void
1076 chattr_done (gboolean need_update)
1077 {
1078 if (need_update)
1079 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
1080 repaint_screen ();
1081 }
1082
1083
1084
1085 static gboolean
1086 try_chattr (const vfs_path_t *p, unsigned long m)
1087 {
1088 const char *fname = NULL;
1089
1090 while (mc_fsetflags (p, m) == -1 && !ignore_all)
1091 {
1092 int my_errno = errno;
1093 int result;
1094 char *msg;
1095
1096 if (fname == NULL)
1097 fname = x_basename (vfs_path_as_str (p));
1098 msg = g_strdup_printf (_ ("Cannot chattr \"%s\"\n%s"), fname, unix_error_string (my_errno));
1099 result = query_dialog (MSG_ERROR, msg, D_ERROR, 4, _ ("&Ignore"), _ ("Ignore &all"),
1100 _ ("&Retry"), _ ("&Cancel"));
1101 g_free (msg);
1102
1103 switch (result)
1104 {
1105 case 0:
1106
1107 return TRUE;
1108
1109 case 1:
1110 ignore_all = TRUE;
1111
1112 return TRUE;
1113
1114 case 2:
1115
1116 break;
1117
1118 case 3:
1119 default:
1120
1121 return FALSE;
1122 }
1123 }
1124
1125 return TRUE;
1126 }
1127
1128
1129
1130 static gboolean
1131 do_chattr (WPanel *panel, const vfs_path_t *p, unsigned long m)
1132 {
1133 gboolean ret;
1134
1135 m &= and_mask;
1136 m |= or_mask;
1137
1138 ret = try_chattr (p, m);
1139
1140 do_file_mark (panel, current_file, 0);
1141
1142 return ret;
1143 }
1144
1145
1146
1147 static void
1148 chattr_apply_mask (WPanel *panel, vfs_path_t *vpath, unsigned long m)
1149 {
1150 gboolean ok;
1151
1152 if (!do_chattr (panel, vpath, m))
1153 return;
1154
1155 do
1156 {
1157 const GString *fname;
1158
1159 fname = panel_find_marked_file (panel, ¤t_file);
1160 vpath = vfs_path_from_str (fname->str);
1161 ok = (mc_fgetflags (vpath, &m) == 0);
1162
1163 if (!ok)
1164 {
1165
1166
1167 do_file_mark (panel, current_file, 0);
1168
1169
1170 ok = TRUE;
1171 }
1172 else
1173 {
1174 flags = m;
1175 ok = do_chattr (panel, vpath, m);
1176 vfs_path_free (vpath, TRUE);
1177 }
1178 }
1179 while (ok && panel->marked != 0);
1180 }
1181
1182
1183
1184
1185
1186 void
1187 chattr_cmd (WPanel *panel)
1188 {
1189 gboolean need_update = FALSE;
1190 gboolean end_chattr = FALSE;
1191
1192 chattr_init ();
1193
1194 current_file = 0;
1195 ignore_all = FALSE;
1196
1197 do
1198 {
1199 vfs_path_t *vpath;
1200 WDialog *ch_dlg;
1201 const GString *fname;
1202 size_t i;
1203 int result;
1204
1205 do_refresh ();
1206
1207 need_update = FALSE;
1208 end_chattr = FALSE;
1209
1210 fname = panel_get_marked_file (panel, ¤t_file);
1211 if (fname == NULL)
1212 break;
1213
1214 vpath = vfs_path_from_str (fname->str);
1215
1216 if (mc_fgetflags (vpath, &flags) != 0)
1217 {
1218 message (D_ERROR, MSG_ERROR, _ ("Cannot get ext2 attributes of \"%s\"\n%s"), fname->str,
1219 unix_error_string (errno));
1220 vfs_path_free (vpath, TRUE);
1221 break;
1222 }
1223
1224 flags_changed = FALSE;
1225
1226 ch_dlg = chattr_dlg_create (panel, fname->str, flags);
1227 result = dlg_run (ch_dlg);
1228 widget_destroy (WIDGET (ch_dlg));
1229
1230 switch (result)
1231 {
1232 case B_CANCEL:
1233 end_chattr = TRUE;
1234 break;
1235
1236 case B_ENTER:
1237 if (flags_changed)
1238 {
1239 if (panel->marked <= 1)
1240 {
1241
1242 if (mc_fsetflags (vpath, flags) == -1 && !ignore_all)
1243 message (D_ERROR, MSG_ERROR, _ ("Cannot chattr \"%s\"\n%s"), fname->str,
1244 unix_error_string (errno));
1245 end_chattr = TRUE;
1246 }
1247 else if (!try_chattr (vpath, flags))
1248 {
1249
1250 result = B_CANCEL;
1251 end_chattr = TRUE;
1252 }
1253 }
1254
1255 need_update = TRUE;
1256 break;
1257
1258 case B_SETALL:
1259 case B_MARKED:
1260 or_mask = 0;
1261 and_mask = ~0;
1262
1263 for (i = 0; i < check_attr_num; i++)
1264 if (chattr_is_modifiable (i) && (check_attr[i].selected || result == B_SETALL))
1265 {
1266 if (check_attr[i].state)
1267 or_mask |= check_attr[i].flags;
1268 else
1269 and_mask &= ~check_attr[i].flags;
1270 }
1271
1272 chattr_apply_mask (panel, vpath, flags);
1273 need_update = TRUE;
1274 end_chattr = TRUE;
1275 break;
1276
1277 case B_SETMRK:
1278 or_mask = 0;
1279 and_mask = ~0;
1280
1281 for (i = 0; i < check_attr_num; i++)
1282 if (chattr_is_modifiable (i) && check_attr[i].selected)
1283 or_mask |= check_attr[i].flags;
1284
1285 chattr_apply_mask (panel, vpath, flags);
1286 need_update = TRUE;
1287 end_chattr = TRUE;
1288 break;
1289
1290 case B_CLRMRK:
1291 or_mask = 0;
1292 and_mask = ~0;
1293
1294 for (i = 0; i < check_attr_num; i++)
1295 if (chattr_is_modifiable (i) && check_attr[i].selected)
1296 and_mask &= ~check_attr[i].flags;
1297
1298 chattr_apply_mask (panel, vpath, flags);
1299 need_update = TRUE;
1300 end_chattr = TRUE;
1301 break;
1302
1303 default:
1304 break;
1305 }
1306
1307 if (panel->marked != 0 && result != B_CANCEL)
1308 {
1309 do_file_mark (panel, current_file, 0);
1310 need_update = TRUE;
1311 }
1312
1313 vfs_path_free (vpath, TRUE);
1314 }
1315 while (panel->marked != 0 && !end_chattr);
1316
1317 chattr_done (need_update);
1318 }
1319
1320
1321
1322 const char *
1323 chattr_get_as_str (unsigned long attr)
1324 {
1325 static char str[32 + 1];
1326
1327 chattr_fill_str (attr, str);
1328
1329 return str;
1330 }
1331
1332