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
11 using namespace std;
12
13 bool g_debug = false;
14 vector<string> g_listFiles;
15 vector<string> g_inputBases;
16 map<string, string> g_variables;
17 string g_outputBase;
18 string g_dependency;
19 bool g_useHardLinks = false;
20
21 const 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
usage()54 int usage()
55 {
56 fwrite(USAGE, strlen(USAGE), 1, stderr);
57 return 1;
58 }
59
60 static bool
add_variable(const char * arg)61 add_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
75 int
main(int argc,char * const * argv)76 main(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