#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; void ready_progress_bar(){ fflush(stdout); } void lock_scrolled(){ pthread_mutex_lock(&mutex); } void unlock_scrolled(){ 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) 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; if (diff < bar->update_rate){ return; } } bar->last_update = ts; RawDisplayProgressBar(bar->bar,offset,total,filename); }