commit e0b7f8b9c63f348fc831ebe8f875818d26f04ccf
Author: David Sorber <david.sorber@gmail.com>
Date:   Sun Feb 19 19:56:48 2017 -0500

    WIP commit. I started converting the argv list to a std::vector and adding logic to make sure it does not exceed the maximum argv size.

diff --git a/software/photo_compress_archiver/PhotoCompressArchiver.cc b/software/photo_compress_archiver/PhotoCompressArchiver.cc
index 42c1ffc..7bacfd1 100644
--- a/software/photo_compress_archiver/PhotoCompressArchiver.cc
+++ b/software/photo_compress_archiver/PhotoCompressArchiver.cc
@@ -2,6 +2,7 @@
 #include <cstdio>
 #include <iomanip>
 #include <iostream>
+#include <limits.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -13,16 +14,19 @@
     x\
 }
 
-const char *PROG_NAME = "./packJPG";
+const char *PROG_NAME = "./packjpg";
 const char *PROG_OPT1 = "-np";
 const char *PROG_OPT2 = "-o";
 const char *PROG_OPT3 = "-p";
 
+const uint32_t ARGV_MAX_SIZE(sysconf(_SC_ARG_MAX));
+
 PhotoCompressArchiver::PhotoCompressArchiver(
     const std::string& path, 
     uint32_t num_cores,
     bool decompress,
-    bool keep_orig)
+    bool keep_orig,
+    boost::regex& filter_regex)
     : m_path(path),
       m_num_cores(num_cores),
       m_decompress(decompress),
@@ -31,8 +35,16 @@ PhotoCompressArchiver::PhotoCompressArchiver(
       m_num_input_files(0),
       m_total_uncompressed_size(0),
       m_total_compressed_size(0),
-      p_finder_thread(nullptr)
-{}
+      p_finder_thread(nullptr),
+      m_filter_empty(true),
+      m_filter_regex(filter_regex)
+{
+    // This is a somewhat crude way to determining if the filter regex is empty
+    m_filter_empty = std::string("").compare(filter_regex.str()) == 0;
+    
+    std::cout << "argv size: " << sysconf(_SC_ARG_MAX) << std::endl;
+    std::cout << "appname size: " << sizeof(PROG_NAME) << std::endl;
+}
 
 PhotoCompressArchiver::~PhotoCompressArchiver()
 {}
@@ -136,6 +148,18 @@ void PhotoCompressArchiver::find_files(
         else if (boost::regex_match(dir_iter->path().filename().string(), file_regex))
         {
             // Found file match
+            
+            // If the filter is not empty, then check against it before proceeding
+            if (! m_filter_empty)
+            {
+                if (boost::regex_match(dir_iter->path().filename().string(), 
+                    m_filter_regex))
+                {
+                    // The filter regex matched, skip this file
+                    continue;
+                }
+            }
+            
             m_file_list.push_back(dir_iter->path());
             ++m_num_input_files;
             
@@ -171,23 +195,50 @@ void PhotoCompressArchiver::fork_worker(
                       << std::endl;)
     }
 #endif
+
+    //~ return; // temporary for debugging
         
-    uint32_t argv_size = file_sublist->size() + 5;
-    const char **exec_argv = new const char* [argv_size];
+    //~ uint32_t argv_size = file_sublist->size() + 5;
+    //~ const char **exec_argv = new const char* [argv_size];
+    
+    // Build the argv array; pass in default program options
+    std::vector<const char*> exec_argv;
+    exec_argv.push_back(&PROG_NAME[2]);
+    exec_argv.push_back(PROG_OPT1);
+    exec_argv.push_back(PROG_OPT2);
+    exec_argv.push_back(PROG_OPT3);
     
     // Build the argv array; pass in default program options
-    exec_argv[0] = &PROG_NAME[2]; // discard leading "./"
-    exec_argv[1] = PROG_OPT1;
-    exec_argv[2] = PROG_OPT2;
-    exec_argv[3] = PROG_OPT3;
+    //~ exec_argv[0] = &PROG_NAME[2]; // discard leading "./"
+    //~ exec_argv[1] = PROG_OPT1;
+    //~ exec_argv[2] = PROG_OPT2;
+    //~ exec_argv[3] = PROG_OPT3;
+    
+    uint32_t argv_size = sizeof(PROG_NAME) + sizeof(PROG_OPT1) + 
+                         sizeof(PROG_OPT2) + sizeof(PROG_OPT3) +  4096;
     
     // Add the sublist files to the argv array
-    uint32_t idx = 4;
+    uint32_t idx = 0;
     for (auto& image_path : *file_sublist)
     {
-        exec_argv[idx++] = image_path.string().c_str();
+        uint32_t image_path_len = image_path.string().size();
+        if (argv_size + image_path_len < ARGV_MAX_SIZE)
+        {
+            exec_argv.push_back(image_path.string().c_str());
+            argv_size += image_path_len + sizeof(char*);
+            ++idx;
+        }
+        else
+        {
+            break;
+        }
+        
+        //~ exec_argv[idx++] = image_path.string().c_str();
     }
-    exec_argv[argv_size - 1] = nullptr;
+    //~ exec_argv[argv_size - 1] = nullptr;
+    exec_argv.push_back(nullptr);
+    
+    std::cout << "IDX: " << idx << std::endl;
     
     
     // Build a list of the output files names for use in output file monitoring
@@ -196,22 +247,14 @@ void PhotoCompressArchiver::fork_worker(
     std::vector<std::string*> output_filenames;
     for (auto& filepath : *file_sublist)
     {
-        // Create new filename that contains the ".jpg" replaced with ".pjg"
+        // Create new filename 
         uint32_t end_pos = filepath.string().find(".");
         std::string* filename = new std::string(filepath.string().begin(), 
                                                 filepath.string().begin() + end_pos);
-                                                
-        if (m_decompress)
-        {
-            // Decompression mode
-            (*filename) += ".jpg";
-        }
-        else
-        {
-            // Compression mode
-            (*filename) += ".pjg";
-        }
-                      
+        
+        // Add extension depending on if decompress mode is enabled
+        (*filename) += (m_decompress ? ".jpg" : ".pjg");
+        
         output_filenames.push_back(filename);
     }
     
@@ -241,7 +284,11 @@ void PhotoCompressArchiver::fork_worker(
         //~ close(filedes[0]);
         
         // Child after fork
-        execv(PROG_NAME, (char **)exec_argv);
+        int rc = execv(PROG_NAME, (char **)exec_argv.data());
+        if (rc)
+        {
+            std::cerr << "execv failed: " << errno << std::endl;
+        }
         _exit(EXIT_FAILURE);   // exec never returns
     }
     
@@ -360,7 +407,7 @@ void PhotoCompressArchiver::fork_worker(
                   << status << ENDC << std::endl;)
     
     // Free up the argv array and output filename list
-    delete[] exec_argv;
+    //~ delete[] exec_argv;
     for (auto output_filename : output_filenames)
     {
         delete output_filename;
diff --git a/software/photo_compress_archiver/PhotoCompressArchiver.hh b/software/photo_compress_archiver/PhotoCompressArchiver.hh
index 076a3cb..7c4361f 100644
--- a/software/photo_compress_archiver/PhotoCompressArchiver.hh
+++ b/software/photo_compress_archiver/PhotoCompressArchiver.hh
@@ -34,7 +34,8 @@ class PhotoCompressArchiver
                 const std::string& path, 
                 uint32_t num_cores,
                 bool decompress,
-                bool keep_orig);
+                bool keep_orig,
+                boost::regex& filter_regex);
         
         ~PhotoCompressArchiver();
         
@@ -79,6 +80,9 @@ class PhotoCompressArchiver
         
         std::thread* p_finder_thread;                   // file finder thread
         std::vector<std::thread*> m_worker_threads;     // worker threads
+        
+        bool m_filter_empty;                    // is the filter regex empty
+        boost::regex& m_filter_regex;           // filter regular expression
 };
 
 #endif // PHOTOCOMPRESSARCHIVER_H
diff --git a/software/photo_compress_archiver/main.cc b/software/photo_compress_archiver/main.cc
index bc1ab7b..999db6d 100644
--- a/software/photo_compress_archiver/main.cc
+++ b/software/photo_compress_archiver/main.cc
@@ -27,7 +27,7 @@ void usage(char** argv)
     }
     
     header();
