+static void
+timespec_to_string(const struct timespec *spec, tchar *buf)
+{
+ time_t t = spec->tv_sec;
+ struct tm tm;
+ gmtime_r(&t, &tm);
+ tstrftime(buf, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
+ buf[TIMESTR_MAX - 1] = '\0';
+}
+
+static void
+print_time(const tchar *type, const struct timespec *spec)
+{
+ tchar timestr[TIMESTR_MAX];
+
+ timespec_to_string(spec, timestr);
+
+ tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
+}
+
+static void print_byte_field(const uint8_t field[], size_t len)
+{
+ while (len--)
+ tprintf(T("%02hhx"), *field++);
+}
+
+static void
+print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
+{
+ tputs(T("WIM Information:"));
+ tputs(T("----------------"));
+ tprintf(T("Path: %"TS"\n"), wimfile);
+ tprintf(T("GUID: 0x"));
+ print_byte_field(info->guid, sizeof(info->guid));
+ tputchar(T('\n'));
+ tprintf(T("Version: %u\n"), info->wim_version);
+ tprintf(T("Image Count: %d\n"), info->image_count);
+ tprintf(T("Compression: %"TS"\n"),
+ wimlib_get_compression_type_string(info->compression_type));
+ tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
+ info->chunk_size);
+ tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
+ tprintf(T("Boot Index: %d\n"), info->boot_index);
+ tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
+ tprintf(T("Integrity Info: %"TS"\n"),
+ info->has_integrity_table ? T("yes") : T("no"));
+ tprintf(T("Relative path junction: %"TS"\n"),
+ info->has_rpfix ? T("yes") : T("no"));
+ tprintf(T("Pipable: %"TS"\n"),
+ info->pipable ? T("yes") : T("no"));
+ tputchar(T('\n'));
+}
+
+static int
+print_resource(const struct wimlib_resource_entry *resource,
+ void *_ignore)
+{
+ tprintf(T("Hash = 0x"));
+ print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
+ tputchar(T('\n'));
+
+ if (!resource->is_missing) {
+ tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
+ resource->uncompressed_size);
+ if (resource->packed) {
+ tprintf(T("Raw compressed size = %"PRIu64" bytes\n"),
+ resource->raw_resource_compressed_size);
+
+ tprintf(T("Raw offset in WIM = %"PRIu64" bytes\n"),
+ resource->raw_resource_offset_in_wim);
+
+ tprintf(T("Offset in raw = %"PRIu64" bytes\n"),
+ resource->offset);
+ } else {
+ tprintf(T("Compressed size = %"PRIu64" bytes\n"),
+ resource->compressed_size);
+
+ tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
+ resource->offset);
+ }
+
+ tprintf(T("Part Number = %u\n"), resource->part_number);
+ tprintf(T("Reference Count = %u\n"), resource->reference_count);
+
+ tprintf(T("Flags = "));
+ if (resource->is_compressed)
+ tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
+ if (resource->is_metadata)
+ tprintf(T("WIM_RESHDR_FLAG_METADATA "));
+ if (resource->is_free)
+ tprintf(T("WIM_RESHDR_FLAG_FREE "));
+ if (resource->is_spanned)
+ tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
+ if (resource->packed)
+ tprintf(T("WIM_RESHDR_FLAG_PACKED_STREAMS "));
+ tputchar(T('\n'));
+ }
+ tputchar(T('\n'));
+ return 0;
+}
+
+static void
+print_lookup_table(WIMStruct *wim)
+{
+ wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
+}
+
+static void
+default_print_security_descriptor(const uint8_t *sd, size_t size)
+{
+ tprintf(T("Security Descriptor = "));
+ print_byte_field(sd, size);
+ tputchar(T('\n'));
+}
+
+static void
+print_dentry_detailed(const struct wimlib_dir_entry *dentry)
+{
+
+ tprintf(T(
+"----------------------------------------------------------------------------\n"));
+ tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
+ if (dentry->dos_name)
+ tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
+ tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
+ for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
+ if (file_attr_flags[i].flag & dentry->attributes)
+ tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
+ file_attr_flags[i].name);
+
+ if (dentry->security_descriptor) {
+ print_security_descriptor(dentry->security_descriptor,
+ dentry->security_descriptor_size);
+ }
+
+ print_time(T("Creation Time"), &dentry->creation_time);
+ print_time(T("Last Write Time"), &dentry->last_write_time);
+ print_time(T("Last Access Time"), &dentry->last_access_time);
+
+
+ if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
+ tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
+
+ tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
+ tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
+
+ if (dentry->unix_mode != 0) {
+ tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
+ "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
+ dentry->unix_uid, dentry->unix_gid,
+ dentry->unix_mode, dentry->unix_rdev);
+ }
+
+ for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
+ if (dentry->streams[i].stream_name) {
+ tprintf(T("\tData stream \"%"TS"\":\n"),
+ dentry->streams[i].stream_name);
+ } else {
+ tprintf(T("\tUnnamed data stream:\n"));
+ }
+ print_resource(&dentry->streams[i].resource, NULL);
+ }
+}
+
+static int
+print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
+{
+ const struct print_dentry_options *options = _options;
+ if (!options->detailed)
+ print_dentry_full_path(dentry);
+ else
+ print_dentry_detailed(dentry);
+ return 0;
+}
+
+/* Print the files contained in an image(s) in a WIM file. */
+static int
+imagex_dir(int argc, tchar **argv, int cmd)
+{
+ const tchar *wimfile;
+ WIMStruct *wim = NULL;
+ int image;
+ int ret;
+ const tchar *path = WIMLIB_WIM_ROOT_PATH;
+ int c;
+ struct print_dentry_options options = {
+ .detailed = false,
+ };
+ int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
+
+ for_opt(c, dir_options) {
+ switch (c) {
+ case IMAGEX_PATH_OPTION:
+ path = optarg;
+ break;
+ case IMAGEX_DETAILED_OPTION:
+ options.detailed = true;
+ break;
+ case IMAGEX_ONE_FILE_ONLY_OPTION:
+ iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
+ break;
+ default:
+ goto out_usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ imagex_error(T("Must specify a WIM file"));
+ goto out_usage;
+ }
+ if (argc > 2) {
+ imagex_error(T("Too many arguments"));
+ goto out_usage;
+ }
+
+ wimfile = argv[0];
+ ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
+ imagex_progress_func, NULL);
+ if (ret)
+ goto out;
+
+ if (argc >= 2) {
+ image = wimlib_resolve_image(wim, argv[1]);
+ ret = verify_image_exists(image, argv[1], wimfile);
+ if (ret)
+ goto out_wimlib_free;
+ } else {
+ /* No image specified; default to image 1, but only if the WIM
+ * contains exactly one image. */
+
+ struct wimlib_wim_info info;
+
+ wimlib_get_wim_info(wim, &info);
+ if (info.image_count != 1) {
+ imagex_error(T("\"%"TS"\" contains %d images; Please "
+ "select one (or all)."),
+ wimfile, info.image_count);
+ wimlib_free(wim);
+ goto out_usage;
+ }
+ image = 1;
+ }
+
+ ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
+ print_dentry, &options);
+out_wimlib_free:
+ wimlib_free(wim);
+out:
+ return ret;
+
+out_usage:
+ usage(CMD_DIR, stderr);
+ ret = -1;
+ goto out;