فهرست منبع

Typ ett fungerande exemplar. Otroligt hemsk kod

Jonatan Gezelius 8 سال پیش
والد
کامیت
0d56c3ae9e
6فایلهای تغییر یافته به همراه1406 افزوده شده و 29 حذف شده
  1. 918 0
      exif.cpp
  2. 168 0
      exif.h
  3. 220 18
      filefunctions.cpp
  4. 9 5
      filefunctions.h
  5. 19 1
      image_sort.cbp
  6. 72 5
      main.cpp

+ 918 - 0
exif.cpp

@@ -0,0 +1,918 @@
+/**************************************************************************
+  exif.cpp  -- A simple ISO C++ library to parse basic EXIF
+               information from a JPEG file.
+
+  Copyright (c) 2010-2015 Mayank Lahiri
+  mlahiri@gmail.com
+  All rights reserved (BSD License).
+
+  See exif.h for version history.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+  -- Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+  -- Redistributions in binary form must reproduce the above copyright notice,
+     this list of conditions and the following disclaimer in the documentation
+     and/or other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+  NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include "exif.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <stdio.h>
+#include <vector>
+
+using std::string;
+
+namespace {
+
+struct Rational {
+  uint32_t numerator, denominator;
+  operator double() const {
+    if (denominator < 1e-20) {
+      return 0;
+    }
+    return static_cast<double>(numerator) / static_cast<double>(denominator);
+  }
+};
+
+// IF Entry
+class IFEntry {
+ public:
+  using byte_vector = std::vector<uint8_t>;
+  using ascii_vector = std::string;
+  using short_vector = std::vector<uint16_t>;
+  using long_vector = std::vector<uint32_t>;
+  using rational_vector = std::vector<Rational>;
+
+  IFEntry()
+      : tag_(0xFF), format_(0xFF), data_(0), length_(0), val_byte_(nullptr) {}
+  IFEntry(const IFEntry &) = delete;
+  IFEntry &operator=(const IFEntry &) = delete;
+  IFEntry(IFEntry &&other)
+      : tag_(other.tag_),
+        format_(other.format_),
+        data_(other.data_),
+        length_(other.length_),
+        val_byte_(other.val_byte_) {
+    other.tag_ = 0xFF;
+    other.format_ = 0xFF;
+    other.data_ = 0;
+    other.length_ = 0;
+    other.val_byte_ = nullptr;
+  }
+  ~IFEntry() { delete_union(); }
+  unsigned short tag() const { return tag_; }
+  void tag(unsigned short tag) { tag_ = tag; }
+  unsigned short format() const { return format_; }
+  bool format(unsigned short format) {
+    switch (format) {
+      case 0x01:
+      case 0x02:
+      case 0x03:
+      case 0x04:
+      case 0x05:
+      case 0x07:
+      case 0x09:
+      case 0x0a:
+      case 0xff:
+        break;
+      default:
+        return false;
+    }
+    delete_union();
+    format_ = format;
+    new_union();
+    return true;
+  }
+  unsigned data() const { return data_; }
+  void data(unsigned data) { data_ = data; }
+  unsigned length() const { return length_; }
+  void length(unsigned length) { length_ = length; }
+
+  // functions to access the data
+  //
+  // !! it's CALLER responsibility to check that format !!
+  // !! is correct before accessing it's field          !!
+  //
+  // - getters are use here to allow future addition
+  //   of checks if format is correct
+  byte_vector &val_byte() { return *val_byte_; }
+  ascii_vector &val_string() { return *val_string_; }
+  short_vector &val_short() { return *val_short_; }
+  long_vector &val_long() { return *val_long_; }
+  rational_vector &val_rational() { return *val_rational_; }
+
+ private:
+  // Raw fields
+  unsigned short tag_;
+  unsigned short format_;
+  unsigned data_;
+  unsigned length_;
+
+  // Parsed fields
+  union {
+    byte_vector *val_byte_;
+    ascii_vector *val_string_;
+    short_vector *val_short_;
+    long_vector *val_long_;
+    rational_vector *val_rational_;
+  };
+
+  void delete_union() {
+    switch (format_) {
+      case 0x1:
+        delete val_byte_;
+        val_byte_ = nullptr;
+        break;
+      case 0x2:
+        delete val_string_;
+        val_string_ = nullptr;
+        break;
+      case 0x3:
+        delete val_short_;
+        val_short_ = nullptr;
+        break;
+      case 0x4:
+        delete val_long_;
+        val_long_ = nullptr;
+        break;
+      case 0x5:
+        delete val_rational_;
+        val_rational_ = nullptr;
+        break;
+      case 0xff:
+        break;
+      default:
+        // should not get here
+        // should I throw an exception or ...?
+        break;
+    }
+  }
+  void new_union() {
+    switch (format_) {
+      case 0x1:
+        val_byte_ = new byte_vector();
+        break;
+      case 0x2:
+        val_string_ = new ascii_vector();
+        break;
+      case 0x3:
+        val_short_ = new short_vector();
+        break;
+      case 0x4:
+        val_long_ = new long_vector();
+        break;
+      case 0x5:
+        val_rational_ = new rational_vector();
+        break;
+      case 0xff:
+        break;
+      default:
+        // should not get here
+        // should I throw an exception or ...?
+        break;
+    }
+  }
+};
+
+// Helper functions
+template <typename T, bool alignIntel>
+T parse(const unsigned char *buf);
+
+template <>
+uint8_t parse<uint8_t, false>(const unsigned char *buf) {
+  return *buf;
+}
+
+template <>
+uint8_t parse<uint8_t, true>(const unsigned char *buf) {
+  return *buf;
+}
+
+template <>
+uint16_t parse<uint16_t, false>(const unsigned char *buf) {
+  return (static_cast<uint16_t>(buf[0]) << 8) | buf[1];
+}
+
+template <>
+uint16_t parse<uint16_t, true>(const unsigned char *buf) {
+  return (static_cast<uint16_t>(buf[1]) << 8) | buf[0];
+}
+
+template <>
+uint32_t parse<uint32_t, false>(const unsigned char *buf) {
+  return (static_cast<uint32_t>(buf[0]) << 24) |
+         (static_cast<uint32_t>(buf[1]) << 16) |
+         (static_cast<uint32_t>(buf[2]) << 8) | buf[3];
+}
+
+template <>
+uint32_t parse<uint32_t, true>(const unsigned char *buf) {
+  return (static_cast<uint32_t>(buf[3]) << 24) |
+         (static_cast<uint32_t>(buf[2]) << 16) |
+         (static_cast<uint32_t>(buf[1]) << 8) | buf[0];
+}
+
+template <>
+Rational parse<Rational, true>(const unsigned char *buf) {
+  Rational r;
+  r.numerator = parse<uint32_t, true>(buf);
+  r.denominator = parse<uint32_t, true>(buf + 4);
+  return r;
+}
+
+template <>
+Rational parse<Rational, false>(const unsigned char *buf) {
+  Rational r;
+  r.numerator = parse<uint32_t, false>(buf);
+  r.denominator = parse<uint32_t, false>(buf + 4);
+  return r;
+}
+
+/**
+ * Try to read entry.length() values for this entry.
+ *
+ * Returns:
+ *  true  - entry.length() values were read
+ *  false - something went wrong, vec's content was not touched
+ */
+template <typename T, bool alignIntel, typename C>
+bool extract_values(C &container, const unsigned char *buf, const unsigned base,
+                    const unsigned len, const IFEntry &entry) {
+  const unsigned char *data;
+  uint32_t reversed_data;
+  // if data fits into 4 bytes, they are stored directly in
+  // the data field in IFEntry
+  if (sizeof(T) * entry.length() <= 4) {
+    if (alignIntel) {
+      reversed_data = entry.data();
+    } else {
+      reversed_data = entry.data();
+      // this reversing works, but is ugly
+      unsigned char *data = reinterpret_cast<unsigned char *>(&reversed_data);
+      unsigned char tmp;
+      tmp = data[0];
+      data[0] = data[3];
+      data[3] = tmp;
+      tmp = data[1];
+      data[1] = data[2];
+      data[2] = tmp;
+    }
+    data = reinterpret_cast<const unsigned char *>(&(reversed_data));
+  } else {
+    data = buf + base + entry.data();
+    if (data + sizeof(T) * entry.length() > buf + len) {
+      return false;
+    }
+  }
+  container.resize(entry.length());
+  for (size_t i = 0; i < entry.length(); ++i) {
+    container[i] = parse<T, alignIntel>(data + sizeof(T) * i);
+  }
+  return true;
+}
+
+template <bool alignIntel>
+void parseIFEntryHeader(const unsigned char *buf, unsigned short &tag,
+                        unsigned short &format, unsigned &length,
+                        unsigned &data) {
+  // Each directory entry is composed of:
+  // 2 bytes: tag number (data field)
+  // 2 bytes: data format
+  // 4 bytes: number of components
+  // 4 bytes: data value or offset to data value
+  tag = parse<uint16_t, alignIntel>(buf);
+  format = parse<uint16_t, alignIntel>(buf + 2);
+  length = parse<uint32_t, alignIntel>(buf + 4);
+  data = parse<uint32_t, alignIntel>(buf + 8);
+}
+
+template <bool alignIntel>
+void parseIFEntryHeader(const unsigned char *buf, IFEntry &result) {
+  unsigned short tag;
+  unsigned short format;
+  unsigned length;
+  unsigned data;
+
+  parseIFEntryHeader<alignIntel>(buf, tag, format, length, data);
+
+  result.tag(tag);
+  result.format(format);
+  result.length(length);
+  result.data(data);
+}
+
+template <bool alignIntel>
+IFEntry parseIFEntry_temp(const unsigned char *buf, const unsigned offs,
+                          const unsigned base, const unsigned len) {
+  IFEntry result;
+
+  // check if there even is enough data for IFEntry in the buffer
+  if (buf + offs + 12 > buf + len) {
+    result.tag(0xFF);
+    return result;
+  }
+
+  parseIFEntryHeader<alignIntel>(buf + offs, result);
+
+  // Parse value in specified format
+  switch (result.format()) {
+    case 1:
+      if (!extract_values<uint8_t, alignIntel>(result.val_byte(), buf, base,
+                                               len, result)) {
+        result.tag(0xFF);
+      }
+      break;
+    case 2:
+      // string is basically sequence of uint8_t (well, according to EXIF even
+      // uint7_t, but
+      // we don't have that), so just read it as bytes
+      if (!extract_values<uint8_t, alignIntel>(result.val_string(), buf, base,
+                                               len, result)) {
+        result.tag(0xFF);
+      }
+      // and cut zero byte at the end, since we don't want that in the
+      // std::string
+      if (result.val_string()[result.val_string().length() - 1] == '\0') {
+        result.val_string().resize(result.val_string().length() - 1);
+      }
+      break;
+    case 3:
+      if (!extract_values<uint16_t, alignIntel>(result.val_short(), buf, base,
+                                                len, result)) {
+        result.tag(0xFF);
+      }
+      break;
+    case 4:
+      if (!extract_values<uint32_t, alignIntel>(result.val_long(), buf, base,
+                                                len, result)) {
+        result.tag(0xFF);
+      }
+      break;
+    case 5:
+      if (!extract_values<Rational, alignIntel>(result.val_rational(), buf,
+                                                base, len, result)) {
+        result.tag(0xFF);
+      }
+      break;
+    case 7:
+    case 9:
+    case 10:
+      break;
+    default:
+      result.tag(0xFF);
+  }
+  return result;
+}
+
+// helper functions for convinience
+template <typename T>
+T parse_value(const unsigned char *buf, bool alignIntel) {
+  if (alignIntel) {
+    return parse<T, true>(buf);
+  } else {
+    return parse<T, false>(buf);
+  }
+}
+
+void parseIFEntryHeader(const unsigned char *buf, bool alignIntel,
+                        unsigned short &tag, unsigned short &format,
+                        unsigned &length, unsigned &data) {
+  if (alignIntel) {
+    parseIFEntryHeader<true>(buf, tag, format, length, data);
+  } else {
+    parseIFEntryHeader<false>(buf, tag, format, length, data);
+  }
+}
+
+IFEntry parseIFEntry(const unsigned char *buf, const unsigned offs,
+                     const bool alignIntel, const unsigned base,
+                     const unsigned len) {
+  if (alignIntel) {
+    return parseIFEntry_temp<true>(buf, offs, base, len);
+  } else {
+    return parseIFEntry_temp<false>(buf, offs, base, len);
+  }
+}
+}
+
+//
+// Locates the EXIF segment and parses it using parseFromEXIFSegment
+//
+int easyexif::EXIFInfo::parseFrom(const unsigned char *buf, unsigned len) {
+  // Sanity check: all JPEG files start with 0xFFD8.
+  if (!buf || len < 4) return PARSE_EXIF_ERROR_NO_JPEG;
+  if (buf[0] != 0xFF || buf[1] != 0xD8) return PARSE_EXIF_ERROR_NO_JPEG;
+
+  // Sanity check: some cameras pad the JPEG image with some bytes at the end.
+  // Normally, we should be able to find the JPEG end marker 0xFFD9 at the end
+  // of the image buffer, but not always. As long as there are some bytes
+  // except 0xD9 at the end of the image buffer, keep decrementing len until
+  // an 0xFFD9 is found. If JPEG end marker 0xFFD9 is not found,
+  // then we can be reasonably sure that the buffer is not a JPEG.
+  while (len > 2) {
+    if (buf[len - 1] == 0xD9 && buf[len - 2] == 0xFF)
+      break;
+    len--;
+  }
+  if (len <= 2)
+    return PARSE_EXIF_ERROR_NO_JPEG;
+
+  clear();
+
+  // Scan for EXIF header (bytes 0xFF 0xE1) and do a sanity check by
+  // looking for bytes "Exif\0\0". The marker length data is in Motorola
+  // byte order, which results in the 'false' parameter to parse16().
+  // The marker has to contain at least the TIFF header, otherwise the
+  // EXIF data is corrupt. So the minimum length specified here has to be:
+  //   2 bytes: section size
+  //   6 bytes: "Exif\0\0" string
+  //   2 bytes: TIFF header (either "II" or "MM" string)
+  //   2 bytes: TIFF magic (short 0x2a00 in Motorola byte order)
+  //   4 bytes: Offset to first IFD
+  // =========
+  //  16 bytes
+  unsigned offs = 0;  // current offset into buffer
+  for (offs = 0; offs < len - 1; offs++)
+    if (buf[offs] == 0xFF && buf[offs + 1] == 0xE1) break;
+  if (offs + 4 > len) return PARSE_EXIF_ERROR_NO_EXIF;
+  offs += 2;
+  unsigned short section_length = parse_value<uint16_t>(buf + offs, false);
+  if (offs + section_length > len || section_length < 16)
+    return PARSE_EXIF_ERROR_CORRUPT;
+  offs += 2;
+
+  return parseFromEXIFSegment(buf + offs, len - offs);
+}
+
+int easyexif::EXIFInfo::parseFrom(const string &data) {
+  return parseFrom(
+      reinterpret_cast<const unsigned char *>(data.data()), static_cast<unsigned>(data.length()));
+}
+
+//
+// Main parsing function for an EXIF segment.
+//
+// PARAM: 'buf' start of the EXIF TIFF, which must be the bytes "Exif\0\0".
+// PARAM: 'len' length of buffer
+//
+int easyexif::EXIFInfo::parseFromEXIFSegment(const unsigned char *buf,
+                                             unsigned len) {
+  bool alignIntel = true;  // byte alignment (defined in EXIF header)
+  unsigned offs = 0;       // current offset into buffer
+  if (!buf || len < 6) return PARSE_EXIF_ERROR_NO_EXIF;
+
+  if (!std::equal(buf, buf + 6, "Exif\0\0")) return PARSE_EXIF_ERROR_NO_EXIF;
+  offs += 6;
+
+  // Now parsing the TIFF header. The first two bytes are either "II" or
+  // "MM" for Intel or Motorola byte alignment. Sanity check by parsing
+  // the unsigned short that follows, making sure it equals 0x2a. The
+  // last 4 bytes are an offset into the first IFD, which are added to
+  // the global offset counter. For this block, we expect the following
+  // minimum size:
+  //  2 bytes: 'II' or 'MM'
+  //  2 bytes: 0x002a
+  //  4 bytes: offset to first IDF
+  // -----------------------------
+  //  8 bytes
+  if (offs + 8 > len) return PARSE_EXIF_ERROR_CORRUPT;
+  unsigned tiff_header_start = offs;
+  if (buf[offs] == 'I' && buf[offs + 1] == 'I')
+    alignIntel = true;
+  else {
+    if (buf[offs] == 'M' && buf[offs + 1] == 'M')
+      alignIntel = false;
+    else
+      return PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN;
+  }
+  this->ByteAlign = alignIntel;
+  offs += 2;
+  if (0x2a != parse_value<uint16_t>(buf + offs, alignIntel))
+    return PARSE_EXIF_ERROR_CORRUPT;
+  offs += 2;
+  unsigned first_ifd_offset = parse_value<uint32_t>(buf + offs, alignIntel);
+  offs += first_ifd_offset - 4;
+  if (offs >= len) return PARSE_EXIF_ERROR_CORRUPT;
+
+  // Now parsing the first Image File Directory (IFD0, for the main image).
+  // An IFD consists of a variable number of 12-byte directory entries. The
+  // first two bytes of the IFD section contain the number of directory
+  // entries in the section. The last 4 bytes of the IFD contain an offset
+  // to the next IFD, which means this IFD must contain exactly 6 + 12 * num
+  // bytes of data.
+  if (offs + 2 > len) return PARSE_EXIF_ERROR_CORRUPT;
+  int num_entries = parse_value<uint16_t>(buf + offs, alignIntel);
+  if (offs + 6 + 12 * num_entries > len) return PARSE_EXIF_ERROR_CORRUPT;
+  offs += 2;
+  unsigned exif_sub_ifd_offset = len;
+  unsigned gps_sub_ifd_offset = len;
+  while (--num_entries >= 0) {
+    IFEntry result =
+        parseIFEntry(buf, offs, alignIntel, tiff_header_start, len);
+    offs += 12;
+    switch (result.tag()) {
+      case 0x102:
+        // Bits per sample
+        if (result.format() == 3 && result.val_short().size())
+          this->BitsPerSample = result.val_short().front();
+        break;
+
+      case 0x10E:
+        // Image description
+        if (result.format() == 2) this->ImageDescription = result.val_string();
+        break;
+
+      case 0x10F:
+        // Digicam make
+        if (result.format() == 2) this->Make = result.val_string();
+        break;
+
+      case 0x110:
+        // Digicam model
+        if (result.format() == 2) this->Model = result.val_string();
+        break;
+
+      case 0x112:
+        // Orientation of image
+        if (result.format() == 3 && result.val_short().size())
+          this->Orientation = result.val_short().front();
+        break;
+
+      case 0x131:
+        // Software used for image
+        if (result.format() == 2) this->Software = result.val_string();
+        break;
+
+      case 0x132:
+        // EXIF/TIFF date/time of image modification
+        if (result.format() == 2) this->DateTime = result.val_string();
+        break;
+
+      case 0x8298:
+        // Copyright information
+        if (result.format() == 2) this->Copyright = result.val_string();
+        break;
+
+      case 0x8825:
+        // GPS IFS offset
+        gps_sub_ifd_offset = tiff_header_start + result.data();
+        break;
+
+      case 0x8769:
+        // EXIF SubIFD offset
+        exif_sub_ifd_offset = tiff_header_start + result.data();
+        break;
+    }
+  }
+
+  // Jump to the EXIF SubIFD if it exists and parse all the information
+  // there. Note that it's possible that the EXIF SubIFD doesn't exist.
+  // The EXIF SubIFD contains most of the interesting information that a
+  // typical user might want.
+  if (exif_sub_ifd_offset + 4 <= len) {
+    offs = exif_sub_ifd_offset;
+    int num_entries = parse_value<uint16_t>(buf + offs, alignIntel);
+    if (offs + 6 + 12 * num_entries > len) return PARSE_EXIF_ERROR_CORRUPT;
+    offs += 2;
+    while (--num_entries >= 0) {
+      IFEntry result =
+          parseIFEntry(buf, offs, alignIntel, tiff_header_start, len);
+      switch (result.tag()) {
+        case 0x829a:
+          // Exposure time in seconds
+          if (result.format() == 5 && result.val_rational().size())
+            this->ExposureTime = result.val_rational().front();
+          break;
+
+        case 0x829d:
+          // FNumber
+          if (result.format() == 5 && result.val_rational().size())
+            this->FNumber = result.val_rational().front();
+          break;
+
+      case 0x8822:
+        // Exposure Program
+        if (result.format() == 3 && result.val_short().size())
+          this->ExposureProgram = result.val_short().front();
+        break;
+
+        case 0x8827:
+          // ISO Speed Rating
+          if (result.format() == 3 && result.val_short().size())
+            this->ISOSpeedRatings = result.val_short().front();
+          break;
+
+        case 0x9003:
+          // Original date and time
+          if (result.format() == 2)
+            this->DateTimeOriginal = result.val_string();
+          break;
+
+        case 0x9004:
+          // Digitization date and time
+          if (result.format() == 2)
+            this->DateTimeDigitized = result.val_string();
+          break;
+
+        case 0x9201:
+          // Shutter speed value
+          if (result.format() == 5 && result.val_rational().size())
+            this->ShutterSpeedValue = result.val_rational().front();
+          break;
+
+        case 0x9204:
+          // Exposure bias value
+          if (result.format() == 5 && result.val_rational().size())
+            this->ExposureBiasValue = result.val_rational().front();
+          break;
+
+        case 0x9206:
+          // Subject distance
+          if (result.format() == 5 && result.val_rational().size())
+            this->SubjectDistance = result.val_rational().front();
+          break;
+
+        case 0x9209:
+          // Flash used
+          if (result.format() == 3 && result.val_short().size()) {
+            uint16_t data = result.val_short().front();
+
+            this->Flash = data & 1;
+            this->FlashReturnedLight = (data & 6) >> 1;
+            this->FlashMode = (data & 24) >> 3;
+          }
+          break;
+
+        case 0x920a:
+          // Focal length
+          if (result.format() == 5 && result.val_rational().size())
+            this->FocalLength = result.val_rational().front();
+          break;
+
+        case 0x9207:
+          // Metering mode
+          if (result.format() == 3 && result.val_short().size())
+            this->MeteringMode = result.val_short().front();
+          break;
+
+        case 0x9291:
+          // Subsecond original time
+          if (result.format() == 2)
+            this->SubSecTimeOriginal = result.val_string();
+          break;
+
+        case 0xa002:
+          // EXIF Image width
+          if (result.format() == 4 && result.val_long().size())
+            this->ImageWidth = result.val_long().front();
+          if (result.format() == 3 && result.val_short().size())
+            this->ImageWidth = result.val_short().front();
+          break;
+
+        case 0xa003:
+          // EXIF Image height
+          if (result.format() == 4 && result.val_long().size())
+            this->ImageHeight = result.val_long().front();
+          if (result.format() == 3 && result.val_short().size())
+            this->ImageHeight = result.val_short().front();
+          break;
+
+        case 0xa20e:
+          // EXIF Focal plane X-resolution
+          if (result.format() == 5) {
+            this->LensInfo.FocalPlaneXResolution = result.val_rational()[0];
+          }
+          break;
+
+        case 0xa20f:
+          // EXIF Focal plane Y-resolution
+          if (result.format() == 5) {
+            this->LensInfo.FocalPlaneYResolution = result.val_rational()[0];
+          }
+          break;
+
+        case 0xa210:
+            // EXIF Focal plane resolution unit
+            if (result.format() == 3 && result.val_short().size()) {
+                this->LensInfo.FocalPlaneResolutionUnit = result.val_short().front();
+            }
+            break;
+
+        case 0xa405:
+          // Focal length in 35mm film
+          if (result.format() == 3 && result.val_short().size())
+            this->FocalLengthIn35mm = result.val_short().front();
+          break;
+
+        case 0xa432:
+          // Focal length and FStop.
+          if (result.format() == 5) {
+            int sz = static_cast<unsigned>(result.val_rational().size());
+            if (sz)
+              this->LensInfo.FocalLengthMin = result.val_rational()[0];
+            if (sz > 1)
+              this->LensInfo.FocalLengthMax = result.val_rational()[1];
+            if (sz > 2)
+              this->LensInfo.FStopMin = result.val_rational()[2];
+            if (sz > 3)
+              this->LensInfo.FStopMax = result.val_rational()[3];
+          }
+          break;
+
+        case 0xa433:
+          // Lens make.
+          if (result.format() == 2) {
+            this->LensInfo.Make = result.val_string();
+          }
+          break;
+
+        case 0xa434:
+          // Lens model.
+          if (result.format() == 2) {
+            this->LensInfo.Model = result.val_string();
+          }
+          break;
+      }
+      offs += 12;
+    }
+  }
+
+  // Jump to the GPS SubIFD if it exists and parse all the information
+  // there. Note that it's possible that the GPS SubIFD doesn't exist.
+  if (gps_sub_ifd_offset + 4 <= len) {
+    offs = gps_sub_ifd_offset;
+    int num_entries = parse_value<uint16_t>(buf + offs, alignIntel);
+    if (offs + 6 + 12 * num_entries > len) return PARSE_EXIF_ERROR_CORRUPT;
+    offs += 2;
+    while (--num_entries >= 0) {
+      unsigned short tag, format;
+      unsigned length, data;
+      parseIFEntryHeader(buf + offs, alignIntel, tag, format, length, data);
+      switch (tag) {
+        case 1:
+          // GPS north or south
+          this->GeoLocation.LatComponents.direction = *(buf + offs + 8);
+          if (this->GeoLocation.LatComponents.direction == 0) {
+            this->GeoLocation.LatComponents.direction = '?';
+          }
+          if ('S' == this->GeoLocation.LatComponents.direction) {
+            this->GeoLocation.Latitude = -this->GeoLocation.Latitude;
+          }
+          break;
+
+        case 2:
+          // GPS latitude
+          if ((format == 5 || format == 10) && length == 3) {
+            this->GeoLocation.LatComponents.degrees = parse_value<Rational>(
+                buf + data + tiff_header_start, alignIntel);
+            this->GeoLocation.LatComponents.minutes = parse_value<Rational>(
+                buf + data + tiff_header_start + 8, alignIntel);
+            this->GeoLocation.LatComponents.seconds = parse_value<Rational>(
+                buf + data + tiff_header_start + 16, alignIntel);
+            this->GeoLocation.Latitude =
+                this->GeoLocation.LatComponents.degrees +
+                this->GeoLocation.LatComponents.minutes / 60 +
+                this->GeoLocation.LatComponents.seconds / 3600;
+            if ('S' == this->GeoLocation.LatComponents.direction) {
+              this->GeoLocation.Latitude = -this->GeoLocation.Latitude;
+            }
+          }
+          break;
+
+        case 3:
+          // GPS east or west
+          this->GeoLocation.LonComponents.direction = *(buf + offs + 8);
+          if (this->GeoLocation.LonComponents.direction == 0) {
+            this->GeoLocation.LonComponents.direction = '?';
+          }
+          if ('W' == this->GeoLocation.LonComponents.direction) {
+            this->GeoLocation.Longitude = -this->GeoLocation.Longitude;
+          }
+          break;
+
+        case 4:
+          // GPS longitude
+          if ((format == 5 || format == 10) && length == 3) {
+            this->GeoLocation.LonComponents.degrees = parse_value<Rational>(
+                buf + data + tiff_header_start, alignIntel);
+            this->GeoLocation.LonComponents.minutes = parse_value<Rational>(
+                buf + data + tiff_header_start + 8, alignIntel);
+            this->GeoLocation.LonComponents.seconds = parse_value<Rational>(
+                buf + data + tiff_header_start + 16, alignIntel);
+            this->GeoLocation.Longitude =
+                this->GeoLocation.LonComponents.degrees +
+                this->GeoLocation.LonComponents.minutes / 60 +
+                this->GeoLocation.LonComponents.seconds / 3600;
+            if ('W' == this->GeoLocation.LonComponents.direction)
+              this->GeoLocation.Longitude = -this->GeoLocation.Longitude;
+          }
+          break;
+
+        case 5:
+          // GPS altitude reference (below or above sea level)
+          this->GeoLocation.AltitudeRef = *(buf + offs + 8);
+          if (1 == this->GeoLocation.AltitudeRef) {
+            this->GeoLocation.Altitude = -this->GeoLocation.Altitude;
+          }
+          break;
+
+        case 6:
+          // GPS altitude
+          if ((format == 5 || format == 10)) {
+            this->GeoLocation.Altitude = parse_value<Rational>(
+                buf + data + tiff_header_start, alignIntel);
+            if (1 == this->GeoLocation.AltitudeRef) {
+              this->GeoLocation.Altitude = -this->GeoLocation.Altitude;
+            }
+          }
+          break;
+
+        case 11:
+          // GPS degree of precision (DOP)
+          if ((format == 5 || format == 10)) {
+            this->GeoLocation.DOP = parse_value<Rational>(
+                buf + data + tiff_header_start, alignIntel);
+          }
+          break;
+      }
+      offs += 12;
+    }
+  }
+
+  return PARSE_EXIF_SUCCESS;
+}
+
+void easyexif::EXIFInfo::clear() {
+  // Strings
+  ImageDescription = "";
+  Make = "";
+  Model = "";
+  Software = "";
+  DateTime = "";
+  DateTimeOriginal = "";
+  DateTimeDigitized = "";
+  SubSecTimeOriginal = "";
+  Copyright = "";
+
+  // Shorts / unsigned / double
+  ByteAlign = 0;
+  Orientation = 0;
+
+  BitsPerSample = 0;
+  ExposureTime = 0;
+  FNumber = 0;
+  ExposureProgram = 0;
+  ISOSpeedRatings = 0;
+  ShutterSpeedValue = 0;
+  ExposureBiasValue = 0;
+  SubjectDistance = 0;
+  FocalLength = 0;
+  FocalLengthIn35mm = 0;
+  Flash = 0;
+  FlashReturnedLight = 0;
+  FlashMode = 0;
+  MeteringMode = 0;
+  ImageWidth = 0;
+  ImageHeight = 0;
+
+  // Geolocation
+  GeoLocation.Latitude = 0;
+  GeoLocation.Longitude = 0;
+  GeoLocation.Altitude = 0;
+  GeoLocation.AltitudeRef = 0;
+  GeoLocation.DOP = 0;
+  GeoLocation.LatComponents.degrees = 0;
+  GeoLocation.LatComponents.minutes = 0;
+  GeoLocation.LatComponents.seconds = 0;
+  GeoLocation.LatComponents.direction = '?';
+  GeoLocation.LonComponents.degrees = 0;
+  GeoLocation.LonComponents.minutes = 0;
+  GeoLocation.LonComponents.seconds = 0;
+  GeoLocation.LonComponents.direction = '?';
+
+  // LensInfo
+  LensInfo.FocalLengthMax = 0;
+  LensInfo.FocalLengthMin = 0;
+  LensInfo.FStopMax = 0;
+  LensInfo.FStopMin = 0;
+  LensInfo.FocalPlaneYResolution = 0;
+  LensInfo.FocalPlaneXResolution = 0;
+  LensInfo.FocalPlaneResolutionUnit = 0;
+  LensInfo.Make = "";
+  LensInfo.Model = "";
+}

