LCOV - code coverage report
Current view: top level - lib/formats - progressbar.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 126 162 77.8 %
Date: 2015-09-30 14:09:30 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :  *  This file is part of rmlint.
       3             :  *
       4             :  *  rmlint is free software: you can redistribute it and/or modify
       5             :  *  it under the terms of the GNU General Public License as published by
       6             :  *  the Free Software Foundation, either version 3 of the License, or
       7             :  *  (at your option) any later version.
       8             :  *
       9             :  *  rmlint is distributed in the hope that it will be useful,
      10             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  *  GNU General Public License for more details.
      13             :  *
      14             :  *  You should have received a copy of the GNU General Public License
      15             :  *  along with rmlint.  If not, see <http://www.gnu.org/licenses/>.
      16             :  *
      17             :  * Authors:
      18             :  *
      19             :  *  - Christopher <sahib> Pahl 2010-2015 (https://github.com/sahib)
      20             :  *  - Daniel <SeeSpotRun> T.   2014-2015 (https://github.com/SeeSpotRun)
      21             :  *
      22             :  * Hosted on http://github.com/sahib/rmlint
      23             :  *
      24             :  */
      25             : 
      26             : #include "../formats.h"
      27             : 
      28             : #include <glib.h>
      29             : #include <stdio.h>
      30             : #include <string.h>
      31             : #include <math.h>
      32             : 
      33             : #include <sys/ioctl.h>
      34             : 
      35             : typedef struct RmFmtHandlerProgress {
      36             :     /* must be first */
      37             :     RmFmtHandler parent;
      38             : 
      39             :     /* user data */
      40             :     gdouble percent;
      41             :     gdouble last_unknown_pos;
      42             :     RmOff total_lint_bytes;
      43             : 
      44             :     char text_buf[1024];
      45             :     guint32 text_len;
      46             :     guint32 update_counter;
      47             :     guint32 update_interval;
      48             :     guint8 use_unicode_glyphs;
      49             : 
      50             :     bool plain;
      51             : 
      52             :     RmFmtProgressState last_state;
      53             :     struct winsize terminal;
      54             : } RmFmtHandlerProgress;
      55             : 
      56         122 : static void rm_fmt_progress_format_preprocess(RmSession *session, char *buf,
      57             :                                               size_t buf_len, FILE *out) {
      58         122 :     if(session->offsets_read > 0) {
      59           0 :         g_snprintf(buf, buf_len, "fiemap: %s+%" LLU "%s %s-%" LLU "%s %s#%" LLU "%s",
      60           0 :                    MAYBE_GREEN(out, session), session->offsets_read,
      61           0 :                    MAYBE_RESET(out, session), MAYBE_RED(out, session),
      62           0 :                    session->offset_fails, MAYBE_RESET(out, session),
      63           0 :                    MAYBE_BLUE(out, session), session->total_filtered_files,
      64           0 :                    MAYBE_RESET(out, session));
      65             :     } else {
      66         244 :         g_snprintf(buf, buf_len, "%s %s%" LLU "%s", _("reduces files to"),
      67         122 :                    MAYBE_GREEN(out, session), session->total_filtered_files,
      68         122 :                    MAYBE_RESET(out, session));
      69             :     }
      70         122 : }
      71             : 
      72         732 : static void rm_fmt_progress_format_text(RmSession *session, RmFmtHandlerProgress *self,
      73             :                                         int max_len, FILE *out) {
      74         732 :     char num_buf[32] = {0};
      75         732 :     char preproc_buf[128] = {0};
      76         732 :     memset(num_buf, 0, sizeof(num_buf));
      77         732 :     memset(preproc_buf, 0, sizeof(preproc_buf));
      78             : 
      79         732 :     switch(self->last_state) {
      80             :     case RM_PROGRESS_STATE_TRAVERSE:
      81         244 :         self->percent = 2.0;
      82        1952 :         self->text_len = g_snprintf(
      83         244 :             self->text_buf, sizeof(self->text_buf), "%s (%s%d%s %s / %s%d%s + %s%d%s %s)",
      84         244 :             _("Traversing"), MAYBE_GREEN(out, session), session->total_files,
      85         488 :             MAYBE_RESET(out, session), _("usable files"), MAYBE_RED(out, session),
      86         488 :             session->ignored_files, MAYBE_RESET(out, session), MAYBE_RED(out, session),
      87         244 :             session->ignored_folders, MAYBE_RESET(out, session),
      88             :             _("ignored files / folders"));
      89         244 :         break;
      90             :     case RM_PROGRESS_STATE_PREPROCESS:
      91         122 :         self->percent = 2.0;
      92         122 :         rm_fmt_progress_format_preprocess(session, preproc_buf, sizeof(preproc_buf), out);
      93         488 :         self->text_len = g_snprintf(
      94         122 :             self->text_buf, sizeof(self->text_buf), "%s (%s / %s %s%" LLU "%s %s)",
      95         122 :             _("Preprocessing"), preproc_buf, _("found"), MAYBE_RED(out, session),
      96         122 :             session->other_lint_cnt, MAYBE_RESET(out, session), _("other lint"));
      97         122 :         break;
      98             :     case RM_PROGRESS_STATE_SHREDDER:
      99         488 :         self->percent = 1.0 - ((gdouble)session->shred_bytes_remaining /
     100         244 :                                (gdouble)session->shred_bytes_after_preprocess);
     101         244 :         rm_util_size_to_human_readable(session->shred_bytes_remaining, num_buf,
     102             :                                        sizeof(num_buf));
     103        2440 :         self->text_len = g_snprintf(
     104         244 :             self->text_buf, sizeof(self->text_buf),
     105             :             "%s (%s%" LLU "%s %s %s%" LLU "%s %s; %s%s%s %s %s%" LLU "%s %s)",
     106         244 :             _("Matching"), MAYBE_RED(out, session), session->dup_counter,
     107         488 :             MAYBE_RESET(out, session), _("dupes of"), MAYBE_YELLOW(out, session),
     108         244 :             session->dup_group_counter, MAYBE_RESET(out, session), _("originals"),
     109         488 :             MAYBE_GREEN(out, session), num_buf, MAYBE_RESET(out, session),
     110         244 :             _("to scan in"), MAYBE_GREEN(out, session), session->shred_files_remaining,
     111         244 :             MAYBE_RESET(out, session), _("files"));
     112         244 :         break;
     113             :     case RM_PROGRESS_STATE_MERGE:
     114           0 :         self->percent = 1.0;
     115           0 :         self->text_len = g_snprintf(self->text_buf, sizeof(self->text_buf),
     116           0 :                                     _("Merging files into directories (stand by...)"));
     117           0 :         break;
     118             :     case RM_PROGRESS_STATE_INIT:
     119             :     case RM_PROGRESS_STATE_PRE_SHUTDOWN:
     120             :     case RM_PROGRESS_STATE_SUMMARY:
     121             :     default:
     122         122 :         self->percent = 0;
     123         122 :         memset(self->text_buf, 0, sizeof(self->text_buf));
     124         122 :         break;
     125             :     }
     126             : 
     127             :     /* Support unicode messages - tranlsated text might contain some. */
     128         732 :     self->text_len = g_utf8_strlen(self->text_buf, self->text_len);
     129             : 
     130             :     /* Get rid of colors */
     131         732 :     int text_iter = 0;
     132         732 :     for(char *iter = &self->text_buf[0]; *iter; iter++) {
     133         610 :         if(*iter == '\x1b') {
     134           0 :             char *jump = strchr(iter, 'm');
     135           0 :             if(jump != NULL) {
     136           0 :                 self->text_len -= jump - iter + 1;
     137           0 :                 iter = jump;
     138           0 :                 continue;
     139             :             }
     140             :         }
     141             : 
     142         610 :         if(text_iter >= max_len) {
     143         610 :             *iter = 0;
     144         610 :             self->text_len = text_iter;
     145         610 :             break;
     146             :         }
     147             : 
     148           0 :         text_iter++;
     149             :     }
     150         732 : }
     151             : 
     152         732 : static void rm_fmt_progress_print_text(RmFmtHandlerProgress *self, int width, FILE *out) {
     153         732 :     if(self->text_len < (unsigned)width) {
     154           0 :         for(guint32 i = 0; i < width - self->text_len; ++i) {
     155           0 :             fprintf(out, " ");
     156             :         }
     157             :     }
     158             : 
     159         732 :     fprintf(out, "%s", self->text_buf);
     160         732 : }
     161             : 
     162             : typedef enum RmProgressBarGlyph {
     163             :     PROGRESS_ARROW,
     164             :     PROGRESS_TICK_LOW,
     165             :     PROGRESS_TICK_HIGH,
     166             :     PROGRESS_TICK_SPACE,
     167             :     PROGRESS_EMPTY,
     168             :     PROGRESS_FULL,
     169             :     PROGRESS_LEFT_BRACKET,
     170             :     PROGRESS_RIGHT_BRACKET
     171             : } RmProgressBarGlyph;
     172             : 
     173             : static const char *PROGRESS_FANCY_UNICODE_TABLE[] = {[PROGRESS_ARROW] = "➤",
     174             :                                                      [PROGRESS_TICK_LOW] = "□",
     175             :                                                      [PROGRESS_TICK_HIGH] = "▢",
     176             :                                                      [PROGRESS_TICK_SPACE] = " ",
     177             :                                                      [PROGRESS_EMPTY] = "⌿",
     178             :                                                      [PROGRESS_FULL] = "—",
     179             :                                                      [PROGRESS_LEFT_BRACKET] = "⦃",
     180             :                                                      [PROGRESS_RIGHT_BRACKET] = "⦄"};
     181             : 
     182             : static const char *PROGRESS_FANCY_ASCII_TABLE[] = {[PROGRESS_ARROW] = ">",
     183             :                                                    [PROGRESS_TICK_LOW] = "o",
     184             :                                                    [PROGRESS_TICK_HIGH] = "O",
     185             :                                                    [PROGRESS_TICK_SPACE] = " ",
     186             :                                                    [PROGRESS_EMPTY] = "/",
     187             :                                                    [PROGRESS_FULL] = "_",
     188             :                                                    [PROGRESS_LEFT_BRACKET] = "{",
     189             :                                                    [PROGRESS_RIGHT_BRACKET] = "}"};
     190             : 
     191             : static const char *PROGRESS_PLAIN_UNICODE_TABLE[] = {[PROGRESS_ARROW] = "▒",
     192             :                                                      [PROGRESS_TICK_LOW] = "░",
     193             :                                                      [PROGRESS_TICK_HIGH] = "▒",
     194             :                                                      [PROGRESS_TICK_SPACE] = "░",
     195             :                                                      [PROGRESS_EMPTY] = "░",
     196             :                                                      [PROGRESS_FULL] = "▓",
     197             :                                                      [PROGRESS_LEFT_BRACKET] = "▕",
     198             :                                                      [PROGRESS_RIGHT_BRACKET] = "▏"};
     199             : 
     200             : static const char *PROGRESS_PLAIN_ASCII_TABLE[] = {[PROGRESS_ARROW] = "_",
     201             :                                                    [PROGRESS_TICK_LOW] = " ",
     202             :                                                    [PROGRESS_TICK_HIGH] = "_",
     203             :                                                    [PROGRESS_TICK_SPACE] = " ",
     204             :                                                    [PROGRESS_EMPTY] = "\\",
     205             :                                                    [PROGRESS_FULL] = "/",
     206             :                                                    [PROGRESS_LEFT_BRACKET] = "|",
     207             :                                                    [PROGRESS_RIGHT_BRACKET] = "|"};
     208             : 
     209        1952 : static const char *rm_fmt_progressbar_get_glyph(RmFmtHandlerProgress *self,
     210             :                                                 RmProgressBarGlyph type) {
     211        1952 :     if(self->plain && self->use_unicode_glyphs) {
     212        1888 :         return PROGRESS_PLAIN_UNICODE_TABLE[type];
     213          64 :     } else if(self->plain) {
     214          32 :         return PROGRESS_PLAIN_ASCII_TABLE[type];
     215          32 :     } else if(self->use_unicode_glyphs) {
     216          32 :         return PROGRESS_FANCY_UNICODE_TABLE[type];
     217             :     } else {
     218           0 :         return PROGRESS_FANCY_ASCII_TABLE[type];
     219             :     }
     220             : }
     221             : 
     222        1952 : static void rm_fmt_progressbar_print_glyph(FILE *out, RmSession *session,
     223             :                                            RmFmtHandlerProgress *self,
     224             :                                            RmProgressBarGlyph type, const char *color) {
     225        1952 :     fprintf(out, "%s%s%s", MAYBE_COLOR(out, session, color),
     226        1952 :             rm_fmt_progressbar_get_glyph(self, type), MAYBE_COLOR(out, session, RESET));
     227        1952 : }
     228             : 
     229         976 : static void rm_fmt_progress_print_bar(RmSession *session, RmFmtHandlerProgress *self,
     230             :                                       int width, FILE *out) {
     231         976 :     int cells = width * self->percent;
     232             : 
     233             :     /* true when we do not know when 100% is reached.
     234             :      * Show a moving something in this case.
     235             :      * */
     236         976 :     bool is_unknown = self->percent > 1.1;
     237             : 
     238         976 :     rm_fmt_progressbar_print_glyph(out, session, self, PROGRESS_LEFT_BRACKET, RED);
     239             : 
     240         976 :     for(int i = 0; i < width - 2; ++i) {
     241           0 :         if(i < cells) {
     242           0 :             if(is_unknown) {
     243           0 :                 if((int)self->last_unknown_pos % 4 == i % 4) {
     244           0 :                     rm_fmt_progressbar_print_glyph(out, session, self, PROGRESS_TICK_LOW,
     245             :                                                    BLUE);
     246           0 :                 } else if((int)self->last_unknown_pos % 2 == i % 2) {
     247           0 :                     rm_fmt_progressbar_print_glyph(out, session, self, PROGRESS_TICK_HIGH,
     248             :                                                    YELLOW);
     249             :                 } else {
     250           0 :                     rm_fmt_progressbar_print_glyph(out, session, self,
     251             :                                                    PROGRESS_TICK_SPACE, GREEN);
     252             :                 }
     253             :             } else {
     254           0 :                 const char *color = (self->percent > 1.01) ? BLUE : GREEN;
     255           0 :                 RmProgressBarGlyph glyph =
     256           0 :                     (self->percent > 1.01) ? PROGRESS_EMPTY : PROGRESS_FULL;
     257           0 :                 rm_fmt_progressbar_print_glyph(out, session, self, glyph, color);
     258             :             }
     259           0 :         } else if(i == cells) {
     260           0 :             rm_fmt_progressbar_print_glyph(out, session, self, PROGRESS_ARROW, YELLOW);
     261             :         } else {
     262           0 :             rm_fmt_progressbar_print_glyph(out, session, self, PROGRESS_EMPTY, BLUE);
     263             :         }
     264             :     }
     265             : 
     266         976 :     rm_fmt_progressbar_print_glyph(out, session, self, PROGRESS_RIGHT_BRACKET, RED);
     267             : 
     268         976 :     self->last_unknown_pos = fmod(self->last_unknown_pos + 0.005, width - 2);
     269         976 : }
     270             : 
     271        1830 : static void rm_fmt_prog(RmSession *session,
     272             :                         RmFmtHandler *parent,
     273             :                         FILE *out,
     274             :                         RmFmtProgressState state) {
     275        1830 :     RmFmtHandlerProgress *self = (RmFmtHandlerProgress *)parent;
     276        1830 :     if(state == RM_PROGRESS_STATE_SUMMARY) {
     277         122 :         return;
     278             :     }
     279             : 
     280        1708 :     if(session->replay_files.length > 0) {
     281             :         /* Makes not much sense to print a progressbar with --replay */
     282           0 :         return;
     283             :     }
     284             : 
     285        1708 :     if(state == RM_PROGRESS_STATE_INIT) {
     286             :         /* Do initializiation here */
     287         122 :         const char *update_interval_str =
     288         122 :             rm_fmt_get_config_value(session->formats, "progressbar", "update_interval");
     289             : 
     290         122 :         self->plain = true;
     291         122 :         if(rm_fmt_get_config_value(session->formats, "progressbar", "fancy") != NULL) {
     292           2 :             self->plain = false;
     293             :         }
     294             : 
     295         122 :         self->use_unicode_glyphs = true;
     296         122 :         if(rm_fmt_get_config_value(session->formats, "progressbar", "ascii") != NULL) {
     297           2 :             self->use_unicode_glyphs = false;
     298             :         }
     299             : 
     300         122 :         if(update_interval_str) {
     301           0 :             self->update_interval = g_ascii_strtoull(update_interval_str, NULL, 10);
     302             :         }
     303             : 
     304         122 :         if(self->update_interval == 0) {
     305         122 :             self->update_interval = 50;
     306             :         }
     307             : 
     308         122 :         self->last_unknown_pos = 0;
     309         122 :         self->total_lint_bytes = 1;
     310             : 
     311         122 :         fprintf(out, "\e[?25l"); /* Hide the cursor */
     312         122 :         fflush(out);
     313         122 :         return;
     314             :     }
     315             : 
     316        1586 :     if(state == RM_PROGRESS_STATE_PRE_SHUTDOWN || rm_session_was_aborted(session)) {
     317         122 :         fprintf(out, "\e[?25h"); /* show the cursor */
     318         122 :         fflush(out);
     319             : 
     320         122 :         if(rm_session_was_aborted(session)) {
     321           0 :             return;
     322             :         }
     323             :     }
     324             : 
     325        1586 :     if(state == RM_PROGRESS_STATE_SHREDDER) {
     326         244 :         self->total_lint_bytes =
     327         244 :             MAX(self->total_lint_bytes, session->shred_bytes_remaining);
     328             :     }
     329             : 
     330        1586 :     if(self->last_state != state && self->last_state != RM_PROGRESS_STATE_INIT) {
     331         366 :         self->percent = 1.05;
     332         366 :         if(state != RM_PROGRESS_STATE_PRE_SHUTDOWN) {
     333         244 :             rm_fmt_progress_print_bar(session, self, self->terminal.ws_col * 0.3, out);
     334         244 :             fprintf(out, "\n");
     335             :         }
     336         366 :         self->update_counter = 0;
     337             :     }
     338             : 
     339        1586 :     if(state == RM_PROGRESS_STATE_TRAVERSE && session->traverse_finished) {
     340         122 :         self->update_counter = 0;
     341             :     }
     342             : 
     343        1586 :     if(state == RM_PROGRESS_STATE_SHREDDER && session->shredder_finished) {
     344         122 :         self->update_counter = 0;
     345             :     }
     346             : 
     347        1586 :     if(ioctl(fileno(out), TIOCGWINSZ, &self->terminal) != 0) {
     348             :         // rm_log_warning_line(_("Cannot figure out terminal width."));
     349             :     }
     350             : 
     351        1586 :     self->last_state = state;
     352             : 
     353        1586 :     if(self->update_counter++ % self->update_interval == 0) {
     354         732 :         int text_width = MAX(self->terminal.ws_col * 0.7 - 1, 0);
     355         732 :         rm_fmt_progress_format_text(session, self, text_width, out);
     356         732 :         if(state == RM_PROGRESS_STATE_PRE_SHUTDOWN) {
     357             :             /* do not overwrite last messages */
     358         122 :             self->percent = 1.05;
     359         122 :             text_width = 0;
     360             :         }
     361             : 
     362         732 :         rm_fmt_progress_print_bar(session, self, self->terminal.ws_col * 0.3, out);
     363         732 :         rm_fmt_progress_print_text(self, text_width, out);
     364         732 :         fprintf(out, "%s\r", MAYBE_RESET(out, session));
     365             :     }
     366             : 
     367        1586 :     if(state == RM_PROGRESS_STATE_PRE_SHUTDOWN) {
     368         122 :         fprintf(out, "\n\n");
     369             :     }
     370             : }
     371             : 
     372             : static RmFmtHandlerProgress PROGRESS_HANDLER_IMPL = {
     373             :     /* Initialize parent */
     374             :     .parent =
     375             :         {
     376             :          .size = sizeof(PROGRESS_HANDLER_IMPL),
     377             :          .name = "progressbar",
     378             :          .head = NULL,
     379             :          .elem = NULL,
     380             :          .prog = rm_fmt_prog,
     381             :          .foot = NULL,
     382             :          .valid_keys = {"update_interval", "ascii", "fancy", NULL},
     383             :         },
     384             : 
     385             :     /* Initialize own stuff */
     386             :     .percent = 0.0f,
     387             :     .text_len = 0,
     388             :     .text_buf = {0},
     389             :     .update_counter = 0,
     390             :     .use_unicode_glyphs = true,
     391             :     .plain = true,
     392             :     .last_state = RM_PROGRESS_STATE_INIT};
     393             : 
     394             : RmFmtHandler *PROGRESS_HANDLER = (RmFmtHandler *)&PROGRESS_HANDLER_IMPL;

Generated by: LCOV version 1.11