File indexing completed on 2024-04-28 16:08:40
0001 /*************************************************************************** 0002 * Copyright (C) 2005-2014 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 #include "src/technical/util.h" 0021 #include "src/foundation/logger.h" 0022 0023 #include <ext/stdio_filebuf.h> 0024 #include <stdio.h> 0025 #include <stdlib.h> 0026 #include <sys/stat.h> 0027 #include <sys/ioctl.h> 0028 #include <unistd.h> 0029 #include <errno.h> 0030 #include <fcntl.h> 0031 #include <string.h> 0032 #include <cassert> 0033 #include <fstream> 0034 #include <ftw.h> 0035 #include <glob.h> 0036 #include <linux/videodev2.h> 0037 #include <new> 0038 0039 using namespace std; 0040 0041 FileLinkException::FileLinkException(const char* message) { 0042 snprintf(msg, sizeof(msg) - 1, "link error: %s", message); 0043 msg[sizeof(msg) - 1] = '\0'; 0044 } 0045 0046 const char* FileLinkException::what() const throw() { 0047 return msg; 0048 } 0049 0050 DirectoryCreationException::DirectoryCreationException(const char* path) { 0051 snprintf(buffer, sizeof(buffer), "Failed to create directory (%s)", path); 0052 } 0053 0054 const char* DirectoryCreationException::what() const 0055 throw() { 0056 return buffer; 0057 } 0058 0059 namespace { 0060 void handleFileLinkError() { 0061 switch(errno) { 0062 case EACCES: 0063 // do not have permissions to write (maybe wrong file system type) 0064 case EXDEV: 0065 // cannot link across file systems 0066 case EMLINK: 0067 // too many links already 0068 // ...worth trying a different method. 0069 return; 0070 case EEXIST: 0071 throw FileLinkException("File already exists"); 0072 case ENOENT: 0073 throw FileLinkException("File does not exist to be linked to"); 0074 case ENOSPC: 0075 throw FileLinkException("Out of disk space"); 0076 case EPERM: 0077 throw FileLinkException("Permission denied"); 0078 case EROFS: 0079 throw FileLinkException("Cannot write to a read-only file system"); 0080 case EIO: 0081 throw FileLinkException("I/O error"); 0082 default: 0083 throw FileLinkException("unknown error"); 0084 } 0085 } 0086 0087 int removeFileOrDirectory(const char *path, const struct stat *, 0088 int flag, struct FTW *info) { 0089 switch (flag) { 0090 case FTW_D: 0091 case FTW_DP: 0092 if (info->level != 0) { 0093 if (0 != rmdir(path)) { 0094 Logger::get().logWarning("Could not remove directory %s", path); 0095 return FTW_STOP; 0096 } 0097 } 0098 break; 0099 default: 0100 if (0 != unlink(path)) { 0101 Logger::get().logWarning("Could not remove file %s", path); 0102 return FTW_STOP; 0103 } 0104 break; 0105 } 0106 return FTW_CONTINUE; 0107 } 0108 0109 } 0110 0111 const char* Util::endOfArgument(const char* in) { 0112 enum CharClass { 0113 normalChar, 0114 backslashChar, 0115 squoteChar, 0116 dquoteChar, 0117 space 0118 }; 0119 enum State { 0120 normal, 0121 backslash, 0122 squote, 0123 dquote, 0124 dqbackslash, 0125 end 0126 } state = normal; 0127 /* transition[state][charClass] */ 0128 static const State transition[5][5] = { 0129 { normal, backslash, squote, dquote, end }, // normal 0130 { normal, normal, normal, normal, normal }, // backslash 0131 { squote, squote, normal, squote, squote }, // squote 0132 { dquote, dqbackslash, dquote, normal, dquote }, // dquote 0133 { dquote, dquote, dquote, dquote, dquote } // dqbackslash 0134 }; 0135 char c = *in; 0136 while (c != '\0') { 0137 CharClass cc = c == '\\'? backslashChar 0138 : c == '\''? squoteChar 0139 : c == '"'? dquoteChar 0140 : c == ' ' || c == '\t'? space 0141 : normalChar; 0142 state = transition[state][cc]; 0143 if (state == end) 0144 return in; 0145 ++in; 0146 c = *in; 0147 } 0148 return in; 0149 } 0150 0151 bool Util::checkCommand(std::string* pathOut, const char* command) { 0152 assert(command != 0); 0153 std::string which("which "); 0154 const char* commandEnd = endOfArgument(command); 0155 which.append(command, commandEnd); 0156 FILE *fp = popen(which.c_str(), "r"); 0157 __gnu_cxx::stdio_filebuf<char> buf(fp, ios::in); 0158 istream bufStream(&buf); 0159 std::string dummy; 0160 if (!pathOut) 0161 pathOut = &dummy; 0162 *pathOut = ""; 0163 std::getline(bufStream, *pathOut); 0164 while (bufStream.good()) { 0165 std::getline(bufStream, dummy); 0166 } 0167 bool bad = bufStream.bad(); 0168 int status = pclose(fp); 0169 int exitStatus = WEXITSTATUS(status); 0170 return !bad && exitStatus == 0; 0171 } 0172 0173 namespace { 0174 0175 template <std::size_t S> void setString(std::string& output, const __u8 (&in)[S]) { 0176 const char* p = reinterpret_cast<const char*>(in); 0177 output.assign(p, strnlen(p, S)); 0178 } 0179 0180 bool getGrabberDevice(const char* dev, GrabberDevice& d) { 0181 int fd; 0182 struct v4l2_capability video_cap; 0183 0184 if((fd = open(dev, O_RDONLY)) == -1) { 0185 Logger::get().logWarning("Could not open device %s", dev); 0186 return false; 0187 } 0188 0189 int vcrv = ioctl(fd, VIDIOC_QUERYCAP, &video_cap); 0190 close(fd); 0191 if (vcrv == -1) { 0192 Logger::get().logWarning("Could not read from device %s", dev); 0193 return false; 0194 } 0195 if (!(video_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { 0196 Logger::get().logDebug("Device %s is not a V4L2 capture device", dev); 0197 return false; 0198 } 0199 d.device.assign(dev); 0200 setString(d.name, video_cap.card); 0201 setString(d.type, video_cap.driver); 0202 Logger::get().logDebug("Got device %s: card %s, type %s", dev, 0203 d.name.c_str(), d.type.c_str()); 0204 return true; 0205 } 0206 } 0207 0208 const vector<GrabberDevice> Util::getGrabberDevices() { 0209 vector<GrabberDevice> devices; 0210 glob_t matches; 0211 int globRv = glob("/dev/video*", 0, 0, &matches); 0212 if (0 < matches.gl_pathc) { 0213 for (char** match = matches.gl_pathv; *match; ++match) { 0214 GrabberDevice gd; 0215 if (getGrabberDevice(*match, gd)) 0216 devices.push_back(gd); 0217 } 0218 } 0219 globfree(&matches); 0220 vector<GrabberDevice>(devices).swap(devices); 0221 if (globRv == GLOB_NOSPACE) 0222 throw std::bad_alloc(); 0223 return devices; 0224 } 0225 0226 bool Util::copyFile(const char *destFileName, const char *srcFileName) { 0227 assert(destFileName != 0); 0228 assert(srcFileName != 0); 0229 0230 FILE *src = fopen(srcFileName, "rb"); 0231 if (!src) { 0232 fprintf(stderr, "Failed to open '%s' for reading: %s\n", srcFileName, strerror(errno)); 0233 return false; 0234 } 0235 0236 FILE *dest = fopen(destFileName, "wb"); 0237 if (!dest) { 0238 fprintf(stderr, "Failed to open '%s' for writing: %s\n", destFileName, strerror(errno)); 0239 fclose (src); 0240 return false; 0241 } 0242 0243 char buf[4096]; 0244 size_t bytesRead; 0245 while ((bytesRead = fread(buf, 1, sizeof(buf), src)) > 0) { 0246 if (fwrite(buf, 1, bytesRead, dest) != bytesRead) { 0247 fprintf (stderr, "Error while writing '%s': %s\n", destFileName, strerror(errno)); 0248 break; 0249 } 0250 } 0251 0252 if (bytesRead == 0 && ferror(src)) 0253 fprintf(stderr, "Error while reading from file '%s': %s\n", srcFileName, strerror(errno)); 0254 0255 fclose(src); 0256 fclose(dest); 0257 0258 return true; 0259 } 0260 0261 void Util::linkOrCopyFile(const char* newName, const char* oldName) { 0262 if (-1 == link(oldName, newName)) { 0263 handleFileLinkError(); 0264 if (-1 == symlink(oldName, newName)) { 0265 handleFileLinkError(); 0266 if (!Util::copyFile(newName, oldName)) 0267 throw FileLinkException("Could not copy file"); 0268 } 0269 } 0270 } 0271 0272 bool Util::removeDirectoryContents(const char* path) { 0273 static const int maxDescripotorsToConsume = 8; 0274 return FTW_STOP != nftw(path, removeFileOrDirectory, 0275 maxDescripotorsToConsume, FTW_PHYS | FTW_DEPTH | FTW_ACTIONRETVAL); 0276 } 0277 0278 void Util::ensurePathExists(const char* path) { 0279 if (0 == access(path, F_OK)) 0280 return; 0281 std::string copy(path); 0282 copy.c_str(); // ensure terminating '\0' 0283 char* parent = ©[0]; 0284 char* end = strrchr(parent, '/'); 0285 if (end && end[1] == '\0') { 0286 // there is a trailing '/', so let's remove it and try again. 0287 *end = '\0'; 0288 end = strrchr(parent, '/'); 0289 } 0290 if (end) { 0291 *end = '\0'; 0292 ensurePathExists(parent); 0293 if (mkdir(path, 0755) < 0) 0294 throw DirectoryCreationException(path); 0295 } else { 0296 throw DirectoryCreationException(path); 0297 } 0298 }