+ 168 - 0
exif.h

@@ -0,0 +1,168 @@
+/**************************************************************************
+  exif.h  -- A simple ISO C++ library to parse basic EXIF
+             information from a JPEG file.
+
+  Based on the description of the EXIF file format at:
+  -- http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
+  -- http://www.media.mit.edu/pia/Research/deepview/exif.html
+  -- http://www.exif.org/Exif2-2.PDF
+
+  Copyright (c) 2010-2016 Mayank Lahiri
+  mlahiri@gmail.com
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+  -- Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+  -- Redistributions in binary form must reproduce the above copyright notice,
+     this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS
+   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+   NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+   OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+   EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef __EXIF_H
+#define __EXIF_H
+
+#include <string>
+
+namespace easyexif {
+
+//
+// Class responsible for storing and parsing EXIF information from a JPEG blob
+//
+class EXIFInfo {
+ public:
+  // Parsing function for an entire JPEG image buffer.
+  //
+  // PARAM 'data': A pointer to a JPEG image.
+  // PARAM 'length': The length of the JPEG image.
+  // RETURN:  PARSE_EXIF_SUCCESS (0) on succes with 'result' filled out
+  //          error code otherwise, as defined by the PARSE_EXIF_ERROR_* macros
+  int parseFrom(const unsigned char *data, unsigned length);
+  int parseFrom(const std::string &data);
+
+  // Parsing function for an EXIF segment. This is used internally by parseFrom()
+  // but can be called for special cases where only the EXIF section is
+  // available (i.e., a blob starting with the bytes "Exif\0\0").
+  int parseFromEXIFSegment(const unsigned char *buf, unsigned len);
+
+  // Set all data members to default values.
+  void clear();
+
+  // Data fields filled out by parseFrom()
+  char ByteAlign;                   // 0 = Motorola byte alignment, 1 = Intel
+  std::string ImageDescription;     // Image description
+  std::string Make;                 // Camera manufacturer's name
+  std::string Model;                // Camera model
+  unsigned short Orientation;       // Image orientation, start of data corresponds to
+                                    // 0: unspecified in EXIF data
+                                    // 1: upper left of image
+                                    // 3: lower right of image
+                                    // 6: upper right of image
+                                    // 8: lower left of image
+                                    // 9: undefined
+  unsigned short BitsPerSample;     // Number of bits per component
+  std::string Software;             // Software used
+  std::string DateTime;             // File change date and time
+  std::string DateTimeOriginal;     // Original file date and time (may not exist)
+  std::string DateTimeDigitized;    // Digitization date and time (may not exist)
+  std::string SubSecTimeOriginal;   // Sub-second time that original picture was taken
+  std::string Copyright;            // File copyright information
+  double ExposureTime;              // Exposure time in seconds
+  double FNumber;                   // F/stop
+  unsigned short ExposureProgram;   // Exposure program
+                                    // 0: Not defined
+                                    // 1: Manual
+                                    // 2: Normal program
+                                    // 3: Aperture priority
+                                    // 4: Shutter priority
+                                    // 5: Creative program
+                                    // 6: Action program
+                                    // 7: Portrait mode
+                                    // 8: Landscape mode
+  unsigned short ISOSpeedRatings;   // ISO speed
+  double ShutterSpeedValue;         // Shutter speed (reciprocal of exposure time)
+  double ExposureBiasValue;         // Exposure bias value in EV
+  double SubjectDistance;           // Distance to focus point in meters
+  double FocalLength;               // Focal length of lens in millimeters
+  unsigned short FocalLengthIn35mm; // Focal length in 35mm film
+  char Flash;                       // 0 = no flash, 1 = flash used
+  unsigned short FlashReturnedLight;// Flash returned light status
+                                    // 0: No strobe return detection function
+                                    // 1: Reserved
+                                    // 2: Strobe return light not detected
+                                    // 3: Strobe return light detected
+  unsigned short FlashMode;         // Flash mode
+                                    // 0: Unknown
+                                    // 1: Compulsory flash firing
+                                    // 2: Compulsory flash suppression
+                                    // 3: Automatic mode
+  unsigned short MeteringMode;      // Metering mode
+                                    // 1: average
+                                    // 2: center weighted average
+                                    // 3: spot
+                                    // 4: multi-spot
+                                    // 5: multi-segment
+  unsigned ImageWidth;              // Image width reported in EXIF data
+  unsigned ImageHeight;             // Image height reported in EXIF data
+  struct Geolocation_t {            // GPS information embedded in file
+    double Latitude;                  // Image latitude expressed as decimal
+    double Longitude;                 // Image longitude expressed as decimal
+    double Altitude;                  // Altitude in meters, relative to sea level
+    char AltitudeRef;                 // 0 = above sea level, -1 = below sea level
+    double DOP;                       // GPS degree of precision (DOP)
+    struct Coord_t {
+      double degrees;
+      double minutes;
+      double seconds;
+      char direction;
+    } LatComponents, LonComponents;   // Latitude, Longitude expressed in deg/min/sec
+  } GeoLocation;
+  struct LensInfo_t {               // Lens information
+    double FStopMin;                // Min aperture (f-stop)
+    double FStopMax;                // Max aperture (f-stop)
+    double FocalLengthMin;          // Min focal length (mm)
+    double FocalLengthMax;          // Max focal length (mm)
+    double FocalPlaneXResolution;   // Focal plane X-resolution
+    double FocalPlaneYResolution;   // Focal plane Y-resolution
+    unsigned short FocalPlaneResolutionUnit; // Focal plane resolution unit
+                                             // 1: No absolute unit of measurement.
+                                             // 2: Inch.
+                                             // 3: Centimeter.
+                                             // 4: Millimeter.
+                                             // 5: Micrometer.
+    std::string Make;               // Lens manufacturer
+    std::string Model;              // Lens model
+  } LensInfo;
+
+
+  EXIFInfo() {
+    clear();
+  }
+};
+
+}
+
+// Parse was successful
+#define PARSE_EXIF_SUCCESS                    0
+// No JPEG markers found in buffer, possibly invalid JPEG file
+#define PARSE_EXIF_ERROR_NO_JPEG              1982
+// No EXIF header found in JPEG file.
+#define PARSE_EXIF_ERROR_NO_EXIF              1983
+// Byte alignment specified in EXIF file was unknown (not Motorola or Intel).
+#define PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN    1984
+// EXIF header was found, but data was corrupted.
+#define PARSE_EXIF_ERROR_CORRUPT              1985
+
+#endif

+ 220 - 18
filefunctions.cpp

@@ -1,12 +1,24 @@
 #include "filefunctions.h"
 #include "debugging.h"
+#include "exif.h"
 
 #include <string>
+#include <algorithm>
+#include <cstdio>
+#include <iostream>
+#include <sstream>
+#include <iomanip>
+#include <locale>
 
 using namespace filefunctions;
 
 std::string filefunctions::filefunctions_wd;
 
+COMMAND_FLAGS filefunctions::operator|(filefunctions::COMMAND_FLAGS a, filefunctions::COMMAND_FLAGS b)
+{
+    return COMMAND_FLAGS(int(a)|int(b));
+}
+
 #ifdef __linux__
     // Linux methods
     Ej implementerat
@@ -14,6 +26,71 @@ std::string filefunctions::filefunctions_wd;
     // Windows methods
 #include <windows.h>
 
+time_t filetime_to_unixtime(FILETIME const &t)
+{
+    LARGE_INTEGER big_time;
+    time_t unix_time;
+
+    big_time.HighPart = t.dwHighDateTime;
+    big_time.LowPart = t.dwLowDateTime;
+    unix_time = big_time.QuadPart/10000000-11644473600;
+    return unix_time;
+}
+
+tm* get_exif_time(std::string path)
+{
+    FILE *filen = fopen(path.c_str(), "rb");
+    if (filen == nullptr)
+    {
+        DEBUG("__GET_EXIF_TIME__ Could not find file: " << path);
+        return nullptr;
+    }
+    DEBUG("__GET_EXIF_TIME__ Opened file: " << path);
+
+    fseek(filen, 0, SEEK_END);
+    unsigned long file_size = ftell(filen);
+    rewind(filen);
+    unsigned char *buf = new unsigned char[file_size];
+
+    DEBUG("__GET_EXIF_TIME__ File size: " << file_size);
+
+    if (fread(buf, 1, file_size, filen) != file_size)
+    {
+        DEBUG("__GET_EXIF_TIME__ Could not read file");
+        delete[] buf;
+        fclose(filen);
+        return nullptr;
+    }
+    fclose(filen);
+
+    easyexif::EXIFInfo exif;
+    if(exif.parseFrom(buf, file_size))
+    {
+        DEBUG("__GET_EXIF_TIME__ Could not parse file");
+        delete[] buf;
+        return nullptr;
+    }
+    delete[] buf;
+
+    DEBUG("__GET_EXIF_TIME__ File parsed!");
+
+    tm *result = new tm;
+    int bajs;
+    sscanf(exif.DateTimeOriginal.c_str(), "%i:%i:%i %i:%i:%i", &result->tm_year, &result->tm_mon, &result->tm_mday, &result->tm_hour, &result->tm_min, &result->tm_sec);
+
+    // Coarse sanity check to avoid very invalid dates
+    if(result->tm_year < 70 || result->tm_yday > 200)
+    {
+        delete result;
+        return nullptr;
+    }
+
+    result->tm_year -= 1900;
+    result->tm_mon -= 1;
+
+    return result;
+}
+
 STATUS_CODE filefunctions::cd(std::string const &path_arg, COMMAND_FLAGS flags)
 {
     DWORD attr;
@@ -21,16 +98,24 @@ STATUS_CODE filefunctions::cd(std::string const &path_arg, COMMAND_FLAGS flags)
 
     if(flags&CF_RELATIVE)
     {
-        int path_len = GetCurrentDirectory(0, nullptr);
-        LPTSTR temp = (LPTSTR)malloc((path_len+1)*sizeof(char));
-        if(!GetCurrentDirectory(path_len+1, temp))
-            return SC_INVALID_PATH;
-        path = std::string(temp);
-        DEBUG("path: " << temp);
-        free(temp);
+        path = filefunctions_wd;
+        if(path.length() == 0)
+        {
+            int path_len = GetCurrentDirectory(0, nullptr);
+            LPTSTR temp = new TCHAR[path_len+1];
+            if(!GetCurrentDirectory(path_len+1, temp))
+                return SC_INVALID_PATH;
+            path = std::string(temp);
+            DEBUG("__CD__ GetCurrentDirectory() = " << temp);
+            delete[] temp;
+        }
+        if(path_arg.length())
+            path += "\\" + path_arg;
+    }
+    else
+    {
+        path = path_arg;
     }
-
-    path += "\\" + path_arg;
 
     attr = GetFileAttributes(path.c_str());
 
@@ -56,23 +141,45 @@ STATUS_CODE filefunctions::mv(std::string const &src_arg, std::string const &des
     {
         src = filefunctions_wd;
         dest = filefunctions_wd;
+        if(src.length())
+                src += "\\" + src_arg;
+        if(dest.length())
+                dest += "\\" + dest_arg;
+    }
+    else
+    {
+        src = src_arg;
+        dest = dest_arg;
     }
-    src += "\\" + src_arg;
-    dest += "\\" + dest_arg;
+
+    DEBUG("__MV__ Source: " << src);
+    DEBUG("__MV__ Destination: " << dest);
 
     attr = GetFileAttributes(src.c_str());
     if (attr == INVALID_FILE_ATTRIBUTES)
     {
+        DEBUG("__MV__ Source does not exist");
         return SC_DOES_NOT_EXIST;
     }
-    else if(attr & FILE_ATTRIBUTE_DIRECTORY)
+
+    if(mkdir(dest.substr(0,dest.find_last_of('/\\'))))
     {
-        return SC_IS_DIRECTORY;
+        DEBUG("__MV__ Could not determine destination directory");
+        return SC_INVALID_DESTINATION;
     }
 
-    mkdir(dest.substr(0,dest.find_last_of('\\')));
+    if(MoveFile(src.c_str(), dest.c_str()))
+    {
+        DEBUG("__MV__ Moved file\n\n");
+        return SC_SUCCESS;
+    }
 
-    return SC_SUCCESS;
+    if(GetLastError() == ERROR_ALREADY_EXISTS)
+    {
+        DEBUG("__MV__ Target already exists");
+        return SC_ALREADY_EXISTS;
+    }
+    return SC_OTHER_FAILIURE;
 }
 
 STATUS_CODE filefunctions::mkdir(std::string const &arg_path, COMMAND_FLAGS flags)
@@ -80,9 +187,16 @@ STATUS_CODE filefunctions::mkdir(std::string const &arg_path, COMMAND_FLAGS flag
     std::string path;
 
     if(flags & CF_RELATIVE)
-        path = filefunctions_wd + "\\" + arg_path;
+    {
+        path = filefunctions_wd;
+        if(arg_path.length())
+            path += "\\" + arg_path;
+    }
     else
+    {
         path = arg_path;
+    }
+
     DEBUG("__MKDIR__ Path: " << path);
 
     DWORD attr = GetFileAttributes(path.c_str());
@@ -98,7 +212,7 @@ STATUS_CODE filefunctions::mkdir(std::string const &arg_path, COMMAND_FLAGS flag
         return SC_IS_NOT_DIRECTORY;
     }
 
-    size_t parent_separator = path.find_last_of('\\');
+    size_t parent_separator = path.find_last_of('/\\');
     if(parent_separator == std::string::npos)
     {
         DEBUG("__MKDIR__ Invalid path");
@@ -133,7 +247,95 @@ std::string filefunctions::pwd(void)
     return filefunctions_wd;
 }
 
-//std::queue<File_Struct> filefunctions::ls(std::string path = filefunctions_wd);
+STATUS_CODE filefunctions::ls(std::string path_arg, std::queue<File_Struct> &content, COMMAND_FLAGS flags)
+{
+    std::string path;
+
+    if(flags & CF_RELATIVE)
+    {
+        path = filefunctions_wd;
+        if(path_arg.length())
+            path += path_arg;
+    }
+    else
+    {
+        path = path_arg;
+    }
+
+    DEBUG("__LS__ ls(" << path << ")");
+
+    DWORD attr = GetFileAttributes(path.c_str());
+    if(attr != FILE_ATTRIBUTE_DIRECTORY)
+        return SC_IS_NOT_DIRECTORY;
+
+    std::string path_joker = path + "\\*";
+
+    WIN32_FIND_DATA file_data;
+    HANDLE status = FindFirstFile(path_joker.c_str(), &file_data);
+
+    if(status == INVALID_HANDLE_VALUE)
+        return SC_OTHER_FAILIURE;
+
+
+
+
+    do
+    {
+        File_Struct temp;
+        temp.path = path + "\\" + file_data.cFileName;
+
+        if (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+        {
+            DEBUG("__LS__ Found directory!");
+            DEBUG("__LS__ Directory: \"" << temp.path << "\"");
+
+            if(flags&CF_RECURSIVE)
+            {
+                if(strcmp(file_data.cFileName, ".")==0 || strcmp(file_data.cFileName, "..")==0)
+                {
+                    DEBUG("__LS__ Ignoring directory . or ..");
+                }
+                else
+                {
+                    DEBUG("__LS__ Which is not . or ..\nGoing in!\n");
+                    ls(temp.path, content, CF_RECURSIVE);
+                }
+            }
+            else
+            {
+                DEBUG("__LS__ Ignoring directory, not recursive");
+            }
+        }
+        else
+        {
+            DEBUG("__LS__ Found file!");
+            DEBUG("__LS__ File: \"" << temp.path << "\"");
+            time_t unix_time = filetime_to_unixtime(file_data.ftLastWriteTime);
+
+
+            DEBUG("__LS__ Modified time: " << std::dec << unix_time);
+
+            temp.modified = new tm;
+            memcpy(temp.modified, localtime(&unix_time), sizeof(tm));
+
+            temp.file_ending = temp.path.substr(temp.path.find_last_of('.')+1);
+            std::transform(temp.file_ending.begin(), temp.file_ending.end(), temp.file_ending.begin(), ::tolower);
+            DEBUG("__LS__ File ending: " << temp.file_ending);
+
+            if(temp.file_ending == "jpeg" || temp.file_ending == "jpg")
+            {
+                DEBUG("__LS__ Found JPEG-file, decoding EXIF");
+                temp.taken = get_exif_time(temp.path);
+            }
+
+            content.push(temp);
+        }
+        DEBUG("__LS__ Checking for other files\n");
+    } while (FindNextFile(status, &file_data) != 0);
+
+    DEBUG("__LS__ Finnished ls()\n\n");
+    return SC_SUCCESS;
+}
 
 
 #else

+ 9 - 5
filefunctions.h

@@ -12,6 +12,7 @@ namespace filefunctions
         SC_SUCCESS = 0,
         SC_INVALID_WD,           // No or invalid working dir
         SC_INVALID_PATH,
+        SC_INVALID_DESTINATION,
         SC_DISK_FULL,
         SC_DOES_NOT_EXIST,
         SC_ALREADY_EXISTS,
@@ -26,26 +27,29 @@ namespace filefunctions
     {
         CF_NONE         = 0,
         CF_FORCE        = 1,
-        CF_RELATIVE     = 2
+        CF_RELATIVE     = 2,
+        CF_RECURSIVE    = 4,
     };
 
     struct File_Struct
     {
         std::string     path;
-        unsigned int    size;
+        std::string     file_ending;
         tm              *modified   = nullptr;
         tm              *taken      = nullptr;
     };
 
+    COMMAND_FLAGS operator|(COMMAND_FLAGS a, COMMAND_FLAGS b);
+
     extern std::string filefunctions_wd;
 
-    STATUS_CODE cd      (std::string const &path = "", COMMAND_FLAGS flags = CF_NONE);
+    STATUS_CODE cd      (std::string const &path = "", COMMAND_FLAGS flags = CF_RELATIVE);
     STATUS_CODE mv      (std::string const &src, std::string const &dest, COMMAND_FLAGS flags = CF_NONE);
     STATUS_CODE mkdir   (std::string const &path, COMMAND_FLAGS flags = CF_NONE);
     STATUS_CODE rm      (std::string const &path, COMMAND_FLAGS flags = CF_NONE);
 
-    std::string             pwd (void);
-    std::queue<File_Struct> ls  (std::string path = filefunctions_wd);
+    std::string pwd     (void);
+    STATUS_CODE ls      (std::string path, std::queue<File_Struct> &content, COMMAND_FLAGS flags = CF_RELATIVE);
 
 }
 #endif // FILEFUNCTIONS_H

+ 19 - 1
image_sort.cbp

@@ -21,7 +21,20 @@
 				<Option type="1" />
 				<Option compiler="gcc" />
 				<Compiler>
-					<Add option="-O2" />
+					<Add option="-O3" />
+					<Add option="-Wshadow" />
+					<Add option="-Wredundant-decls" />
+					<Add option="-Wundef" />
+					<Add option="-Wfloat-equal" />
+					<Add option="-Winline" />
+					<Add option="-Wunreachable-code" />
+					<Add option="-Wmissing-include-dirs" />
+					<Add option="-Wswitch-default" />
+					<Add option="-Wmain" />
+					<Add option="-pedantic" />
+					<Add option="-Wextra" />
+					<Add option="-Wall" />
+					<Add option="-std=c++11" />
 				</Compiler>
 				<Linker>
 					<Add option="-s" />
@@ -32,6 +45,11 @@
 			<Add option="-Wall" />
 			<Add option="-fexceptions" />
 		</Compiler>
+		<Unit filename="debugging.h" />
+		<Unit filename="exif.cpp" />
+		<Unit filename="exif.h" />
+		<Unit filename="filefunctions.cpp" />
+		<Unit filename="filefunctions.h" />
 		<Unit filename="main.cpp" />
 		<Extensions>
 			<code_completion />

+ 72 - 5
main.cpp

@@ -2,6 +2,9 @@
 #include "filefunctions.h"
 #include "debugging.h"
 #include <windows.h>
+#include <sstream>
+#include <string>
+#include <iomanip>
 
 using namespace filefunctions;
 
@@ -11,17 +14,81 @@ int main(int argc, char **argv)
 
     DEBUG(std::hex << INVALID_FILE_ATTRIBUTES << std::endl << argv[0]);
 
+    cd();
+/*
     if(cd("testfiler", CF_RELATIVE))
         printf("FAIL!\n");
     else
         printf("SUCCESS! Current dir: %s\n", pwd().c_str());
-
+*/
     printf("\n\n\n\n");
 
