Home | History | Annotate | Download | only in squashfs-tools
      1 /*
      2  * Create a squashfs filesystem.  This is a highly compressed read only
      3  * filesystem.
      4  *
      5  * Copyright (c) 2012, 2013, 2014
      6  * Phillip Lougher <phillip (at) squashfs.org.uk>
      7  *
      8  * This program is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU General Public License
     10  * as published by the Free Software Foundation; either version 2,
     11  * or (at your option) any later version.
     12  *
     13  * This program is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  * GNU General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU General Public License
     19  * along with this program; if not, write to the Free Software
     20  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     21  *
     22  * progressbar.c
     23  */
     24 
     25 #include <pthread.h>
     26 #include <sys/ioctl.h>
     27 #include <unistd.h>
     28 #include <signal.h>
     29 #include <sys/time.h>
     30 #include <stdio.h>
     31 #include <math.h>
     32 #include <stdarg.h>
     33 #include <errno.h>
     34 #include <stdlib.h>
     35 
     36 #include "error.h"
     37 
     38 #define FALSE 0
     39 #define TRUE 1
     40 
     41 /* flag whether progressbar display is enabled or not */
     42 int display_progress_bar = FALSE;
     43 
     44 /* flag whether the progress bar is temporarily disbled */
     45 int temp_disabled = FALSE;
     46 
     47 int rotate = 0;
     48 int cur_uncompressed = 0, estimated_uncompressed = 0;
     49 int columns;
     50 
     51 pthread_t progress_thread;
     52 pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
     53 
     54 
     55 static void sigwinch_handler()
     56 {
     57 	struct winsize winsize;
     58 
     59 	if(ioctl(1, TIOCGWINSZ, &winsize) == -1) {
     60 		if(isatty(STDOUT_FILENO))
     61 			ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 "
     62 				"columns\n");
     63 		columns = 80;
     64 	} else
     65 		columns = winsize.ws_col;
     66 }
     67 
     68 
     69 static void sigalrm_handler()
     70 {
     71 	rotate = (rotate + 1) % 4;
     72 }
     73 
     74 
     75 void inc_progress_bar()
     76 {
     77 	cur_uncompressed ++;
     78 }
     79 
     80 
     81 void dec_progress_bar(int count)
     82 {
     83 	cur_uncompressed -= count;
     84 }
     85 
     86 
     87 void progress_bar_size(int count)
     88 {
     89 	estimated_uncompressed += count;
     90 }
     91 
     92 
     93 static void progress_bar(long long current, long long max, int columns)
     94 {
     95 	char rotate_list[] = { '|', '/', '-', '\\' };
     96 	int max_digits, used, hashes, spaces;
     97 	static int tty = -1;
     98 
     99 	if(max == 0)
    100 		return;
    101 
    102 	max_digits = floor(log10(max)) + 1;
    103 	used = max_digits * 2 + 11;
    104 	hashes = (current * (columns - used)) / max;
    105 	spaces = columns - used - hashes;
    106 
    107 	if((current > max) || (columns - used < 0))
    108 		return;
    109 
    110 	if(tty == -1)
    111 		tty = isatty(STDOUT_FILENO);
    112 	if(!tty) {
    113 		static long long previous = -1;
    114 
    115 		/* Updating much more frequently than this results in huge
    116 		 * log files. */
    117 		if((current % 100) != 0 && current != max)
    118 			return;
    119 		/* Don't update just to rotate the spinner. */
    120 		if(current == previous)
    121 			return;
    122 		previous = current;
    123 	}
    124 
    125 	printf("\r[");
    126 
    127 	while (hashes --)
    128 		putchar('=');
    129 
    130 	putchar(rotate_list[rotate]);
    131 
    132 	while(spaces --)
    133 		putchar(' ');
    134 
    135 	printf("] %*lld/%*lld", max_digits, current, max_digits, max);
    136 	printf(" %3lld%%", current * 100 / max);
    137 	fflush(stdout);
    138 }
    139 
    140 
    141 void enable_progress_bar()
    142 {
    143 	pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
    144 	pthread_mutex_lock(&progress_mutex);
    145 	if(display_progress_bar)
    146 		progress_bar(cur_uncompressed, estimated_uncompressed, columns);
    147 	temp_disabled = FALSE;
    148 	pthread_cleanup_pop(1);
    149 }
    150 
    151 
    152 void disable_progress_bar()
    153 {
    154 	pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
    155 	pthread_mutex_lock(&progress_mutex);
    156 	if(display_progress_bar)
    157 		printf("\n");
    158 	temp_disabled = TRUE;
    159 	pthread_cleanup_pop(1);
    160 }
    161 
    162 
    163 void set_progressbar_state(int state)
    164 {
    165 	pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
    166 	pthread_mutex_lock(&progress_mutex);
    167 	if(display_progress_bar != state) {
    168 		if(display_progress_bar && !temp_disabled) {
    169 			progress_bar(cur_uncompressed, estimated_uncompressed,
    170 				columns);
    171 			printf("\n");
    172 		}
    173 		display_progress_bar = state;
    174 	}
    175 	pthread_cleanup_pop(1);
    176 }
    177 
    178 
    179 void *progress_thrd(void *arg)
    180 {
    181 	struct timespec requested_time, remaining;
    182 	struct itimerval itimerval;
    183 	struct winsize winsize;
    184 
    185 	if(ioctl(1, TIOCGWINSZ, &winsize) == -1) {
    186 		if(isatty(STDOUT_FILENO))
    187 			ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 "
    188 				"columns\n");
    189 		columns = 80;
    190 	} else
    191 		columns = winsize.ws_col;
    192 	signal(SIGWINCH, sigwinch_handler);
    193 	signal(SIGALRM, sigalrm_handler);
    194 
    195 	itimerval.it_value.tv_sec = 0;
    196 	itimerval.it_value.tv_usec = 250000;
    197 	itimerval.it_interval.tv_sec = 0;
    198 	itimerval.it_interval.tv_usec = 250000;
    199 	setitimer(ITIMER_REAL, &itimerval, NULL);
    200 
    201 	requested_time.tv_sec = 0;
    202 	requested_time.tv_nsec = 250000000;
    203 
    204 	while(1) {
    205 		int res = nanosleep(&requested_time, &remaining);
    206 
    207 		if(res == -1 && errno != EINTR)
    208 			BAD_ERROR("nanosleep failed in progress thread\n");
    209 
    210 		pthread_mutex_lock(&progress_mutex);
    211 		if(display_progress_bar && !temp_disabled)
    212 			progress_bar(cur_uncompressed, estimated_uncompressed,
    213 				columns);
    214 		pthread_mutex_unlock(&progress_mutex);
    215 	}
    216 }
    217 
    218 
    219 void init_progress_bar()
    220 {
    221 	pthread_create(&progress_thread, NULL, progress_thrd, NULL);
    222 }
    223 
    224 
    225 void progressbar_error(char *fmt, ...)
    226 {
    227 	va_list ap;
    228 
    229 	pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
    230 	pthread_mutex_lock(&progress_mutex);
    231 
    232 	if(display_progress_bar && !temp_disabled)
    233 		fprintf(stderr, "\n");
    234 
    235 	va_start(ap, fmt);
    236 	vfprintf(stderr, fmt, ap);
    237 	va_end(ap);
    238 
    239 	pthread_cleanup_pop(1);
    240 }
    241 
    242 
    243 void progressbar_info(char *fmt, ...)
    244 {
    245 	va_list ap;
    246 
    247 	pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
    248 	pthread_mutex_lock(&progress_mutex);
    249 
    250 	if(display_progress_bar && !temp_disabled)
    251 		printf("\n");
    252 
    253 	va_start(ap, fmt);
    254 	vprintf(fmt, ap);
    255 	va_end(ap);
    256 
    257 	pthread_cleanup_pop(1);
    258 }
    259 
    260