]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
progress: narrower time display, multiple fixes
authorDaniel Stenberg <daniel@haxx.se>
Sat, 3 Jan 2026 11:16:06 +0000 (12:16 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Sun, 4 Jan 2026 13:42:08 +0000 (14:42 +0100)
- Each time field is now 7 characters wide, so that the total width
  never exceeds 79 columns so that it works correctly also in Windows
  terminals. The title lines are adjusted accordingly.

  This is accomplished by using h:mm:ss style up to 10 hours, and for
  longer periods switch to "nnX nnY" style output. For hours, days,
  months and years.

  For less than one hour, the hour field is now dropped.

  When no time info is provided, the field is now space-only. No more
  `-:--:--`.

  Also fixed the output for really long times which previously was
  completely broken. The largest time now shows as ">99999y". (Becase
  I can't figure out a better way).

- For sizes, the widths are now properly fixed to 6 characters. When
  displaying a unit with less than 3 digits, it shows two decimal
  precision like "16777215 => 15.99M" and one decmal otherwise: "262143
  => 255.9k"

  Also fixes the decimal math. 131071 is 127.9k, which it previously did
  not show.

- The time and size field outputs are now properly verified in test
  1636.

Fixes #20122
Closes #20173

fixup use only space when no time exists

Drop the hour from the display when zero

lib/progress.c
tests/data/Makefile.am
tests/data/test1636 [new file with mode: 0644]
tests/unit/Makefile.inc
tests/unit/unit1636.c [new file with mode: 0644]

index 89c245f2fb7524d9b01d41c9885cd499a93ad3d5..9cbd2674f9676a58f37dbd279875137e6724df25 100644 (file)
 #define MIN_RATE_LIMIT_PERIOD 3000
 
 #ifndef CURL_DISABLE_PROGRESS_METER
-/* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero
-   byte) */
-static void time2str(char *r, size_t rsize, curl_off_t seconds)
+/* Provide a string that is 7 letters long (plus the zero byte).
+
+   Unit test 1636.
+*/
+UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds);
+UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds)
 {
   curl_off_t h;
   if(seconds <= 0) {
-    curlx_strcopy(r, rsize, "--:--:--", 8);
+    curlx_strcopy(r, rsize, "       ", 7);
     return;
   }
   h = seconds / 3600;
   if(h <= 99) {
     curl_off_t m = (seconds - (h * 3600)) / 60;
-    curl_off_t s = (seconds - (h * 3600)) - (m * 60);
-    curl_msnprintf(r, rsize, "%2" FMT_OFF_T ":%02" FMT_OFF_T ":%02" FMT_OFF_T,
-                   h, m, s);
+    if(h <= 9) {
+      curl_off_t s = (seconds - (h * 3600)) - (m * 60);
+      if(h)
+        curl_msnprintf(r, rsize, "%" FMT_OFF_T ":%02" FMT_OFF_T ":"
+                       "%02" FMT_OFF_T, h, m, s);
+      else
+        curl_msnprintf(r, rsize, "  %02" FMT_OFF_T ":%02" FMT_OFF_T, m, s);
+    }
+    else
+      curl_msnprintf(r, rsize, "%" FMT_OFF_T "h %02" FMT_OFF_T "m", h, m);
   }
   else {
-    /* this equals to more than 99 hours, switch to a more suitable output
-       format to fit within the limits. */
     curl_off_t d = seconds / 86400;
     h = (seconds - (d * 86400)) / 3600;
-    if(d <= 999)
-      curl_msnprintf(r, rsize, "%3" FMT_OFF_T "d %02" FMT_OFF_T "h", d, h);
-    else
-      curl_msnprintf(r, rsize, "%7" FMT_OFF_T "d", d);
+    if(d <= 99)
+      curl_msnprintf(r, rsize, "%2" FMT_OFF_T "d %02" FMT_OFF_T "h", d, h);
+    else if(d <= 999)
+      curl_msnprintf(r, rsize, "%6" FMT_OFF_T "d", d);
+    else { /* more than 999 days */
+      curl_off_t m = d / 30;
+      if(m <= 999)
+        curl_msnprintf(r, rsize, "%6" FMT_OFF_T "m", m);
+      else { /* more than 999 months */
+        curl_off_t y = d / 365;
+        if(y <= 99999)
+          curl_msnprintf(r, rsize, "%6" FMT_OFF_T "y", y);
+        else
+          curlx_strcopy(r, rsize, ">99999y", 7);
+      }
+    }
   }
 }
 
 /* The point of this function would be to return a string of the input data,
    but never longer than 6 columns (+ one zero byte).
-   Add suffix k, M, G when suitable... */
-static char *max6out(curl_off_t bytes, char *max6, size_t mlen)
+   Add suffix k, M, G when suitable...
+
+   Unit test 1636
+*/
+UNITTEST char *max6out(curl_off_t bytes, char *max6, size_t mlen);
+UNITTEST char *max6out(curl_off_t bytes, char *max6, size_t mlen)
 {
   /* a signed 64-bit value is 8192 petabytes maximum, shown as
      8.0E (exabytes)*/
@@ -75,6 +99,7 @@ static char *max6out(curl_off_t bytes, char *max6, size_t mlen)
     const char unit[] = { 'k', 'M', 'G', 'T', 'P', 'E', 0 };
     int k = 0;
     curl_off_t nbytes;
+    curl_off_t rest;
     do {
       nbytes = bytes / 1024;
       if(nbytes < 1000)
@@ -83,10 +108,17 @@ static char *max6out(curl_off_t bytes, char *max6, size_t mlen)
       k++;
       DEBUGASSERT(unit[k]);
     } while(unit[k]);
-    /* xxx.yU */
-    curl_msnprintf(max6, mlen, "%3" CURL_FORMAT_CURL_OFF_T
-                   ".%" CURL_FORMAT_CURL_OFF_T "%c", nbytes,
-                   (bytes % 1024) / (1024 / 10), unit[k]);
+    rest = bytes % 1024;
+    if(nbytes <= 99)
+      /* xx.yyU */
+      curl_msnprintf(max6, mlen, "%2" CURL_FORMAT_CURL_OFF_T
+                     ".%02" CURL_FORMAT_CURL_OFF_T "%c", nbytes,
+                     rest * 100 / 1024, unit[k]);
+    else
+      /* xxx.yU */
+      curl_msnprintf(max6, mlen, "%3" CURL_FORMAT_CURL_OFF_T
+                     ".%" CURL_FORMAT_CURL_OFF_T "%c", nbytes,
+                     rest * 10 / 1024, unit[k]);
   }
   return max6;
 }
