Add rm and strip abilities to atree.

The new line syntax is:
  [SRC] [rm|strip] DEST

This allows one to write things like this in atree:

  bin/src
  bin/src bin/dest
  bin/src "bin/another file name"

  rm dest/file
  rm dest/dir  # recursive

  strip bin/src
  bin/src strip bin/dest

Src and dest can contain spaces if full enclosed in double-quotes.
The strip command can be overridden using the STRIP env var.

Change-Id: I22aae7a87c36c082e1aab87132099a3c644914da
diff --git a/tools/atree/atree.cpp b/tools/atree/atree.cpp
index aee2b3c..2ba284f 100644
--- a/tools/atree/atree.cpp
+++ b/tools/atree/atree.cpp
@@ -1,6 +1,8 @@
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <stdarg.h>
 #include "options.h"
 #include "files.h"
 #include "fs.h"
@@ -10,7 +12,7 @@
 
 using namespace std;
 
-bool g_debug = false;
+bool g_debug = getenv("ATREE_DEBUG") != NULL;
 vector<string> g_listFiles;
 vector<string> g_inputBases;
 map<string, string> g_variables;
@@ -33,6 +35,7 @@
 "  -m DEPENDENCY  Output a make-formatted file containing the list.\n"
 "                 of files included.  It sets the variable ATREE_FILES.\n"
 "  -v VAR=VAL     Replaces ${VAR} by VAL when reading input files.\n"
+"  -d             Verbose debug mode.\n"
 "\n"
 "FILELIST file format:\n"
 "  The FILELIST files contain the list of files that will end up\n"
@@ -42,11 +45,13 @@
 "  In a FILELIST file, comment lines start with a #.  Other lines\n"
 "  are of the format:\n"
 "\n"
-"    DEST\n"
-"    SRC DEST\n"
+"    [rm|strip] DEST\n"
+"    SRC [strip] DEST\n"
 "    -SRCPATTERN\n"
 "\n"
 "  DEST should be path relative to the output directory.\n"
+"  'rm DEST' removes the destination file and fails if it's missing.\n"
+"  'strip DEST' strips the binary destination file.\n"
 "  If SRC is supplied, the file names can be different.\n"
 "  SRCPATTERN is a pattern for the filenames.\n"
 "\n";
@@ -72,13 +77,26 @@
     return true;
 }
 
+static void
+debug_printf(const char* format, ...)
+{
+    if (g_debug) {
+        fflush(stderr);
+        va_list ap;
+        va_start(ap, format);
+        vprintf(format, ap);
+        va_end(ap);
+        fflush(stdout);
+    }
+}
+
 int
 main(int argc, char* const* argv)
 {
     int err;
     bool done = false;
     while (!done) {
-        int opt = getopt(argc, argv, "f:I:o:hlm:v:");
+        int opt = getopt(argc, argv, "f:I:o:hlm:v:d");
         switch (opt)
         {
             case -1:
@@ -117,6 +135,9 @@
                     return usage();
                 }
                 break;
+            case 'd':
+                g_debug = true;
+                break;
             default:
             case '?':
             case 'h':
@@ -169,7 +190,7 @@
 
     // read file lists
     for (vector<string>::iterator it=g_listFiles.begin();
-                                it!=g_listFiles.end(); it++) {
+                                 it!=g_listFiles.end(); it++) {
         err = read_list_file(*it, g_variables, &files, &excludes);
         if (err != 0) {
             return err;
@@ -181,9 +202,8 @@
     for (vector<FileRecord>::iterator it=files.begin();
                                 it!=files.end(); it++) {
         err |= locate(&(*it), g_inputBases);
-
     }
-    
+
     // expand the directories that we should copy into a list of files
     for (vector<FileRecord>::iterator it=files.begin();
                                 it!=files.end(); it++) {
@@ -219,8 +239,8 @@
         }
     }
 
-    // gather files that should become directores and directories that should
-    // become files
+    // gather files that should become directores
+    // and directories that should become files
     for (vector<FileRecord>::iterator it=files.begin();
                                 it!=files.end(); it++) {
         if (it->outMod != 0 && it->sourceIsDir != it->outIsDir) {
@@ -231,48 +251,66 @@
     // delete files
     for (set<string>::iterator it=deleted.begin();
                                 it!=deleted.end(); it++) {
-        if (g_debug) {
-            printf("deleting %s\n", it->c_str());
-        }
+        debug_printf("deleting %s\n", it->c_str());
         err = remove_recursively(*it);
         if (err != 0) {
             return err;
         }
     }
 
+    // remove all files or directories as requested from the input atree file.
+    // must be done before create new directories.
+    for (vector<FileRecord>::iterator it=files.begin();
+                                it!=files.end(); it++) {
+        if (!it->sourceIsDir) {
+            if (it->fileOp == FILE_OP_REMOVE &&
+                    deleted.count(it->outPath) == 0) {
+                debug_printf("remove %s\n", it->outPath.c_str());
+                err = remove_recursively(it->outPath);
+                if (err != 0) {
+                    return err;
+                }
+            }
+        }
+    }
+
     // make directories
     for (set<string>::iterator it=directories.begin();
                                 it!=directories.end(); it++) {
-        if (g_debug) {
-            printf("mkdir %s\n", it->c_str());
-        }
+        debug_printf("mkdir %s\n", it->c_str());
         err = mkdir_recursively(*it);
         if (err != 0) {
             return err;
         }
     }
 
-    // copy (or link) files
+    // copy (or link) files that are newer or of different size
     for (vector<FileRecord>::iterator it=files.begin();
                                 it!=files.end(); it++) {
         if (!it->sourceIsDir) {
-            if (g_debug) {
-                printf("copy %s(%ld) ==> %s(%ld)", it->sourcePath.c_str(),
-                    it->sourceMod, it->outPath.c_str(), it->outMod);
-                fflush(stdout);
+            if (it->fileOp == FILE_OP_REMOVE) {
+                continue;
             }
 
-            if (it->outMod < it->sourceMod) {
+            debug_printf("copy %s(%ld) ==> %s(%ld)",
+                it->sourcePath.c_str(), it->sourceMod,
+                it->outPath.c_str(), it->outMod);
+
+            if (it->outSize != it->sourceSize || it->outMod < it->sourceMod) {
                 err = copy_file(it->sourcePath, it->outPath);
-                if (g_debug) {
-                    printf(" done.\n");
-                }
+                debug_printf(" done.\n");
                 if (err != 0) {
                     return err;
                 }
             } else {
-                if (g_debug) {
-                    printf(" skipping.\n");
+                debug_printf(" skipping.\n");
+            }
+
+            if (it->fileOp == FILE_OP_STRIP) {
+                debug_printf("strip %s\n", it->outPath.c_str());
+                err = strip_file(it->outPath);
+                if (err != 0) {
+                    return err;
                 }
             }
         }