atree.cpp revision dcc08f073b6873c69ab891d4f69f7c568e282df7
1#include <stdio.h> 2#include <string.h> 3#include <unistd.h> 4#include "options.h" 5#include "files.h" 6#include "fs.h" 7#include <set> 8#include <iostream> 9#include <sstream> 10 11using namespace std; 12 13bool g_debug = false; 14vector<string> g_listFiles; 15vector<string> g_inputBases; 16map<string, string> g_variables; 17string g_outputBase; 18string g_dependency; 19bool g_useHardLinks = false; 20 21const char* USAGE = 22"\n" 23"Usage: atree OPTIONS\n" 24"\n" 25"Options:\n" 26" -f FILELIST Specify one or more files containing the\n" 27" list of files to copy.\n" 28" -I INPUTDIR Specify one or more base directories in\n" 29" which to look for the files\n" 30" -o OUTPUTDIR Specify the directory to copy all of the\n" 31" output files to.\n" 32" -l Use hard links instead of copying the files.\n" 33" -m DEPENDENCY Output a make-formatted file containing the list.\n" 34" of files included. It sets the variable ATREE_FILES.\n" 35" -v VAR=VAL Replaces ${VAR} by VAL when reading input files.\n" 36"\n" 37"FILELIST file format:\n" 38" The FILELIST files contain the list of files that will end up\n" 39" in the final OUTPUTDIR. Atree will look for files in the INPUTDIR\n" 40" directories in the order they are specified.\n" 41"\n" 42" In a FILELIST file, comment lines start with a #. Other lines\n" 43" are of the format:\n" 44"\n" 45" DEST\n" 46" SRC DEST\n" 47" -SRCPATTERN\n" 48"\n" 49" DEST should be path relative to the output directory.\n" 50" If SRC is supplied, the file names can be different.\n" 51" SRCPATTERN is a pattern for the filenames.\n" 52"\n"; 53 54int usage() 55{ 56 fwrite(USAGE, strlen(USAGE), 1, stderr); 57 return 1; 58} 59 60static bool 61add_variable(const char* arg) { 62 const char* p = arg; 63 while (*p && *p != '=') p++; 64 65 if (*p == 0 || p == arg || p[1] == 0) { 66 return false; 67 } 68 69 ostringstream var; 70 var << "${" << string(arg, p-arg) << "}"; 71 g_variables[var.str()] = string(p+1); 72 return true; 73} 74 75int 76main(int argc, char* const* argv) 77{ 78 int err; 79 bool done = false; 80 while (!done) { 81 int opt = getopt(argc, argv, "f:I:o:hlm:v:"); 82 switch (opt) 83 { 84 case -1: 85 done = true; 86 break; 87 case 'f': 88 g_listFiles.push_back(string(optarg)); 89 break; 90 case 'I': 91 g_inputBases.push_back(string(optarg)); 92 break; 93 case 'o': 94 if (g_outputBase.length() != 0) { 95 fprintf(stderr, "%s: -o may only be supplied once -- " 96 "-o %s\n", argv[0], optarg); 97 return usage(); 98 } 99 g_outputBase = optarg; 100 break; 101 case 'l': 102 g_useHardLinks = true; 103 break; 104 case 'm': 105 if (g_dependency.length() != 0) { 106 fprintf(stderr, "%s: -m may only be supplied once -- " 107 "-m %s\n", argv[0], optarg); 108 return usage(); 109 } 110 g_dependency = optarg; 111 break; 112 case 'v': 113 if (!add_variable(optarg)) { 114 fprintf(stderr, "%s Invalid expression in '-v %s': " 115 "expected format is '-v VAR=VALUE'.\n", 116 argv[0], optarg); 117 return usage(); 118 } 119 break; 120 default: 121 case '?': 122 case 'h': 123 return usage(); 124 } 125 } 126 if (optind != argc) { 127 fprintf(stderr, "%s: invalid argument -- %s\n", argv[0], argv[optind]); 128 return usage(); 129 } 130 131 if (g_listFiles.size() == 0) { 132 fprintf(stderr, "%s: At least one -f option must be supplied.\n", 133 argv[0]); 134 return usage(); 135 } 136 137 if (g_inputBases.size() == 0) { 138 fprintf(stderr, "%s: At least one -I option must be supplied.\n", 139 argv[0]); 140 return usage(); 141 } 142 143 if (g_outputBase.length() == 0) { 144 fprintf(stderr, "%s: -o option must be supplied.\n", argv[0]); 145 return usage(); 146 } 147 148 149#if 0 150 for (vector<string>::iterator it=g_listFiles.begin(); 151 it!=g_listFiles.end(); it++) { 152 printf("-f \"%s\"\n", it->c_str()); 153 } 154 for (vector<string>::iterator it=g_inputBases.begin(); 155 it!=g_inputBases.end(); it++) { 156 printf("-I \"%s\"\n", it->c_str()); 157 } 158 printf("-o \"%s\"\n", g_outputBase.c_str()); 159 if (g_useHardLinks) { 160 printf("-l\n"); 161 } 162#endif 163 164 vector<FileRecord> files; 165 vector<FileRecord> more; 166 vector<string> excludes; 167 set<string> directories; 168 set<string> deleted; 169 170 // read file lists 171 for (vector<string>::iterator it=g_listFiles.begin(); 172 it!=g_listFiles.end(); it++) { 173 err = read_list_file(*it, g_variables, &files, &excludes); 174 if (err != 0) { 175 return err; 176 } 177 } 178 179 // look for input files 180 err = 0; 181 for (vector<FileRecord>::iterator it=files.begin(); 182 it!=files.end(); it++) { 183 err |= locate(&(*it), g_inputBases); 184 185 } 186 187 // expand the directories that we should copy into a list of files 188 for (vector<FileRecord>::iterator it=files.begin(); 189 it!=files.end(); it++) { 190 if (it->sourceIsDir) { 191 err |= list_dir(*it, excludes, &more); 192 } 193 } 194 for (vector<FileRecord>::iterator it=more.begin(); 195 it!=more.end(); it++) { 196 files.push_back(*it); 197 } 198 199 // get the name and modtime of the output files 200 for (vector<FileRecord>::iterator it=files.begin(); 201 it!=files.end(); it++) { 202 stat_out(g_outputBase, &(*it)); 203 } 204 205 if (err != 0) { 206 return 1; 207 } 208 209 // gather directories 210 for (vector<FileRecord>::iterator it=files.begin(); 211 it!=files.end(); it++) { 212 if (it->sourceIsDir) { 213 directories.insert(it->outPath); 214 } else { 215 string s = dir_part(it->outPath); 216 if (s != ".") { 217 directories.insert(s); 218 } 219 } 220 } 221 222 // gather files that should become directores and directories that should 223 // become files 224 for (vector<FileRecord>::iterator it=files.begin(); 225 it!=files.end(); it++) { 226 if (it->outMod != 0 && it->sourceIsDir != it->outIsDir) { 227 deleted.insert(it->outPath); 228 } 229 } 230 231 // delete files 232 for (set<string>::iterator it=deleted.begin(); 233 it!=deleted.end(); it++) { 234 if (g_debug) { 235 printf("deleting %s\n", it->c_str()); 236 } 237 err = remove_recursively(*it); 238 if (err != 0) { 239 return err; 240 } 241 } 242 243 // make directories 244 for (set<string>::iterator it=directories.begin(); 245 it!=directories.end(); it++) { 246 if (g_debug) { 247 printf("mkdir %s\n", it->c_str()); 248 } 249 err = mkdir_recursively(*it); 250 if (err != 0) { 251 return err; 252 } 253 } 254 255 // copy (or link) files 256 for (vector<FileRecord>::iterator it=files.begin(); 257 it!=files.end(); it++) { 258 if (!it->sourceIsDir) { 259 if (g_debug) { 260 printf("copy %s(%ld) ==> %s(%ld)", it->sourcePath.c_str(), 261 it->sourceMod, it->outPath.c_str(), it->outMod); 262 fflush(stdout); 263 } 264 265 if (it->outMod < it->sourceMod) { 266 err = copy_file(it->sourcePath, it->outPath); 267 if (g_debug) { 268 printf(" done.\n"); 269 } 270 if (err != 0) { 271 return err; 272 } 273 } else { 274 if (g_debug) { 275 printf(" skipping.\n"); 276 } 277 } 278 } 279 } 280 281 // output the dependency file 282 if (g_dependency.length() != 0) { 283 FILE *f = fopen(g_dependency.c_str(), "w"); 284 if (f != NULL) { 285 fprintf(f, "ATREE_FILES := $(ATREE_FILES) \\\n"); 286 for (vector<FileRecord>::iterator it=files.begin(); 287 it!=files.end(); it++) { 288 if (!it->sourceIsDir) { 289 fprintf(f, "%s \\\n", it->sourcePath.c_str()); 290 } 291 } 292 fprintf(f, "\n"); 293 fclose(f); 294 } else { 295 fprintf(stderr, "error opening manifest file for write: %s\n", 296 g_dependency.c_str()); 297 } 298 } 299 300 return 0; 301} 302