Enable RTC sections for the gigabeat manual (Thanks to Sylvan Mably, FS#6924)
[Rockbox.git] / rbutil / rbutil.cpp
blobff01a659231b478e05bda5fddb6b6341dd3cc3a1
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * Module: rbutil
9 * File: rbutil.cpp
11 * Copyright (C) 2005 Christi Alice Scarborough
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
21 #include "rbutil.h"
22 #include "installlog.h"
25 // This class allows us to return directories as well as files to
26 // wxDir::Traverse
27 class wxDirTraverserIncludeDirs : public wxDirTraverser
29 public:
30 wxDirTraverserIncludeDirs(wxArrayString& files) : m_files(files) { }
32 virtual wxDirTraverseResult OnFile(const wxString& filename)
34 m_files.Add(filename);
35 return wxDIR_CONTINUE;
38 virtual wxDirTraverseResult OnDir(const wxString& dirname)
40 m_files.Add(dirname);
41 return wxDIR_CONTINUE;
44 private:
45 wxArrayString& m_files;
48 wxDEFINE_SCOPED_PTR_TYPE(wxZipEntry);
50 const wxChar* _rootmatch[] = {
51 wxT("rockbox.*"),
52 wxT("ajbrec.ajz"),
53 wxT("archos.mod"),
54 wxT(".scrobbler.*"),
55 wxT("battery_bench.txt"),
56 wxT("battery.dummy"),
58 const wxArrayString* rootmatch = new wxArrayString(
59 (size_t) (sizeof(_rootmatch) / sizeof(wxChar*)), _rootmatch);
61 bool InstallTheme(wxString Themesrc)
63 wxString dest,src,err;
65 int pos = Themesrc.Find('/',true);
66 wxString themename = Themesrc.SubString(pos+1,Themesrc.Length());
68 src.Printf(wxT("%s/%s"), gv->themes_url.c_str(),Themesrc.c_str());
69 dest.Printf(wxT("%s" PATH_SEP "download" PATH_SEP "%s"),
70 gv->stdpaths->GetUserDataDir().c_str(),themename.c_str());
71 if( DownloadURL(src, dest) )
73 wxRemoveFile(dest);
74 err.Printf(wxT("Unable to download %s"), src.c_str() );
75 ERR_DIALOG(err, wxT("Install Theme"));
76 return false;
79 if(!checkZip(dest))
81 err.Printf(wxT("The Zip %s does not contain the correct dir structure"), dest.c_str());
82 ERR_DIALOG(err, wxT("Install Theme"));
83 return false;
86 if(UnzipFile(dest,gv->curdestdir, true))
88 err.Printf(wxT("Unable to unzip %s to %s"), dest.c_str(), gv->curdestdir.c_str());
89 ERR_DIALOG(err, wxT("Install Theme"));
90 return false;
93 return true;
96 bool checkZip(wxString zipname)
98 wxZipEntryPtr entry;
100 wxFFileInputStream* in_file = new wxFFileInputStream(zipname);
101 wxZipInputStream* in_zip = new wxZipInputStream(*in_file);
103 entry.reset(in_zip->GetNextEntry());
105 wxString name = entry->GetName();
106 if(entry->IsDir())
108 if(name.Contains(wxT(".rockbox")))
110 return true;
113 return false;
116 int DownloadURL(wxString src, wxString dest)
118 int input, errnum = 0, success = false;
119 wxString buf, errstr;
120 wxLogVerbose(wxT("=== begin DownloadURL(%s,%s)"), src.c_str(),
121 dest.c_str());
123 buf.Printf(wxT("Fetching %s"), src.c_str());
124 wxProgressDialog* progress = new wxProgressDialog(wxT("Downloading"),
125 buf, 100, NULL, wxPD_APP_MODAL |
126 wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_ELAPSED_TIME |
127 wxPD_REMAINING_TIME | wxPD_CAN_ABORT);
128 progress->Update(0);
129 progress->SetSize(500,200);
131 input = true;
132 wxURL* in_http = new wxURL(src);
133 if (in_http->GetError() == wxURL_NOERR)
136 wxFFileOutputStream* os = new wxFFileOutputStream(dest);
137 input = false;
138 if (os->IsOk())
140 wxInputStream* is = in_http->GetInputStream();
141 input = true;
142 if (is)
144 size_t filesize = is->GetSize();
145 input = true;
146 if (is->IsOk())
148 char buffer[FILE_BUFFER_SIZE + 1];
149 size_t current = 0;
151 while (! is->Eof())
153 is->Read(buffer, FILE_BUFFER_SIZE);
154 input = true;
155 if (is->LastRead() )
157 os->Write(buffer, is->LastRead());
158 input = false;
159 if (os->IsOk())
161 current += os->LastWrite();
162 if (!progress->Update(current * 100 / filesize))
164 errstr = wxT("Download aborted by user");
165 errnum = 1000;
166 break;
169 } else
171 errnum = os->GetLastError();
172 errstr.Printf(wxT("Can't write to output stream (%s)"),
173 stream_err_str(errnum).c_str() );
175 break;
178 } else
180 errnum = is->GetLastError();
181 if (errnum == wxSTREAM_EOF)
183 errnum = 0;
184 break;
186 errstr.Printf(wxT("Can't read from input stream (%s)"),
187 stream_err_str(errnum).c_str() );
191 os->Close();
192 if (! errnum)
194 errnum = os->GetLastError();
195 errstr.Printf(wxT("Can't close output file (%s)"),
196 stream_err_str(errnum).c_str() );
198 input = false;
201 if (! errnum) success = true;
203 } else
205 errnum = is->GetLastError();
206 errstr.Printf(wxT("Can't get input stream size (%s)"),
207 stream_err_str(errnum).c_str() );
210 } else
212 errnum = in_http->GetError();
213 errstr.Printf(wxT("Can't get input stream (%d)"), errnum);
215 delete is;
216 } else
218 errnum = os->GetLastError();
219 errstr.Printf(wxT("Can't create output stream (%s)"),
220 stream_err_str(errnum).c_str() );
222 delete os;
223 } else
225 errstr.Printf(wxT("Can't open URL %s (%d)"), src.c_str(),
226 in_http->GetError() );
227 errnum = 100;
230 delete in_http;
231 delete progress;
233 if (!success)
235 if (errnum == 0) errnum = 999;
236 if (input)
238 buf.Printf(wxT("%s reading\n%s"),
239 errstr.c_str(), src.c_str());
240 ERR_DIALOG(buf, wxT("Download URL"));
241 } else
243 buf.Printf(wxT("%s writing to download\n/%s"),
244 errstr.c_str(), dest.c_str());
245 ERR_DIALOG(buf, wxT("Download URL"));
250 wxLogVerbose(wxT("=== end DownloadURL"));
251 return errnum;
254 int UnzipFile(wxString src, wxString destdir, bool isInstall)
256 wxZipEntryPtr entry;
257 wxString in_str, progress_msg, buf;
258 int errnum = 0, curfile = 0, totalfiles = 0;
259 InstallLog* log = NULL;
261 wxLogVerbose(wxT("===begin UnzipFile(%s,%s,%i)"),
262 src.c_str(), destdir.c_str(), isInstall);
264 wxFFileInputStream* in_file = new wxFFileInputStream(src);
265 wxZipInputStream* in_zip = new wxZipInputStream(*in_file);
266 if (in_file->Ok() )
268 if (! in_zip->IsOk() )
270 errnum = in_zip->GetLastError();
271 buf.Printf(wxT("Can't open ZIP stream %s for reading (%s)"),
272 src.c_str(), stream_err_str(errnum).c_str() );
273 ERR_DIALOG(buf, wxT("Unzip File") );
274 delete in_zip;
275 delete in_file;
276 return true;
279 totalfiles = in_zip->GetTotalEntries();
280 if (! in_zip->IsOk() )
282 errnum = in_zip->GetLastError();
283 buf.Printf(wxT("Error Getting total ZIP entries for %s (%s)"),
284 src.c_str(), stream_err_str(errnum).c_str() );
285 ERR_DIALOG(buf, wxT("Unzip File") );
286 delete in_zip;
287 delete in_file;
288 return true;
290 } else
292 errnum = in_file->GetLastError();
293 buf.Printf(wxT("Can't open %s (%s)"), src.c_str(),
294 stream_err_str(errnum).c_str() );
295 ERR_DIALOG(buf, wxT("Unzip File") );
296 delete in_zip;
297 delete in_file;
298 return true;
301 wxProgressDialog* progress = new wxProgressDialog(wxT("Unpacking archive"),
302 wxT("Preparing to unpack the downloaded files to your audio"
303 "device"), totalfiles, NULL, wxPD_APP_MODAL |
304 wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_ELAPSED_TIME |
305 wxPD_REMAINING_TIME | wxPD_CAN_ABORT);
306 progress->Update(0);
308 // We're not overly worried if the logging fails
309 if (isInstall)
311 buf.Printf(wxT("%s" PATH_SEP UNINSTALL_FILE), destdir.c_str());
312 log = new InstallLog(buf);
315 while (! errnum &&
316 (entry.reset(in_zip->GetNextEntry()), entry.get() != NULL) )
319 curfile++;
320 wxString name = entry->GetName();
321 progress_msg.Printf(wxT("Unpacking %s"), name.c_str());
322 if (! progress->Update(curfile, progress_msg) ) {
323 buf.Printf(wxT("Unpacking cancelled by user"));
324 MESG_DIALOG(buf);
325 errnum = 1000;
326 break;
329 in_str.Printf(wxT("%s" PATH_SEP "%s"), destdir.c_str(), name.c_str());
331 if (entry->IsDir() ) {
332 if (!wxDirExists(in_str) ) {
333 if (! wxMkdir(in_str, 0777) ) {
334 buf.Printf(wxT("Unable to create directory %s"),
335 in_str.c_str() );
336 errnum = 100;
337 break;
340 log->WriteFile(name, true); // Directory
341 continue;
344 wxFFileOutputStream* out = new wxFFileOutputStream(in_str);
345 if (! out->IsOk() )
347 buf.Printf(wxT("Can't open file %s for writing"), in_str.c_str() );
348 delete out;
349 return 100;
350 } else if (isInstall)
352 log->WriteFile(name);
355 in_zip->Read(*out);
356 if (! out->IsOk()) {
357 buf.Printf(wxT("Can't write to %s (%d)"), in_str.c_str(),
358 errnum = out->GetLastError() );
361 if (!in_zip->IsOk() && ! in_file->GetLastError() == wxSTREAM_EOF)
363 buf.Printf(wxT("Can't read from %s (%d)"), src.c_str(),
364 errnum = in_file->GetLastError() );
367 if (! out->Close() && errnum == 0)
369 buf.Printf(wxT("Unable to close %s (%d)"), in_str.c_str(),
370 errnum = out->GetLastError() );
374 delete out;
378 delete in_zip; delete in_file; delete progress;
380 if (errnum)
382 ERR_DIALOG(buf, wxT("Unzip File"));
385 if (log) delete log;
386 wxLogVerbose(wxT("=== end UnzipFile"));
387 return(errnum);
390 int Uninstall(const wxString dir, bool isFullUninstall) {
391 wxString buf, uninst;
392 unsigned int i;
393 bool errflag = false;
394 InstallLog *log = NULL;
395 wxArrayString* FilesToRemove = NULL;
397 wxLogVerbose(wxT("=== begin Uninstall(%s,%i)"), dir.c_str(), isFullUninstall);
399 wxProgressDialog* progress = new wxProgressDialog(wxT("Uninstalling"),
400 wxT("Reading uninstall data from jukebox"), 100, NULL,
401 wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH |
402 wxPD_ELAPSED_TIME | wxPD_REMAINING_TIME | wxPD_CAN_ABORT);
403 progress->Update(0);
405 if (! isFullUninstall)
408 buf.Printf(wxT("%s" PATH_SEP UNINSTALL_FILE), dir.c_str());
409 log = new InstallLog(buf, false); // Don't create the log
410 FilesToRemove = log->GetInstalledFiles();
411 if (log) delete log;
413 if (FilesToRemove == NULL || FilesToRemove->GetCount() < 1) {
414 wxLogNull lognull;
415 if ( wxMessageDialog(NULL,
416 wxT("Rockbox Utility can't find any uninstall data on this "
417 "jukebox.\n"
418 "Would you like to attempt a full uninstall?\n"
419 "(WARNING: A full uninstall removes all files in your Rockbox "
420 "folder)"),
421 wxT("Standard uninstall not possible"),
422 wxICON_EXCLAMATION | wxYES_NO | wxNO_DEFAULT).ShowModal()
423 == wxID_YES)
425 isFullUninstall = true;
427 else {
428 MESG_DIALOG(wxT("Uninstall cancelled by user"));
429 delete progress;
430 return 1000;
435 if (isFullUninstall )
437 buf.Printf(wxT("%s" PATH_SEP ".rockbox"), dir.c_str());
438 if (rm_rf(buf) )
440 WARN_DIALOG(wxT("Unable to completely remove Rockbox directory"),
441 wxT("Full uninstall") );
442 errflag = true;
445 wxDir* root = new wxDir(dir);
446 wxArrayString* special = new wxArrayString();
447 // Search for files for deletion in the jukebox root
448 for (i = 0; i < rootmatch->GetCount(); i++)
450 const wxString match = (*rootmatch)[i];
451 root->GetAllFiles(dir, special, match, wxDIR_FILES);
453 delete root;
455 // Sort in reverse order so we get directories last
456 special->Sort(true);
458 for (i = 0; i < special->GetCount(); i++)
461 if (wxDirExists((*special)[i]) )
463 // We don't check the return code since we don't want non
464 // empty dirs disappearing.
465 wxRmdir((*special)[i]);
467 } else if (wxFileExists((*special)[i]) )
469 if (! wxRemoveFile((*special)[i]) )
471 buf.Printf(wxT("Can't delete %s"), (*special)[i].c_str());
472 WARN_DIALOG(buf.c_str(), wxT("Full uninstall"));
473 errflag = true;
476 // Otherwise there isn't anything there, so we don't have to worry.
478 delete special;
479 } else
481 wxString instplat, this_path_sep;
482 unsigned int totalfiles, rc;
483 totalfiles = FilesToRemove->GetCount();
484 FilesToRemove->Sort(true); // Reverse alphabetical ie dirs after files
486 for (i = 0; i < totalfiles; i++)
488 // If we're running on the device, let's not delete our own
489 // installation, eh?
490 if (gv->portable &&
491 FilesToRemove->Item(i).StartsWith(PATH_SEP
492 wxT("RockboxUtility")) )
494 continue;
497 wxString* buf2 = new wxString;
498 buf.Printf(wxT("%s%s"), dir.c_str() , FilesToRemove->Item(i).c_str() );
499 buf2->Format(wxT("Deleting %s"), buf.c_str());
501 if (! progress->Update((i + 1) * 100 / totalfiles, *buf2) )
503 WARN_DIALOG(wxT("Cancelled by user"), wxT("Normal Uninstall"));
504 delete progress;
505 return true;
508 if (wxDirExists(buf) )
510 // If we're about to attempt to remove .rockbox. delete
511 // install data first
512 buf2->Printf(wxT("%s" PATH_SEP ".rockbox"), dir.c_str() );
513 if ( buf.IsSameAs(buf2->c_str()) )
515 buf2->Printf(wxT("%s" PATH_SEP UNINSTALL_FILE), dir.c_str());
516 wxRemoveFile(*buf2);
519 if ( (rc = ! wxRmdir(buf)) )
521 buf = buf.Format(wxT("Can't remove directory %s"),
522 buf.c_str());
523 errflag = true;
524 WARN_DIALOG(buf.c_str(), wxT("Standard uninstall"));
526 } else if (wxFileExists(buf) )
528 if ( (rc = ! wxRemoveFile(buf)) )
530 buf = buf.Format(wxT("Can't delete file %s"),
531 buf.c_str());
532 errflag = true;
533 WARN_DIALOG(buf.c_str(), wxT("Standard uninstall"));
535 } else
537 errflag = true;
538 buf = buf.Format(wxT("Can't find file or directory %s"),
539 buf.c_str() );
540 WARN_DIALOG(buf.c_str(), wxT("Standard uninstall") );
543 uninst = uninst.AfterFirst('\n');
545 if (errflag)
547 ERR_DIALOG(wxT("Unable to remove some files"),
548 wxT("Standard uninstall")) ;
551 if (FilesToRemove != NULL) delete FilesToRemove;
554 delete progress;
555 wxLogVerbose(wxT("=== end Uninstall"));
556 return errflag;
560 wxString stream_err_str(int errnum)
562 wxString out;
564 switch (errnum) {
565 case wxSTREAM_NO_ERROR:
566 out = wxT("wxSTREAM_NO_ERROR");
567 break;
568 case wxSTREAM_EOF:
569 out = wxT("wxSTREAM_EOF");
570 break;
571 case wxSTREAM_WRITE_ERROR:
572 out = wxT("wxSTREAM_WRITE_ERROR");
573 break;
574 case wxSTREAM_READ_ERROR:
575 out = wxT("wxSTREAM_READ_ERROR");
576 break;
577 default:
578 out = wxT("UNKNOWN");
579 break;
581 return out;
584 bool InstallRbutil(wxString dest)
586 wxArrayString filestocopy;
587 wxString str, buf, dstr, localpath, destdir;
588 unsigned int i;
589 wxDir dir;
590 bool copied_exe = false, made_rbdir = false;
591 InstallLog* log;
593 buf.Printf(wxT("%s" PATH_SEP ".rockbox"), dest.c_str() );
595 if (! wxDirExists(buf) )
597 wxMkdir(buf);
598 made_rbdir = true;
601 buf.Printf(wxT("%s" PATH_SEP UNINSTALL_FILE), dest.c_str() );
602 log = new InstallLog(buf);
603 if (made_rbdir) log->WriteFile(wxT(".rockbox"), true);
605 destdir.Printf(wxT("%s" PATH_SEP "RockboxUtility"), dest.c_str());
606 if (! wxDirExists(destdir) )
608 if (! wxMkdir(destdir, 0777) )
610 buf.Printf(wxT("%s (%s)"),
611 wxT("Unable to create directory for installer"), destdir.c_str());
612 WARN_DIALOG(buf , wxT("Portable install") );
613 return false;
615 log->WriteFile(wxT("RockboxUtility"), true);
618 dir.GetAllFiles(gv->ResourceDir, &filestocopy, wxT("*"),
619 wxDIR_FILES);
620 if (filestocopy.GetCount() < 1)
622 WARN_DIALOG(wxT("No files to copy"), wxT("Portable install") );
623 return false;
626 // Copy the contents of the program directory
627 for (i = 0; i < filestocopy.GetCount(); i++)
629 if (filestocopy[i].AfterLast(PATH_SEP_CHR) == EXE_NAME)
631 copied_exe = true;
634 dstr.Printf(wxT("%s" PATH_SEP "%s"), destdir.c_str(),
635 filestocopy[i].AfterLast(PATH_SEP_CHR).c_str());
636 if (! wxCopyFile(filestocopy[i], dstr) )
638 buf.Printf(wxT("%s (%s -> %s)"),
639 wxT("Error copying file"), filestocopy[i].c_str(), dstr.c_str());
640 WARN_DIALOG(buf, wxT("Portable Install") );
641 return false;
643 buf = dstr;
644 buf.Replace(dest, wxEmptyString, false);
645 log->WriteFile(buf);
648 if (! copied_exe)
650 str.Printf(wxT("%s" PATH_SEP EXE_NAME), gv->AppDir.c_str());
651 dstr.Printf(wxT("%s" PATH_SEP EXE_NAME), destdir.c_str());
652 if (! wxCopyFile(str, dstr) )
654 buf.Printf(wxT("Can't copy program binary %s -> %s"),
655 str.c_str(), dstr.c_str() );
656 WARN_DIALOG(buf, wxT("Portable Install") );
657 return false;
659 buf = dstr;
660 buf.Replace(dest, wxEmptyString, false);
661 log->WriteFile(buf);
664 // Copy the local ini file so that it knows that it's a portable copy
665 gv->UserConfig->Flush();
666 dstr.Printf(wxT("%s" PATH_SEP "RockboxUtility.cfg"), destdir.c_str());
667 if (! wxCopyFile(gv->UserConfigFile, dstr) )
669 buf.Printf(wxT("%s (%s -> %s)"),
670 wxT("Unable to install user config file"), gv->UserConfigFile.c_str(),
671 dstr.c_str() );
672 WARN_DIALOG(buf, wxT("Portable Install") );
673 return false;
675 buf = dstr;
676 buf.Replace(dest, wxEmptyString, false);
677 log->WriteFile(buf);
679 delete log;
680 return true;
683 bool rm_rf(wxString file)
685 wxLogVerbose(wxT("=== begin rm-rf(%s)"), file.c_str() );
687 wxString buf;
688 wxArrayString selected;
689 wxDirTraverserIncludeDirs wxdtid(selected);
690 unsigned int rc = 0, i;
691 bool errflag = false;
693 if (wxFileExists(file) )
695 rc = ! wxRemoveFile(file);
696 } else if (wxDirExists(file) )
698 wxDir* dir = new wxDir(file);;
699 dir->Traverse(wxdtid);
700 delete dir;
701 // Sort into reverse alphabetical order for deletion in correct order
702 // (directories after files)
703 selected.Sort(true);
704 selected.Add(file);
706 wxProgressDialog* progress = new wxProgressDialog(wxT("Removing files"),
707 wxT("Deleting files"), selected.GetCount(), NULL,
708 wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH |
709 wxPD_ELAPSED_TIME | wxPD_REMAINING_TIME | wxPD_CAN_ABORT);
711 for (i = 0; i < selected.GetCount(); i++)
713 wxLogVerbose(selected[i]);
714 if (progress != NULL)
716 buf.Printf(wxT("Deleting %s"), selected[i].c_str() );
717 if (! progress->Update(i, buf))
719 WARN_DIALOG(wxT("Cancelled by user"), wxT("Erase Files"));
720 delete progress;
721 return true;
725 if (wxDirExists(selected[i]) )
727 if ((rc = ! wxRmdir(selected[i])) )
729 buf.Printf(wxT("Can't remove directory %s"),
730 selected[i].c_str());
731 errflag = true;
732 WARN_DIALOG(buf.c_str(), wxT("Erase files"));
734 } else if ((rc = ! wxRemoveFile(selected[i])) )
736 buf.Printf(wxT("Error deleting file %s"), selected[i].c_str() );
737 errflag = true;
738 WARN_DIALOG(buf.c_str(),wxT("Erase files"));
741 delete progress;
742 } else
744 buf.Printf(wxT("Can't find expected file %s"), file.c_str());
745 WARN_DIALOG(buf.c_str(), wxT("Erase files"));
746 return true;
749 wxLogVerbose(wxT("=== end rm-rf"));
750 return rc ? true : false;