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