#ifndef RLIB_TERMINAL_H
#define RLIB_TERMINAL_H
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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