1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4
5 #include <IceUtil/Options.h>
6 #include <IceUtil/StringUtil.h>
7 #include <Ice/Ice.h>
8 #include <Ice/ConsoleUtil.h>
9 #include <IcePatch2Lib/Util.h>
10 #include <IcePatch2/ClientUtil.h>
11
12 #ifdef _WIN32
13 # include <conio.h>
14 #else
15 # include <fcntl.h>
16 # include <termios.h>
17 #endif
18
19 using namespace std;
20 using namespace IceInternal;
21
22 class TextPatcherFeedback : public IcePatch2::PatcherFeedback
23 {
24 public:
25
TextPatcherFeedback()26 TextPatcherFeedback() :
27 _pressAnyKeyMessage(false)
28 {
29 #ifndef _WIN32
30 tcgetattr(0, &_savedTerm);
31 _savedFlags = fcntl(0, F_GETFL);
32 _block = true;
33 #endif
34 }
35
~TextPatcherFeedback()36 virtual ~TextPatcherFeedback()
37 {
38 #ifndef _WIN32
39 tcsetattr(0, TCSANOW, &_savedTerm);
40 fcntl(0, F_SETFL, _savedFlags);
41 #endif
42 }
43
44 virtual bool
noFileSummary(const string & reason)45 noFileSummary(const string& reason)
46 {
47 consoleOut << "Cannot load file summary:\n" << reason << endl;
48 string answer;
49 do
50 {
51 consoleOut << "Do a thorough patch? (yes/no)" << endl;
52 cin >> answer;
53 answer = IceUtilInternal::toLower(answer);
54 if(answer == "no")
55 {
56 return false;
57 }
58 }
59 while(answer != "yes");
60 return true;
61 }
62
63 virtual bool
checksumStart()64 checksumStart()
65 {
66 if(!_pressAnyKeyMessage)
67 {
68 consoleOut << "[Press any key to abort]" << endl;
69 _pressAnyKeyMessage = true;
70 }
71
72 return !keyPressed();
73 }
74
75 virtual bool
checksumProgress(const string & path)76 checksumProgress(const string& path)
77 {
78 consoleOut << "Calculating checksum for " << IcePatch2Internal::getBasename(path) << endl;
79 return !keyPressed();
80 }
81
82 virtual bool
checksumEnd()83 checksumEnd()
84 {
85 return !keyPressed();
86 }
87
88 virtual bool
fileListStart()89 fileListStart()
90 {
91 if(!_pressAnyKeyMessage)
92 {
93 consoleOut << "[Press any key to abort]" << endl;
94 _pressAnyKeyMessage = true;
95 }
96
97 _lastProgress = "0%";
98 consoleOut << "Getting list of files to patch: " << _lastProgress << flush;
99 return !keyPressed();
100 }
101
102 virtual bool
fileListProgress(Ice::Int percent)103 fileListProgress(Ice::Int percent)
104 {
105 for(unsigned int i = 0; i < _lastProgress.size(); ++i)
106 {
107 consoleOut << '\b';
108 }
109 ostringstream s;
110 s << percent << '%';
111 _lastProgress = s.str();
112 consoleOut << _lastProgress << flush;
113 return !keyPressed();
114 }
115
116 virtual bool
fileListEnd()117 fileListEnd()
118 {
119 consoleOut << endl;
120 return !keyPressed();
121 }
122
123 virtual bool
patchStart(const string & path,Ice::Long size,Ice::Long totalProgress,Ice::Long totalSize)124 patchStart(const string& path, Ice::Long size, Ice::Long totalProgress, Ice::Long totalSize)
125 {
126 if(!_pressAnyKeyMessage)
127 {
128 consoleOut << "[Press any key to abort]" << endl;
129 _pressAnyKeyMessage = true;
130 }
131
132 ostringstream s;
133 s << "0/" << size << " (" << totalProgress << '/' << totalSize << ')';
134 _lastProgress = s.str();
135 consoleOut << IcePatch2Internal::getBasename(path) << ' ' << _lastProgress << flush;
136 return !keyPressed();
137 }
138
139 virtual bool
patchProgress(Ice::Long progress,Ice::Long size,Ice::Long totalProgress,Ice::Long totalSize)140 patchProgress(Ice::Long progress, Ice::Long size, Ice::Long totalProgress, Ice::Long totalSize)
141 {
142 for(unsigned int i = 0; i < _lastProgress.size(); ++i)
143 {
144 consoleOut << '\b';
145 }
146 ostringstream s;
147 s << progress << '/' << size << " (" << totalProgress << '/' << totalSize << ')';
148 _lastProgress = s.str();
149 consoleOut << _lastProgress << flush;
150 return !keyPressed();
151 }
152
153 virtual bool
patchEnd()154 patchEnd()
155 {
156 consoleOut << endl;
157 return !keyPressed();
158 }
159
160 private:
161
162 #ifdef _WIN32
163
164 bool
keyPressed()165 keyPressed()
166 {
167 bool pressed = false;
168 while(_kbhit())
169 {
170 pressed = true;
171 _getch();
172 }
173 if(pressed)
174 {
175 pressed = confirmAbort();
176 }
177 return pressed;
178 }
179
180 #else
181
182 bool
183 keyPressed()
184 {
185 if(_block)
186 {
187 termios term;
188 memcpy(&term, &_savedTerm, sizeof(termios));
189 term.c_lflag &= ~(ECHO | ICANON);
190 term.c_cc[VTIME] = 0;
191 term.c_cc[VMIN] = 1;
192 tcsetattr(0, TCSANOW, &term);
193
194 int flags = _savedFlags;
195 flags |= O_NONBLOCK;
196 fcntl(0, F_SETFL, flags);
197
198 _block = false;
199 }
200
201 bool pressed = false;
202 char c;
203 while(read(0, &c, 1) > 0)
204 {
205 pressed = true;
206 }
207 if(pressed)
208 {
209 pressed = confirmAbort();
210 }
211 return pressed;
212 }
213
214 termios _savedTerm;
215 int _savedFlags;
216 bool _block;
217
218 #endif
219
220 bool
confirmAbort()221 confirmAbort()
222 {
223 #ifndef _WIN32
224 if(!_block)
225 {
226 tcsetattr(0, TCSANOW, &_savedTerm);
227 fcntl(0, F_SETFL, _savedFlags);
228 _block = true;
229 }
230 #endif
231 string answer;
232 do
233 {
234 consoleOut << "\nConfirm abort? (Y/N)" << endl;
235 cin >> answer;
236 answer = IceUtilInternal::toLower(answer);
237 if(answer == "n")
238 {
239 return false;
240 }
241 }
242 while(answer != "y");
243 return true;
244 }
245
246 string _lastProgress;
247 bool _pressAnyKeyMessage;
248 };
249
250 int run(const Ice::StringSeq&);
251
252 Ice::CommunicatorPtr communicator;
253
254 void
destroyCommunicator(int)255 destroyCommunicator(int)
256 {
257 communicator->destroy();
258 }
259
260 int
261 #ifdef _WIN32
wmain(int argc,wchar_t * argv[])262 wmain(int argc, wchar_t* argv[])
263 #else
264 main(int argc, char* argv[])
265 #endif
266 {
267 int status = 0;
268
269 try
270 {
271 Ice::CtrlCHandler ctrlCHandler;
272 Ice::CommunicatorHolder ich(argc, argv);
273 communicator = ich.communicator();
274
275 ctrlCHandler.setCallback(&destroyCommunicator);
276
277 status = run(Ice::argsToStringSeq(argc, argv));
278 }
279 catch(const std::exception& ex)
280 {
281 consoleErr << ex.what() << endl;
282 status = 1;
283 }
284
285 return status;
286 }
287
288 void
usage(const string & appName)289 usage(const string& appName)
290 {
291 string options =
292 "Options:\n"
293 "-h, --help Show this message.\n"
294 "-v, --version Display the Ice version.\n"
295 "-t, --thorough Recalculate all checksums.";
296
297 consoleErr << "Usage: " << appName << " [options] [DIR]" << endl;
298 consoleErr << options << endl;
299 }
300
301 int
run(const Ice::StringSeq & args)302 run(const Ice::StringSeq& args)
303 {
304 Ice::PropertiesPtr properties = communicator->getProperties();
305
306 IceUtilInternal::Options opts;
307 opts.addOpt("h", "help");
308 opts.addOpt("v", "version");
309 opts.addOpt("t", "thorough");
310
311 vector<string> props;
312 try
313 {
314 props = opts.parse(args);
315 }
316 catch(const IceUtilInternal::BadOptException& e)
317 {
318 consoleErr << e.reason << endl;
319 usage(args[0]);
320 return 1;
321 }
322
323 if(opts.isSet("help"))
324 {
325 usage(args[0]);
326 return 0;
327 }
328 if(opts.isSet("version"))
329 {
330 consoleOut << ICE_STRING_VERSION << endl;
331 return 0;
332 }
333 if(opts.isSet("thorough"))
334 {
335 properties->setProperty("IcePatch2Client.Thorough", "1");
336 }
337
338 if(props.size() > 1)
339 {
340 consoleErr << args[0] << ": too many arguments" << endl;
341 usage(args[0]);
342 return 1;
343 }
344 if(props.size() == 1)
345 {
346 properties->setProperty("IcePatch2Client.Directory", IcePatch2Internal::simplify(props[0]));
347 }
348
349 bool aborted = false;
350
351 try
352 {
353 IcePatch2::PatcherFeedbackPtr feedback = new TextPatcherFeedback;
354 IcePatch2::PatcherPtr patcher = IcePatch2::PatcherFactory::create(communicator, feedback);
355
356 aborted = !patcher->prepare();
357
358 if(!aborted)
359 {
360 aborted = !patcher->patch("");
361 }
362
363 if(!aborted)
364 {
365 patcher->finish();
366 }
367 }
368 catch(const exception& ex)
369 {
370 consoleErr << args[0] << ": " << ex.what() << endl;
371 return 1;
372 }
373
374 if(aborted)
375 {
376 consoleOut << "\n[Aborted]" << endl;
377 return 1;
378 }
379 else
380 {
381 return 0;
382 }
383 }
384