files.cpp revision 8ae3ad5802c7fe78d6b353b0d9090276a4f6a210
1#include "files.h" 2#include <stdio.h> 3#include <errno.h> 4#include <sys/stat.h> 5#include <unistd.h> 6#include <dirent.h> 7#include <fnmatch.h> 8#include <string.h> 9#include <stdlib.h> 10 11static bool 12is_comment_line(const char* p) 13{ 14 while (*p && isspace(*p)) { 15 p++; 16 } 17 return *p == '#'; 18} 19 20static string 21path_append(const string& base, const string& leaf) 22{ 23 string full = base; 24 if (base.length() > 0 && leaf.length() > 0) { 25 full += '/'; 26 } 27 full += leaf; 28 return full; 29} 30 31static bool 32is_whitespace_line(const char* p) 33{ 34 while (*p) { 35 if (!isspace(*p)) { 36 return false; 37 } 38 p++; 39 } 40 return true; 41} 42 43static bool 44is_exclude_line(const char* p) { 45 while (*p) { 46 if (*p == '-') { 47 return true; 48 } 49 else if (isspace(*p)) { 50 p++; 51 } 52 else { 53 return false; 54 } 55 } 56 return false; 57} 58 59void 60split_line(const char* p, vector<string>* out) 61{ 62 const char* q = p; 63 enum { WHITE, TEXT } state = WHITE; 64 while (*p) { 65 if (*p == '#') { 66 break; 67 } 68 69 switch (state) 70 { 71 case WHITE: 72 if (!isspace(*p)) { 73 q = p; 74 state = TEXT; 75 } 76 break; 77 case TEXT: 78 if (isspace(*p)) { 79 if (q != p) { 80 out->push_back(string(q, p-q)); 81 } 82 state = WHITE; 83 } 84 break; 85 } 86 p++; 87 } 88 if (state == TEXT) { 89 out->push_back(string(q, p-q)); 90 } 91} 92 93static void 94add_file(vector<FileRecord>* files, const string& listFile, int listLine, 95 const string& sourceName, const string& outName) 96{ 97 FileRecord rec; 98 rec.listFile = listFile; 99 rec.listLine = listLine; 100 rec.sourceName = sourceName; 101 rec.outName = outName; 102 files->push_back(rec); 103} 104 105int 106read_list_file(const string& filename, vector<FileRecord>* files, 107 vector<string>* excludes) 108{ 109 int err = 0; 110 FILE* f = NULL; 111 long size; 112 char* buf = NULL; 113 char *p, *q; 114 int i, lineCount; 115 116 f = fopen(filename.c_str(), "r"); 117 if (f == NULL) { 118 fprintf(stderr, "Could not open list file (%s): %s\n", 119 filename.c_str(), strerror(errno)); 120 err = errno; 121 goto cleanup; 122 } 123 124 err = fseek(f, 0, SEEK_END); 125 if (err != 0) { 126 fprintf(stderr, "Could not seek to the end of file %s. (%s)\n", 127 filename.c_str(), strerror(errno)); 128 err = errno; 129 goto cleanup; 130 } 131 132 size = ftell(f); 133 134 err = fseek(f, 0, SEEK_SET); 135 if (err != 0) { 136 fprintf(stderr, "Could not seek to the beginning of file %s. (%s)\n", 137 filename.c_str(), strerror(errno)); 138 err = errno; 139 goto cleanup; 140 } 141 142 buf = (char*)malloc(size+1); 143 if (buf == NULL) { 144 // (potentially large) 145 fprintf(stderr, "out of memory (%ld)\n", size); 146 err = ENOMEM; 147 goto cleanup; 148 } 149 150 if (1 != fread(buf, size, 1, f)) { 151 fprintf(stderr, "error reading file %s. (%s)\n", 152 filename.c_str(), strerror(errno)); 153 err = errno; 154 goto cleanup; 155 } 156 157 // split on lines 158 p = buf; 159 q = buf+size; 160 lineCount = 0; 161 while (p<q) { 162 if (*p == '\r' || *p == '\n') { 163 *p = '\0'; 164 lineCount++; 165 } 166 p++; 167 } 168 169 // read lines 170 p = buf; 171 for (i=0; i<lineCount; i++) { 172 int len = strlen(p); 173 q = p + len + 1; 174 if (is_whitespace_line(p) || is_comment_line(p)) { 175 ; 176 } 177 else if (is_exclude_line(p)) { 178 while (*p != '-') p++; 179 p++; 180 excludes->push_back(string(p)); 181 } 182 else { 183 vector<string> words; 184 185 split_line(p, &words); 186 187#if 0 188 printf("[ "); 189 for (size_t k=0; k<words.size(); k++) { 190 printf("'%s' ", words[k].c_str()); 191 } 192 printf("]\n"); 193#endif 194 195 if (words.size() == 1) { 196 // pattern: DEST 197 add_file(files, filename, i+1, words[0], words[0]); 198 } 199 else if (words.size() == 2) { 200 // pattern: SRC DEST 201 add_file(files, filename, i+1, words[0], words[1]); 202 } 203 else { 204 fprintf(stderr, "%s:%d: bad format: %s\n", filename.c_str(), 205 i+1, p); 206 err = 1; 207 } 208 } 209 p = q; 210 } 211 212cleanup: 213 if (buf != NULL) { 214 free(buf); 215 } 216 if (f != NULL) { 217 fclose(f); 218 } 219 return err; 220} 221 222 223int 224locate(FileRecord* rec, const vector<string>& search) 225{ 226 int err; 227 228 for (vector<string>::const_iterator it=search.begin(); 229 it!=search.end(); it++) { 230 string full = path_append(*it, rec->sourceName); 231 struct stat st; 232 err = stat(full.c_str(), &st); 233 if (err == 0) { 234 rec->sourceBase = *it; 235 rec->sourcePath = full; 236 rec->sourceMod = st.st_mtime; 237 rec->sourceIsDir = S_ISDIR(st.st_mode); 238 return 0; 239 } 240 } 241 242 fprintf(stderr, "%s:%d: couldn't locate source file: %s\n", 243 rec->listFile.c_str(), rec->listLine, rec->sourceName.c_str()); 244 return 1; 245} 246 247void 248stat_out(const string& base, FileRecord* rec) 249{ 250 rec->outPath = path_append(base, rec->outName); 251 252 int err; 253 struct stat st; 254 err = stat(rec->outPath.c_str(), &st); 255 if (err == 0) { 256 rec->outMod = st.st_mtime; 257 rec->outIsDir = S_ISDIR(st.st_mode); 258 } else { 259 rec->outMod = 0; 260 rec->outIsDir = false; 261 } 262} 263 264string 265dir_part(const string& filename) 266{ 267 int pos = filename.rfind('/'); 268 if (pos <= 0) { 269 return "."; 270 } 271 return filename.substr(0, pos); 272} 273 274static void 275add_more(const string& entry, bool isDir, 276 const FileRecord& rec, vector<FileRecord>*more) 277{ 278 FileRecord r; 279 r.listFile = rec.listFile; 280 r.listLine = rec.listLine; 281 r.sourceName = path_append(rec.sourceName, entry); 282 r.sourcePath = path_append(rec.sourceBase, r.sourceName); 283 struct stat st; 284 int err = stat(r.sourcePath.c_str(), &st); 285 if (err == 0) { 286 r.sourceMod = st.st_mtime; 287 } 288 r.sourceIsDir = isDir; 289 r.outName = path_append(rec.outName, entry); 290 more->push_back(r); 291} 292 293static bool 294matches_excludes(const char* file, const vector<string>& excludes) 295{ 296 for (vector<string>::const_iterator it=excludes.begin(); 297 it!=excludes.end(); it++) { 298 if (0 == fnmatch(it->c_str(), file, FNM_PERIOD)) { 299 return true; 300 } 301 } 302 return false; 303} 304 305static int 306list_dir(const string& path, const FileRecord& rec, 307 const vector<string>& excludes, 308 vector<FileRecord>* more) 309{ 310 int err; 311 312 string full = path_append(rec.sourceBase, rec.sourceName); 313 full = path_append(full, path); 314 315 DIR *d = opendir(full.c_str()); 316 if (d == NULL) { 317 return errno; 318 } 319 320 vector<string> dirs; 321 322 struct dirent *ent; 323 while (NULL != (ent = readdir(d))) { 324 if (0 == strcmp(".", ent->d_name) 325 || 0 == strcmp("..", ent->d_name)) { 326 continue; 327 } 328 if (matches_excludes(ent->d_name, excludes)) { 329 continue; 330 } 331 string entry = path_append(path, ent->d_name); 332#ifdef HAVE_DIRENT_D_TYPE 333 bool is_directory = (ent->d_type == DT_DIR); 334#else 335 // If dirent.d_type is missing, then use stat instead 336 struct stat stat_buf; 337 stat(entry.c_str(), &stat_buf); 338 bool is_directory = S_ISDIR(stat_buf.st_mode); 339#endif 340 add_more(entry, is_directory, rec, more); 341 if (is_directory) { 342 dirs.push_back(entry); 343 } 344 } 345 closedir(d); 346 347 for (vector<string>::iterator it=dirs.begin(); it!=dirs.end(); it++) { 348 list_dir(*it, rec, excludes, more); 349 } 350 351 return 0; 352} 353 354int 355list_dir(const FileRecord& rec, const vector<string>& excludes, 356 vector<FileRecord>* files) 357{ 358 return list_dir("", rec, excludes, files); 359} 360