github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/make/tools/atree/files.cpp (about)

     1  #include "files.h"
     2  #include <stdio.h>
     3  #include <string.h>
     4  #include <stdlib.h>
     5  #include <errno.h>
     6  #include <sys/stat.h>
     7  #include <unistd.h>
     8  #include <dirent.h>
     9  #include <fnmatch.h>
    10  #include <string.h>
    11  #include <stdlib.h>
    12  
    13  static bool
    14  is_comment_line(const char* p)
    15  {
    16      while (*p && isspace(*p)) {
    17          p++;
    18      }
    19      return *p == '#';
    20  }
    21  
    22  static string
    23  path_append(const string& base, const string& leaf)
    24  {
    25      string full = base;
    26      if (base.length() > 0 && leaf.length() > 0) {
    27          full += '/';
    28      }
    29      full += leaf;
    30      return full;
    31  }
    32  
    33  static bool
    34  is_whitespace_line(const char* p)
    35  {
    36      while (*p) {
    37          if (!isspace(*p)) {
    38              return false;
    39          }
    40          p++;
    41      }
    42      return true;
    43  }
    44  
    45  static bool
    46  is_exclude_line(const char* p) {
    47      while (*p) {
    48          if (*p == '-') {
    49              return true;
    50          }
    51          else if (isspace(*p)) {
    52              p++;
    53          }
    54          else {
    55              return false;
    56          }
    57      }
    58      return false;
    59  }
    60  
    61  void
    62  split_line(const char* p, vector<string>* out)
    63  {
    64      const char* q = p;
    65      enum { WHITE, TEXT, IN_QUOTE } state = WHITE;
    66      while (*p) {
    67          if (*p == '#') {
    68              break;
    69          }
    70  
    71          switch (state)
    72          {
    73              case WHITE:
    74                  if (!isspace(*p)) {
    75                      q = p;
    76                      state = (*p == '"') ? IN_QUOTE : TEXT;
    77                  }
    78                  break;
    79              case IN_QUOTE:
    80                  if (*p == '"') {
    81                      state = TEXT;
    82                      break;
    83                  }
    84                  // otherwise fall-through to TEXT case
    85              case TEXT:
    86                  if (state != IN_QUOTE && isspace(*p)) {
    87                      if (q != p) {
    88                          const char* start = q;
    89                          size_t len = p-q;
    90                          if (len > 2 && *start == '"' && start[len - 1] == '"') {
    91                              start++;
    92                              len -= 2;
    93                          }
    94                          out->push_back(string(start, len));
    95                      }
    96                      state = WHITE;
    97                  }
    98                  break;
    99          }
   100          p++;
   101      }
   102      if (state == TEXT) {
   103          const char* start = q;
   104          size_t len = p-q;
   105          if (len > 2 && *start == '"' && start[len - 1] == '"') {
   106              start++;
   107              len -= 2;
   108          }
   109          out->push_back(string(start, len));
   110      }
   111  }
   112  
   113  static void
   114  add_file(vector<FileRecord>* files, const FileOpType fileOp,
   115              const string& listFile, int listLine,
   116              const string& sourceName, const string& outName)
   117  {
   118      FileRecord rec;
   119      rec.listFile = listFile;
   120      rec.listLine = listLine;
   121      rec.fileOp = fileOp;
   122      rec.sourceName = sourceName;
   123      rec.outName = outName;
   124      files->push_back(rec);
   125  }
   126  
   127  static string
   128  replace_variables(const string& input,
   129                    const map<string, string>& variables,
   130                    bool* error) {
   131      if (variables.empty()) {
   132          return input;
   133      }
   134  
   135      // Abort if the variable prefix is not found
   136      if (input.find("${") == string::npos) {
   137          return input;
   138      }
   139  
   140      string result = input;
   141  
   142      // Note: rather than be fancy to detect recursive replacements,
   143      // we simply iterate till a given threshold is met.
   144  
   145      int retries = 1000;
   146      bool did_replace;
   147  
   148      do {
   149          did_replace = false;
   150          for (map<string, string>::const_iterator it = variables.begin();
   151               it != variables.end(); ++it) {
   152              string::size_type pos = 0;
   153              while((pos = result.find(it->first, pos)) != string::npos) {
   154                  result = result.replace(pos, it->first.length(), it->second);
   155                  pos += it->second.length();
   156                  did_replace = true;
   157              }
   158          }
   159          if (did_replace && --retries == 0) {
   160              *error = true;
   161              fprintf(stderr, "Recursive replacement detected during variables "
   162                      "substitution. Full list of variables is: ");
   163  
   164              for (map<string, string>::const_iterator it = variables.begin();
   165                   it != variables.end(); ++it) {
   166                  fprintf(stderr, "  %s=%s\n",
   167                          it->first.c_str(), it->second.c_str());
   168              }
   169  
   170              return result;
   171          }
   172      } while (did_replace);
   173  
   174      return result;
   175  }
   176  
   177  int
   178  read_list_file(const string& filename,
   179                 const map<string, string>& variables,
   180                 vector<FileRecord>* files,
   181                 vector<string>* excludes)
   182  {
   183      int err = 0;
   184      FILE* f = NULL;
   185      long size;
   186      char* buf = NULL;
   187      char *p, *q;
   188      int i, lineCount;
   189  
   190      f = fopen(filename.c_str(), "r");
   191      if (f == NULL) {
   192          fprintf(stderr, "Could not open list file (%s): %s\n",
   193                      filename.c_str(), strerror(errno));
   194          err = errno;
   195          goto cleanup;
   196      }
   197  
   198      err = fseek(f, 0, SEEK_END);
   199      if (err != 0) {
   200          fprintf(stderr, "Could not seek to the end of file %s. (%s)\n",
   201                      filename.c_str(), strerror(errno));
   202          err = errno;
   203          goto cleanup;
   204      }
   205  
   206      size = ftell(f);
   207  
   208      err = fseek(f, 0, SEEK_SET);
   209      if (err != 0) {
   210          fprintf(stderr, "Could not seek to the beginning of file %s. (%s)\n",
   211                      filename.c_str(), strerror(errno));
   212          err = errno;
   213          goto cleanup;
   214      }
   215  
   216      buf = (char*)malloc(size+1);
   217      if (buf == NULL) {
   218          // (potentially large)
   219          fprintf(stderr, "out of memory (%ld)\n", size);
   220          err = ENOMEM;
   221          goto cleanup;
   222      }
   223  
   224      if (1 != fread(buf, size, 1, f)) {
   225          fprintf(stderr, "error reading file %s. (%s)\n",
   226                      filename.c_str(), strerror(errno));
   227          err = errno;
   228          goto cleanup;
   229      }
   230  
   231      // split on lines
   232      p = buf;
   233      q = buf+size;
   234      lineCount = 0;
   235      while (p<q) {
   236          if (*p == '\r' || *p == '\n') {
   237              *p = '\0';
   238              lineCount++;
   239          }
   240          p++;
   241      }
   242  
   243      // read lines
   244      p = buf;
   245      for (i=0; i<lineCount; i++) {
   246          int len = strlen(p);
   247          q = p + len + 1;
   248          if (is_whitespace_line(p) || is_comment_line(p)) {
   249              ;
   250          }
   251          else if (is_exclude_line(p)) {
   252              while (*p != '-') p++;
   253              p++;
   254              excludes->push_back(string(p));
   255          }
   256          else {
   257              vector<string> words;
   258  
   259              split_line(p, &words);
   260  
   261  #if 0
   262              printf("[ ");
   263              for (size_t k=0; k<words.size(); k++) {
   264                  printf("'%s' ", words[k].c_str());
   265              }
   266              printf("]\n");
   267  #endif
   268              FileOpType op = FILE_OP_COPY;
   269              string paths[2];
   270              int pcount = 0;
   271              string errstr;
   272              for (vector<string>::iterator it = words.begin(); it != words.end(); ++it) {
   273                  const string& word = *it;
   274                  if (word == "rm") {
   275                      if (op != FILE_OP_COPY) {
   276                          errstr = "Error: you can only specifiy 'rm' or 'strip' once per line.";
   277                          break;
   278                      }
   279                      op = FILE_OP_REMOVE;
   280                  } else if (word == "strip") {
   281                      if (op != FILE_OP_COPY) {
   282                          errstr = "Error: you can only specifiy 'rm' or 'strip' once per line.";
   283                          break;
   284                      }
   285                      op = FILE_OP_STRIP;
   286                  } else if (pcount < 2) {
   287                      bool error = false;
   288                      paths[pcount++] = replace_variables(word, variables, &error);
   289                      if (error) {
   290                          err = 1;
   291                          goto cleanup;
   292                      }
   293                  } else {
   294                      errstr = "Error: More than 2 paths per line.";
   295                      break;
   296                  }
   297              }
   298  
   299              if (pcount == 0 && !errstr.empty()) {
   300                  errstr = "Error: No path found on line.";
   301              }
   302  
   303              if (!errstr.empty()) {
   304                  fprintf(stderr, "%s:%d: bad format: %s\n%s\nExpected: [SRC] [rm|strip] DEST\n",
   305                          filename.c_str(), i+1, p, errstr.c_str());
   306                  err = 1;
   307              } else {
   308                  if (pcount == 1) {
   309                      // pattern: [rm|strip] DEST
   310                      paths[1] = paths[0];
   311                  }
   312  
   313                  add_file(files, op, filename, i+1, paths[0], paths[1]);
   314              }
   315          }
   316          p = q;
   317      }
   318  
   319  cleanup:
   320      if (buf != NULL) {
   321          free(buf);
   322      }
   323      if (f != NULL) {
   324          fclose(f);
   325      }
   326      return err;
   327  }
   328  
   329  
   330  int
   331  locate(FileRecord* rec, const vector<string>& search)
   332  {
   333      if (rec->fileOp == FILE_OP_REMOVE) {
   334          // Don't touch source files when removing a destination.
   335          rec->sourceMod = 0;
   336          rec->sourceSize = 0;
   337          rec->sourceIsDir = false;
   338          return 0;
   339      }
   340  
   341      int err;
   342  
   343      for (vector<string>::const_iterator it=search.begin();
   344                  it!=search.end(); it++) {
   345          string full = path_append(*it, rec->sourceName);
   346          struct stat st;
   347          err = stat(full.c_str(), &st);
   348          if (err == 0) {
   349              rec->sourceBase = *it;
   350              rec->sourcePath = full;
   351              rec->sourceMod = st.st_mtime;
   352              rec->sourceSize = st.st_size;
   353              rec->sourceIsDir = S_ISDIR(st.st_mode);
   354              return 0;
   355          }
   356      }
   357  
   358      fprintf(stderr, "%s:%d: couldn't locate source file: %s\n",
   359                  rec->listFile.c_str(), rec->listLine, rec->sourceName.c_str());
   360      return 1;
   361  }
   362  
   363  void
   364  stat_out(const string& base, FileRecord* rec)
   365  {
   366      rec->outPath = path_append(base, rec->outName);
   367  
   368      int err;
   369      struct stat st;
   370      err = stat(rec->outPath.c_str(), &st);
   371      if (err == 0) {
   372          rec->outMod = st.st_mtime;
   373          rec->outSize = st.st_size;
   374          rec->outIsDir = S_ISDIR(st.st_mode);
   375      } else {
   376          rec->outMod = 0;
   377          rec->outSize = 0;
   378          rec->outIsDir = false;
   379      }
   380  }
   381  
   382  string
   383  dir_part(const string& filename)
   384  {
   385      int pos = filename.rfind('/');
   386      if (pos <= 0) {
   387          return ".";
   388      }
   389      return filename.substr(0, pos);
   390  }
   391  
   392  static void
   393  add_more(const string& entry, bool isDir,
   394           const FileRecord& rec, vector<FileRecord>*more)
   395  {
   396      FileRecord r;
   397      r.listFile = rec.listFile;
   398      r.listLine = rec.listLine;
   399      r.sourceName = path_append(rec.sourceName, entry);
   400      r.sourcePath = path_append(rec.sourceBase, r.sourceName);
   401      struct stat st;
   402      int err = stat(r.sourcePath.c_str(), &st);
   403      if (err == 0) {
   404          r.sourceMod = st.st_mtime;
   405      }
   406      r.sourceIsDir = isDir;
   407      r.outName = path_append(rec.outName, entry);
   408      more->push_back(r);
   409  }
   410  
   411  static bool
   412  matches_excludes(const char* file, const vector<string>& excludes)
   413  {
   414      for (vector<string>::const_iterator it=excludes.begin();
   415              it!=excludes.end(); it++) {
   416          if (0 == fnmatch(it->c_str(), file, FNM_PERIOD)) {
   417              return true;
   418          }
   419      }
   420      return false;
   421  }
   422  
   423  static int
   424  list_dir(const string& path, const FileRecord& rec,
   425                  const vector<string>& excludes,
   426                  vector<FileRecord>* more)
   427  {
   428      string full = path_append(rec.sourceBase, rec.sourceName);
   429      full = path_append(full, path);
   430  
   431      DIR *d = opendir(full.c_str());
   432      if (d == NULL) {
   433          return errno;
   434      }
   435  
   436      vector<string> dirs;
   437  
   438      struct dirent *ent;
   439      while (NULL != (ent = readdir(d))) {
   440          if (0 == strcmp(".", ent->d_name)
   441                  || 0 == strcmp("..", ent->d_name)) {
   442              continue;
   443          }
   444          if (matches_excludes(ent->d_name, excludes)) {
   445              continue;
   446          }
   447          string entry = path_append(path, ent->d_name);
   448          bool is_directory = (ent->d_type == DT_DIR);
   449          add_more(entry, is_directory, rec, more);
   450          if (is_directory) {
   451              dirs.push_back(entry);
   452          }
   453      }
   454      closedir(d);
   455  
   456      for (vector<string>::iterator it=dirs.begin(); it!=dirs.end(); it++) {
   457          list_dir(*it, rec, excludes, more);
   458      }
   459  
   460      return 0;
   461  }
   462  
   463  int
   464  list_dir(const FileRecord& rec, const vector<string>& excludes,
   465              vector<FileRecord>* files)
   466  {
   467      return list_dir("", rec, excludes, files);
   468  }
   469  
   470  FileRecord::FileRecord() {
   471      fileOp = FILE_OP_COPY;
   472  }
   473