#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include enum { PROGRESS_FLAG_NORMAL, PROGRESS_FLAG_INIT, PROGRESS_FLAG_DONE }; void do_notify_progress(long long total, int flag) { const char *du_size_env = getenv("FILECOPY_TOTAL_SIZE"); const char *progress_type_env = getenv("PROGRESS_TYPE"); const char *saved_stdout_env = getenv("SAVED_FD_1"); int ignore; if (!progress_type_env) return; if (!strcmp(progress_type_env, "console") && du_size_env) { char msg[256]; snprintf(msg, sizeof(msg), "sent %lld/%lld KB\r", total / 1024, strtoull(du_size_env, NULL, 0)); ignore = write(2, msg, strlen(msg)); if (flag == PROGRESS_FLAG_DONE) ignore = write(2, "\n", 1); if (ignore < 0) { /* silence gcc warning */ } } if (!strcmp(progress_type_env, "gui") && saved_stdout_env) { char msg[256]; snprintf(msg, sizeof(msg), "%lld\n", total); if (write(strtoul(saved_stdout_env, NULL, 0), msg, strlen(msg)) == -1 && errno == EPIPE) exit(32); } } void notify_progress(int size, int flag) { static long long total = 0; static long long prev_total = 0; total += size; if (total > prev_total + PROGRESS_NOTIFY_DELTA || (flag != PROGRESS_FLAG_NORMAL)) { // check for possible error from qfile-unpacker; if error occured, // exit() will be called, so don't bother with current state // (notify_progress can be called as callback from copy_file()) if (flag == PROGRESS_FLAG_NORMAL) wait_for_result(); do_notify_progress(total, flag); prev_total = total; } } char *get_abs_path(const char *cwd, const char *pathname) { char *ret; if (pathname[0] == '/') return strdup(pathname); if (asprintf(&ret, "%s/%s", cwd, pathname) < 0) return NULL; else return ret; } int main(int argc, char **argv) { int i; char *entry; char *cwd; char *sep; int ignore_symlinks = 0; qfile_pack_init(); register_error_handler(qfile_gui_fatal); register_notify_progress(¬ify_progress); notify_progress(0, PROGRESS_FLAG_INIT); cwd = getcwd(NULL, 0); for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--ignore-symlinks")==0) { ignore_symlinks = 1; continue; } entry = get_abs_path(cwd, argv[i]); do { sep = rindex(entry, '/'); if (!sep) gui_fatal ("Internal error: nonabsolute filenames not allowed"); *sep = 0; } while (sep[1] == 0); if (entry[0] == 0) { if (chdir("/") < 0) { gui_fatal("Internal error: chdir(\"/\") failed?!"); } } else if (chdir(entry)) gui_fatal("chdir to %s", entry); do_fs_walk(sep + 1, ignore_symlinks); free(entry); } notify_end_and_wait_for_result(); notify_progress(0, PROGRESS_FLAG_DONE); return 0; }