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