@@ -501,9 +533,9 @@ static void progress_meter(struct Curl_easy *data)
   curl_off_t total_cur_size;
   curl_off_t total_expected_size;
   curl_off_t dl_size;
-  char time_left[10];
-  char time_total[10];
-  char time_spent[10];
+  char time_left[8];
+  char time_total[8];
+  char time_spent[8];
   curl_off_t cur_secs = (curl_off_t)p->timespent / 1000000; /* seconds */
 
   if(!p->headers_out) {
@@ -514,9 +546,9 @@ static void progress_meter(struct Curl_easy *data)
     }
     curl_mfprintf(data->set.err,
                   "  %% Total    %% Received %% Xferd  Average Speed  "
-                  "Time     Time     Time   Current\n"
+                  "Time    Time    Time   Current\n"
                   "                                 Dload  Upload  "
-                  "Total    Spent    Left   Speed\n");
+                  "Total   Spent   Left   Speed\n");
     p->headers_out = TRUE; /* headers are shown */
   }
 
@@ -567,9 +599,9 @@ static void progress_meter(struct Curl_easy *data)
                         sizeof(max6[3])),              /* avrg dl speed */
                 max6out(p->ul.speed, max6[4],
                         sizeof(max6[4])),              /* avrg ul speed */
-                time_total,    /* 8 letters */         /* total time */
-                time_spent,    /* 8 letters */         /* time spent */
-                time_left,     /* 8 letters */         /* time left */
+                time_total,    /* 7 letters */         /* total time */
+                time_spent,    /* 7 letters */         /* time spent */
+                time_left,     /* 7 letters */         /* time left */
                 max6out(p->current_speed, max6[5],
                         sizeof(max6[5]))               /* current speed */
     );
