#include "fileCopier.h" #include #include //#include #include #include fileCopySet::fileCopySet() { // do we need this - they're FilePath's, not pointers // this->sourceDir = NULL; // this->destDir = NULL; } fileCopySet::fileCopySet(string label, FileVector files, FilePath &from, FilePath &to) { this->fileList = files; this->sourceDir = from; this->label = label; this->destDir = to; this->dataSize = 0; this->dataFiles = 0; FileVector::iterator here = files.begin(); FileVector::iterator end = files.end(); while (here != end) { unsigned long size, files; here->getFileSize(size, files); this->dataSize += size; this->dataFiles += files; here ++; } } /** * Get the destination file name for a given source file; source and destination * directory locations are obtained from the object */ char *fileCopySet::destination(File *file) { const char *oldname = file->cString(); char *newname = (char *) malloc(strlen(oldname) - strlen(this->sourceDir.cString()) + strlen(this->destDir.cString()) + 1); if (newname != NULL) { strcpy(newname, this->destDir.cString()); strcat(newname, &oldname[strlen(this->sourceDir.cString())]); } return newname; } bool fileCopySet::ensureParent(FilePath &child, fileCopyMonitor *monitor) { FilePath *parentName = child.parent(); // check for existence of parent if (parentName->exists() == false) { // ensure the grandparent exists if (!this->ensureParent(*parentName, monitor)) { return false; } // create parent directory _mkdir(parentName->cString()); // log the creation - we don't give a source - it's not needed for directories if (monitor != NULL) { monitor->createdDir(parentName->cString()); } } delete parentName; return true; } /** * Copy a particular file to its destination */ bool fileCopySet::copyFile(File *file, fileCopyMonitor *monitor, copyProgressBar *progressBar) { unsigned long copied = 0; unsigned long bytes_read, bytes_written; static unsigned char buffer[65536]; FILE *read_handle, *write_handle; char *destination; // get destination as a char[] block; will free it later destination = this->destination(file); // ensure that the parent exists, as some install requirements are for files with // directory paths which may not preexist FilePath destPath(destination); this->ensureParent(destPath, monitor); // get read and write file handles read_handle = fopen(file->cString(), "rb"); if (read_handle == NULL) { return false; } write_handle = fopen(destination, "wb"); if (write_handle == NULL) { fclose(read_handle); return false; } while (copied < file->getFileSize()) { // TODO: lock // read so many bytes if (file->getFileSize() - copied < 65536) { bytes_read = file->getFileSize() - copied; } else { bytes_read = 65536; } bytes_read = fread((void *) buffer, 1, bytes_read, read_handle); // write so many bytes bytes_written = fwrite((void *) buffer, 1, bytes_read, write_handle); // TODO: unlock // note new bytes copied copied += bytes_written; // this->dataSize += bytes_written; } fclose(read_handle); fclose(write_handle); // this->dataFiles += 1; // free destination filename text if (monitor != NULL) { monitor->copiedFile(file->cString(), destination); } delete destination; return true; } /** * Copy a file "object" (may be a directory or a "proper" file) to it's * destination */ void fileCopySet::copy(File *file, fileCopyMonitor *monitor, copyProgressBar *progressBar) { // just skip this file if it doesn't exist if (file->exists() == false) { return; } // if a directory, scan it. if (file->isDirectory()) { // get destination directory char *thisDestDir = this->destination(file); FilePath destPath(thisDestDir); // create a directory if needsbe if (destPath.exists() == false) { _mkdir(thisDestDir); // note amount of data copied // this->copied += file->getRawFileSize(); // note for uninstall the action done if (monitor != NULL) { monitor->copiedDir(destPath.pathString()); } } // copy children vector::iterator here = file->childBegin(); vector::iterator end = file->childEnd(); while (here != end) { this->copy(here, monitor, progressBar); here ++; } // delete constructed destination name delete thisDestDir; } // not a directory; just copy the file else { // copy file itself this->copyFile(file, monitor, progressBar); } if (progressBar != NULL) { progressBar->done(file->getRawFileSize()); } } void fileCopySet::copy(fileCopyMonitor *monitor, copyProgressBar *progressBar) { FileVector::iterator here = fileList.begin(); FileVector::iterator end = fileList.end(); while (here != end) { this->copy(here, monitor, progressBar); here ++; } } unsigned double fileCopySet::getCopiedSize(DiskSpace &space) { // initialise counters for size unsigned long copiedSize = 0; // iterate through our list of files to obtain total space requirement FileVector::iterator here = fileList.begin(); FileVector::iterator end = fileList.end(); while (here != end) { copiedSize += here->getDiskSpace(space); here ++; } return (double) copiedSize / (double) 1024; } unsigned long fileCopySet::getOriginalSize() { // initialise counters for size return this->dataSize; } unsigned long fileCopySet::getOriginalFiles() { // initialise counters for size return this->dataFiles; } /** * Perform a check on the total space available at the destination; if not * enough then return false to indicate the error state */ bool fileCopier::checkSpace() { fileCopySetList localSet = this->list; this->progressBar.message("Checking disk space requirements"); do { unsigned int i = 0; // get destination root and use it to obtain space check information FilePath *destRoot = localSet[0].destDir.root(); DiskSpace space(destRoot->cString()); // check that diskspace checking is functional if (space.initialisedOk() == false) { delete destRoot; return false; } // get size of the first set unsigned long copiedSize = localSet[0].getCopiedSize(space); // dispose of the first set localSet.erase(localSet.begin()); // get all remaining sets and in turn if they share the root with // the first root, add their size to the count and remove them from // the local list of sets; otherwise skip while (i < localSet.size()) { // get a new root object for this set's destination FilePath *p = localSet[i].destDir.root(); if (*destRoot == *p) { copiedSize += localSet[i].getCopiedSize(space); localSet.erase(localSet.begin() + i); } else { i ++; } // delete the constructed root delete p; } // dispose of destination root delete destRoot; // return false if not enough space if (space.totalFreeSpace() < copiedSize) { char buffer[100]; sprintf(buffer, "Total free space on disk %3.1fMb, but the size of the copied collection is %3.1fMb", (double) space.totalFreeSpace() / (double) (1024), (double) copiedSize / (double) (1024)); MessageBox(0, buffer, "Greenstone Installer", MB_OK); return false; } } while (localSet.size() != 0); // record size and initialise the progressbar with the same this->dataSize = 0; this->dataFiles = 0; for (unsigned int i = 0; i < this->list.size(); i ++) { this->dataSize += this->list[i].getOriginalSize(); this->dataFiles += this->list[i].getOriginalFiles(); } this->progressBar.init(this->dataSize, this->dataFiles); // return postive check return true; } fileCopier::fileCopier(fileCopyMonitor *monitor, fileCopySetList &_list) { this->list = _list; this->dataSize = 0; this->copied = 0; this->dataFiles = 0; this->monitor = monitor; this->progressBar.show(); this->progressBar.message("Initialising"); } fileCopier::fileCopier(fileCopyMonitor *monitor) { this->dataSize = 0; this->copied = 0; this->dataFiles = 0; this->monitor = monitor; this->progressBar.show(); this->progressBar.message("Initialising"); } void fileCopier::addSet(fileCopySet &set) { this->list.push_back(set); this->progressBar.message("Gathering files to copy"); } // Do the actual file copy void fileCopier::copy() { fileCopySetList::iterator here = list.begin(); fileCopySetList::iterator end = list.end(); // Tell the user what is happening char buffer[100]; sprintf(buffer, "Copying %3.1fMb of files", (double) this->dataSize / (double) (1024 * 1024)); this->progressBar.message(buffer); this->progressBar.setStartTime(); // Copy each file set individually, using the progress bar as a monitor while (here != end) { here->copy(this->monitor, &this->progressBar); here ++; } // Close the progress bar, as it is no longer needed this->progressBar.close(); }