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/files.cpp b/tools/atree/files.cpp
index d4866d4..df3e987 100644
--- a/tools/atree/files.cpp
+++ b/tools/atree/files.cpp
@@ -62,7 +62,7 @@
 split_line(const char* p, vector<string>* out)
 {
     const char* q = p;
-    enum { WHITE, TEXT } state = WHITE;
+    enum { WHITE, TEXT, IN_QUOTE } state = WHITE;
     while (*p) {
         if (*p == '#') {
             break;
@@ -73,13 +73,25 @@
             case WHITE:
                 if (!isspace(*p)) {
                     q = p;
-                    state = TEXT;
+                    state = (*p == '"') ? IN_QUOTE : TEXT;
                 }
                 break;
+            case IN_QUOTE:
+                if (*p == '"') {
+                    state = TEXT;
+                    break;
+                }
+                // otherwise fall-through to TEXT case
             case TEXT:
-                if (isspace(*p)) {
+                if (state != IN_QUOTE && isspace(*p)) {
                     if (q != p) {
-                        out->push_back(string(q, p-q));
+                        const char* start = q;
+                        size_t len = p-q;
+                        if (len > 2 && *start == '"' && start[len - 1] == '"') {
+                            start++;
+                            len -= 2;
+                        }
+                        out->push_back(string(start, len));
                     }
                     state = WHITE;
                 }
@@ -88,17 +100,25 @@
         p++;
     }
     if (state == TEXT) {
-        out->push_back(string(q, p-q));
+        const char* start = q;
+        size_t len = p-q;
+        if (len > 2 && *start == '"' && start[len - 1] == '"') {
+            start++;
+            len -= 2;
+        }
+        out->push_back(string(start, len));
     }
 }
 
 static void
-add_file(vector<FileRecord>* files, const string& listFile, int listLine,
+add_file(vector<FileRecord>* files, const FileOpType fileOp,
+            const string& listFile, int listLine,
             const string& sourceName, const string& outName)
 {
     FileRecord rec;
     rec.listFile = listFile;
     rec.listLine = listLine;
+    rec.fileOp = fileOp;
     rec.sourceName = sourceName;
     rec.outName = outName;
     files->push_back(rec);
@@ -182,7 +202,7 @@
         err = errno;
         goto cleanup;
     }
-    
+
     size = ftell(f);
 
     err = fseek(f, 0, SEEK_SET);
@@ -245,35 +265,52 @@
             }
             printf("]\n");
 #endif
-            
-            if (words.size() == 1) {
-                // pattern: DEST
-                bool error = false;
-                string w0 = replace_variables(words[0], variables, &error);
-                if (error) {
-                    err = 1;
-                    goto cleanup;
+            FileOpType op = FILE_OP_COPY;
+            string paths[2];
+            int pcount = 0;
+            string errstr;
+            for (vector<string>::iterator it = words.begin(); it != words.end(); ++it) {
+                const string& word = *it;
+                if (word == "rm") {
+                    if (op != FILE_OP_COPY) {
+                        errstr = "Error: you can only specifiy 'rm' or 'strip' once per line.";
+                        break;
+                    }
+                    op = FILE_OP_REMOVE;
+                } else if (word == "strip") {
+                    if (op != FILE_OP_COPY) {
+                        errstr = "Error: you can only specifiy 'rm' or 'strip' once per line.";
+                        break;
+                    }
+                    op = FILE_OP_STRIP;
+                } else if (pcount < 2) {
+                    bool error = false;
+                    paths[pcount++] = replace_variables(word, variables, &error);
+                    if (error) {
+                        err = 1;
+                        goto cleanup;
+                    }
+                } else {
+                    errstr = "Error: More than 2 paths per line.";
+                    break;
                 }
-                add_file(files, filename, i+1, w0, w0);
             }
-            else if (words.size() == 2) {
-                // pattern: SRC DEST
-                bool error = false;
-                string w0, w1;
-                w0 = replace_variables(words[0], variables, &error);
-                if (!error) {
-                    w1 = replace_variables(words[1], variables, &error);
-                }
-                if (error) {
-                    err = 1;
-                    goto cleanup;
-                }
-                add_file(files, filename, i+1, w0, w1);
+
+            if (pcount == 0 && !errstr.empty()) {
+                errstr = "Error: No path found on line.";
             }
-            else {
-                fprintf(stderr, "%s:%d: bad format: %s\n", filename.c_str(),
-                        i+1, p);
+
+            if (!errstr.empty()) {
+                fprintf(stderr, "%s:%d: bad format: %s\n%s\nExpected: [SRC] [rm|strip] DEST\n",
+                        filename.c_str(), i+1, p, errstr.c_str());
                 err = 1;
+            } else {
+                if (pcount == 1) {
+                    // pattern: [rm|strip] DEST
+                    paths[1] = paths[0];
+                }
+
+                add_file(files, op, filename, i+1, paths[0], paths[1]);
             }
         }
         p = q;
@@ -293,6 +330,14 @@
 int
 locate(FileRecord* rec, const vector<string>& search)
 {
+    if (rec->fileOp == FILE_OP_REMOVE) {
+        // Don't touch source files when removing a destination.
+        rec->sourceMod = 0;
+        rec->sourceSize = 0;
+        rec->sourceIsDir = false;
+        return 0;
+    }
+
     int err;
 
     for (vector<string>::const_iterator it=search.begin();
@@ -304,6 +349,7 @@
             rec->sourceBase = *it;
             rec->sourcePath = full;
             rec->sourceMod = st.st_mtime;
+            rec->sourceSize = st.st_size;
             rec->sourceIsDir = S_ISDIR(st.st_mode);
             return 0;
         }
@@ -324,9 +370,11 @@
     err = stat(rec->outPath.c_str(), &st);
     if (err == 0) {
         rec->outMod = st.st_mtime;
+        rec->outSize = st.st_size;
         rec->outIsDir = S_ISDIR(st.st_mode);
     } else {
         rec->outMod = 0;
+        rec->outSize = 0;
         rec->outIsDir = false;
     }
 }
@@ -427,3 +475,8 @@
 {
     return list_dir("", rec, excludes, files);
 }
+
+FileRecord::FileRecord() {
+    fileOp = FILE_OP_COPY;
+}
+