diff --git a/lib/file.c b/lib/file.c index f43d758..f38fe9b 100644 --- a/lib/file.c +++ b/lib/file.c @@ -987,6 +987,21 @@ bool unshield_file_iso_date (Unshield* unshield, int index, char* buf, size_t si return fd ? true : false; }/*}}}*/ +time_t unshield_file_timestamp (Unshield* unshield, int index)/*{{{*/ +{ + FileDescriptor* fd = unshield_get_file_descriptor(unshield, index); + struct tm date; + + if (fd) + { + unshield_dos_to_tm(fd->dos_date, fd->dos_time, &date); + return mktime(&date); + } + + unshield_warning("Failed to get file descriptor %i", index); + return 0; +}/*}}}*/ + bool unshield_file_save_raw(Unshield* unshield, int index, const char* filename) { /* XXX: Thou Shalt Not Cut & Paste... */ diff --git a/lib/libunshield.h b/lib/libunshield.h index fbdc350..9ae50eb 100644 --- a/lib/libunshield.h +++ b/lib/libunshield.h @@ -110,6 +110,7 @@ UNSHIELD_API bool unshield_file_save (Unshield* unshield, int in UNSHIELD_API int unshield_file_directory (Unshield* unshield, int index); UNSHIELD_API size_t unshield_file_size (Unshield* unshield, int index); UNSHIELD_API bool unshield_file_iso_date (Unshield* unshield, int index, char* buf, size_t size); +UNSHIELD_API time_t unshield_file_timestamp (Unshield* unshield, int index); /** For investigation of compressed data */ UNSHIELD_API bool unshield_file_save_raw(Unshield* unshield, int index, const char* filename); diff --git a/man/unshield.1 b/man/unshield.1 index 284dc67..271e6c9 100644 --- a/man/unshield.1 +++ b/man/unshield.1 @@ -1,8 +1,8 @@ -.TH UNSHIELD "1" "November 2023" "The Unshield project" "https://github.com/twogood/unshield" +.TH UNSHIELD "1" "December 2024" "The Unshield project" "https://github.com/twogood/unshield" .SH NAME unshield \- extract CAB files from an InstallShield installer archive .SH SYNOPSIS -unshield [\-c COMPONENT] [\-d DIRECTORY] [\-D LEVEL] [\-g GROUP] [\-h] [\-i VERSION] [\-e ENCODING] [\-j] [\-L] [\-O] [\-r] [\-R] [\-V] c|g|l|t|x CABFILE [FILENAME...] +unshield [\-c COMPONENT] [\-d DIRECTORY] [\-D LEVEL] [\-g GROUP] [\-h] [\-i VERSION] [\-e ENCODING] [\-j] [\-L] [\-O] [\-r] [\-R] [\-t] [\-V] c|g|l|t|x CABFILE [FILENAME...] .SH DESCRIPTION Unshield extracts CAB files from InstallShield installers, used to install software on Microsoft Windows based machines. @@ -50,6 +50,9 @@ Save raw data (do not decompress) \fB\-R\fR Don't do any conversion to file and directory names when extracting .TP +\fB-t\fR +Set the modification date/time from the metadata when extracting +.TP \fB\-V\fR, \fB\-\-version\fR Print version information .SS "Commands:" diff --git a/src/unshield.c b/src/unshield.c index 1793356..c138df9 100644 --- a/src/unshield.c +++ b/src/unshield.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "../lib/libunshield.h" #ifdef HAVE_CONFIG_H @@ -68,6 +69,7 @@ static const char* component_name = NULL; static bool junk_paths = false; static bool make_lowercase = false; static bool raw_filename = false; +static bool set_timestamp = false; static ACTION action = ACTION_EXTRACT; static int log_level = UNSHIELD_LOG_LEVEL_LOWEST; static int exit_status = 0; @@ -192,7 +194,7 @@ static void show_usage(const char* name) fprintf(stderr, "Syntax:\n" "\n" - "\t%s [-c COMPONENT] [-d DIRECTORY] [-D LEVEL] [-g GROUP] [-h] [-i VERSION] [-e ENCODING] [-j] [-L] [-O] [-r] [-R] [-V] c|g|l|t|x CABFILE [FILENAME...]\n" + "\t%s [-c COMPONENT] [-d DIRECTORY] [-D LEVEL] [-g GROUP] [-h] [-i VERSION] [-e ENCODING] [-j] [-L] [-O] [-r] [-R] [-t] [-V] c|g|l|t|x CABFILE [FILENAME...]\n" "\n" "Options:\n" "\t-c COMPONENT Only list/extract this component\n" @@ -211,6 +213,7 @@ static void show_usage(const char* name) "\t-O Use old compression\n" "\t-r Save raw data (do not decompress)\n" "\t-R Don't do any conversion to file and directory names when extracting\n" + "\t-t Set the modification date/time from the metadata when extracting\n" "\t-V --version Print copyright and version information\n" "\n" "Commands:\n" @@ -243,7 +246,7 @@ static bool handle_parameters( { NULL, 0, NULL, 0 } }; - while ((c = getopt_long(argc, argv, "c:d:D:g:hi:e:jLOrRV", long_options, NULL)) != -1) + while ((c = getopt_long(argc, argv, "c:d:D:g:hi:e:jLOrRtV", long_options, NULL)) != -1) { switch (c) { @@ -297,6 +300,10 @@ static bool handle_parameters( format = FORMAT_RAW; break; + case 't': + set_timestamp = true; + break; + case 'V': printf("Unshield version " VERSION ". MIT License. (C) 2003-2023 David Eriksson.\n"); exit(0); @@ -540,6 +547,18 @@ static bool extract_file(Unshield* unshield, const char* prefix, int index) break; } + if (success && set_timestamp) + { + /* XXX: Use file function wrappers? */ + struct utimbuf utim; + struct stat info; + + stat(filename, &info); + utim.actime = info.st_atim.tv_sec; + utim.modtime = unshield_file_timestamp(unshield, index); + utime(filename, &utim); + } + exit: if (!success) {