#include #include #include #include #include #include #include #include #include #include #include #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 quit_progress_bar(){ if(tty_fd != STDIN_FILENO){ close(tty_fd); } } 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(tty_fd,TIOCGWINSZ,(char *)&wnd_size); if (wnd_size.ws_row < pos){//render nothing if render position is out of window size. 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(); } } int myd_vfprintf(int line,FILE * f,const char * msg, va_list arg){ if(isatty_file(f)){ lock_scrolled(); add_scrolled_unlocked(line); vfprintf(f,msg,arg); unlock_scrolled(); } else{ vfprintf(f,msg,arg); } } 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); }