HW12/display_bar.c

149 lines
3.6 KiB
C

#include<stdio.h>
#include<stdbool.h>
#include<stdarg.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<termio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#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
MAX_TERMINAL_ROW = DEFAULT_MAX_TERMINAL_ROW;
#endif
};
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static raw_progress_bar_t scrolled = 1;
static int tty_fd = STDIN_FILENO;
#ifdef DEBUG
static int deadlock = 0;
#endif
void ready_progress_bar(){
fflush(stdout);
if(!isatty(STDIN_FILENO)){
char * buf = ctermid(NULL);
tty_fd = open(buf,O_RDONLY);
}
}
void lock_scrolled(){
#ifdef DEBUG
deadlock++;
if (deadlock > 1)
{
fprintf(stderr,"deadlock occur\n");
exit(EXIT_FAILURE);
}
#endif
pthread_mutex_lock(&mutex);
}
void unlock_scrolled(){
#ifdef DEBUG
deadlock--;
#endif
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();
pos = scrolled - bar;
if (pos > MAX_TERMINAL_ROW) {
unlock_scrolled();
return;
}//optimization.
//if ioctl failed? what should i do...
ioctl(STDIN_FILENO,TIOCGWINSZ,(char *)&wnd_size);
if (wnd_size.ws_row < pos){
unlock_scrolled();
return;
}
fprintf(stdout,"\x1b[%dA[%s]: %.2f%% %s bytes: %ld/%ld \x1b[%dB\r",pos,buf,cur_progress,filename,total,offset,pos);
fflush(stdout);
unlock_scrolled();
}
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();
}
}
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);
diff = dif.tv_nsec / 1000000 + dif.tv_sec*1000;
if (diff < bar->update_rate){
return;
}
}
bar->last_update = ts;
RawDisplayProgressBar(bar->bar,offset,total,filename);
}
void quit_progress_bar(){
if(tty_fd != STDIN_FILENO){
close(tty_fd);
}
}