]> git.saurik.com Git - apt.git/blob - apt-pkg/install-progress.cc
Use C locale instead of C.UTF-8 for protocol strings
[apt.git] / apt-pkg / install-progress.cc
1 #include <config.h>
2
3 #include <apt-pkg/configuration.h>
4 #include <apt-pkg/fileutl.h>
5 #include <apt-pkg/strutl.h>
6 #include <apt-pkg/install-progress.h>
7
8 #include <signal.h>
9 #include <unistd.h>
10 #include <iostream>
11 #include <vector>
12 #include <sys/ioctl.h>
13 #include <fcntl.h>
14 #include <algorithm>
15 #include <stdio.h>
16 #include <sstream>
17
18 #include <apti18n.h>
19
20 namespace APT {
21 namespace Progress {
22
23 PackageManager::PackageManager() : d(NULL), percentage(0.0), last_reported_progress(-1) {}
24 PackageManager::~PackageManager() {}
25
26 /* Return a APT::Progress::PackageManager based on the global
27 * apt configuration (i.e. APT::Status-Fd and APT::Status-deb822-Fd)
28 */
29 PackageManager* PackageManagerProgressFactory()
30 {
31 // select the right progress
32 int status_fd = _config->FindI("APT::Status-Fd", -1);
33 int status_deb822_fd = _config->FindI("APT::Status-deb822-Fd", -1);
34
35 APT::Progress::PackageManager *progress = NULL;
36 if (status_deb822_fd > 0)
37 progress = new APT::Progress::PackageManagerProgressDeb822Fd(
38 status_deb822_fd);
39 else if (status_fd > 0)
40 progress = new APT::Progress::PackageManagerProgressFd(status_fd);
41 else if(_config->FindB("Dpkg::Progress-Fancy", false) == true)
42 progress = new APT::Progress::PackageManagerFancy();
43 else if (_config->FindB("Dpkg::Progress",
44 _config->FindB("DpkgPM::Progress", false)) == true)
45 progress = new APT::Progress::PackageManagerText();
46 else
47 progress = new APT::Progress::PackageManager();
48 return progress;
49 }
50
51 bool PackageManager::StatusChanged(std::string /*PackageName*/,
52 unsigned int StepsDone,
53 unsigned int TotalSteps,
54 std::string /*HumanReadableAction*/)
55 {
56 int reporting_steps = _config->FindI("DpkgPM::Reporting-Steps", 1);
57 percentage = StepsDone/(float)TotalSteps * 100.0;
58 strprintf(progress_str, _("Progress: [%3i%%]"), (int)percentage);
59
60 if(percentage < (last_reported_progress + reporting_steps))
61 return false;
62
63 return true;
64 }
65
66 PackageManagerProgressFd::PackageManagerProgressFd(int progress_fd)
67 : d(NULL), StepsDone(0), StepsTotal(1)
68 {
69 OutStatusFd = progress_fd;
70 }
71 PackageManagerProgressFd::~PackageManagerProgressFd() {}
72
73 void PackageManagerProgressFd::WriteToStatusFd(std::string s)
74 {
75 if(OutStatusFd <= 0)
76 return;
77 FileFd::Write(OutStatusFd, s.c_str(), s.size());
78 }
79
80 static std::string GetProgressFdString(char const * const status,
81 char const * const pkg, unsigned long long Done,
82 unsigned long long Total, char const * const msg)
83 {
84 float const progress{Done / static_cast<float>(Total) * 100};
85 std::ostringstream str;
86 str.imbue(std::locale::classic());
87 str.precision(4);
88 str << status << ':' << pkg << ':' << std::fixed << progress << ':' << msg << '\n';
89 return str.str();
90 }
91
92 void PackageManagerProgressFd::StartDpkg()
93 {
94 if(OutStatusFd <= 0)
95 return;
96
97 // FIXME: use SetCloseExec here once it taught about throwing
98 // exceptions instead of doing _exit(100) on failure
99 fcntl(OutStatusFd,F_SETFD,FD_CLOEXEC);
100
101 // send status information that we are about to fork dpkg
102 WriteToStatusFd(GetProgressFdString("pmstatus", "dpkg-exec", StepsDone, StepsTotal, _("Running dpkg")));
103 }
104
105 APT_CONST void PackageManagerProgressFd::Stop()
106 {
107 }
108
109 void PackageManagerProgressFd::Error(std::string PackageName,
110 unsigned int StepsDone,
111 unsigned int TotalSteps,
112 std::string ErrorMessage)
113 {
114 WriteToStatusFd(GetProgressFdString("pmerror", PackageName.c_str(),
115 StepsDone, TotalSteps, ErrorMessage.c_str()));
116 }
117
118 void PackageManagerProgressFd::ConffilePrompt(std::string PackageName,
119 unsigned int StepsDone,
120 unsigned int TotalSteps,
121 std::string ConfMessage)
122 {
123 WriteToStatusFd(GetProgressFdString("pmconffile", PackageName.c_str(),
124 StepsDone, TotalSteps, ConfMessage.c_str()));
125 }
126
127
128 bool PackageManagerProgressFd::StatusChanged(std::string PackageName,
129 unsigned int xStepsDone,
130 unsigned int xTotalSteps,
131 std::string pkg_action)
132 {
133 StepsDone = xStepsDone;
134 StepsTotal = xTotalSteps;
135
136 WriteToStatusFd(GetProgressFdString("pmstatus", StringSplit(PackageName, ":")[0].c_str(),
137 StepsDone, StepsTotal, pkg_action.c_str()));
138
139 if(_config->FindB("Debug::APT::Progress::PackageManagerFd", false) == true)
140 std::cerr << "progress: " << PackageName << " " << xStepsDone
141 << " " << xTotalSteps << " " << pkg_action
142 << std::endl;
143
144
145 return true;
146 }
147
148
149 PackageManagerProgressDeb822Fd::PackageManagerProgressDeb822Fd(int progress_fd)
150 : d(NULL), StepsDone(0), StepsTotal(1)
151 {
152 OutStatusFd = progress_fd;
153 }
154 PackageManagerProgressDeb822Fd::~PackageManagerProgressDeb822Fd() {}
155
156 void PackageManagerProgressDeb822Fd::WriteToStatusFd(std::string s)
157 {
158 FileFd::Write(OutStatusFd, s.c_str(), s.size());
159 }
160
161 static std::string GetProgressDeb822String(char const * const status,
162 char const * const pkg, unsigned long long Done,
163 unsigned long long Total, char const * const msg)
164 {
165 float const progress{Done / static_cast<float>(Total) * 100};
166 std::ostringstream str;
167 str.imbue(std::locale::classic());
168 str.precision(4);
169 str << "Status: " << status << '\n';
170 if (pkg != nullptr)
171 str << "Package: " << pkg << '\n';
172 str << "Percent: " << std::fixed << progress << '\n'
173 << "Message: " << msg << "\n\n";
174 return str.str();
175 }
176
177 void PackageManagerProgressDeb822Fd::StartDpkg()
178 {
179 // FIXME: use SetCloseExec here once it taught about throwing
180 // exceptions instead of doing _exit(100) on failure
181 fcntl(OutStatusFd,F_SETFD,FD_CLOEXEC);
182
183 WriteToStatusFd(GetProgressDeb822String("progress", nullptr, StepsDone, StepsTotal, _("Running dpkg")));
184 }
185
186 APT_CONST void PackageManagerProgressDeb822Fd::Stop()
187 {
188 }
189
190 void PackageManagerProgressDeb822Fd::Error(std::string PackageName,
191 unsigned int StepsDone,
192 unsigned int TotalSteps,
193 std::string ErrorMessage)
194 {
195 WriteToStatusFd(GetProgressDeb822String("Error", PackageName.c_str(), StepsDone, TotalSteps, ErrorMessage.c_str()));
196 }
197
198 void PackageManagerProgressDeb822Fd::ConffilePrompt(std::string PackageName,
199 unsigned int StepsDone,
200 unsigned int TotalSteps,
201 std::string ConfMessage)
202 {
203 WriteToStatusFd(GetProgressDeb822String("ConfFile", PackageName.c_str(), StepsDone, TotalSteps, ConfMessage.c_str()));
204 }
205
206
207 bool PackageManagerProgressDeb822Fd::StatusChanged(std::string PackageName,
208 unsigned int xStepsDone,
209 unsigned int xTotalSteps,
210 std::string message)
211 {
212 StepsDone = xStepsDone;
213 StepsTotal = xTotalSteps;
214
215 WriteToStatusFd(GetProgressDeb822String("progress", PackageName.c_str(), StepsDone, StepsTotal, message.c_str()));
216 return true;
217 }
218
219
220 PackageManagerFancy::PackageManagerFancy()
221 : d(NULL), child_pty(-1)
222 {
223 // setup terminal size
224 old_SIGWINCH = signal(SIGWINCH, PackageManagerFancy::staticSIGWINCH);
225 instances.push_back(this);
226 }
227 std::vector<PackageManagerFancy*> PackageManagerFancy::instances;
228
229 PackageManagerFancy::~PackageManagerFancy()
230 {
231 instances.erase(find(instances.begin(), instances.end(), this));
232 signal(SIGWINCH, old_SIGWINCH);
233 }
234
235 void PackageManagerFancy::staticSIGWINCH(int signum)
236 {
237 std::vector<PackageManagerFancy *>::const_iterator I;
238 for(I = instances.begin(); I != instances.end(); ++I)
239 (*I)->HandleSIGWINCH(signum);
240 }
241
242 PackageManagerFancy::TermSize
243 PackageManagerFancy::GetTerminalSize()
244 {
245 struct winsize win;
246 PackageManagerFancy::TermSize s = { 0, 0 };
247
248 // FIXME: get from "child_pty" instead?
249 if(ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&win) != 0)
250 return s;
251
252 if(_config->FindB("Debug::InstallProgress::Fancy", false) == true)
253 std::cerr << "GetTerminalSize: " << win.ws_row << " x " << win.ws_col << std::endl;
254
255 s.rows = win.ws_row;
256 s.columns = win.ws_col;
257 return s;
258 }
259
260 void PackageManagerFancy::SetupTerminalScrollArea(int nr_rows)
261 {
262 if(_config->FindB("Debug::InstallProgress::Fancy", false) == true)
263 std::cerr << "SetupTerminalScrollArea: " << nr_rows << std::endl;
264
265 if (unlikely(nr_rows <= 1))
266 return;
267
268 // scroll down a bit to avoid visual glitch when the screen
269 // area shrinks by one row
270 std::cout << "\n";
271
272 // save cursor
273 std::cout << "\0337";
274
275 // set scroll region (this will place the cursor in the top left)
276 std::cout << "\033[0;" << std::to_string(nr_rows - 1) << "r";
277
278 // restore cursor but ensure its inside the scrolling area
279 std::cout << "\0338";
280 static const char *move_cursor_up = "\033[1A";
281 std::cout << move_cursor_up;
282
283 // ensure its flushed
284 std::flush(std::cout);
285
286 // setup tty size to ensure xterm/linux console are working properly too
287 // see bug #731738
288 struct winsize win;
289 if (ioctl(child_pty, TIOCGWINSZ, (char *)&win) != -1)
290 {
291 win.ws_row = nr_rows - 1;
292 ioctl(child_pty, TIOCSWINSZ, (char *)&win);
293 }
294 }
295
296 void PackageManagerFancy::HandleSIGWINCH(int)
297 {
298 int const nr_terminal_rows = GetTerminalSize().rows;
299 SetupTerminalScrollArea(nr_terminal_rows);
300 DrawStatusLine();
301 }
302
303 void PackageManagerFancy::Start(int a_child_pty)
304 {
305 child_pty = a_child_pty;
306 int const nr_terminal_rows = GetTerminalSize().rows;
307 SetupTerminalScrollArea(nr_terminal_rows);
308 }
309
310 void PackageManagerFancy::Stop()
311 {
312 int const nr_terminal_rows = GetTerminalSize().rows;
313 if (nr_terminal_rows > 0)
314 {
315 SetupTerminalScrollArea(nr_terminal_rows + 1);
316
317 // override the progress line (sledgehammer)
318 static const char* clear_screen_below_cursor = "\033[J";
319 std::cout << clear_screen_below_cursor;
320 std::flush(std::cout);
321 }
322 child_pty = -1;
323 }
324
325 std::string
326 PackageManagerFancy::GetTextProgressStr(float Percent, int OutputSize)
327 {
328 std::string output;
329 int i;
330
331 // should we raise a exception here instead?
332 if (Percent < 0.0 || Percent > 1.0 || OutputSize < 3)
333 return output;
334
335 int BarSize = OutputSize - 2; // bar without the leading "[" and trailing "]"
336 output += "[";
337 for(i=0; i < BarSize*Percent; i++)
338 output += "#";
339 for (/*nothing*/; i < BarSize; i++)
340 output += ".";
341 output += "]";
342 return output;
343 }
344
345 bool PackageManagerFancy::StatusChanged(std::string PackageName,
346 unsigned int StepsDone,
347 unsigned int TotalSteps,
348 std::string HumanReadableAction)
349 {
350 if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps,
351 HumanReadableAction))
352 return false;
353
354 return DrawStatusLine();
355 }
356 bool PackageManagerFancy::DrawStatusLine()
357 {
358 PackageManagerFancy::TermSize const size = GetTerminalSize();
359 if (unlikely(size.rows < 1 || size.columns < 1))
360 return false;
361
362 static std::string save_cursor = "\0337";
363 static std::string restore_cursor = "\0338";
364
365 // green
366 static std::string set_bg_color = DeQuoteString(
367 _config->Find("Dpkg::Progress-Fancy::Progress-fg", "%1b[42m"));
368 // black
369 static std::string set_fg_color = DeQuoteString(
370 _config->Find("Dpkg::Progress-Fancy::Progress-bg", "%1b[30m"));
371
372 static std::string restore_bg = "\033[49m";
373 static std::string restore_fg = "\033[39m";
374
375 std::cout << save_cursor
376 // move cursor position to last row
377 << "\033[" << std::to_string(size.rows) << ";0f"
378 << set_bg_color
379 << set_fg_color
380 << progress_str
381 << restore_bg
382 << restore_fg;
383 std::flush(std::cout);
384
385 // draw text progress bar
386 if (_config->FindB("Dpkg::Progress-Fancy::Progress-Bar", true))
387 {
388 int padding = 4;
389 float progressbar_size = size.columns - padding - progress_str.size();
390 float current_percent = percentage / 100.0;
391 std::cout << " "
392 << GetTextProgressStr(current_percent, progressbar_size)
393 << " ";
394 std::flush(std::cout);
395 }
396
397 // restore
398 std::cout << restore_cursor;
399 std::flush(std::cout);
400
401 last_reported_progress = percentage;
402
403 return true;
404 }
405
406 bool PackageManagerText::StatusChanged(std::string PackageName,
407 unsigned int StepsDone,
408 unsigned int TotalSteps,
409 std::string HumanReadableAction)
410 {
411 if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps, HumanReadableAction))
412 return false;
413
414 std::cout << progress_str << "\r\n";
415 std::flush(std::cout);
416
417 last_reported_progress = percentage;
418
419 return true;
420 }
421
422 PackageManagerText::PackageManagerText() : PackageManager(), d(NULL) {}
423 PackageManagerText::~PackageManagerText() {}
424
425
426
427
428 } // namespace progress
429 } // namespace apt