HW12/display_bar.c

167 lines
4.1 KiB
C
Raw Permalink Normal View History

2020-12-08 02:59:11 +09:00
#include<stdio.h>
#include<stdbool.h>
#include<stdarg.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<termio.h>
#include<pthread.h>
2020-12-09 01:49:50 +09:00
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
2020-12-08 02:59:11 +09:00
#include "display_bar.h"
#include "timerhelper.h"
enum{
#ifndef DEFAULT_PROGRESS_BAR_WIDTH
PROGRESS_BAR_WIDTH = 30,
#else
PROGRESS_BAR_WIDTH = DEFAULT_PROGRESS_BAR_WIDTH;
#endif
#ifndef DEFAULT_MAX_TERMINAL_ROW
MAX_TERMINAL_ROW = 1000,
#else
2020-12-08 23:37:10 +09:00
MAX_TERMINAL_ROW = DEFAULT_MAX_TERMINAL_ROW;
2020-12-08 02:59:11 +09:00
#endif
};
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static raw_progress_bar_t scrolled = 1;
2020-12-09 01:49:50 +09:00
static int tty_fd = STDIN_FILENO;
#ifdef DEBUG
static int deadlock = 0;
#endif
2020-12-08 02:59:11 +09:00
void ready_progress_bar(){
fflush(stdout);
2020-12-09 01:49:50 +09:00
if(!isatty(STDIN_FILENO)){
char * buf = ctermid(NULL);
tty_fd = open(buf,O_RDONLY);
}
2020-12-08 02:59:11 +09:00
}
2020-12-14 19:20:16 +09:00
void quit_progress_bar(){
if(tty_fd != STDIN_FILENO){
close(tty_fd);
}
}
2020-12-08 02:59:11 +09:00
void lock_scrolled(){
2020-12-09 01:49:50 +09:00
#ifdef DEBUG
deadlock++;
if (deadlock > 1)
{
fprintf(stderr,"deadlock occur\n");
exit(EXIT_FAILURE);
}
#endif
2020-12-08 02:59:11 +09:00
pthread_mutex_lock(&mutex);
}
void unlock_scrolled(){
2020-12-09 01:49:50 +09:00
#ifdef DEBUG
deadlock--;
#endif
2020-12-08 02:59:11 +09:00
pthread_mutex_unlock(&mutex);
}
void add_scrolled_unlocked(unsigned int i){
scrolled += i;
}
raw_progress_bar_t create_raw_progress_bar(){
int ret;
lock_scrolled();
ret = scrolled;
//make space for progress bar
fprintf(stdout,"\n");
scrolled++;
unlock_scrolled();
return ret;
}
void RawDisplayProgressBar(raw_progress_bar_t bar,size_t offset,size_t total,const char * filename){
char buf[PROGRESS_BAR_WIDTH];
size_t i;
double cur_progress = ((double)offset)/(double)total *100.f;
size_t cur_pos = (cur_progress / 100.0 * PROGRESS_BAR_WIDTH); //must be less than SIZE_MAX. other value is undefined behavior.
int pos;
struct winsize wnd_size;
if (offset == total)
cur_pos = PROGRESS_BAR_WIDTH;
for (i = 0; i < PROGRESS_BAR_WIDTH; i++){
if (i < cur_pos)
buf[i] = '=';
else if(i == cur_pos)
buf[i] = '>';
else buf[i] = '.';
}
lock_scrolled();
2020-12-08 13:55:21 +09:00
pos = scrolled - bar;
if (pos > MAX_TERMINAL_ROW) {
unlock_scrolled();
return;
}//optimization.
2020-12-08 02:59:11 +09:00
//if ioctl failed? what should i do...
2020-12-14 19:20:16 +09:00
ioctl(tty_fd,TIOCGWINSZ,(char *)&wnd_size);
if (wnd_size.ws_row < pos){//render nothing if render position is out of window size.
2020-12-09 01:49:50 +09:00
unlock_scrolled();
return;
}
2020-12-08 13:55:21 +09:00
fprintf(stdout,"\x1b[%dA[%s]: %.2f%% %s bytes: %ld/%ld \x1b[%dB\r",pos,buf,cur_progress,filename,total,offset,pos);
2020-12-08 02:59:11 +09:00
fflush(stdout);
unlock_scrolled();
}
2020-12-14 19:20:16 +09:00
2020-12-08 02:59:11 +09:00
bool isatty_file(FILE * file){
return isatty(fileno(file));
}
void myd_perror(const char * msg){
char buf[1024];
int err = errno;
strerror_r(errno,buf,sizeof(buf));
errno = err;
if (isatty(STDERR_FILENO)){
lock_scrolled();
scrolled++;
fprintf(stderr,"%s: %s\n",msg,buf);
unlock_scrolled();
}
}
2020-12-14 19:20:16 +09:00
int myd_vfprintf(int line,FILE * f,const char * msg, va_list arg){
2020-12-14 20:09:12 +09:00
int ret;
2020-12-14 19:20:16 +09:00
if(isatty_file(f)){
lock_scrolled();
add_scrolled_unlocked(line);
2020-12-14 20:09:12 +09:00
ret = vfprintf(f,msg,arg);
2020-12-14 19:20:16 +09:00
unlock_scrolled();
}
else{
2020-12-14 20:09:12 +09:00
ret = vfprintf(f,msg,arg);
2020-12-14 19:20:16 +09:00
}
2020-12-14 20:09:12 +09:00
return ret;
}
int myd_fprintf(int line,FILE * f,const char * msg, ...){
va_list va;
va_start(va,msg);
return myd_vfprintf(line,f,msg,va);
2020-12-14 19:20:16 +09:00
}
2020-12-08 02:59:11 +09:00
void init_progress_bar(progress_bar_t * bar,int update_rate){
memset(bar,0,sizeof(*bar));
bar->bar = create_raw_progress_bar();
bar->update_rate = update_rate;
}
void DisplayProgressBar(progress_bar_t * bar,size_t offset,size_t total,const char * filename, bool sync){
unsigned diff;
struct timespec ts,dif;
clock_gettime(CLOCK_REALTIME,&ts);
if (!sync){
dif = timespec_sub(ts,bar->last_update);
2020-12-09 00:14:42 +09:00
diff = dif.tv_nsec / 1000000 + dif.tv_sec*1000;
2020-12-08 02:59:11 +09:00
if (diff < bar->update_rate){
return;
}
}
bar->last_update = ts;
RawDisplayProgressBar(bar->bar,offset,total,filename);
2020-12-09 01:49:50 +09:00
}