+/*
+ * Insert a single file, or read a list of files from a file
+ */
+static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
+{
+ FILE *ffd;
+ char file[5000];
+ char *p = ua->cmd;
+ int line = 0;
+
+ switch (*p) {
+ case '<':
+ p++;
+ if ((ffd = fopen(p, "rb")) == NULL) {
+ berrno be;
+ ua->error_msg(_("Cannot open file %s: ERR=%s\n"),
+ p, be.strerror());
+ break;
+ }
+ while (fgets(file, sizeof(file), ffd)) {
+ line++;
+ if (dir) {
+ if (!insert_dir_into_findex_list(ua, rx, file, date)) {
+ ua->error_msg(_("Error occurred on line %d of file \"%s\"\n"), line, p);
+ }
+ } else {
+ if (!insert_file_into_findex_list(ua, rx, file, date)) {
+ ua->error_msg(_("Error occurred on line %d of file \"%s\"\n"), line, p);
+ }
+ }
+ }
+ fclose(ffd);
+ break;
+ case '?':
+ p++;
+ insert_table_into_findex_list(ua, rx, p);
+ break;
+ default:
+ if (dir) {
+ insert_dir_into_findex_list(ua, rx, ua->cmd, date);
+ } else {
+ insert_file_into_findex_list(ua, rx, ua->cmd, date);
+ }
+ break;
+ }
+}
+
+/*
+ * For a given file (path+filename), split into path and file, then
+ * lookup the most recent backup in the catalog to get the JobId
+ * and FileIndex, then insert them into the findex list.
+ */
+static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
+ char *date)
+{
+ strip_trailing_newline(file);
+ split_path_and_filename(rx, file);
+ if (*rx->JobIds == 0) {
+ Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
+ rx->ClientName);
+ } else {
+ Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
+ rx->path, rx->fname, rx->ClientName);
+ }
+ rx->found = false;
+ /* Find and insert jobid and File Index */
+ if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
+ ua->error_msg(_("Query failed: %s. ERR=%s\n"),
+ rx->query, db_strerror(ua->db));
+ }
+ if (!rx->found) {
+ ua->error_msg(_("No database record found for: %s\n"), file);
+// ua->error_msg("Query=%s\n", rx->query);
+ return true;
+ }
+ return true;
+}
+
+/*
+ * For a given path lookup the most recent backup in the catalog
+ * to get the JobId and FileIndexes of all files in that directory.
+ */
+static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
+ char *date)
+{
+ strip_trailing_junk(dir);
+ if (*rx->JobIds == 0) {
+ ua->error_msg(_("No JobId specified cannot continue.\n"));
+ return false;
+ } else {
+ Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
+ dir, rx->ClientName);
+ }
+ rx->found = false;
+ /* Find and insert jobid and File Index */
+ if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
+ ua->error_msg(_("Query failed: %s. ERR=%s\n"),
+ rx->query, db_strerror(ua->db));
+ }
+ if (!rx->found) {
+ ua->error_msg(_("No database record found for: %s\n"), dir);
+ return true;
+ }
+ return true;
+}
+
+/*
+ * Get the JobId and FileIndexes of all files in the specified table
+ */
+static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
+{
+ strip_trailing_junk(table);
+ Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
+
+ rx->found = false;
+ /* Find and insert jobid and File Index */
+ if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
+ ua->error_msg(_("Query failed: %s. ERR=%s\n"),
+ rx->query, db_strerror(ua->db));
+ }
+ if (!rx->found) {
+ ua->error_msg(_("No table found: %s\n"), table);
+ return true;
+ }
+ return true;
+}
+
+static void split_path_and_filename(RESTORE_CTX *rx, char *name)
+{
+ char *p, *f;
+
+ /* Find path without the filename.
+ * I.e. everything after the last / is a "filename".
+ * OK, maybe it is a directory name, but we treat it like
+ * a filename. If we don't find a / then the whole name
+ * must be a path name (e.g. c:).
+ */
+ for (p=f=name; *p; p++) {
+ if (IsPathSeparator(*p)) {
+ f = p; /* set pos of last slash */
+ }
+ }
+ if (IsPathSeparator(*f)) { /* did we find a slash? */
+ f++; /* yes, point to filename */
+ } else { /* no, whole thing must be path name */
+ f = p;
+ }
+
+ /* If filename doesn't exist (i.e. root directory), we
+ * simply create a blank name consisting of a single
+ * space. This makes handling zero length filenames
+ * easier.
+ */
+ rx->fnl = p - f;
+ if (rx->fnl > 0) {
+ rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
+ memcpy(rx->fname, f, rx->fnl); /* copy filename */
+ rx->fname[rx->fnl] = 0;
+ } else {
+ rx->fname[0] = 0;
+ rx->fnl = 0;
+ }
+
+ rx->pnl = f - name;
+ if (rx->pnl > 0) {
+ rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
+ memcpy(rx->path, name, rx->pnl);
+ rx->path[rx->pnl] = 0;
+ } else {
+ rx->path[0] = 0;
+ rx->pnl = 0;
+ }
+
+ Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
+}
+
+static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)