#ifndef RLIB_TERMINAL_H #define RLIB_TERMINAL_H #include #include #include #include #include "rtest.h" char *rfcaptured = NULL; void rfcapture(FILE *f, char *buff, size_t size) { rfcaptured = buff; setvbuf(f, rfcaptured, _IOFBF, size); } void rfstopcapture(FILE *f) { setvbuf(f, 0, _IOFBF, 0); } bool _r_disable_stdout_toggle = false; FILE *_r_original_stdout = NULL; bool rr_enable_stdout() { if (_r_disable_stdout_toggle) return false; if (!_r_original_stdout) { stdout = fopen("/dev/null", "rb"); return false; } if (_r_original_stdout && _r_original_stdout != stdout) { fclose(stdout); } stdout = _r_original_stdout; return true; } bool rr_disable_stdout() { if (_r_disable_stdout_toggle) { return false; } if (_r_original_stdout == NULL) { _r_original_stdout = stdout; } if (stdout == _r_original_stdout) { stdout = fopen("/dev/null", "rb"); return true; } return false; } bool rr_toggle_stdout() { if (!_r_original_stdout) { rr_disable_stdout(); return true; } else if (stdout != _r_original_stdout) { rr_enable_stdout(); return true; } else { rr_disable_stdout(); return true; } } typedef struct rprogressbar_t { unsigned long current_value; unsigned long min_value; unsigned long max_value; unsigned int length; bool changed; double percentage; unsigned int width; unsigned long draws; FILE *fout; } rprogressbar_t; rprogressbar_t *rprogressbar_new(long min_value, long max_value, unsigned int width, FILE *fout) { rprogressbar_t *pbar = (rprogressbar_t *)malloc(sizeof(rprogressbar_t)); pbar->min_value = min_value; pbar->max_value = max_value; pbar->current_value = min_value; pbar->width = width; pbar->draws = 0; pbar->length = 0; pbar->changed = false; pbar->fout = fout ? fout : stdout; return pbar; } void rprogressbar_free(rprogressbar_t *pbar) { free(pbar); } void rprogressbar_draw(rprogressbar_t *pbar) { if (!pbar->changed) { return; } else { pbar->changed = false; } pbar->draws++; char draws_text[22]; draws_text[0] = 0; sprintf(draws_text, "%ld", pbar->draws); char *draws_textp = draws_text; // bool draws_text_len = strlen(draws_text); char bar_begin_char = ' '; char bar_progress_char = ' '; char bar_empty_char = ' '; char bar_end_char = ' '; char content[4096] = {0}; char bar_content[1024]; char buff[2048] = {0}; bar_content[0] = '\r'; bar_content[1] = bar_begin_char; unsigned int index = 2; for (unsigned long i = 0; i < pbar->length; i++) { if (*draws_textp) { bar_content[index] = *draws_textp; draws_textp++; } else { bar_content[index] = bar_progress_char; } index++; } char infix[] = "\033[0m"; for (unsigned long i = 0; i < strlen(infix); i++) { bar_content[index] = infix[i]; index++; } for (unsigned long i = 0; i < pbar->width - pbar->length; i++) { bar_content[index] = bar_empty_char; index++; } bar_content[index] = bar_end_char; bar_content[index + 1] = '\0'; sprintf(buff, "\033[43m%s\033[0m \033[33m%.2f%%\033[0m ", bar_content, pbar->percentage * 100); strcat(content, buff); if (pbar->width == pbar->length) { strcat(content, "\r"); for (unsigned long i = 0; i < pbar->width + 10; i++) { strcat(content, " "); } strcat(content, "\r"); } fprintf(pbar->fout, "%s", content); fflush(pbar->fout); } bool rprogressbar_update(rprogressbar_t *pbar, unsigned long value) { if (value == pbar->current_value) { return false; } pbar->current_value = value; pbar->percentage = (double)pbar->current_value / (double)(pbar->max_value - pbar->min_value); unsigned long new_length = (unsigned long)(pbar->percentage * pbar->width); pbar->changed = new_length != pbar->length; if (pbar->changed) { pbar->length = new_length; rprogressbar_draw(pbar); return true; } return false; } size_t rreadline(char *data, size_t len, bool strip_ln) { __attribute__((unused)) char *unused = fgets(data, len, stdin); size_t length = strlen(data); if (length && strip_ln) data[length - 1] = 0; return length; } void rlib_test_progressbar() { rtest_banner("Progress bar"); rprogressbar_t *pbar = rprogressbar_new(0, 1000, 10, stderr); rprogressbar_draw(pbar); // No draws executed, nothing to show rassert(pbar->draws == 0); rprogressbar_update(pbar, 500); rassert(pbar->percentage == 0.5); rprogressbar_update(pbar, 500); rprogressbar_update(pbar, 501); rprogressbar_update(pbar, 502); // Should only have drawn one time since value did change, but percentage // did not rassert(pbar->draws == 1); // Changed is false because update function calls draw rassert(pbar->changed == false); rprogressbar_update(pbar, 777); rassert(pbar->percentage == 0.777); rprogressbar_update(pbar, 1000); rassert(pbar->percentage == 1); } #endif