-    std::cout << "USAGE: " << app_name << " [-hkd] <file path>\n\n";
+    std::cout << "USAGE: " << app_name << " [-hkd] [-f <filter regex>] <file path>\n\n";
     std::cout << "Positional arguments:\n";
     std::cout << "  <file path>           path in which to recursively look "
               << "for input files\n\n";
@@ -35,6 +35,7 @@ void usage(char** argv)
     std::cout << "  -h [ --help ]         display this help message\n";
     std::cout << "  -d [ --decompress ]   find '*.pjg' files and decompress them\n";
     std::cout << "  -k [ --keep-orig ]    keep original files; don't delete them\n";
+    std::cout << "  -f [ --filter ]       regex with which to filter out input files\n";
     std::cout << std::endl;
 }
 
@@ -51,6 +52,8 @@ int main(int argc, char** argv)
             ("help,h", "display this help message")
             ("decompress,d", "find '*.pjg' files and decompress them")
             ("keep-orig,k", "keep orignal files")
+            ("filter,f", bpo::value<std::string>(), 
+               "regex with which to filter out input files")
             ("file_path", "path in which to recursively look for input files")
         ;
 
@@ -60,16 +63,17 @@ int main(int argc, char** argv)
         
         // Parse general/positional options
         bpo::store(bpo::command_line_parser(argc, argv).options(general_opts)
-                   .positional(pos_opts).allow_unregistered().run(), 
+                   .positional(pos_opts).run(), 
                    general_opts_vm);
         
-        
         // Display help if the option is listed or if no arguments are provided
         if (general_opts_vm.count("help") || (argc == 1)) 
         {
             usage(argv);
             return 0;
         }
+        
+        bpo::notify(general_opts_vm);
     }    
     catch (std::exception& e) 
     {
@@ -77,7 +81,6 @@ int main(int argc, char** argv)
         return -1;
     }
     
-    
     // Make a copy of the file search path for passing into the object below
     std::string search_path(general_opts_vm["file_path"].as<std::string>());
     
@@ -110,10 +113,29 @@ int main(int argc, char** argv)
         std::cout << "    keep original files => ON\n";
     }
     
+    // Check for, and if present validate the filter regex
+    boost::regex filter_regex("");
+    if (general_opts_vm.count("filter"))
+    {
+        try
+        {
+            filter_regex = general_opts_vm["filter"].as<std::string>().c_str();
+        }
+        catch (std::exception& e) 
+        {
+            std::cerr << BOLD << RED << "ERROR: " << ENDC << "the filter \"" 
+                      << general_opts_vm["filter"].as<std::string>() 
+                      << "\" is not a valid regular expression.\n";
+            std::cerr << "       " << e.what() << std::endl;
+            return -1;
+        }
+    }
+    
     std::cout << std::endl;
     
+    // Create the PCA object and let 'er rip!
     auto pca = new PhotoCompressArchiver(search_path, num_cores, decompress, 
-                                         keep_orig);
+                                         keep_orig, filter_regex);
     pca->execute();
     delete pca;
     
