]> git.saurik.com Git - apt.git/blob - apt-private/acqprogress.cc
Deprecate SPtrArray<T> and convert everyone to unique_ptr<T[]>
[apt.git] / apt-private / acqprogress.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 /* ######################################################################
4
5 Acquire Progress - Command line progress meter
6
7 ##################################################################### */
8 /*}}}*/
9 // Include files /*{{{*/
10 #include<config.h>
11
12 #include <apt-pkg/acquire.h>
13 #include <apt-pkg/acquire-item.h>
14 #include <apt-pkg/acquire-worker.h>
15 #include <apt-pkg/configuration.h>
16 #include <apt-pkg/strutl.h>
17 #include <apt-pkg/error.h>
18
19 #include <apt-private/acqprogress.h>
20
21 #include <string.h>
22 #include <stdio.h>
23 #include <signal.h>
24 #include <iostream>
25 #include <sstream>
26 #include <unistd.h>
27
28 #include <apti18n.h>
29 /*}}}*/
30
31 // AcqTextStatus::AcqTextStatus - Constructor /*{{{*/
32 // ---------------------------------------------------------------------
33 /* */
34 AcqTextStatus::AcqTextStatus(std::ostream &out, unsigned int &ScreenWidth,unsigned int const Quiet) :
35 pkgAcquireStatus(), out(out), ScreenWidth(ScreenWidth), LastLineLength(0), ID(0), Quiet(Quiet)
36 {
37 // testcases use it to disable pulses without disabling other user messages
38 if (Quiet == 0 && _config->FindB("quiet::NoUpdate", false) == true)
39 this->Quiet = 1;
40 }
41 /*}}}*/
42 // AcqTextStatus::Start - Downloading has started /*{{{*/
43 // ---------------------------------------------------------------------
44 /* */
45 void AcqTextStatus::Start()
46 {
47 pkgAcquireStatus::Start();
48 LastLineLength = 0;
49 ID = 1;
50 }
51 /*}}}*/
52 void AcqTextStatus::AssignItemID(pkgAcquire::ItemDesc &Itm) /*{{{*/
53 {
54 /* In theory calling it from Fetch() would be enough, but to be
55 safe we call it from IMSHit and Fail as well.
56 Also, an Item can pass through multiple stages, so ensure
57 that it keeps the same number */
58 if (Itm.Owner->ID == 0)
59 Itm.Owner->ID = ID++;
60 }
61 /*}}}*/
62 // AcqTextStatus::IMSHit - Called when an item got a HIT response /*{{{*/
63 // ---------------------------------------------------------------------
64 /* */
65 void AcqTextStatus::IMSHit(pkgAcquire::ItemDesc &Itm)
66 {
67 if (Quiet > 1)
68 return;
69
70 AssignItemID(Itm);
71 clearLastLine();
72
73 // TRANSLATOR: Very short word to be displayed before unchanged files in 'apt-get update'
74 ioprintf(out, _("Hit:%lu %s"), Itm.Owner->ID, Itm.Description.c_str());
75 out << std::endl;
76 Update = true;
77 }
78 /*}}}*/
79 // AcqTextStatus::Fetch - An item has started to download /*{{{*/
80 // ---------------------------------------------------------------------
81 /* This prints out the short description and the expected size */
82 void AcqTextStatus::Fetch(pkgAcquire::ItemDesc &Itm)
83 {
84 Update = true;
85 if (Itm.Owner->Complete == true)
86 return;
87 AssignItemID(Itm);
88
89 if (Quiet > 1)
90 return;
91
92 clearLastLine();
93
94 // TRANSLATOR: Very short word to be displayed for files processed in 'apt-get update'
95 // Potentially replaced later by "Hit:", "Ign:" or "Err:" if something (bad) happens
96 ioprintf(out, _("Get:%lu %s"), Itm.Owner->ID, Itm.Description.c_str());
97 if (Itm.Owner->FileSize != 0)
98 out << " [" << SizeToStr(Itm.Owner->FileSize) << "B]";
99 out << std::endl;
100 }
101 /*}}}*/
102 // AcqTextStatus::Done - Completed a download /*{{{*/
103 // ---------------------------------------------------------------------
104 /* We don't display anything... */
105 void AcqTextStatus::Done(pkgAcquire::ItemDesc &Itm)
106 {
107 Update = true;
108 AssignItemID(Itm);
109 }
110 /*}}}*/
111 // AcqTextStatus::Fail - Called when an item fails to download /*{{{*/
112 // ---------------------------------------------------------------------
113 /* We print out the error text */
114 void AcqTextStatus::Fail(pkgAcquire::ItemDesc &Itm)
115 {
116 if (Quiet > 1)
117 return;
118
119 AssignItemID(Itm);
120 clearLastLine();
121
122 if (Itm.Owner->Status == pkgAcquire::Item::StatDone || Itm.Owner->Status == pkgAcquire::Item::StatIdle)
123 {
124 // TRANSLATOR: Very short word to be displayed for files in 'apt-get update'
125 // which failed to download, but the error is ignored (compare "Err:")
126 ioprintf(out, _("Ign:%lu %s"), Itm.Owner->ID, Itm.Description.c_str());
127 if (Itm.Owner->ErrorText.empty() == false &&
128 _config->FindB("Acquire::Progress::Ignore::ShowErrorText", false) == true)
129 out << std::endl << " " << Itm.Owner->ErrorText;
130 out << std::endl;
131 }
132 else
133 {
134 // TRANSLATOR: Very short word to be displayed for files in 'apt-get update'
135 // which failed to download and the error is critical (compare "Ign:")
136 ioprintf(out, _("Err:%lu %s"), Itm.Owner->ID, Itm.Description.c_str());
137 out << std::endl << " " << Itm.Owner->ErrorText << std::endl;
138 }
139
140 Update = true;
141 }
142 /*}}}*/
143 // AcqTextStatus::Stop - Finished downloading /*{{{*/
144 // ---------------------------------------------------------------------
145 /* This prints out the bytes downloaded and the overall average line
146 speed */
147 void AcqTextStatus::Stop()
148 {
149 pkgAcquireStatus::Stop();
150 if (Quiet > 1)
151 return;
152
153 clearLastLine();
154
155 if (_config->FindB("quiet::NoStatistic", false) == true)
156 return;
157
158 if (FetchedBytes != 0 && _error->PendingError() == false)
159 ioprintf(out,_("Fetched %sB in %s (%sB/s)\n"),
160 SizeToStr(FetchedBytes).c_str(),
161 TimeToStr(ElapsedTime).c_str(),
162 SizeToStr(CurrentCPS).c_str());
163 }
164 /*}}}*/
165 // AcqTextStatus::Pulse - Regular event pulse /*{{{*/
166 // ---------------------------------------------------------------------
167 /* This draws the current progress. Each line has an overall percent
168 meter and a per active item status meter along with an overall
169 bandwidth and ETA indicator. */
170 bool AcqTextStatus::Pulse(pkgAcquire *Owner)
171 {
172 pkgAcquireStatus::Pulse(Owner);
173
174 if (Quiet > 0)
175 return true;
176
177 enum {Long = 0,Medium,Short} Mode = Medium;
178
179 std::string Line;
180 {
181 std::stringstream S;
182 for (pkgAcquire::Worker *I = Owner->WorkersBegin(); I != 0;
183 I = Owner->WorkerStep(I))
184 {
185 // There is no item running
186 if (I->CurrentItem == 0)
187 {
188 if (I->Status.empty() == false)
189 S << " [" << I->Status << "]";
190
191 continue;
192 }
193
194 // Add in the short description
195 S << " [";
196 if (I->CurrentItem->Owner->ID != 0)
197 S << I->CurrentItem->Owner->ID << " ";
198 S << I->CurrentItem->ShortDesc;
199
200 // Show the short mode string
201 if (I->CurrentItem->Owner->ActiveSubprocess.empty() == false)
202 S << " " << I->CurrentItem->Owner->ActiveSubprocess;
203
204 // Add the current progress
205 if (Mode == Long)
206 S << " " << I->CurrentSize;
207 else
208 {
209 if (Mode == Medium || I->TotalSize == 0)
210 S << " " << SizeToStr(I->CurrentSize) << "B";
211 }
212
213 // Add the total size and percent
214 if (I->TotalSize > 0 && I->CurrentItem->Owner->Complete == false)
215 {
216 if (Mode == Short)
217 ioprintf(S, " %.0f%%", (I->CurrentSize*100.0)/I->TotalSize);
218 else
219 ioprintf(S, "/%sB %.0f%%", SizeToStr(I->TotalSize).c_str(),
220 (I->CurrentSize*100.0)/I->TotalSize);
221 }
222 S << "]";
223 }
224
225 // Show at least something
226 Line = S.str();
227 S.clear();
228 if (Line.empty() == true)
229 Line = _(" [Working]");
230 }
231 // Put in the percent done
232 {
233 std::stringstream S;
234 ioprintf(S, "%.0f%%", Percent);
235 S << Line;
236 Line = S.str();
237 S.clear();
238 }
239
240 /* Put in the ETA and cps meter, block off signals to prevent strangeness
241 during resizing */
242 sigset_t Sigs,OldSigs;
243 sigemptyset(&Sigs);
244 sigaddset(&Sigs,SIGWINCH);
245 sigprocmask(SIG_BLOCK,&Sigs,&OldSigs);
246
247 if (CurrentCPS != 0)
248 {
249 unsigned long long ETA = (TotalBytes - CurrentBytes)/CurrentCPS;
250 std::string Tmp = " " + SizeToStr(CurrentCPS) + "B/s " + TimeToStr(ETA);
251 size_t alignment = Line.length() + Tmp.length();
252 if (alignment < ScreenWidth)
253 {
254 alignment = ScreenWidth - alignment;
255 for (size_t i = 0; i < alignment; ++i)
256 Line.append(" ");
257 Line.append(Tmp);
258 }
259 }
260 if (Line.length() > ScreenWidth)
261 Line.erase(ScreenWidth);
262 sigprocmask(SIG_SETMASK,&OldSigs,0);
263
264 // Draw the current status
265 if (_config->FindB("Apt::Color", false) == true)
266 out << _config->Find("APT::Color::Yellow");
267 if (LastLineLength > Line.length())
268 clearLastLine();
269 else
270 out << '\r';
271 out << Line << std::flush;
272 if (_config->FindB("Apt::Color", false) == true)
273 out << _config->Find("APT::Color::Neutral") << std::flush;
274
275 LastLineLength = Line.length();
276 Update = false;
277
278 return true;
279 }
280 /*}}}*/
281 // AcqTextStatus::MediaChange - Media need to be swapped /*{{{*/
282 // ---------------------------------------------------------------------
283 /* Prompt for a media swap */
284 bool AcqTextStatus::MediaChange(std::string Media, std::string Drive)
285 {
286 // If we do not output on a terminal and one of the options to avoid user
287 // interaction is given, we assume that no user is present who could react
288 // on your media change request
289 if (isatty(STDOUT_FILENO) != 1 && Quiet >= 2 &&
290 (_config->FindB("APT::Get::Assume-Yes",false) == true ||
291 _config->FindB("APT::Get::Force-Yes",false) == true ||
292 _config->FindB("APT::Get::Trivial-Only",false) == true))
293
294 return false;
295
296 clearLastLine();
297 ioprintf(out,_("Media change: please insert the disc labeled\n"
298 " '%s'\n"
299 "in the drive '%s' and press enter\n"),
300 Media.c_str(),Drive.c_str());
301
302 char C = 0;
303 bool bStatus = true;
304 while (C != '\n' && C != '\r')
305 {
306 int len = read(STDIN_FILENO,&C,1);
307 if(C == 'c' || len <= 0)
308 bStatus = false;
309 }
310
311 if(bStatus)
312 Update = true;
313 return bStatus;
314 }
315 /*}}}*/
316 void AcqTextStatus::clearLastLine() { /*{{{*/
317 if (Quiet > 0 || LastLineLength == 0)
318 return;
319
320 // do not try to clear more than the (now smaller) screen
321 if (LastLineLength > ScreenWidth)
322 LastLineLength = ScreenWidth;
323
324 out << '\r';
325 for (size_t i = 0; i < LastLineLength; ++i)
326 out << ' ';
327 out << '\r' << std::flush;
328 }
329 /*}}}*/