<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[117186] trunk/base/src/port/port.tcl</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="https://trac.macports.org/changeset/117186">117186</a></dd>
<dt>Author</dt> <dd>cal@macports.org</dd>
<dt>Date</dt> <dd>2014-02-18 16:30:22 -0800 (Tue, 18 Feb 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>port.tcl: switch progress bars to ascii-only, capsule in a subnamespace

The Unicode block drawing characters produce unsatisfactory results with the
default configurations of Terminal.app. This change reverts to ASCII-only
characters for the progress bars (even just using a single full block looks
ugly in some of the fonts in the default configurations).

Unfortunately it seems we'll have to wait until Unicode fonts provide better
support for the block drawing characters.

This change also prevents the progress bars from overflowing the width of the
terminal window and provides a namespace for progress functions to avoid
cluttering the global namespace with the required helper variables.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkbasesrcportporttcl">trunk/base/src/port/port.tcl</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkbasesrcportporttcl"></a>
<div class="modfile"><h4>Modified: trunk/base/src/port/port.tcl (117185 => 117186)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/port/port.tcl        2014-02-18 23:56:49 UTC (rev 117185)
+++ trunk/base/src/port/port.tcl        2014-02-19 00:30:22 UTC (rev 117186)
</span><span class="lines">@@ -40,6 +40,8 @@
</span><span class="cx"> package require macports
</span><span class="cx"> package require Pextlib 1.0
</span><span class="cx"> 
</span><ins>+# Create a namespace for some local variables
+namespace eval portclient {}
</ins><span class="cx"> 
</span><span class="cx"> # Standard procedures
</span><span class="cx"> proc print_usage {{verbose 1}} {
</span><span class="lines">@@ -4789,226 +4791,321 @@
</span><span class="cx">     return $exit_status
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-##
-# Progress callback for generic operations executed by macports 1.0.
-#
-# @param action
-#        One of &quot;start&quot;, &quot;update&quot;, &quot;intermission&quot; or &quot;finish&quot;, where start will
-#        be called before any number of update calls, interrupted by any number
-#        of intermission calls (called because other output is being produced),
-#        followed by one call to finish.
-# @param args
-#        A list of variadic args that differ for each action.
-#        For &quot;start&quot;: empty.
-#        For &quot;update&quot;: contains the arguments $cur and $total where $cur is the
-#        current number of units processed and $total is the total number of
-#        units to be processed.
-#        For &quot;intermission&quot;: empty.
-#        For &quot;finish&quot;: empty.
-proc port_progress_generic {action args} {
-    global _port_progress_starttime _port_progress_display_bar
-    switch -nocase -- $action {
-        start {
-            set _port_progress_starttime [clock milliseconds]
-            set _port_progress_display_bar no
</del><ins>+namespace eval portclient::progress {
+    ##
+    # Maximum width of the progress bar or indicator when displaying it.
+    variable maxWidth 50
+
+    ##
+    # The start time of the last progress callback as returned by [clock time].
+    # Since only one progress indicator is active at a time, this variable is
+    # shared between the different variants of progress functions.
+    variable startTime
+
+    ##
+    # Delay in milliseconds after the start of the operation before deciding
+    # that showing a progress bar makes sense.
+    variable showTimeThreshold 500
+
+    ##
+    # Percentage value between 0 and 1 that must not have been reached yet when
+    # $showTimeThreshold has passed for a progress bar to be shown. If the
+    # operation has proceeded above e.g. 75% after 500ms we won't bother
+    # displaying a progress indicator anymore -- the operation will be finished
+    # in well below a second anyway.
+    variable showPercentageThreshold 0.75
+
+    ##
+    # Boolean indication whether the progress indicator should be shown or is
+    # still hidden because the current operation didn't need enough time for
+    # a progress indicator to make sense, yet.
+    variable show no
+
+    ##
+    # Initialize the progress bar display delay; call this from the start
+    # action of the progress functions.
+    proc initDelay {} {
+        variable show
+        variable startTime
+
+        set startTime [clock milliseconds]
+        set show no
+    }
+
+    ##
+    # Determine whether a progress bar should be shown for the current
+    # operation in its current state. You must have called initDelay for the
+    # current operation before calling this method.
+    #
+    # @param cur
+    #        Current progress in abstract units.
+    # @param total
+    #        Total number of abstract units to be processed, if known. Pass
+    #        0 if unknown.
+    # @return
+    #        &quot;yes&quot;, if the progress indicator should be shown, &quot;no&quot; otherwise.
+    proc showProgress {cur total} {
+        variable show
+        variable startTime
+        variable showTimeThreshold
+        variable showPercentageThreshold
+
+        if {$show eq yes} {
+            return yes
+        } else {
+            if {[expr {[clock milliseconds] - $startTime}] &gt; $showTimeThreshold &amp;&amp;
+                ($total == 0 || [expr {double($cur) / double($total)}] &lt; $showPercentageThreshold)} {
+                set show yes
+            }
+            return $show
</ins><span class="cx">         }
</span><del>-        update {
-            # the for loop is a simple hack because Tcl 8.4 doesn't have
-            # lassign
-            foreach {now total} $args {
-                if {${_port_progress_display_bar} ne yes} {
-                    # check whether we should show a progress bar for this transfer
-                    if {[expr {[clock milliseconds] - ${_port_progress_starttime}}] &gt; 500 &amp;&amp; ($total == 0 || [expr {$now / $total}] &lt; 0.5)} {
-                        # wait 500ms, then, if we don't know the total or we're
-                        # not past 50% yet, display a progress bar.
-                        set _port_progress_display_bar yes
</del><ins>+    }
+
+    ##
+    # Progress callback for generic operations executed by macports 1.0.
+    #
+    # @param action
+    #        One of &quot;start&quot;, &quot;update&quot;, &quot;intermission&quot; or &quot;finish&quot;, where start
+    #        will be called before any number of update calls, interrupted by
+    #        any number of intermission calls (called because other output is
+    #        being produced), followed by one call to finish.
+    # @param args
+    #        A list of variadic args that differ for each action. For &quot;start&quot;,
+    #        &quot;intermission&quot; and &quot;finish&quot;, the args are empty and unused. For
+    #        &quot;update&quot;, args contains $cur and $total, where $cur is the current
+    #        number of units processed and $total is the total number of units
+    #        to be processed. If the total is not known, it is 0.
+    proc generic {action args} {
+        global env
+        variable maxWidth
+
+        switch -nocase -- $action {
+            start {
+                initDelay
+            }
+            update {
+                # the for loop is a simple hack because Tcl 8.4 doesn't have
+                # lassign
+                foreach {now total} $args {
+                    if {[showProgress $now $total] eq yes} {
+                        set barPrefix &quot;      &quot;
+                        set barPrefixLen [string length $barPrefix]
+                        if {$total != 0} {
+                            progressbar $now $total [expr {min($maxWidth, $env(COLUMNS) - $barPrefixLen)}] $barPrefix
+                        } else {
+                            unprogressbar $now [expr {min($maxWidth, $env(COLUMNS) - $barPrefixLen)}] $barPrefix
+                        }
</ins><span class="cx">                     }
</span><span class="cx">                 }
</span><del>-                if {${_port_progress_display_bar} eq yes} {
-                    set barprefix &quot;      &quot;
-                    if {$total != 0} {
-                        progress_bar $now $total 20 $barprefix
-                    } else {
-                        unprogress_bar $now 20 $barprefix
-                    }
-                }
</del><span class="cx">             }
</span><ins>+            intermission -
+            finish {
+                # erase to start of line
+                ::term::ansi::send::esol
+                # return cursor to start of line
+                puts -nonewline &quot;\r&quot;
+                flush stdout
+            }
</ins><span class="cx">         }
</span><del>-        intermission -
-        finish {
-            # erase to start of line
-            ::term::ansi::send::esol
-            # return cursor to start of line
-            puts -nonewline &quot;\r&quot;
-            flush stdout
-        }
</del><ins>+
+        return 0
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    return 0
-}
</del><ins>+    ##
+    # Progress callback for downloads executed by macports 1.0.
+    #
+    # This is essentially a cURL progress callback.
+    #
+    # @param action
+    #        One of &quot;start&quot;, &quot;update&quot; or &quot;finish&quot;, where start will be called
+    #        before any number of update calls, followed by one call to finish.
+    # @param args
+    #        A list of variadic args that differ for each action. For &quot;start&quot;,
+    #        contains a single argument &quot;ul&quot; or &quot;dl&quot; indicating whether this is
+    #        an up- or download. For &quot;update&quot;, contains the arguments
+    #        (&quot;ul&quot;|&quot;dl&quot;) $total $now $speed where ul/dl are as for start, and
+    #        total, now and speed are doubles indicating the total transfer
+    #        size, currently transferred amount and average speed per second in
+    #        bytes. Unused for &quot;finish&quot;.
+    proc download {action args} {
+        global env
+        variable maxWidth
</ins><span class="cx"> 
</span><ins>+        switch -nocase -- $action {
+            start {
+                initDelay
+            }
+            update {
+                # the for loop is a simple hack because Tcl 8.4 doesn't have
+                # lassign
+                foreach {type total now speed} $args {
+                    if {[showProgress $now $total] eq yes} {
+                        set barPrefix &quot;      &quot;
+                        set barPrefixLen [string length $barPrefix]
+                        if {$total != 0} {
+                            set barSuffix [format &quot;        speed: %-13s&quot; &quot;[bytesize $speed {} &quot;%.1f&quot;]/s&quot;]
+                            set barSuffixLen [string length $barSuffix]
</ins><span class="cx"> 
</span><del>-##
-# Progress callback for downloads executed by macports 1.0.
-#
-# This is essentially a cURL progress callback.
-#
-# @param action
-#        One of &quot;start&quot;, &quot;update&quot; or &quot;finish&quot;, where start will be called
-#        before any number of update calls, followed by one call to finish.
-# @param args
-#        A list of variadic args that differ for each action.
-#        For &quot;start&quot;: contains a single argument &quot;ul&quot; or &quot;dl&quot; indicating
-#        whether this is an up- or download.
-#        For &quot;update&quot;: contains the arguments (&quot;ul&quot;|&quot;dl&quot;) total now speed where
-#        ul/dl are as for start, and total, now and speed are doubles
-#        indicating the total transfer size, currently transferred amount and
-#        average speed per second in bytes.
-#        For &quot;finish&quot;: empty.
-proc port_progress_download {action args} {
-    global _port_progress_starttime _port_progress_display_bar
-    switch -nocase -- $action {
-        start {
-            set _port_progress_starttime [clock milliseconds]
-            set _port_progress_display_bar no
-        }
-        update {
-            # the for loop is a simple hack because Tcl 8.4 doesn't have
-            # lassign
-            foreach {type total now speed} $args {
-                if {${_port_progress_display_bar} ne yes} {
-                    # check whether we should show a progress bar for this transfer
-                    if {[expr {[clock milliseconds] - ${_port_progress_starttime}}] &gt; 500 &amp;&amp; ($total == 0 || [expr {$now / $total}] &lt; 0.5)} {
-                        # wait 500ms, then, if we don't know the total or we're
-                        # not past 50% yet, display a progress bar.
-                        set _port_progress_display_bar yes
</del><ins>+                            set barLen [expr {min($maxWidth, $env(COLUMNS) - $barPrefixLen - $barSuffixLen)}]
+                            progressbar $now $total $barLen $barPrefix $barSuffix
+                        } else {
+                            set barSuffix [format &quot; %-10s     speed: %-13s&quot; [bytesize $now {} &quot;%6.1f&quot;] &quot;[bytesize $speed {} &quot;%.1f&quot;]/s&quot;]
+                            set barSuffixLen [string length $barSuffix]
+
+                            set barLen [expr {min($maxWidth, $env(COLUMNS) - $barPrefixLen - $barSuffixLen)}]
+                            unprogressbar $now $total $barLen $barPrefix $barSuffix
+                        }
</ins><span class="cx">                     }
</span><span class="cx">                 }
</span><del>-                if {${_port_progress_display_bar} eq yes} {
-                    set barprefix &quot;      &quot;
-                    if {$total != 0} {
-                        set barsuffix [format &quot;        speed: %-13s&quot; &quot;[bytesize $speed {} &quot;%.1f&quot;]/s&quot;]
-                        progress_bar $now $total 20 $barprefix $barsuffix
-                    } else {
-                        set barsuffix [format &quot; %-10s     speed: %-13s&quot; [bytesize $now {} &quot;%6.1f&quot;] &quot;[bytesize $speed {} &quot;%.1f&quot;]/s&quot;]
-                        unprogress_bar $now 20 $barprefix $barsuffix
-                    }
-                }
</del><span class="cx">             }
</span><ins>+            finish {
+                # erase to start of line
+                ::term::ansi::send::esol
+                # return cursor to start of line
+                puts -nonewline &quot;\r&quot;
+                flush stdout
+            }
</ins><span class="cx">         }
</span><del>-        finish {
-            # erase to start of line
-            ::term::ansi::send::esol
-            # return cursor to start of line
-            puts -nonewline &quot;\r&quot;
-            flush stdout
-        }
</del><ins>+
+        return 0
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    return 0
-}
</del><ins>+    ##
+    # Draw a progress bar using unicode block drawing characters
+    #
+    # @param current
+    #        The current progress value.
+    # @param total
+    #        The progress value representing 100%.
+    # @param width
+    #        The width in characters of the progress bar. This includes percentage
+    #        output, which takes up 8 characters.
+    # @param prefix
+    #        Prefix to be printed in front of the progress bar.
+    # @param suffix
+    #        Suffix to be printed after the progress bar.
+    proc progressbar {current total width {prefix &quot;&quot;} {suffix &quot;&quot;}} {
+        # Subtract the width of the percentage output, also subtract the two
+        # characters [ and ] bounding the progress bar.
+        set percentageWidth 8
+        set barWidth      [expr entier($width) - $percentageWidth - 2]
</ins><span class="cx"> 
</span><del>-##
-# Draw a progress bar using unicode block drawing characters
-#
-# @param current
-#        the current progress value
-# @param total
-#        the progress value representing 100%
-# @param halfwidth
-#        the half width in characters of the progress bar
-# @param prefix
-#        prefix to be printed in front of the progress bar
-# @param suffix
-#        suffix to be printed after the progress bar
-proc progress_bar {current total halfwidth {prefix &quot;&quot;} {suffix &quot;&quot;}} {
-    # we use 8 different states per character, so let's multiply the width by
-    # 8 and map the percentage to this range
-    set percent [expr {(double($current) * 100 / double($total))}]
-    set progress [expr {int(round(($current * $halfwidth * 8) / $total))}]
-    set fullfields [expr {int($progress / 8)}]
-    set remainder [expr {$progress % 8}]
</del><ins>+        # Map the range (0, $total) to (0, 4 * $width) where $width is the maximum
+        # numebr of characters to be printed for the progress bar. Multiply the
+        # upper bound with 8 because we have 8 sub-states per character.
+        set barProgress   [expr {entier(round(($current * $barWidth * 8) / $total))}]
</ins><span class="cx"> 
</span><del>-    # clear the current line
-    set progressbar &quot;&quot;
-    for {set i 0} {$i &lt; $fullfields} {incr i} {
-        # U+2588 FULL BLOCK doesn't match the other blocks in some fonts :/
-        # Use two half blocks instead
-        # Since we use two chars here, make sure to remove a space for each of
-        # those used!
-        append progressbar &quot;\u258c\u258c&quot;
-    }
</del><ins>+        set barInteger    [expr {$barProgress / 8}]
+        #set barRemainder  [expr {$barProgress % 8}]
</ins><span class="cx"> 
</span><del>-    if {$remainder == 0 &amp;&amp; $fullfields &lt; $halfwidth} {
-        append progressbar &quot; &quot;
-    } elseif {$remainder == 1} {
-        # U+258F LEFT ONE EIGHTH BLOCK
-        append progressbar &quot;\u258f&quot;
-    } elseif {$remainder == 2} {
-        # U+258E LEFT ONE QUARTER BLOCK
-        append progressbar &quot;\u258e&quot;
-    } elseif {$remainder == 3} {
-        # U+258D LEFT THREE EIGHTHS BLOCK
-        append progressbar &quot;\u258d&quot;
-    } elseif {$remainder == 4} {
-        # U+258C LEFT HALF BLOCK
-        append progressbar &quot;\u258c&quot;
-    } elseif {$remainder == 5} {
-        # U+258B LEFT FIVE EIGHTHS BLOCK
-        append progressbar &quot;\u258b&quot;
-    } elseif {$remainder == 6} {
-        # U+258A LEFT THREE QUARTERS BLOCK
-        append progressbar &quot;\u258a&quot;
-    } elseif {$remainder == 7} {
-        # U+2589 LEFT SEVEN EIGHTHS BLOCK
-        append progressbar &quot;\u2589&quot;
-    }
</del><ins>+        # Finally, also provide a percentage value to print behind the progress bar
+        set percentage [expr {double($current) * 100 / double($total)}]
</ins><span class="cx"> 
</span><del>-    for {set i [expr {[string length $progressbar]}]} {$i &lt; [expr {2 * $halfwidth}]} {incr i} {
-        append progressbar &quot; &quot;
-    }
-    set percentagesuffix [format &quot; %5.1f %%&quot; $percent]
</del><ins>+        # clear the current line
+        set progressbar &quot;&quot;
+        for {set i 0} {$i &lt; $barInteger} {incr i} {
+            # U+2588 FULL BLOCK doesn't match the other blocks in some fonts :/
+            # Two half blocks work better in some fonts, but not in others (because
+            # they leave ugly spaces). So, one or the other choice isn't better or
+            # worse and even just using full blocks looks ugly in a few fonts.
</ins><span class="cx"> 
</span><del>-    puts -nonewline &quot;\r${prefix}\[${progressbar}\]${percentagesuffix}${suffix}&quot;
-    flush stdout
-}
</del><ins>+            # Use pure ASCII until somebody fixes most of the default terminal fonts :/
+            append progressbar &quot;#&quot;
+        }
</ins><span class="cx"> 
</span><del>-##
-# Draw a progress indicator
-#
-# @param current
-#        the number of bytes currently downloaded
-# @param halfwidth
-#        the half width in characters of the progress indicator
-# @param prefix
-#        prefix to be printed in front of the progress indicator
-# @param suffix
-#        suffix to be printed after the progress indicator
-proc unprogress_bar {current halfwidth {prefix &quot;&quot;} {suffix &quot;&quot;}} {
-    global _port_progress_unprogressbar_state
</del><ins>+        #switch $barRemainder {
+        #    0 {
+        #        if {$barInteger &lt; $barWidth} {
+        #            append progressbar &quot; &quot;
+        #        }
+        #    }
+        #    1 {
+        #        # U+258F LEFT ONE EIGHTH BLOCK
+        #        append progressbar &quot;\u258f&quot;
+        #    }
+        #    2 {
+        #        # U+258E LEFT ONE QUARTER BLOCK
+        #        append progressbar &quot;\u258e&quot;
+        #    }
+        #    3 {
+        #        # U+258D LEFT THREE EIGHTHS BLOCK
+        #        append progressbar &quot;\u258d&quot;
+        #    }
+        #    3 {
+        #        # U+258D LEFT THREE EIGHTHS BLOCK
+        #        append progressbar &quot;\u258d&quot;
+        #    }
+        #    4 {
+        #        # U+258C LEFT HALF BLOCK
+        #        append progressbar &quot;\u258c&quot;
+        #    }
+        #    5 {
+        #        # U+258B LEFT FIVE EIGHTHS BLOCK
+        #        append progressbar &quot;\u258b&quot;
+        #    }
+        #    6 {
+        #        # U+258A LEFT THREE QUARTERS BLOCK
+        #        append progressbar &quot;\u258a&quot;
+        #    }
+        #    7 {
+        #        # U+2589 LEFT SEVEN EIGHTHS BLOCK
+        #        append progressbar &quot;\u2589&quot;
+        #    }
+        #}
</ins><span class="cx"> 
</span><del>-    set numstates 4
</del><ins>+        # Fill the progress bar with spaces
+        for {set i [string length $progressbar]} {$i &lt; $barWidth} {incr i} {
+            append progressbar &quot; &quot;
+        }
</ins><span class="cx"> 
</span><del>-    if {![info exists _port_progress_unprogressbar_state]} {
-        set _port_progress_unprogressbar_state 0
-    } else {
-        set _port_progress_unprogressbar_state [expr {(${_port_progress_unprogressbar_state} + 1) % $numstates}]
</del><ins>+        # Format the percentage using the space that has been reserved for it
+        set percentagesuffix [format &quot; %[expr $percentageWidth - 3].1f %%&quot; $percentage]
+
+        puts -nonewline &quot;\r${prefix}\[${progressbar}\]${percentagesuffix}${suffix}&quot;
+        flush stdout
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    # clear the current line
-    set progressbar &quot;&quot;
</del><span class="cx"> 
</span><del>-    for {set i 0} {$i &lt; [expr {2 * $halfwidth}]} {incr i} {
-        if {[expr $i % $numstates] == ${_port_progress_unprogressbar_state}} {
-            # U+2022 BULLET
-            append progressbar &quot;\u2022&quot;
-        } else {
-            append progressbar &quot; &quot;
</del><ins>+    ##
+    # Internal state of the progress indicator; unless you're hacking the
+    # unprogressbar code you should never touch this.
+    variable unprogressState 0
+
+    ##
+    # Draw a progress indicator
+    #
+    # @param width
+    #        The width in characters of the progress indicator.
+    # @param prefix
+    #        Prefix to be printed in front of the progress indicator.
+    # @param suffix
+    #        Suffix to be printed after the progress indicator.
+    proc unprogressbar {width {prefix &quot;&quot;} {suffix &quot;&quot;}} {
+        variable unprogressState
+
+        # Subtract the two characters [ and ] bounding the progress indicator
+        # from the width.
+        set barWidth [expr {int($width) - 2}]
+
+        # Number of states of the progress bar, or rather: the number of
+        # characters before the sequence repeats.
+        set numStates 4
+
+        set unprogressState [expr {($unprogressState + 1) % $numStates}]
+
+        set progressbar &quot;&quot;
+        for {set i 0} {$i &lt; $barWidth} {incr i} {
+            if {[expr {$i % $numStates}] == $unprogressState} {
+                # U+2022 BULLET
+                append progressbar &quot;\u2022&quot;
+            } else {
+                append progressbar &quot; &quot;
+            }
</ins><span class="cx">         }
</span><ins>+
+        puts -nonewline &quot;\r${prefix}\[${progressbar}\]${suffix}&quot;
+        flush stdout
</ins><span class="cx">     }
</span><del>-
-    puts -nonewline &quot;\r${prefix}\[${progressbar}\]${suffix}&quot;
-    flush stdout
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -5060,8 +5157,8 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> if {[isatty stdout] &amp;&amp; (![info exists ui_options(ports_quiet)] || $ui_options(ports_quiet) ne &quot;yes&quot;)} {
</span><del>-    set ui_options(progress_download) port_progress_download
-    set ui_options(progress_generic)  port_progress_generic
</del><ins>+    set ui_options(progress_download) portclient::progress::download
+    set ui_options(progress_generic)  portclient::progress::generic
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> # Get arguments remaining after option processing
</span></span></pre>
</div>
</div>

</body>
</html>