⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 progress.c

📁 wget (command line browser) source code
💻 C
📖 第 1 页 / 共 2 页
字号:
    int pos;    long times[DLSPEED_HISTORY_SIZE];    long bytes[DLSPEED_HISTORY_SIZE];    /* The sum of times and bytes respectively, maintained for       efficiency. */    long total_time;    long total_bytes;  } hist;  double recent_start;		/* timestamp of beginning of current				   position. */  long recent_bytes;		/* bytes downloaded so far. */  /* create_image() uses these to make sure that ETA information     doesn't flash. */  double last_eta_time;		/* time of the last update to download				   speed and ETA, measured since the				   beginning of download. */  long last_eta_value;};static void create_image PARAMS ((struct bar_progress *, double));static void display_image PARAMS ((char *));static void *bar_create (long initial, long total){  struct bar_progress *bp = xmalloc (sizeof (struct bar_progress));  memset (bp, 0, sizeof (*bp));  /* In theory, our callers should take care of this pathological     case, but it can sometimes happen. */  if (initial > total)    total = initial;  bp->initial_length = initial;  bp->total_length   = total;  /* - 1 because we don't want to use the last screen column. */  bp->width = screen_width - 1;  /* + 1 for the terminating zero. */  bp->buffer = xmalloc (bp->width + 1);  logputs (LOG_VERBOSE, "\n");  create_image (bp, 0);  display_image (bp->buffer);  return bp;}static void update_speed_ring PARAMS ((struct bar_progress *, long, double));static voidbar_update (void *progress, long howmuch, double dltime){  struct bar_progress *bp = progress;  int force_screen_update = 0;  bp->count += howmuch;  if (bp->total_length > 0      && bp->count + bp->initial_length > bp->total_length)    /* We could be downloading more than total_length, e.g. when the       server sends an incorrect Content-Length header.  In that case,       adjust bp->total_length to the new reality, so that the code in       create_image() that depends on total size being smaller or       equal to the expected size doesn't abort.  */    bp->total_length = bp->initial_length + bp->count;  update_speed_ring (bp, howmuch, dltime);  if (screen_width - 1 != bp->width)    {      bp->width = screen_width - 1;      bp->buffer = xrealloc (bp->buffer, bp->width + 1);      force_screen_update = 1;    }  if (dltime - bp->last_screen_update < 200 && !force_screen_update)    /* Don't update more often than five times per second. */    return;  create_image (bp, dltime);  display_image (bp->buffer);  bp->last_screen_update = dltime;}static voidbar_finish (void *progress, double dltime){  struct bar_progress *bp = progress;  if (bp->total_length > 0      && bp->count + bp->initial_length > bp->total_length)    /* See bar_update() for explanation. */    bp->total_length = bp->initial_length + bp->count;  create_image (bp, dltime);  display_image (bp->buffer);  logputs (LOG_VERBOSE, "\n\n");  xfree (bp->buffer);  xfree (bp);}/* This code attempts to maintain the notion of a "current" download   speed, over the course of no less than 3s.  (Shorter intervals   produce very erratic results.)   To do so, it samples the speed in 150ms intervals and stores the   recorded samples in a FIFO history ring.  The ring stores no more   than 20 intervals, hence the history covers the period of at least   three seconds and at most 20 reads into the past.  This method   should produce reasonable results for downloads ranging from very   slow to very fast.   The idea is that for fast downloads, we get the speed over exactly   the last three seconds.  For slow downloads (where a network read   takes more than 150ms to complete), we get the speed over a larger   time period, as large as it takes to complete thirty reads.  This   is good because slow downloads tend to fluctuate more and a   3-second average would be too erratic.  */static voidupdate_speed_ring (struct bar_progress *bp, long howmuch, double dltime){  struct bar_progress_hist *hist = &bp->hist;  double recent_age = dltime - bp->recent_start;  /* Update the download count. */  bp->recent_bytes += howmuch;  /* For very small time intervals, we return after having updated the     "recent" download count.  When its age reaches or exceeds minimum     sample time, it will be recorded in the history ring.  */  if (recent_age < DLSPEED_SAMPLE_MIN)    return;  /* Store "recent" bytes and download time to history ring at the     position POS.  */  /* To correctly maintain the totals, first invalidate existing data     (least recent in time) at this position. */  hist->total_time  -= hist->times[hist->pos];  hist->total_bytes -= hist->bytes[hist->pos];  /* Now store the new data and update the totals. */  hist->times[hist->pos] = recent_age;  hist->bytes[hist->pos] = bp->recent_bytes;  hist->total_time  += recent_age;  hist->total_bytes += bp->recent_bytes;  /* Start a new "recent" period. */  bp->recent_start = dltime;  bp->recent_bytes = 0;  /* Advance the current ring position. */  if (++hist->pos == DLSPEED_HISTORY_SIZE)    hist->pos = 0;#if 0  /* Sledgehammer check to verify that the totals are accurate. */  {    int i;    double sumt = 0, sumb = 0;    for (i = 0; i < DLSPEED_HISTORY_SIZE; i++)      {	sumt += hist->times[i];	sumb += hist->bytes[i];      }    assert (sumt == hist->total_time);    assert (sumb == hist->total_bytes);  }#endif}#define APPEND_LITERAL(s) do {			\  memcpy (p, s, sizeof (s) - 1);		\  p += sizeof (s) - 1;				\} while (0)#ifndef MAX# define MAX(a, b) ((a) >= (b) ? (a) : (b))#endifstatic voidcreate_image (struct bar_progress *bp, double dl_total_time){  char *p = bp->buffer;  long size = bp->initial_length + bp->count;  char *size_legible = legible (size);  int size_legible_len = strlen (size_legible);  struct bar_progress_hist *hist = &bp->hist;  /* The progress bar should look like this:     xx% [=======>             ] nn,nnn 12.34K/s ETA 00:00     Calculate the geometry.  The idea is to assign as much room as     possible to the progress bar.  The other idea is to never let     things "jitter", i.e. pad elements that vary in size so that     their variance does not affect the placement of other elements.     It would be especially bad for the progress bar to be resized     randomly.     "xx% " or "100%"  - percentage               - 4 chars     "[]"              - progress bar decorations - 2 chars     " nnn,nnn,nnn"    - downloaded bytes         - 12 chars or very rarely more     " 1012.56K/s"     - dl rate                  - 11 chars     " ETA xx:xx:xx"   - ETA                      - 13 chars     "=====>..."       - progress bar             - the rest  */  int dlbytes_size = 1 + MAX (size_legible_len, 11);  int progress_size = bp->width - (4 + 2 + dlbytes_size + 11 + 13);  if (progress_size < 5)    progress_size = 0;  /* "xx% " */  if (bp->total_length > 0)    {      int percentage = (int)(100.0 * size / bp->total_length);      assert (percentage <= 100);      if (percentage < 100)	sprintf (p, "%2d%% ", percentage);      else	strcpy (p, "100%");      p += 4;    }  else    APPEND_LITERAL ("    ");  /* The progress bar: "[====>      ]" or "[++==>      ]". */  if (progress_size && bp->total_length > 0)    {      /* Size of the initial portion. */      int insz = (double)bp->initial_length / bp->total_length * progress_size;      /* Size of the downloaded portion. */      int dlsz = (double)size / bp->total_length * progress_size;      char *begin;      int i;      assert (dlsz <= progress_size);      assert (insz <= dlsz);      *p++ = '[';      begin = p;      /* Print the initial portion of the download with '+' chars, the	 rest with '=' and one '>'.  */      for (i = 0; i < insz; i++)	*p++ = '+';      dlsz -= insz;      if (dlsz > 0)	{	  for (i = 0; i < dlsz - 1; i++)	    *p++ = '=';	  *p++ = '>';	}      while (p - begin < progress_size)	*p++ = ' ';      *p++ = ']';    }  else if (progress_size)    {      /* If we can't draw a real progress bar, then at least show	 *something* to the user.  */      int ind = bp->tick % (progress_size * 2 - 6);      int i, pos;      /* Make the star move in two directions. */      if (ind < progress_size - 2)	pos = ind + 1;      else	pos = progress_size - (ind - progress_size + 5);      *p++ = '[';      for (i = 0; i < progress_size; i++)	{	  if      (i == pos - 1) *p++ = '<';	  else if (i == pos    ) *p++ = '=';	  else if (i == pos + 1) *p++ = '>';	  else	    *p++ = ' ';	}      *p++ = ']';      ++bp->tick;    }  /* " 234,567,890" */  sprintf (p, " %-11s", legible (size));  p += strlen (p);  /* " 1012.45K/s" */  if (hist->total_time && hist->total_bytes)    {      static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };      int units = 0;      /* Calculate the download speed using the history ring and	 recent data that hasn't made it to the ring yet.  */      long dlquant = hist->total_bytes + bp->recent_bytes;      double dltime = hist->total_time + (dl_total_time - bp->recent_start);      double dlspeed = calc_rate (dlquant, dltime, &units);      sprintf (p, " %7.2f%s", dlspeed, short_units[units]);      p += strlen (p);    }  else    APPEND_LITERAL ("   --.--K/s");  /* " ETA xx:xx:xx"; wait for three seconds before displaying the ETA.     That's because the ETA value needs a while to become     reliable.  */  if (bp->total_length > 0 && dl_total_time > 3000)    {      long eta;      int eta_hrs, eta_min, eta_sec;      /* Don't change the value of ETA more than approximately once	 per second; doing so would cause flashing without providing	 any value to the user. */      if (bp->total_length != size	  && bp->last_eta_value != 0	  && dl_total_time - bp->last_eta_time < 900)	eta = bp->last_eta_value;      else	{	  /* Calculate ETA using the average download speed to predict	     the future speed.  If you want to use a speed averaged	     over a more recent period, replace dl_total_time with	     hist->total_time and bp->count with hist->total_bytes.	     I found that doing that results in a very jerky and	     ultimately unreliable ETA.  */	  double time_sofar = (double)dl_total_time / 1000;	  long bytes_remaining = bp->total_length - size;	  eta = (long) (time_sofar * bytes_remaining / bp->count);	  bp->last_eta_value = eta;	  bp->last_eta_time = dl_total_time;	}      eta_hrs = eta / 3600, eta %= 3600;      eta_min = eta / 60,   eta %= 60;      eta_sec = eta;      if (eta_hrs > 99)	goto no_eta;      if (eta_hrs == 0)	{	  /* Hours not printed: pad with three spaces. */	  APPEND_LITERAL ("   ");	  sprintf (p, " ETA %02d:%02d", eta_min, eta_sec);	}      else	{	  if (eta_hrs < 10)	    /* Hours printed with one digit: pad with one space. */	    *p++ = ' ';	  sprintf (p, " ETA %d:%02d:%02d", eta_hrs, eta_min, eta_sec);	}      p += strlen (p);    }  else if (bp->total_length > 0)    {    no_eta:      APPEND_LITERAL ("             ");    }  assert (p - bp->buffer <= bp->width);  while (p < bp->buffer + bp->width)    *p++ = ' ';  *p = '\0';}/* Print the contents of the buffer as a one-line ASCII "image" so   that it can be overwritten next time.  */static voiddisplay_image (char *buf){  int old = log_set_save_context (0);  logputs (LOG_VERBOSE, "\r");  logputs (LOG_VERBOSE, buf);  log_set_save_context (old);}static voidbar_set_params (const char *params){  int sw;  char *term = getenv ("TERM");  if (params      && 0 == strcmp (params, "force"))    current_impl_locked = 1;  if ((opt.lfilename#ifdef HAVE_ISATTY       /* The progress bar doesn't make sense if the output is not a	  TTY -- when logging to file, it is better to review the	  dots.  */       || !isatty (fileno (stderr))#else       1#endif       /* Normally we don't depend on terminal type because the	  progress bar only uses ^M to move the cursor to the	  beginning of line, which works even on dumb terminals.  But	  Jamie Zawinski reports that ^M and ^H tricks don't work in	  Emacs shell buffers, and only make a mess.  */       || (term && 0 == strcmp (term, "emacs"))       )      && !current_impl_locked)    {      /* We're not printing to a TTY, so revert to the fallback	 display.  #### We're recursively calling	 set_progress_implementation here, which is slightly kludgy.	 It would be nicer if we provided that function a return value	 indicating a failure of some sort.  */      set_progress_implementation (FALLBACK_PROGRESS_IMPLEMENTATION);      return;    }  sw = determine_screen_width ();  if (sw && sw >= MINIMUM_SCREEN_WIDTH)    screen_width = sw;}#ifdef SIGWINCHRETSIGTYPEprogress_handle_sigwinch (int sig){  int sw = determine_screen_width ();  if (sw && sw >= MINIMUM_SCREEN_WIDTH)    screen_width = sw;  signal (SIGWINCH, progress_handle_sigwinch);}#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -