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