index 386bd9455f75aa8520431253cd6a118d42fe7114..6475055e1eb74c68fa792bbaf32685aa54611ddf 100644 (file)
@@ -216,7 +216,7 @@ test1606 test1607 test1608 test1609 test1610 test1611 test1612 test1613 \
 test1614 test1615 test1616 test1617 \
 test1620 test1621 test1622 \
 \
-test1630 test1631 test1632 test1633 test1634 test1635 \
+test1630 test1631 test1632 test1633 test1634 test1635 test1636 \
 \
 test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \
 test1658 \
diff --git a/tests/data/test1636 b/tests/data/test1636
new file mode 100644 (file)
index 0000000..eeec5a6
--- /dev/null
@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<testcase>
+<info>
+<keywords>
+unittest
+time2str
+max6out
+</keywords>
+</info>
+
+<client>
+<features>
+unittest
+</features>
+
+<name>
+time2str and max6out
+</name>
+<command>
+%LOGDIR/%TESTNUMBER
+</command>
+</client>
+<verify>
+<stdout mode="text">
+time2str
+                   0 - %SP%SP%SP%SP%SP%SP%SP
+                   1 -   00:01
+                   3 -   00:03
+                   7 -   00:07
+                  15 -   00:15
+                  31 -   00:31
+                  63 -   01:03
+                 127 -   02:07
+                 255 -   04:15
+                 511 -   08:31
+                1023 -   17:03
+                2047 -   34:07
+                4095 - 1:08:15
+                8191 - 2:16:31
+               16383 - 4:33:03
+               32767 - 9:06:07
+               65535 - 18h 12m
+              131071 - 36h 24m
+              262143 - 72h 49m
+              524287 -  6d 01h
+             1048575 - 12d 03h
+             2097151 - 24d 06h
+             4194303 - 48d 13h
+             8388607 - 97d 02h
+            16777215 -    194d
+            33554431 -    388d
+            67108863 -    776d
+           134217727 -     51m
+           268435455 -    103m
+           536870911 -    207m
+          1073741823 -    414m
+          2147483647 -    828m
+          4294967295 -    136y
+          8589934591 -    272y
+         17179869183 -    544y
+         34359738367 -   1089y
+         68719476735 -   2179y
+        137438953471 -   4358y
+        274877906943 -   8716y
+        549755813887 -  17432y
+       1099511627775 -  34865y
+       2199023255551 -  69730y
+       4398046511103 - >99999y
+       8796093022207 - >99999y
+      17592186044415 - >99999y
+      35184372088831 - >99999y
+      70368744177663 - >99999y
+     140737488355327 - >99999y
+     281474976710655 - >99999y
+     562949953421311 - >99999y
+    1125899906842623 - >99999y
+    2251799813685247 - >99999y
+    4503599627370495 - >99999y
+    9007199254740991 - >99999y
+   18014398509481983 - >99999y
+   36028797018963967 - >99999y
+   72057594037927935 - >99999y
+  144115188075855871 - >99999y
+  288230376151711743 - >99999y
+  576460752303423487 - >99999y
+ 1152921504606846975 - >99999y
+ 2305843009213693951 - >99999y
+ 4611686018427387903 - >99999y
+max6out
+                   0 -      0
+                   1 -      1
+                   3 -      3
+                   7 -      7
+                  15 -     15
+                  31 -     31
+                  63 -     63
+                 127 -    127
+                 255 -    255
+                 511 -    511
+                1023 -   1023
+                2047 -   2047
+                4095 -   4095
+                8191 -   8191
+               16383 -  16383
+               32767 -  32767
+               65535 -  65535
+              131071 - 127.9k
+              262143 - 255.9k
+              524287 - 511.9k
+             1048575 -  0.99M
+             2097151 -  1.99M
+             4194303 -  3.99M
+             8388607 -  7.99M
+            16777215 - 15.99M
+            33554431 - 31.99M
+            67108863 - 63.99M
+           134217727 - 127.9M
+           268435455 - 255.9M
+           536870911 - 511.9M
+          1073741823 -  0.99G
+          2147483647 -  1.99G
+          4294967295 -  3.99G
+          8589934591 -  7.99G
+         17179869183 - 15.99G
+         34359738367 - 31.99G
+         68719476735 - 63.99G
+        137438953471 - 127.9G
+        274877906943 - 255.9G
+        549755813887 - 511.9G
+       1099511627775 -  0.99T
+       2199023255551 -  1.99T
+       4398046511103 -  3.99T
+       8796093022207 -  7.99T
+      17592186044415 - 15.99T
+      35184372088831 - 31.99T
+      70368744177663 - 63.99T
+     140737488355327 - 127.9T
+     281474976710655 - 255.9T
+     562949953421311 - 511.9T
+    1125899906842623 -  0.99P
+    2251799813685247 -  1.99P
+    4503599627370495 -  3.99P
+    9007199254740991 -  7.99P
+   18014398509481983 - 15.99P
+   36028797018963967 - 31.99P
+   72057594037927935 - 63.99P
+  144115188075855871 - 127.9P
+  288230376151711743 - 255.9P
+  576460752303423487 - 511.9P
+ 1152921504606846975 -  0.99E
+ 2305843009213693951 -  1.99E
+ 4611686018427387903 -  3.99E
+              131072 - 128.0k
+            12645826 - 12.05M
+          1073741824 -  1.00G
+         12938588979 - 12.04G
+    1099445657078333 - 999.9T
+</stdout>
+</verify>
+
+</testcase>
index af5e2ebc75945cfffcf19ab602d3b664ffb8bbcc..149636cbcc8b95cd9979ae9e22c5b097877a386a 100644 (file)
@@ -37,6 +37,7 @@ TESTS_C = \
   unit1600.c unit1601.c unit1602.c unit1603.c            unit1605.c unit1606.c \
   unit1607.c unit1608.c unit1609.c unit1610.c unit1611.c unit1612.c unit1614.c \
   unit1615.c unit1616.c                                  unit1620.c \
