File indexing completed on 2024-05-12 16:23:26
0001 /*************************************************************************** 0002 * Copyright (C) 2013-2017 by Linuxstopmotion contributors; * 0003 * see the AUTHORS file for details. * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; if not, write to the * 0017 * Free Software Foundation, Inc., * 0018 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 0019 ***************************************************************************/ 0020 0021 #include "workspacefile.h" 0022 0023 #include "src/technical/util.h" 0024 #include "src/foundation/uiexception.h" 0025 0026 #include <stdio.h> 0027 #include <stdlib.h> 0028 #include <unistd.h> 0029 #include <string.h> 0030 #include <sstream> 0031 #include <string> 0032 0033 namespace { 0034 0035 uint32_t fileNum; 0036 uint32_t soundNumber; 0037 0038 uint32_t nextFileNumber() { 0039 return ++fileNum; 0040 } 0041 0042 enum WorkspacePath { workspacePath }; 0043 enum WorkspacePathFrames { workspacePathFrames }; 0044 0045 std::ostream& operator<<(std::ostream& s, WorkspacePath) { 0046 s << getenv("HOME"); 0047 s << "/.stopmotion/"; 0048 return s; 0049 } 0050 0051 std::ostream& operator<<(std::ostream& s, WorkspacePathFrames) { 0052 s << getenv("HOME"); 0053 s << "/.stopmotion/frames/"; 0054 return s; 0055 } 0056 0057 /** 0058 * Gets a fresh filename in the workspace that doesn't clash with any other 0059 * file. 0060 * @param [out] path Will get a @code{.cpp} new char[] @endcode containing 0061 * the full path to the new file. 0062 * @param [out] namePart Will get a pointer into @a path that points to the 0063 * basename part of the path. 0064 * @param [in] extension Characters that must come at the end of the filename, 0065 * for example ".jpg". 0066 */ 0067 void getFreshFilename(char*& path, const char*& namePart, 0068 const char* extension) { 0069 std::string pathOut; 0070 std::stringstream p; 0071 int indexOfName = 0; 0072 int size = 0; 0073 do { 0074 p.str(""); 0075 p << workspacePathFrames; 0076 indexOfName = p.str().length(); 0077 p.fill('0'); 0078 p.width(8); 0079 p << nextFileNumber(); 0080 if (extension) 0081 p << extension; 0082 pathOut = p.str(); 0083 size = pathOut.length() + 1; 0084 // keep going until we find a filename that doesn't already exist. 0085 } while (0 == access(pathOut.c_str(), F_OK)); 0086 path = new char[size]; 0087 strncpy(path, pathOut.c_str(), size); 0088 namePart = path + indexOfName; 0089 } 0090 0091 char upper(char c) { 0092 return 'a' <= c && c <= 'z'? c - ('a' - 'A') : c; 0093 } 0094 0095 /** ASCII case-insensitive string compare. */ 0096 bool asciiEqualI(const char* a, const char* b) { 0097 for (; *a != '\0' && *b != '\0'; ++a, ++b) { 0098 char ca = upper(*a); 0099 char cb = upper(*b); 0100 if (ca != cb) 0101 return false; 0102 } 0103 return true; 0104 } 0105 0106 } 0107 0108 WorkspaceFileType::~WorkspaceFileType() { 0109 } 0110 0111 class ImageFileType : public WorkspaceFileType { 0112 public: 0113 static ImageFileType* instance; 0114 const char* preferredExtension(const char*) const { 0115 return ".jpg"; 0116 } 0117 bool isType(const char* path) const { 0118 const char* extension = strrchr(path,'.'); 0119 return extension && 0120 (asciiEqualI(extension, ".jpg") || asciiEqualI(extension, ".jpeg")); 0121 } 0122 }; 0123 0124 class SoundFileType : public WorkspaceFileType { 0125 public: 0126 static SoundFileType* instance; 0127 const char* preferredExtension(const char*) const { 0128 return ".ogg"; 0129 } 0130 bool isType(const char* path) const { 0131 const char* extension = strrchr(path,'.'); 0132 return extension && 0133 (asciiEqualI(extension, ".ogg") 0134 || asciiEqualI(extension, ".test-sound")); 0135 } 0136 }; 0137 0138 ImageFileType* ImageFileType::instance; 0139 SoundFileType* SoundFileType::instance; 0140 0141 const WorkspaceFileType& WorkspaceFileType::image() { 0142 if (!ImageFileType::instance) 0143 ImageFileType::instance = new ImageFileType(); 0144 return *ImageFileType::instance; 0145 } 0146 0147 const WorkspaceFileType& WorkspaceFileType::sound() { 0148 if (!SoundFileType::instance) 0149 SoundFileType::instance = new SoundFileType(); 0150 return *SoundFileType::instance; 0151 } 0152 0153 /** 0154 * Sets @c fullPath and namePart. 0155 * @param basename The name of the file. 0156 * @param inFrames True for a sound or image file. 0157 */ 0158 void WorkspaceFile::setFilename(const char* basename, bool inFrames) { 0159 namePart = 0; 0160 delete[] fullPath; 0161 fullPath = 0; 0162 std::stringstream p; 0163 if (p.fail()) 0164 throw UiException(UiException::failedToCopyFilesToWorkspace); 0165 if (inFrames) { 0166 p << workspacePathFrames; 0167 } else { 0168 p << workspacePath; 0169 } 0170 int indexOfName = p.tellp(); 0171 p << basename; 0172 int size = p.tellp(); 0173 if (p.fail()) 0174 throw UiException(UiException::failedToCopyFilesToWorkspace); 0175 fullPath = new char[size + 1]; 0176 p.read(fullPath, size); 0177 if (p.fail()) 0178 throw UiException(UiException::failedToCopyFilesToWorkspace); 0179 fullPath[size] = '\0'; 0180 namePart = fullPath + indexOfName; 0181 } 0182 0183 void WorkspaceFile::ensureStopmotionDirectoriesExist(AndClear clear) { 0184 { 0185 std::stringstream pathStr; 0186 pathStr << workspacePath; 0187 std::string path = pathStr.str(); 0188 Util::ensurePathExists(path.c_str()); 0189 } 0190 { 0191 std::stringstream pathStr; 0192 pathStr << workspacePathFrames; 0193 std::string path = pathStr.str(); 0194 Util::ensurePathExists(path.c_str()); 0195 if (clear == andClear) { 0196 Util::removeDirectoryContents(path.c_str()); 0197 } 0198 } 0199 } 0200 0201 void WorkspaceFile::clear() { 0202 ensureStopmotionDirectoriesExist(andClear); 0203 WorkspaceFile nm(newModelFile); 0204 unlink(nm.path()); 0205 WorkspaceFile cm(currentModelFile); 0206 unlink(cm.path()); 0207 WorkspaceFile cl(commandLogFile); 0208 unlink(cl.path()); 0209 fileNum = 0; 0210 soundNumber = 0; 0211 } 0212 0213 uint32_t WorkspaceFile::getSoundNumber() { 0214 return soundNumber; 0215 } 0216 0217 void WorkspaceFile::nextSoundNumber() { 0218 ++soundNumber; 0219 } 0220 0221 WorkspaceFile::WorkspaceFile() 0222 : fullPath(0), namePart(0) { 0223 } 0224 0225 WorkspaceFile& WorkspaceFile::operator=(const WorkspaceFile& other) { 0226 if (other.fullPath) { 0227 int nameStart = other.namePart - other.fullPath; 0228 size_t length = 1 + nameStart + strlen(other.namePart); 0229 char* buffer = new char[length]; 0230 delete[] fullPath; 0231 fullPath = buffer; 0232 strncpy(fullPath, other.fullPath, length); 0233 namePart = fullPath + nameStart; 0234 } 0235 return *this; 0236 } 0237 0238 WorkspaceFile::WorkspaceFile(const WorkspaceFile& t) 0239 : fullPath(0), namePart(0) { 0240 *this = t; 0241 } 0242 0243 WorkspaceFile::WorkspaceFile(const char* name) 0244 : fullPath(0), namePart(0) { 0245 setFilename(name, true); 0246 } 0247 0248 WorkspaceFile::WorkspaceFile(NewModelFile) 0249 : fullPath(0), namePart(0) { 0250 setFilename("new.dat"); 0251 } 0252 0253 WorkspaceFile::WorkspaceFile(CurrentModelFile) 0254 : fullPath(0), namePart(0) { 0255 setFilename("current.dat"); 0256 } 0257 0258 WorkspaceFile::WorkspaceFile(CommandLogFile) 0259 : fullPath(0), namePart(0) { 0260 setFilename("command.log"); 0261 } 0262 0263 WorkspaceFile::WorkspaceFile(CapturedImage) 0264 : fullPath(0), namePart(0) { 0265 setFilename("capturedfile.jpg"); 0266 } 0267 0268 WorkspaceFile::WorkspaceFile(PreferencesFile) 0269 : fullPath(0), namePart(0) { 0270 setFilename("preferences.xml"); 0271 } 0272 0273 WorkspaceFile::WorkspaceFile(PreferencesFileOld) 0274 : fullPath(0), namePart(0) { 0275 setFilename("preferences.xml.OLD"); 0276 } 0277 0278 WorkspaceFile::~WorkspaceFile() { 0279 delete[] fullPath; 0280 } 0281 0282 const char* WorkspaceFile::basename() const { 0283 return namePart; 0284 } 0285 0286 const char* WorkspaceFile::path() const { 0287 return fullPath; 0288 } 0289 0290 void WorkspaceFile::swap(WorkspaceFile& w) { 0291 char* t = w.fullPath; 0292 w.fullPath = fullPath; 0293 fullPath = t; 0294 const char* t0 = w.namePart; 0295 w.namePart = namePart; 0296 namePart = t0; 0297 } 0298 0299 void TemporaryWorkspaceFile::copyToWorkspace(const char* filename, 0300 const WorkspaceFileType& type) { 0301 if (!type.isType(filename)) { 0302 throw UiException(UiException::unsupportedImageType); 0303 } 0304 getFreshFilename(fullPath, namePart, type.preferredExtension(filename)); 0305 toBeDeleted = false; 0306 if (!Util::copyFile(fullPath, filename)) { 0307 throw UiException(UiException::failedToCopyFilesToWorkspace); 0308 } 0309 toBeDeleted = true; 0310 } 0311 0312 TemporaryWorkspaceFile::TemporaryWorkspaceFile(const char* filename, 0313 const WorkspaceFileType& type) 0314 : fullPath(0), namePart(0), toBeDeleted(false) { 0315 // not a totally foolproof test... 0316 if (strstr(filename, "/.stopmotion/frames/") != NULL) { 0317 // Already a workspace file; no need to copy it again 0318 int size = strlen(filename) + 1; 0319 fullPath = new char[size]; 0320 strncpy(fullPath, filename, size); 0321 namePart = strrchr(fullPath,'/') + 1; 0322 } else { 0323 copyToWorkspace(filename, type); 0324 } 0325 } 0326 0327 TemporaryWorkspaceFile::TemporaryWorkspaceFile(const char* filename, 0328 const WorkspaceFileType& type, ForceCopy) 0329 : fullPath(0), namePart(0), toBeDeleted(false) { 0330 copyToWorkspace(filename, type); 0331 } 0332 0333 TemporaryWorkspaceFile::~TemporaryWorkspaceFile() { 0334 if (toBeDeleted) { 0335 unlink(fullPath); 0336 } 0337 delete[] fullPath; 0338 fullPath = 0; 0339 namePart = 0; 0340 } 0341 0342 ExportDirectory::ExportDirectory() : p(0) { 0343 std::stringstream s; 0344 s << getenv("HOME"); 0345 s << "/.stopmotion/export/"; 0346 std::string pathOut = s.str(); 0347 pathOut.c_str(); // force allocation of trailing '\0' 0348 p = new char[pathOut.length() + 1]; 0349 strcpy(p, pathOut.c_str()); 0350 } 0351 0352 ExportDirectory::~ExportDirectory() { 0353 delete[] p; 0354 } 0355 0356 void ExportDirectory::makeEmpty() { 0357 //TODO 0358 }