-    if(mkdir("manick\\bajs\\snorkråka\\hitler\\adolf", CF_RELATIVE))
-        printf("FAIL!\n");
-    else
-        printf("Success!");
+    DEBUG("Test operator: " << (CF_RECURSIVE | CF_RELATIVE));
+
+    std::queue<File_Struct> files;
+    ls("", files, CF_RECURSIVE | CF_RELATIVE);
+
+    while(files.size())
+    {
+        File_Struct temp = files.front();
+        files.pop();
+        DEBUG("Fil: " << temp.path);
+        if(temp.file_ending == "exe")
+        {
+            if(temp.modified)
+                delete temp.modified;
+            continue;
+        }
+        //DEBUG("Modified: " << asctime(temp.modified));
+        /*
+        if(temp.taken)
+            DEBUG("Tagen: " << asctime(temp.taken));
+            */
+
+        std::ostringstream new_path;
+        new_path << pwd() << "\\sorterat\\";
+        if(temp.taken)
+        {
+            DEBUG("Moving based on date taken");
+            new_path << (temp.taken->tm_year+1900) << "\\" << std::setfill('0') << std::setw(2) << (temp.taken->tm_mon+1) << "\\" << (temp.taken->tm_year+1900) << "-" << std::setw(2) << (temp.taken->tm_mon+1) << "-" \
+                     << std::setw(2) << temp.taken->tm_mday << "_" << std::setw(2) << temp.taken->tm_hour << "'" << std::setw(2) << temp.taken->tm_min << "'" << std::setw(2) << temp.taken->tm_sec;
+        }
+        else if(temp.modified)
+        {
+            DEBUG("Moving based on date modified");
+            new_path << (temp.modified->tm_year+1900) << "\\" << std::setfill('0') << std::setw(2) << (temp.modified->tm_mon+1) << "\\" << (temp.modified->tm_year+1900) << "-" << std::setw(2) << (temp.modified->tm_mon+1) << "-" \
+                     << std::setw(2) << temp.modified->tm_mday << "_" << std::setw(2) << temp.modified->tm_hour << "'" << std::setw(2) << temp.modified->tm_min << "'" << std::setw(2) << temp.modified->tm_sec;
+        }
+        else
+        {
+            DEBUG("No date found");
+            new_path << "osorterat/" << temp.path.substr(temp.path.find_last_of('/\\')+1, temp.path.find_last_of('.'));
+        }
+
+        std::ostringstream path_postfix;
+        path_postfix << new_path.str() << "." << temp.file_ending;
+        if(mv(temp.path, path_postfix.str(), CF_NONE) == SC_ALREADY_EXISTS)
+        {
+            int postfix = 1;
+            do
+            {
+                path_postfix.str("");
+                path_postfix << new_path.str() << "(" << postfix++ << ")" << "." << temp.file_ending;
+            } while(mv(temp.path, path_postfix.str()) == SC_ALREADY_EXISTS);
+        }
+        DEBUG("New filename: " << new_path.str() << "\n\n");
+
+        // Free memory
+        if(temp.taken)
+            delete temp.taken;
+        if(temp.modified)
+            delete temp.modified;
+
+    }
+
+    printf("Klart utan jättefatala fel troligtvis! :D\n");
+    char c;
+    std::cin >> c;
 
     return 0;
 }