+  unit1636.c \
   unit1650.c unit1651.c unit1652.c unit1653.c unit1654.c unit1655.c unit1656.c \
   unit1657.c unit1658.c            unit1660.c unit1661.c unit1663.c unit1664.c \
   unit1979.c unit1980.c \
diff --git a/tests/unit/unit1636.c b/tests/unit/unit1636.c
new file mode 100644 (file)
index 0000000..d62ae48
--- /dev/null
@@ -0,0 +1,74 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "unitcheck.h"
+#include "unitprotos.h"
+
+static CURLcode test_unit1636(const char *arg)
+{
+  UNITTEST_BEGIN_SIMPLE
+  {
+    char buffer[9];
+    curl_off_t secs;
+    int i;
+    static const curl_off_t check[] = {
+      /* bytes to check */
+      131072,
+      12645826,
+      1073741824,
+      12938588979,
+      1099445657078333,
+      0 /* end of list */
+    };
+
+    puts("time2str");
+    for(i = 0, secs = 0; i < 63; i++) {
+      time2str(buffer, sizeof(buffer), secs);
+      curl_mprintf("%20" FMT_OFF_T " - %s\n", secs, buffer);
+      if(strlen(buffer) != 7) {
+        curl_mprintf("^^ was too long!\n");
+      }
+      secs *= 2;
+      secs++;
+    }
+    puts("max6out");
+    for(i = 0, secs = 0; i < 63; i++) {
+      max6out(secs, buffer, sizeof(buffer));
+      curl_mprintf("%20" FMT_OFF_T " - %s\n", secs, buffer);
+      if(strlen(buffer) != 6) {
+        curl_mprintf("^^ was too long!\n");
+      }
+      secs *= 2;
+      secs++;
+    }
+    for(i = 0; check[i]; i++) {
+      secs = check[i];
+      max6out(secs, buffer, sizeof(buffer));
+      curl_mprintf("%20" FMT_OFF_T " - %s\n", secs, buffer);
+      if(strlen(buffer) != 6) {
+        curl_mprintf("^^ was too long!\n");
+      }
+    }
+  }
+  UNITTEST_END(curl_global_cleanup())
+}