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