fdupes  2.1.2
About: FDUPES finds duplicate files in a given set of directories.
  Fossies Dox: fdupes-2.1.2.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

ncurses-interface.c
Go to the documentation of this file.
1 /* FDUPES Copyright (c) 2018 Adrian Lopez
2 
3  Permission is hereby granted, free of charge, to any person
4  obtaining a copy of this software and associated documentation files
5  (the "Software"), to deal in the Software without restriction,
6  including without limitation the rights to use, copy, modify, merge,
7  publish, distribute, sublicense, and/or sell copies of the Software,
8  and to permit persons to whom the Software is furnished to do so,
9  subject to the following conditions:
10 
11  The above copyright notice and this permission notice shall be
12  included in all copies or substantial portions of the Software.
13 
14  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
21 
22 #include "config.h"
23 #include <stdlib.h>
24 #include <string.h>
25 #include <wchar.h>
26 #ifdef HAVE_NCURSESW_CURSES_H
27  #include <ncursesw/curses.h>
28 #else
29  #include <curses.h>
30 #endif
31 #include "ncurses-interface.h"
32 #include "ncurses-getcommand.h"
33 #include "ncurses-commands.h"
34 #include "ncurses-prompt.h"
35 #include "ncurses-status.h"
36 #include "ncurses-print.h"
38 #include "positive_wcwidth.h"
39 #include "commandidentifier.h"
40 #include "filegroup.h"
41 #include "errormsg.h"
42 #include "log.h"
43 #include "sigint.h"
44 #include "flags.h"
45 
46 char *fmttime(time_t t);
47 
49 {
54 };
55 
56 enum linestyle getlinestyle(struct filegroup *group, int line)
57 {
58  if (line <= group->startline)
59  return linestyle_groupheader;
60  else if (line == group->startline + 1)
62  else if (line >= group->endline)
64  else
65  return linestyle_filename;
66 }
67 
68 #define FILENAME_INDENT_EXTRA 5
69 #define FILE_INDEX_MIN_WIDTH 3
70 
71 int filerowcount(file_t *file, const int columns, int group_file_count)
72 {
73  int lines;
74  int line_remaining;
75  size_t x = 0;
76  size_t read;
77  size_t filename_bytes;
78  wchar_t ch;
79  mbstate_t mbstate;
80  int index_width;
81  int timestamp_width;
82  size_t needed;
83  wchar_t *wcfilename;
84 
85  memset(&mbstate, 0, sizeof(mbstate));
86 
87  needed = mbstowcs_escape_invalid(0, file->d_name, 0);
88 
89  wcfilename = (wchar_t*)malloc(sizeof(wchar_t) * needed);
90  if (wcfilename == 0)
91  return 0;
92 
93  mbstowcs_escape_invalid(wcfilename, file->d_name, needed);
94 
95  index_width = get_num_digits(group_file_count);
96  if (index_width < FILE_INDEX_MIN_WIDTH)
97  index_width = FILE_INDEX_MIN_WIDTH;
98 
99  timestamp_width = ISFLAG(flags, F_SHOWTIME) ? 19 : 0;
100 
101  lines = (index_width + timestamp_width + FILENAME_INDENT_EXTRA) / columns + 1;
102 
103  line_remaining = columns - (index_width + timestamp_width + FILENAME_INDENT_EXTRA) % columns;
104 
105  while (wcfilename[x] != L'\0')
106  {
107  if (positive_wcwidth(wcfilename[x]) <= line_remaining)
108  {
109  line_remaining -= positive_wcwidth(wcfilename[x]);
110  }
111  else
112  {
113  line_remaining = columns - positive_wcwidth(wcfilename[x]);
114  ++lines;
115  }
116 
117  ++x;
118  }
119 
120  free(wcfilename);
121 
122  return lines;
123 }
124 
125 int getgroupindex(struct filegroup *groups, int group_count, int group_hint, int line)
126 {
127  int group = group_hint;
128 
129  while (group > 0 && line < groups[group].startline)
130  --group;
131 
132  while (group < group_count && line > groups[group].endline)
133  ++group;
134 
135  return group;
136 }
137 
138 int getgroupfileindex(int *row, struct filegroup *group, int line, int columns)
139 {
140  int l;
141  int f = 0;
142  int rowcount;
143 
144  l = group->startline + 2;
145 
146  while (f < group->filecount)
147  {
148  rowcount = filerowcount(group->files[f].file, columns, group->filecount);
149 
150  if (line <= l + rowcount - 1)
151  {
152  *row = line - l;
153  return f;
154  }
155 
156  l += rowcount;
157  ++f;
158  }
159 
160  return -1;
161 }
162 
163 int getgroupfileline(struct filegroup *group, int fileindex, int columns)
164 {
165  int l;
166  int f = 0;
167  int rowcount;
168 
169  l = group->startline + 2;
170 
171  while (f < fileindex && f < group->filecount)
172  {
173  rowcount = filerowcount(group->files[f].file, columns, group->filecount);
174  l += rowcount;
175  ++f;
176  }
177 
178  return l;
179 }
180 
181 void set_file_action(struct groupfile *file, int new_action, size_t *deletion_tally)
182 {
183  switch (file->action)
184  {
185  case -1:
186  if (new_action != -1)
187  --*deletion_tally;
188  break;
189 
190  default:
191  if (new_action == -1)
192  ++*deletion_tally;
193  break;
194  }
195 
196  file->action = new_action;
197 }
198 
199 void scroll_to_group(int *topline, int group, int tail, struct filegroup *groups, WINDOW *filewin)
200 {
201  if (*topline < groups[group].startline)
202  {
203  if (groups[group].endline >= *topline + getmaxy(filewin))
204  {
205  if (groups[group].endline - groups[group].startline < getmaxy(filewin))
206  *topline = groups[group].endline - getmaxy(filewin) + 1;
207  else
208  *topline = groups[group].startline;
209  }
210  }
211  else
212  {
213  if (groups[group].endline - groups[group].startline < getmaxy(filewin) || !tail)
214  *topline = groups[group].startline;
215  else
216  *topline = groups[group].endline - getmaxy(filewin);
217  }
218 }
219 
220 void move_to_next_group(int *topline, int *cursorgroup, int *cursorfile, struct filegroup *groups, WINDOW *filewin)
221 {
222  *cursorgroup += 1;
223 
224  *cursorfile = 0;
225 
226  scroll_to_group(topline, *cursorgroup, 0, groups, filewin);
227 }
228 
229 int move_to_next_selected_group(int *topline, int *cursorgroup, int *cursorfile, struct filegroup *groups, int totalgroups, WINDOW *filewin)
230 {
231  size_t g;
232 
233  for (g = *cursorgroup + 1; g < totalgroups; ++g)
234  {
235  if (groups[g].selected)
236  {
237  *cursorgroup = g;
238  *cursorfile = 0;
239 
240  scroll_to_group(topline, *cursorgroup, 0, groups, filewin);
241 
242  return 1;
243  }
244  }
245 
246  return 0;
247 }
248 
249 void move_to_next_file(int *topline, int *cursorgroup, int *cursorfile, struct filegroup *groups, WINDOW *filewin)
250 {
251  *cursorfile += 1;
252 
253  if (getgroupfileline(&groups[*cursorgroup], *cursorfile, COLS) >= *topline + getmaxy(filewin))
254  {
255  if (groups[*cursorgroup].endline - getgroupfileline(&groups[*cursorgroup], *cursorfile, COLS) < getmaxy(filewin))
256  *topline = groups[*cursorgroup].endline - getmaxy(filewin) + 1;
257  else
258  *topline = getgroupfileline(&groups[*cursorgroup], *cursorfile, COLS);
259  }
260 }
261 
262 void move_to_previous_group(int *topline, int *cursorgroup, int *cursorfile, struct filegroup *groups, WINDOW *filewin)
263 {
264  *cursorgroup -= 1;
265 
266  *cursorfile = groups[*cursorgroup].filecount - 1;
267 
268  scroll_to_group(topline, *cursorgroup, 1, groups, filewin);
269 }
270 
271 int move_to_previous_selected_group(int *topline, int *cursorgroup, int *cursorfile, struct filegroup *groups, int totalgroups, WINDOW *filewin)
272 {
273  size_t g;
274 
275  for (g = *cursorgroup; g > 0; --g)
276  {
277  if (groups[g - 1].selected)
278  {
279  *cursorgroup = g - 1;
280  *cursorfile = 0;
281 
282  scroll_to_group(topline, *cursorgroup, 0, groups, filewin);
283 
284  return 1;
285  }
286  }
287 
288  return 0;
289 }
290 
291 void move_to_previous_file(int *topline, int *cursorgroup, int *cursorfile, struct filegroup *groups, WINDOW *filewin)
292 {
293  *cursorfile -= 1;
294 
295  if (getgroupfileline(&groups[*cursorgroup], *cursorfile, COLS) < *topline)
296  {
297  if (getgroupfileline(&groups[*cursorgroup], *cursorfile, COLS) - groups[*cursorgroup].startline < getmaxy(filewin))
298  *topline -= getgroupfileline(&groups[*cursorgroup], *cursorfile, COLS) - groups[*cursorgroup].startline + 1;
299  else
300  *topline -= getmaxy(filewin);
301  }
302 }
303 
304 #define FILE_LIST_OK 1
305 #define FILE_LIST_ERROR_INDEX_OUT_OF_RANGE -1
306 #define FILE_LIST_ERROR_LIST_CONTAINS_INVALID_INDEX -2
307 #define FILE_LIST_ERROR_UNKNOWN_COMMAND -3
308 #define FILE_LIST_ERROR_OUT_OF_MEMORY -4
309 
310 int validate_file_list(struct filegroup *currentgroup, wchar_t *commandbuffer_in)
311 {
312  wchar_t *commandbuffer;
313  wchar_t *token;
314  wchar_t *wcsptr;
315  wchar_t *wcstolcheck;
316  long int number;
317  int parts = 0;
318  int parse_error = 0;
319  int out_of_bounds_error = 0;
320 
321  commandbuffer = malloc(sizeof(wchar_t) * (wcslen(commandbuffer_in)+1));
322  if (commandbuffer == 0)
324 
325  wcscpy(commandbuffer, commandbuffer_in);
326 
327  token = wcstok(commandbuffer, L",", &wcsptr);
328 
329  while (token != NULL)
330  {
331  ++parts;
332 
333  number = wcstol(token, &wcstolcheck, 10);
334  if (wcstolcheck == token || *wcstolcheck != '\0')
335  parse_error = 1;
336 
337  if (number > currentgroup->filecount || number < 1)
338  out_of_bounds_error = 1;
339 
340  token = wcstok(NULL, L",", &wcsptr);
341  }
342 
343  free(commandbuffer);
344 
345  if (parts == 1 && parse_error)
347  else if (parse_error)
349  else if (out_of_bounds_error)
351 
352  return FILE_LIST_OK;
353 }
354 
355 void deletefiles_ncurses(file_t *files, char *logfile)
356 {
357  WINDOW *filewin;
358  WINDOW *promptwin;
359  WINDOW *statuswin;
360  file_t *curfile;
361  file_t *dupefile;
362  struct filegroup *groups;
363  struct filegroup *reallocgroups;
364  size_t groupfilecount;
365  int topline = 0;
366  int cursorgroup = 0;
367  int cursorfile = 0;
368  int cursor_x;
369  int cursor_y;
370  int groupfirstline = 0;
371  int totallines = 0;
372  int allocatedgroups = 0;
373  int totalgroups = 0;
374  size_t groupindex = 0;
375  enum linestyle linestyle;
376  int preservecount;
377  int deletecount;
378  int unresolvedcount;
379  size_t globaldeletiontally = 0;
380  int row;
381  int x;
382  int g;
383  wint_t wch;
384  int keyresult;
385  int cy;
386  int f;
387  wchar_t *commandbuffer;
388  size_t commandbuffersize;
389  wchar_t *commandarguments;
390  struct command_identifier_node *commandidentifier;
391  struct command_identifier_node *confirmationkeywordidentifier;
392  int doprune;
393  wchar_t *token;
394  wchar_t *wcsptr;
395  wchar_t *wcstolcheck;
396  long int number;
397  struct status_text *status;
398  struct prompt_info *prompt;
399  int dupesfound;
400  int intresult;
401  int resumecommandinput = 0;
402  int index_width;
403  int timestamp_width;
404 
405  noecho();
406  cbreak();
407  halfdelay(5);
408 
409  filewin = newwin(LINES - 2, COLS, 0, 0);
410  statuswin = newwin(1, COLS, LINES - 1, 0);
411  promptwin = newwin(1, COLS, LINES - 2, 0);
412 
413  scrollok(filewin, FALSE);
414  scrollok(statuswin, FALSE);
415  scrollok(promptwin, FALSE);
416 
417  wattron(statuswin, A_REVERSE);
418 
419  keypad(promptwin, 1);
420 
421  commandbuffersize = 80;
422  commandbuffer = malloc(commandbuffersize * sizeof(wchar_t));
423  if (commandbuffer == 0)
424  {
425  endwin();
426  errormsg("out of memory\n");
427  exit(1);
428  }
429 
430  allocatedgroups = 1024;
431  groups = malloc(sizeof(struct filegroup) * allocatedgroups);
432  if (groups == 0)
433  {
434  free(commandbuffer);
435 
436  endwin();
437  errormsg("out of memory\n");
438  exit(1);
439  }
440 
441  commandidentifier = build_command_identifier_tree(command_list);
442  if (commandidentifier == 0)
443  {
444  free(groups);
445  free(commandbuffer);
446 
447  endwin();
448  errormsg("out of memory\n");
449  exit(1);
450  }
451 
452  confirmationkeywordidentifier = build_command_identifier_tree(confirmation_keyword_list);
453  if (confirmationkeywordidentifier == 0)
454  {
455  free(groups);
456  free(commandbuffer);
457  free_command_identifier_tree(commandidentifier);
458 
459  endwin();
460  errormsg("out of memory\n");
461  exit(1);
462  }
463 
465 
466  curfile = files;
467  while (curfile)
468  {
469  if (!curfile->hasdupes)
470  {
471  curfile = curfile->next;
472  continue;
473  }
474 
475  if (totalgroups + 1 > allocatedgroups)
476  {
477  allocatedgroups *= 2;
478 
479  reallocgroups = realloc(groups, sizeof(struct filegroup) * allocatedgroups);
480  if (reallocgroups == 0)
481  {
482  for (g = 0; g < totalgroups; ++g)
483  free(groups[g].files);
484 
485  free(groups);
486  free(commandbuffer);
487  free_command_identifier_tree(commandidentifier);
488  free_command_identifier_tree(confirmationkeywordidentifier);
489 
490  endwin();
491  errormsg("out of memory\n");
492  exit(1);
493  }
494 
495  groups = reallocgroups;
496  }
497 
498  groups[totalgroups].startline = groupfirstline;
499  groups[totalgroups].endline = groupfirstline + 2;
500  groups[totalgroups].selected = 0;
501 
502  groupfilecount = 0;
503 
504  dupefile = curfile;
505  do
506  {
507  ++groupfilecount;
508 
509  dupefile = dupefile->duplicates;
510  } while(dupefile);
511 
512  dupefile = curfile;
513  do
514  {
515  groups[totalgroups].endline += filerowcount(dupefile, COLS, groupfilecount);
516 
517  dupefile = dupefile->duplicates;
518  } while (dupefile);
519 
520  groups[totalgroups].files = malloc(sizeof(struct groupfile) * groupfilecount);
521  if (groups[totalgroups].files == 0)
522  {
523  for (g = 0; g < totalgroups; ++g)
524  free(groups[g].files);
525 
526  free(groups);
527  free(commandbuffer);
528  free_command_identifier_tree(commandidentifier);
529  free_command_identifier_tree(confirmationkeywordidentifier);
530 
531  endwin();
532  errormsg("out of memory\n");
533  exit(1);
534  }
535 
536  groupfilecount = 0;
537 
538  dupefile = curfile;
539  do
540  {
541  groups[totalgroups].files[groupfilecount].file = dupefile;
542  groups[totalgroups].files[groupfilecount].action = 0;
543  groups[totalgroups].files[groupfilecount].selected = 0;
544  ++groupfilecount;
545 
546  dupefile = dupefile->duplicates;
547  } while (dupefile);
548 
549  groups[totalgroups].filecount = groupfilecount;
550 
551  groupfirstline = groups[totalgroups].endline + 1;
552 
553  ++totalgroups;
554 
555  curfile = curfile->next;
556  }
557 
558  dupesfound = totalgroups > 0;
559 
560  status = status_text_alloc(0, COLS);
561  if (status == 0)
562  {
563  for (g = 0; g < totalgroups; ++g)
564  free(groups[g].files);
565 
566  free(groups);
567  free(commandbuffer);
568  free_command_identifier_tree(commandidentifier);
569  free_command_identifier_tree(confirmationkeywordidentifier);
570 
571  endwin();
572  errormsg("out of memory\n");
573  exit(1);
574  }
575 
576  format_status_left(status, L"Ready");
577 
578  prompt = prompt_info_alloc(80);
579  if (prompt == 0)
580  {
581  free_status_text(status);
582 
583  for (g = 0; g < totalgroups; ++g)
584  free(groups[g].files);
585 
586  free(groups);
587  free(commandbuffer);
588  free_command_identifier_tree(commandidentifier);
589  free_command_identifier_tree(confirmationkeywordidentifier);
590 
591  endwin();
592  errormsg("out of memory\n");
593  exit(1);
594  }
595 
596  doprune = 1;
597  do
598  {
599  wmove(filewin, 0, 0);
600  werase(filewin);
601 
602  if (totalgroups > 0)
603  totallines = groups[totalgroups-1].endline;
604  else
605  totallines = 0;
606 
607  for (x = topline; x < topline + getmaxy(filewin); ++x)
608  {
609  if (x >= totallines)
610  {
611  wclrtoeol(filewin);
612  continue;
613  }
614 
615  groupindex = getgroupindex(groups, totalgroups, groupindex, x);
616 
617  index_width = get_num_digits(groups[groupindex].filecount);
618 
619  if (index_width < FILE_INDEX_MIN_WIDTH)
620  index_width = FILE_INDEX_MIN_WIDTH;
621 
622  timestamp_width = ISFLAG(flags, F_SHOWTIME) ? 19 : 0;
623 
624  linestyle = getlinestyle(groups + groupindex, x);
625 
627  {
628  wattron(filewin, A_BOLD);
629  if (groups[groupindex].selected)
630  wattron(filewin, A_REVERSE);
631  wprintw(filewin, "Set %d of %d:\n", groupindex + 1, totalgroups);
632  if (groups[groupindex].selected)
633  wattroff(filewin, A_REVERSE);
634  wattroff(filewin, A_BOLD);
635  }
637  {
638  wprintw(filewin, "\n");
639  }
640  else if (linestyle == linestyle_filename)
641  {
642  f = getgroupfileindex(&row, groups + groupindex, x, COLS);
643 
644  if (cursorgroup != groupindex)
645  {
646  if (row == 0)
647  {
648  print_spaces(filewin, index_width);
649 
650  wprintw(filewin, " [%c] ", groups[groupindex].files[f].action > 0 ? '+' : groups[groupindex].files[f].action < 0 ? '-' : ' ');
651 
652  if (ISFLAG(flags, F_SHOWTIME))
653  wprintw(filewin, "[%s] ", fmttime(groups[groupindex].files[f].file->mtime));
654  }
655 
656  cy = getcury(filewin);
657 
658  if (groups[groupindex].files[f].selected)
659  wattron(filewin, A_REVERSE);
660  putline(filewin, groups[groupindex].files[f].file->d_name, row, COLS, index_width + timestamp_width + FILENAME_INDENT_EXTRA);
661  if (groups[groupindex].files[f].selected)
662  wattroff(filewin, A_REVERSE);
663 
664  wclrtoeol(filewin);
665  wmove(filewin, cy+1, 0);
666  }
667  else
668  {
669  if (row == 0)
670  {
671  print_right_justified_int(filewin, f+1, index_width);
672  wprintw(filewin, " ");
673 
674  if (cursorgroup == groupindex && cursorfile == f)
675  wattron(filewin, A_REVERSE);
676  wprintw(filewin, "[%c]", groups[groupindex].files[f].action > 0 ? '+' : groups[groupindex].files[f].action < 0 ? '-' : ' ');
677  if (cursorgroup == groupindex && cursorfile == f)
678  wattroff(filewin, A_REVERSE);
679  wprintw(filewin, " ");
680 
681  if (ISFLAG(flags, F_SHOWTIME))
682  wprintw(filewin, "[%s] ", fmttime(groups[groupindex].files[f].file->mtime));
683  }
684 
685  cy = getcury(filewin);
686 
687  if (groups[groupindex].files[f].selected)
688  wattron(filewin, A_REVERSE);
689  putline(filewin, groups[groupindex].files[f].file->d_name, row, COLS, index_width + timestamp_width + FILENAME_INDENT_EXTRA);
690  if (groups[groupindex].files[f].selected)
691  wattroff(filewin, A_REVERSE);
692 
693  wclrtoeol(filewin);
694  wmove(filewin, cy+1, 0);
695  }
696  }
698  {
699  wprintw(filewin, "\n");
700  }
701  }
702 
703  if (totalgroups > 0)
704  format_status_right(status, L"Set %d of %d", cursorgroup+1, totalgroups);
705  else
706  format_status_right(status, L"Finished");
707 
708  print_status(statuswin, status);
709 
710  if (totalgroups > 0)
711  format_prompt(prompt, L"( Preserve files [1 - %d, all, help] )", groups[cursorgroup].filecount);
712  else if (dupesfound)
713  format_prompt(prompt, L"( No duplicates remaining; type 'exit' to exit program )");
714  else
715  format_prompt(prompt, L"( No duplicates found; type 'exit' to exit program )");
716 
717  print_prompt(promptwin, prompt, L"");
718 
719  /* refresh windows (using wrefresh instead of wnoutrefresh to avoid bug in gnome-terminal) */
720  wrefresh(filewin);
721  wrefresh(statuswin);
722  wrefresh(promptwin);
723 
724  /* wait for user input */
725  if (!resumecommandinput)
726  {
727  do
728  {
729  keyresult = wget_wch(promptwin, &wch);
730 
731  if (got_sigint)
732  {
733  getyx(promptwin, cursor_y, cursor_x);
734 
735  format_status_left(status, L"Type 'exit' to exit fdupes.");
736  print_status(statuswin, status);
737 
738  wmove(promptwin, cursor_y, cursor_x);
739 
740  got_sigint = 0;
741 
742  wrefresh(statuswin);
743  }
744  } while (keyresult == ERR);
745 
746  if (keyresult == OK && iswprint(wch))
747  {
748  commandbuffer[0] = wch;
749  commandbuffer[1] = '\0';
750  }
751  else
752  {
753  commandbuffer[0] = '\0';
754  }
755  }
756 
757  if (resumecommandinput || (keyresult == OK && iswprint(wch) && ((wch != '\t' && wch != '\n' && wch != '?'))))
758  {
759  resumecommandinput = 0;
760 
761  switch (get_command_text(&commandbuffer, &commandbuffersize, promptwin, prompt, 1, 1))
762  {
763  case GET_COMMAND_OK:
764  format_status_left(status, L"Ready");
765 
766  get_command_arguments(&commandarguments, commandbuffer);
767 
768  switch (identify_command(commandidentifier, commandbuffer, 0))
769  {
771  cmd_select_containing(groups, totalgroups, commandarguments, status);
772  break;
773 
775  cmd_select_beginning(groups, totalgroups, commandarguments, status);
776  break;
777 
779  cmd_select_ending(groups, totalgroups, commandarguments, status);
780  break;
781 
783  cmd_select_matching(groups, totalgroups, commandarguments, status);
784  break;
785 
787  cmd_select_regex(groups, totalgroups, commandarguments, status);
788  break;
789 
791  cmd_clear_selections_containing(groups, totalgroups, commandarguments, status);
792  break;
793 
795  cmd_clear_selections_beginning(groups, totalgroups, commandarguments, status);
796  break;
797 
799  cmd_clear_selections_ending(groups, totalgroups, commandarguments, status);
800  break;
801 
803  cmd_clear_selections_matching(groups, totalgroups, commandarguments, status);
804  break;
805 
807  cmd_clear_selections_regex(groups, totalgroups, commandarguments, status);
808  break;
809 
811  cmd_clear_all_selections(groups, totalgroups, commandarguments, status);
812  break;
813 
815  cmd_invert_group_selections(groups, totalgroups, commandarguments, status);
816  break;
817 
819  cmd_keep_selected(groups, totalgroups, commandarguments, &globaldeletiontally, status);
820  break;
821 
823  cmd_delete_selected(groups, totalgroups, commandarguments, &globaldeletiontally, status);
824  break;
825 
827  cmd_reset_selected(groups, totalgroups, commandarguments, &globaldeletiontally, status);
828  break;
829 
830  case COMMAND_RESET_GROUP:
831  for (x = 0; x < groups[cursorgroup].filecount; ++x)
832  set_file_action(&groups[cursorgroup].files[x], 0, &globaldeletiontally);
833 
834  format_status_left(status, L"Reset all files in current group.");
835 
836  break;
837 
839  /* mark all files for preservation */
840  for (x = 0; x < groups[cursorgroup].filecount; ++x)
841  set_file_action(&groups[cursorgroup].files[x], 1, &globaldeletiontally);
842 
843  format_status_left(status, L"%d files marked for preservation", groups[cursorgroup].filecount);
844 
845  if (cursorgroup < totalgroups - 1)
846  move_to_next_group(&topline, &cursorgroup, &cursorfile, groups, filewin);
847 
848  break;
849 
850  case COMMAND_GOTO_SET:
851  number = wcstol(commandarguments, &wcstolcheck, 10);
852  if (wcstolcheck != commandarguments && *wcstolcheck == '\0')
853  {
854  if (number >= 1 && number <= totalgroups)
855  {
856  scroll_to_group(&topline, number - 1, 0, groups, filewin);
857 
858  cursorgroup = number - 1;
859  cursorfile = 0;
860  }
861  else
862  {
863  format_status_left(status, L"Group index out of range.");
864  }
865  }
866  else
867  {
868  format_status_left(status, L"Invalid group index.");
869  }
870 
871  break;
872 
873  case COMMAND_HELP:
874  endwin();
875 
876  if (system(HELP_COMMAND_STRING) == -1)
877  format_status_left(status, L"Could not display help text.");
878 
879  refresh();
880 
881  break;
882 
883  case COMMAND_PRUNE:
884  cmd_prune(groups, totalgroups, commandarguments, &globaldeletiontally, &totalgroups, &cursorgroup, &cursorfile, &topline, logfile, filewin, status);
885  break;
886 
887  case COMMAND_EXIT: /* exit program */
888  if (totalgroups == 0)
889  {
890  doprune = 0;
891  continue;
892  }
893  else
894  {
895  if (globaldeletiontally != 0)
896  format_prompt(prompt, L"( There are files marked for deletion. Exit without deleting? )");
897  else
898  format_prompt(prompt, L"( There are duplicates remaining. Exit anyway? )");
899 
900  print_prompt(promptwin, prompt, L"");
901 
902  wrefresh(promptwin);
903 
904  switch (get_command_text(&commandbuffer, &commandbuffersize, promptwin, prompt, 0, 0))
905  {
906  case GET_COMMAND_OK:
907  switch (identify_command(confirmationkeywordidentifier, commandbuffer, 0))
908  {
909  case COMMAND_YES:
910  doprune = 0;
911  continue;
912 
913  case COMMAND_NO:
914  case COMMAND_UNDEFINED:
915  commandbuffer[0] = '\0';
916  continue;
917  }
918  break;
919 
921  commandbuffer[0] = '\0';
922  continue;
923 
925  /* resize windows */
926  wresize(filewin, LINES - 2, COLS);
927 
928  wresize(statuswin, 1, COLS);
929  wresize(promptwin, 1, COLS);
930  mvwin(statuswin, LINES - 1, 0);
931  mvwin(promptwin, LINES - 2, 0);
932 
933  status_text_alloc(status, COLS);
934 
935  /* recalculate line boundaries */
936  groupfirstline = 0;
937 
938  for (g = 0; g < totalgroups; ++g)
939  {
940  groups[g].startline = groupfirstline;
941  groups[g].endline = groupfirstline + 2;
942 
943  for (f = 0; f < groups[g].filecount; ++f)
944  groups[g].endline += filerowcount(groups[g].files[f].file, COLS, groups[g].filecount);
945 
946  groupfirstline = groups[g].endline + 1;
947  }
948 
949  commandbuffer[0] = '\0';
950 
951  break;
952 
954  for (g = 0; g < totalgroups; ++g)
955  free(groups[g].files);
956 
957  free(groups);
958  free(commandbuffer);
959  free_command_identifier_tree(commandidentifier);
960  free_command_identifier_tree(confirmationkeywordidentifier);
961 
962  endwin();
963  errormsg("out of memory\n");
964  exit(1);
965  break;
966  }
967  }
968  break;
969 
970  default: /* parse list of files to preserve and mark for preservation */
971  intresult = validate_file_list(groups + cursorgroup, commandbuffer);
972  if (intresult != FILE_LIST_OK)
973  {
974  if (intresult == FILE_LIST_ERROR_UNKNOWN_COMMAND)
975  {
976  format_status_left(status, L"Unrecognized command");
977  break;
978  }
979  else if (intresult == FILE_LIST_ERROR_INDEX_OUT_OF_RANGE)
980  {
981  format_status_left(status, L"Index out of range (1 - %d).", groups[cursorgroup].filecount);
982  break;
983  }
984  else if (intresult == FILE_LIST_ERROR_LIST_CONTAINS_INVALID_INDEX)
985  {
986  format_status_left(status, L"Invalid index");
987  break;
988  }
989  else if (intresult == FILE_LIST_ERROR_OUT_OF_MEMORY)
990  {
991  free(commandbuffer);
992 
993  free_command_identifier_tree(commandidentifier);
994 
995  for (g = 0; g < totalgroups; ++g)
996  free(groups[g].files);
997 
998  free(groups);
999 
1000  endwin();
1001  errormsg("out of memory\n");
1002  exit(1);
1003  }
1004  else
1005  {
1006  format_status_left(status, L"Could not interpret command");
1007  break;
1008  }
1009  }
1010 
1011  token = wcstok(commandbuffer, L",", &wcsptr);
1012 
1013  while (token != NULL)
1014  {
1015  number = wcstol(token, &wcstolcheck, 10);
1016  if (wcstolcheck != token && *wcstolcheck == '\0')
1017  {
1018  if (number > 0 && number <= groups[cursorgroup].filecount)
1019  set_file_action(&groups[cursorgroup].files[number - 1], 1, &globaldeletiontally);
1020  }
1021 
1022  token = wcstok(NULL, L",", &wcsptr);
1023  }
1024 
1025  /* mark remaining files for deletion */
1026  preservecount = 0;
1027  deletecount = 0;
1028 
1029  for (x = 0; x < groups[cursorgroup].filecount; ++x)
1030  {
1031  if (groups[cursorgroup].files[x].action == 1)
1032  ++preservecount;
1033  if (groups[cursorgroup].files[x].action == -1)
1034  ++deletecount;
1035  }
1036 
1037  if (preservecount > 0)
1038  {
1039  for (x = 0; x < groups[cursorgroup].filecount; ++x)
1040  {
1041  if (groups[cursorgroup].files[x].action == 0)
1042  {
1043  set_file_action(&groups[cursorgroup].files[x], -1, &globaldeletiontally);
1044  ++deletecount;
1045  }
1046  }
1047  }
1048 
1049  format_status_left(status, L"%d files marked for preservation, %d for deletion", preservecount, deletecount);
1050 
1051  if (cursorgroup < totalgroups - 1 && preservecount > 0)
1052  move_to_next_group(&topline, &cursorgroup, &cursorfile, groups, filewin);
1053 
1054  break;
1055  }
1056 
1057  break;
1058 
1059  case GET_COMMAND_KEY_SF:
1060  ++topline;
1061 
1062  resumecommandinput = 1;
1063 
1064  continue;
1065 
1066  case GET_COMMAND_KEY_SR:
1067  if (topline > 0)
1068  --topline;
1069 
1070  resumecommandinput = 1;
1071 
1072  continue;
1073 
1074  case GET_COMMAND_KEY_NPAGE:
1075  topline += getmaxy(filewin);
1076 
1077  resumecommandinput = 1;
1078 
1079  continue;
1080 
1081  case GET_COMMAND_KEY_PPAGE:
1082  topline -= getmaxy(filewin);
1083 
1084  if (topline < 0)
1085  topline = 0;
1086 
1087  resumecommandinput = 1;
1088 
1089  continue;
1090 
1091  case GET_COMMAND_CANCELED:
1092  break;
1093 
1095  /* resize windows */
1096  wresize(filewin, LINES - 2, COLS);
1097 
1098  wresize(statuswin, 1, COLS);
1099  wresize(promptwin, 1, COLS);
1100  mvwin(statuswin, LINES - 1, 0);
1101  mvwin(promptwin, LINES - 2, 0);
1102 
1103  status_text_alloc(status, COLS);
1104 
1105  /* recalculate line boundaries */
1106  groupfirstline = 0;
1107 
1108  for (g = 0; g < totalgroups; ++g)
1109  {
1110  groups[g].startline = groupfirstline;
1111  groups[g].endline = groupfirstline + 2;
1112 
1113  for (f = 0; f < groups[g].filecount; ++f)
1114  groups[g].endline += filerowcount(groups[g].files[f].file, COLS, groups[g].filecount);
1115 
1116  groupfirstline = groups[g].endline + 1;
1117  }
1118 
1119  commandbuffer[0] = '\0';
1120 
1121  break;
1122 
1124  for (g = 0; g < totalgroups; ++g)
1125  free(groups[g].files);
1126 
1127  free(groups);
1128  free(commandbuffer);
1129  free_command_identifier_tree(commandidentifier);
1130  free_command_identifier_tree(confirmationkeywordidentifier);
1131 
1132  endwin();
1133  errormsg("out of memory\n");
1134  exit(1);
1135 
1136  break;
1137  }
1138 
1139  commandbuffer[0] = '\0';
1140  }
1141  else if (keyresult == KEY_CODE_YES)
1142  {
1143  switch (wch)
1144  {
1145  case KEY_DOWN:
1146  if (cursorfile < groups[cursorgroup].filecount - 1)
1147  move_to_next_file(&topline, &cursorgroup, &cursorfile, groups, filewin);
1148  else if (cursorgroup < totalgroups - 1)
1149  move_to_next_group(&topline, &cursorgroup, &cursorfile, groups, filewin);
1150 
1151  break;
1152 
1153  case KEY_UP:
1154  if (cursorfile > 0)
1155  move_to_previous_file(&topline, &cursorgroup, &cursorfile, groups, filewin);
1156  else if (cursorgroup > 0)
1157  move_to_previous_group(&topline, &cursorgroup, &cursorfile, groups, filewin);
1158 
1159  break;
1160 
1161  case KEY_SF:
1162  ++topline;
1163  break;
1164 
1165  case KEY_SR:
1166  if (topline > 0)
1167  --topline;
1168  break;
1169 
1170  case KEY_NPAGE:
1171  topline += getmaxy(filewin);
1172  break;
1173 
1174  case KEY_PPAGE:
1175  topline -= getmaxy(filewin);
1176 
1177  if (topline < 0)
1178  topline = 0;
1179 
1180  break;
1181 
1182  case KEY_SRIGHT:
1183  set_file_action(&groups[cursorgroup].files[cursorfile], 1, &globaldeletiontally);
1184 
1185  format_status_left(status, L"1 file marked for preservation.");
1186 
1187  if (cursorfile < groups[cursorgroup].filecount - 1)
1188  move_to_next_file(&topline, &cursorgroup, &cursorfile, groups, filewin);
1189  else if (cursorgroup < totalgroups - 1)
1190  move_to_next_group(&topline, &cursorgroup, &cursorfile, groups, filewin);
1191 
1192  break;
1193 
1194  case KEY_SLEFT:
1195  deletecount = 0;
1196 
1197  set_file_action(&groups[cursorgroup].files[cursorfile], -1, &globaldeletiontally);
1198 
1199  format_status_left(status, L"1 file marked for deletion.");
1200 
1201  for (x = 0; x < groups[cursorgroup].filecount; ++x)
1202  if (groups[cursorgroup].files[x].action == -1)
1203  ++deletecount;
1204 
1205  if (deletecount < groups[cursorgroup].filecount)
1206  {
1207  if (cursorfile < groups[cursorgroup].filecount - 1)
1208  move_to_next_file(&topline, &cursorgroup, &cursorfile, groups, filewin);
1209  else if (cursorgroup < totalgroups - 1)
1210  move_to_next_group(&topline, &cursorgroup, &cursorfile, groups, filewin);
1211  }
1212 
1213  break;
1214 
1215  case KEY_BACKSPACE:
1216  if (cursorgroup > 0)
1217  --cursorgroup;
1218 
1219  cursorfile = 0;
1220 
1221  scroll_to_group(&topline, cursorgroup, 0, groups, filewin);
1222 
1223  break;
1224 
1225  case KEY_F(3):
1226  move_to_next_selected_group(&topline, &cursorgroup, &cursorfile, groups, totalgroups, filewin);
1227  break;
1228 
1229  case KEY_F(2):
1230  move_to_previous_selected_group(&topline, &cursorgroup, &cursorfile, groups, totalgroups, filewin);
1231  break;
1232 
1233  case KEY_DC:
1234  cmd_prune(groups, totalgroups, commandarguments, &globaldeletiontally, &totalgroups, &cursorgroup, &cursorfile, &topline, logfile, filewin, status);
1235  break;
1236 
1237  case KEY_RESIZE:
1238  /* resize windows */
1239  wresize(filewin, LINES - 2, COLS);
1240 
1241  wresize(statuswin, 1, COLS);
1242  wresize(promptwin, 1, COLS);
1243  mvwin(statuswin, LINES - 1, 0);
1244  mvwin(promptwin, LINES - 2, 0);
1245 
1246  status_text_alloc(status, COLS);
1247 
1248  /* recalculate line boundaries */
1249  groupfirstline = 0;
1250 
1251  for (g = 0; g < totalgroups; ++g)
1252  {
1253  groups[g].startline = groupfirstline;
1254  groups[g].endline = groupfirstline + 2;
1255 
1256  for (f = 0; f < groups[g].filecount; ++f)
1257  groups[g].endline += filerowcount(groups[g].files[f].file, COLS, groups[g].filecount);
1258 
1259  groupfirstline = groups[g].endline + 1;
1260  }
1261 
1262  break;
1263  }
1264  }
1265  else if (keyresult == OK)
1266  {
1267  switch (wch)
1268  {
1269  case '?':
1270  if (groups[cursorgroup].files[cursorfile].action == 0)
1271  break;
1272 
1273  set_file_action(&groups[cursorgroup].files[cursorfile], 0, &globaldeletiontally);
1274 
1275  if (cursorfile < groups[cursorgroup].filecount - 1)
1276  move_to_next_file(&topline, &cursorgroup, &cursorfile, groups, filewin);
1277  else if (cursorgroup < totalgroups - 1)
1278  move_to_next_group(&topline, &cursorgroup, &cursorfile, groups, filewin);
1279 
1280  break;
1281 
1282  case '\n':
1283  deletecount = 0;
1284  preservecount = 0;
1285 
1286  for (x = 0; x < groups[cursorgroup].filecount; ++x)
1287  {
1288  if (groups[cursorgroup].files[x].action == 1)
1289  ++preservecount;
1290  }
1291 
1292  if (preservecount == 0)
1293  break;
1294 
1295  for (x = 0; x < groups[cursorgroup].filecount; ++x)
1296  {
1297  if (groups[cursorgroup].files[x].action == 0)
1298  set_file_action(&groups[cursorgroup].files[x], -1, &globaldeletiontally);
1299 
1300  if (groups[cursorgroup].files[x].action == -1)
1301  ++deletecount;
1302  }
1303 
1304  if (cursorgroup < totalgroups - 1 && deletecount < groups[cursorgroup].filecount)
1305  move_to_next_group(&topline, &cursorgroup, &cursorfile, groups, filewin);
1306 
1307  break;
1308 
1309  case '\t':
1310  if (cursorgroup < totalgroups - 1)
1311  move_to_next_group(&topline, &cursorgroup, &cursorfile, groups, filewin);
1312 
1313  break;
1314  }
1315  }
1316  } while (doprune);
1317 
1318  endwin();
1319 
1320  free(commandbuffer);
1321 
1322  free_prompt_info(prompt);
1323 
1324  free_status_text(status);
1325 
1326  free_command_identifier_tree(commandidentifier);
1327  free_command_identifier_tree(confirmationkeywordidentifier);
1328 
1329  for (g = 0; g < totalgroups; ++g)
1330  free(groups[g].files);
1331 
1332  free(groups);
1333 }
COMMAND_RESET_GROUP
#define COMMAND_RESET_GROUP
Definition: ncurses-commands.h:42
COMMAND_YES
#define COMMAND_YES
Definition: ncurses-commands.h:46
ncurses-status.h
cmd_delete_selected
int cmd_delete_selected(struct filegroup *groups, int groupcount, wchar_t *commandarguments, size_t *deletiontally, struct status_text *status)
Definition: ncurses-commands.c:634
FILE_LIST_ERROR_OUT_OF_MEMORY
#define FILE_LIST_ERROR_OUT_OF_MEMORY
Definition: ncurses-interface.c:308
COMMAND_KEEP_SELECTED
#define COMMAND_KEEP_SELECTED
Definition: ncurses-commands.h:39
filegroup::selected
int selected
Definition: filegroup.h:40
COMMAND_CLEAR_SELECTIONS_BEGINNING
#define COMMAND_CLEAR_SELECTIONS_BEGINNING
Definition: ncurses-commands.h:34
mbstowcs_escape_invalid
size_t mbstowcs_escape_invalid(wchar_t *dest, const char *src, size_t n)
Definition: mbstowcs_escape_invalid.c:56
_file
Definition: fdupes.h:28
filegroup::filecount
size_t filecount
Definition: filegroup.h:37
ncurses-print.h
GET_COMMAND_KEY_SF
#define GET_COMMAND_KEY_SF
Definition: ncurses-getcommand.h:39
COMMAND_CLEAR_ALL_SELECTIONS
#define COMMAND_CLEAR_ALL_SELECTIONS
Definition: ncurses-commands.h:37
cmd_invert_group_selections
int cmd_invert_group_selections(struct filegroup *groups, int groupcount, wchar_t *commandarguments, struct status_text *status)
Definition: ncurses-commands.c:581
COMMAND_INVERT_GROUP_SELECTIONS
#define COMMAND_INVERT_GROUP_SELECTIONS
Definition: ncurses-commands.h:38
COMMAND_DELETE_SELECTED
#define COMMAND_DELETE_SELECTED
Definition: ncurses-commands.h:40
cmd_select_regex
int cmd_select_regex(struct filegroup *groups, int groupcount, wchar_t *commandarguments, struct status_text *status)
Definition: ncurses-commands.c:214
groupfile::file
file_t * file
Definition: filegroup.h:29
prompt_info_alloc
struct prompt_info * prompt_info_alloc(size_t initial_size)
Definition: ncurses-prompt.c:27
errormsg
void errormsg(char *message,...)
Definition: errormsg.c:26
scroll_to_group
void scroll_to_group(int *topline, int group, int tail, struct filegroup *groups, WINDOW *filewin)
Definition: ncurses-interface.c:199
linestyle
linestyle
Definition: ncurses-interface.c:49
GET_COMMAND_OK
#define GET_COMMAND_OK
Definition: ncurses-getcommand.h:33
putline
void putline(WINDOW *window, const char *str, const int line, const int columns, const int compensate_indent)
Definition: ncurses-print.c:30
flags
unsigned long flags
Definition: flags.c:3
prompt_info
Definition: ncurses-prompt.h:33
COMMAND_SELECT_MATCHING
#define COMMAND_SELECT_MATCHING
Definition: ncurses-commands.h:32
linestyle_groupheaderspacing
@ linestyle_groupheaderspacing
Definition: ncurses-interface.c:51
FILE_LIST_OK
#define FILE_LIST_OK
Definition: ncurses-interface.c:304
GET_COMMAND_KEY_PPAGE
#define GET_COMMAND_KEY_PPAGE
Definition: ncurses-getcommand.h:38
commandidentifier.h
COMMAND_SELECT_BEGINNING
#define COMMAND_SELECT_BEGINNING
Definition: ncurses-commands.h:30
_file::mtime
time_t mtime
Definition: fdupes.h:35
cmd_clear_selections_ending
int cmd_clear_selections_ending(struct filegroup *groups, int groupcount, wchar_t *commandarguments, struct status_text *status)
Definition: ncurses-commands.c:381
free_command_identifier_tree
void free_command_identifier_tree(struct command_identifier_node *tree)
Definition: commandidentifier.c:139
GET_COMMAND_KEY_SR
#define GET_COMMAND_KEY_SR
Definition: ncurses-getcommand.h:40
move_to_next_selected_group
int move_to_next_selected_group(int *topline, int *cursorgroup, int *cursorfile, struct filegroup *groups, int totalgroups, WINDOW *filewin)
Definition: ncurses-interface.c:229
get_command_text
int get_command_text(wchar_t **commandbuffer, size_t *commandbuffersize, WINDOW *promptwin, struct prompt_info *prompt, int cancel_on_erase, int append)
Definition: ncurses-getcommand.c:49
status_text
Definition: ncurses-status.h:33
_file::duplicates
struct _file * duplicates
Definition: fdupes.h:38
groupfile
Definition: filegroup.h:28
format_status_right
void format_status_right(struct status_text *status, wchar_t *format,...)
Definition: ncurses-status.c:110
F_SHOWTIME
#define F_SHOWTIME
Definition: flags.h:24
groupfile::action
int action
Definition: filegroup.h:30
FILE_LIST_ERROR_LIST_CONTAINS_INVALID_INDEX
#define FILE_LIST_ERROR_LIST_CONTAINS_INVALID_INDEX
Definition: ncurses-interface.c:306
linestyle_groupheader
@ linestyle_groupheader
Definition: ncurses-interface.c:50
validate_file_list
int validate_file_list(struct filegroup *currentgroup, wchar_t *commandbuffer_in)
Definition: ncurses-interface.c:310
move_to_previous_group
void move_to_previous_group(int *topline, int *cursorgroup, int *cursorfile, struct filegroup *groups, WINDOW *filewin)
Definition: ncurses-interface.c:262
getgroupfileline
int getgroupfileline(struct filegroup *group, int fileindex, int columns)
Definition: ncurses-interface.c:163
cmd_clear_selections_regex
int cmd_clear_selections_regex(struct filegroup *groups, int groupcount, wchar_t *commandarguments, struct status_text *status)
Definition: ncurses-commands.c:481
COMMAND_NO
#define COMMAND_NO
Definition: ncurses-commands.h:47
ncurses-getcommand.h
format_prompt
int format_prompt(struct prompt_info *prompt, wchar_t *format,...)
Definition: ncurses-prompt.c:60
COMMAND_SELECT_CONTAINING
#define COMMAND_SELECT_CONTAINING
Definition: ncurses-commands.h:29
GET_COMMAND_CANCELED
#define GET_COMMAND_CANCELED
Definition: ncurses-getcommand.h:34
COMMAND_CLEAR_SELECTIONS_REGEX
#define COMMAND_CLEAR_SELECTIONS_REGEX
Definition: ncurses-commands.h:49
move_to_previous_selected_group
int move_to_previous_selected_group(int *topline, int *cursorgroup, int *cursorfile, struct filegroup *groups, int totalgroups, WINDOW *filewin)
Definition: ncurses-interface.c:271
cmd_clear_selections_beginning
int cmd_clear_selections_beginning(struct filegroup *groups, int groupcount, wchar_t *commandarguments, struct status_text *status)
Definition: ncurses-commands.c:331
command_list
struct command_map command_list[]
Definition: ncurses-commands.c:33
errormsg.h
COMMAND_UNDEFINED
#define COMMAND_UNDEFINED
Definition: commandidentifier.h:26
cmd_select_ending
int cmd_select_ending(struct filegroup *groups, int groupcount, wchar_t *commandarguments, struct status_text *status)
Definition: ncurses-commands.c:140
free_status_text
void free_status_text(struct status_text *status)
Definition: ncurses-status.c:84
print_spaces
void print_spaces(WINDOW *window, int spaces)
Definition: ncurses-print.c:106
positive_wcwidth
int positive_wcwidth(wchar_t ch)
Definition: positive_wcwidth.c:22
cmd_clear_selections_matching
int cmd_clear_selections_matching(struct filegroup *groups, int groupcount, wchar_t *commandarguments, struct status_text *status)
Definition: ncurses-commands.c:431
cmd_clear_all_selections
int cmd_clear_all_selections(struct filegroup *groups, int groupcount, wchar_t *commandarguments, struct status_text *status)
Definition: ncurses-commands.c:561
print_prompt
void print_prompt(WINDOW *promptwin, struct prompt_info *prompt, wchar_t *commandbuffer)
Definition: ncurses-prompt.c:128
move_to_next_group
void move_to_next_group(int *topline, int *cursorgroup, int *cursorfile, struct filegroup *groups, WINDOW *filewin)
Definition: ncurses-interface.c:220
ncurses-interface.h
COMMAND_RESET_SELECTED
#define COMMAND_RESET_SELECTED
Definition: ncurses-commands.h:41
print_right_justified_int
void print_right_justified_int(WINDOW *window, int number, int width)
Definition: ncurses-print.c:114
get_command_arguments
void get_command_arguments(wchar_t **arguments, wchar_t *input)
Definition: ncurses-getcommand.c:32
linestyle_groupfooterspacing
@ linestyle_groupfooterspacing
Definition: ncurses-interface.c:53
sigint.h
_file::d_name
char * d_name
Definition: fdupes.h:29
COMMAND_PRESERVE_ALL
#define COMMAND_PRESERVE_ALL
Definition: ncurses-commands.h:43
mbstowcs_escape_invalid.h
free_prompt_info
void free_prompt_info(struct prompt_info *info)
Definition: ncurses-prompt.c:54
getlinestyle
enum linestyle getlinestyle(struct filegroup *group, int line)
Definition: ncurses-interface.c:56
filegroup::files
struct groupfile * files
Definition: filegroup.h:36
print_status
void print_status(WINDOW *statuswin, struct status_text *status)
Definition: ncurses-status.c:129
get_num_digits
int get_num_digits(int value)
Definition: ncurses-print.c:144
_file::next
struct _file * next
Definition: fdupes.h:39
GET_COMMAND_KEY_NPAGE
#define GET_COMMAND_KEY_NPAGE
Definition: ncurses-getcommand.h:37
FILE_INDEX_MIN_WIDTH
#define FILE_INDEX_MIN_WIDTH
Definition: ncurses-interface.c:69
log.h
cmd_keep_selected
int cmd_keep_selected(struct filegroup *groups, int groupcount, wchar_t *commandarguments, size_t *deletiontally, struct status_text *status)
Definition: ncurses-commands.c:610
filegroup::endline
int endline
Definition: filegroup.h:39
move_to_previous_file
void move_to_previous_file(int *topline, int *cursorgroup, int *cursorfile, struct filegroup *groups, WINDOW *filewin)
Definition: ncurses-interface.c:291
GET_COMMAND_RESIZE_REQUESTED
#define GET_COMMAND_RESIZE_REQUESTED
Definition: ncurses-getcommand.h:36
confirmation_keyword_list
struct command_map confirmation_keyword_list[]
Definition: ncurses-commands.c:59
groupfile::selected
int selected
Definition: filegroup.h:31
GET_COMMAND_ERROR_OUT_OF_MEMORY
#define GET_COMMAND_ERROR_OUT_OF_MEMORY
Definition: ncurses-getcommand.h:35
cmd_select_beginning
int cmd_select_beginning(struct filegroup *groups, int groupcount, wchar_t *commandarguments, struct status_text *status)
Definition: ncurses-commands.c:103
COMMAND_GOTO_SET
#define COMMAND_GOTO_SET
Definition: ncurses-commands.h:50
COMMAND_EXIT
#define COMMAND_EXIT
Definition: ncurses-commands.h:44
deletefiles_ncurses
void deletefiles_ncurses(file_t *files, char *logfile)
Definition: ncurses-interface.c:355
cmd_prune
int cmd_prune(struct filegroup *groups, int groupcount, wchar_t *commandarguments, size_t *deletiontally, int *totalgroups, int *cursorgroup, int *cursorfile, int *topline, char *logfile, WINDOW *filewin, struct status_text *status)
Definition: ncurses-commands.c:684
ISFLAG
#define ISFLAG(a, b)
Definition: flags.h:4
COMMAND_PRUNE
#define COMMAND_PRUNE
Definition: ncurses-commands.h:51
got_sigint
volatile sig_atomic_t got_sigint
Definition: sigint.c:22
filegroup
Definition: filegroup.h:35
filegroup.h
identify_command
int identify_command(struct command_identifier_node *tree, wchar_t *command_buffer, size_t ch)
Definition: commandidentifier.c:179
positive_wcwidth.h
FILE_LIST_ERROR_INDEX_OUT_OF_RANGE
#define FILE_LIST_ERROR_INDEX_OUT_OF_RANGE
Definition: ncurses-interface.c:305
_file::hasdupes
int hasdupes
Definition: fdupes.h:37
flags.h
cmd_select_containing
int cmd_select_containing(struct filegroup *groups, int groupcount, wchar_t *commandarguments, struct status_text *status)
Definition: ncurses-commands.c:66
filerowcount
int filerowcount(file_t *file, const int columns, int group_file_count)
Definition: ncurses-interface.c:71
linestyle_filename
@ linestyle_filename
Definition: ncurses-interface.c:52
COMMAND_HELP
#define COMMAND_HELP
Definition: ncurses-commands.h:45
set_file_action
void set_file_action(struct groupfile *file, int new_action, size_t *deletion_tally)
Definition: ncurses-interface.c:181
COMMAND_CLEAR_SELECTIONS_CONTAINING
#define COMMAND_CLEAR_SELECTIONS_CONTAINING
Definition: ncurses-commands.h:33
FILENAME_INDENT_EXTRA
#define FILENAME_INDENT_EXTRA
Definition: ncurses-interface.c:68
fmttime
char * fmttime(time_t t)
Definition: fdupes.c:145
COMMAND_CLEAR_SELECTIONS_ENDING
#define COMMAND_CLEAR_SELECTIONS_ENDING
Definition: ncurses-commands.h:35
ncurses-commands.h
status_text_alloc
struct status_text * status_text_alloc(struct status_text *status, size_t width)
Definition: ncurses-status.c:27
COMMAND_SELECT_REGEX
#define COMMAND_SELECT_REGEX
Definition: ncurses-commands.h:48
COMMAND_SELECT_ENDING
#define COMMAND_SELECT_ENDING
Definition: ncurses-commands.h:31
cmd_reset_selected
int cmd_reset_selected(struct filegroup *groups, int groupcount, wchar_t *commandarguments, size_t *deletiontally, struct status_text *status)
Definition: ncurses-commands.c:658
COMMAND_CLEAR_SELECTIONS_MATCHING
#define COMMAND_CLEAR_SELECTIONS_MATCHING
Definition: ncurses-commands.h:36
cmd_select_matching
int cmd_select_matching(struct filegroup *groups, int groupcount, wchar_t *commandarguments, struct status_text *status)
Definition: ncurses-commands.c:177
register_sigint_handler
void register_sigint_handler()
Definition: sigint.c:29
format_status_left
void format_status_left(struct status_text *status, wchar_t *format,...)
Definition: ncurses-status.c:91
command_identifier_node
Definition: commandidentifier.h:36
move_to_next_file
void move_to_next_file(int *topline, int *cursorgroup, int *cursorfile, struct filegroup *groups, WINDOW *filewin)
Definition: ncurses-interface.c:249
FILE_LIST_ERROR_UNKNOWN_COMMAND
#define FILE_LIST_ERROR_UNKNOWN_COMMAND
Definition: ncurses-interface.c:307
build_command_identifier_tree
struct command_identifier_node * build_command_identifier_tree(struct command_map *commands)
Definition: commandidentifier.c:112
cmd_clear_selections_containing
int cmd_clear_selections_containing(struct filegroup *groups, int groupcount, wchar_t *commandarguments, struct status_text *status)
Definition: ncurses-commands.c:281
getgroupfileindex
int getgroupfileindex(int *row, struct filegroup *group, int line, int columns)
Definition: ncurses-interface.c:138
getgroupindex
int getgroupindex(struct filegroup *groups, int group_count, int group_hint, int line)
Definition: ncurses-interface.c:125
filegroup::startline
int startline
Definition: filegroup.h:38
ncurses-prompt.h