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 = &copy[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 }