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