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