diff --git a/src/fr-command-cfile.c b/src/fr-command-cfile.c index 0500df75a..6aa638315 100644 --- a/src/fr-command-cfile.c +++ b/src/fr-command-cfile.c @@ -60,12 +60,13 @@ get_uncompressed_name_from_archive (FrCommand *comm, char buffer[10]; if (g_input_stream_read (stream, buffer, 10, NULL, NULL) >= 0) { + unsigned char flag = buffer[3]; /* Check whether the FLG.FNAME is set */ - if (((unsigned char)(buffer[3]) & 0x08) != 0x08) + if ((flag & 0x08) != 0x08) filename_present = FALSE; /* Check whether the FLG.FEXTRA is set */ - if (((unsigned char)(buffer[3]) & 0x04) == 0x04) + if ((flag & 0x04) == 0x04) filename_present = FALSE; } @@ -93,9 +94,14 @@ get_uncompressed_name_from_archive (FrCommand *comm, } +/** + * Parse `gzip -lq archive.gz` output: + * @param line output line in format "compressed uncompressed ratio uncompressed_name" e.g. "758185 3454395 78.1% file.txt" + * @param data FrCommand* + */ static void -list__process_line (char *line, - gpointer data) +list__process_line_gzip (gchar *line, + gpointer data) { FrCommand *comm = FR_COMMAND (data); FileData *fdata; @@ -105,8 +111,9 @@ list__process_line (char *line, fdata = file_data_new (); fields = split_line (line, 2); - if (strcmp (fields[1], "-1") != 0) - fdata->size = g_ascii_strtoull (fields[1], NULL, 10); + const char *field_uncompressed = fields[1]; // e.g. "3454395" + if (strcmp (field_uncompressed, "-1") != 0) + fdata->size = g_ascii_strtoull (field_uncompressed, NULL, 10); g_strfreev (fields); if (fdata->size == 0) @@ -134,6 +141,213 @@ list__process_line (char *line, fr_command_add_file (comm, fdata); } +/* gzip let us known the uncompressed size. To get it run: + * $ gzip -l file.txt.gz + * compressed uncompressed ratio uncompressed_name + * 758185 3454395 78.1% file.txt + * We can simplify output with --quiet (-q) option: + * $ gzip -lq file.txt.gz + * 758185 3454395 78.1% file.txt + */ +static void +fr_command_cfile_list__gzip (FrCommand *comm) +{ + fr_process_set_out_line_func (FR_COMMAND (comm)->process, + list__process_line_gzip, + comm); + + fr_process_begin_command (comm->process, "gzip"); + fr_process_add_arg (comm->process, "-l"); + fr_process_add_arg (comm->process, "-q"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + +/** + * Parse `lzop -lvN file.txt.lzo` output: + * Method Length Packed Ratio Date Time Name + * ------ ------ ------ ----- ---- ---- ---- + * LZO1X-1 3454395 758185 78.1% 2018-11-02 12:33 ./original_file.txt + */ +static void +list__process_line_lzop (char *line, + gpointer data) +{ + // Skip first two lines. + // First line have a caption with "Method" in first column + // Second line have "------" in first column + if (strncmp (line, "Method ", 7) == 0 || strncmp (line, "------ ", 7) == 0) + return; + + FrCommand *comm = FR_COMMAND (data); + FileData *fdata; + char **fields; + char *filename; + + fdata = file_data_new (); + + fields = split_line (line, 6); + const char *field_length = fields[1]; // e.g. "3454395" + const char *field_date = fields[4]; // e.g. "2018-11-02" + const char *field_time = fields[5]; // e.g. "12:33" + + // parse uncompressed size column + if (strcmp (field_length, "-1") != 0) + fdata->size = g_ascii_strtoull (field_length, NULL, 10); + if (fdata->size == 0) { + g_warning("Unable to parse uncompressed size: %s\n", field_length); + fdata->size = get_file_size(comm->filename); + } + + // parse modified date and time columns + char *iso8601_datetime = g_strconcat (field_date, "T", field_time, ":00", NULL); + GTimeZone *local_tz = g_time_zone_new_local (); + GDateTime *modified = g_date_time_new_from_iso8601 (iso8601_datetime, local_tz); + g_time_zone_unref (local_tz); + if (modified != NULL) { + fdata->modified = g_date_time_to_unix(modified); + g_date_time_unref(modified); + } else { + g_warning("Unable to parse modification date: %s %s\n", field_date, field_time); + fdata->modified = get_file_mtime_for_path(comm->filename); + } + g_free (iso8601_datetime); + + g_strfreev (fields); + + // parse original file name + filename = g_strdup (get_last_field (line, 6)); + if (filename == NULL) + filename = remove_extension_from_path (comm->filename); + + fdata->full_path = g_strconcat ("/", + file_name_from_path (filename), + NULL); + g_free (filename); + + fdata->original_path = fdata->full_path + 1; + fdata->link = NULL; + + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); +} + +/* lzop let us known the uncompressed size, original date and name. To get it run: + * $ lzop -lvN file.txt.lzo + * Method Length Packed Ratio Date Time Name + * ------ ------ ------ ----- ---- ---- ---- + * LZO1X-1 3454395 758185 78.1% 2018-11-02 12:33 ./original_file.txt + */ +static void +fr_command_cfile_list__lzop (FrCommand *comm) +{ + fr_process_set_out_line_func (FR_COMMAND (comm)->process, + list__process_line_lzop, + comm); + + fr_process_begin_command (comm->process, "lzop"); + fr_process_add_arg (comm->process, "-lvN"); // list verbose and show original name + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + +/** + * Parse file size from xz which was generated by uint64_to_nicestr() function and convertes it to bytes. + * NOTE: resulting bytes may be not precise size due to rounding. + * @param str_size e.g. "3454395" + * @param str_units "B", "KiB", "MiB", "GiB", "TiB" + * @return + */ +static goffset +parse_file_size (const gchar *str_size, + const gchar *str_units) +{ + gdouble size_in_units = 0.0; + sscanf (str_size, "%'lf", &size_in_units); + gdouble size_in_bytes = size_in_units; + static const char eci_suffixes[5][4] = { "B", "KiB", "MiB", "GiB", "TiB" }; + for (int i = 0; i < 5; i++) { + if (strcmp (str_units, eci_suffixes[i]) == 0) { + return (goffset) size_in_bytes; + } + size_in_bytes *= 1024.0; + } + // if we was unable to determine the unit then let's return the size as is i.e. in bytes + return (goffset) size_in_units; +} + +/* Parse xz `xz -l file.txt.xz` output: + * Strms Blocks Compressed Uncompressed Ratio Check Filename + * 1 1 496.3 KiB 3,373.4 KiB 0.147 CRC64 file.txt.xz + */ +static void +list__process_line_xz (gchar *line, + gpointer data) +{ + // Skip first line. + // First line have a caption with "Strms" in first column + if (strncmp (line, "Strms ", 6) == 0) + return; + + FrCommand *comm = FR_COMMAND (data); + FileData *fdata; + gchar **fields; + gchar *filename; + + fdata = file_data_new (); + + fields = split_line (line, 6); + const gchar *field_uncompressed_size = fields[4]; // e.g. "3454395" + const gchar *field_uncompressed_units = fields[5]; // e.g. "KiB" + fdata->size = parse_file_size (field_uncompressed_size, field_uncompressed_units); + g_strfreev (fields); + + filename = remove_extension_from_path (comm->filename); + + fdata->full_path = g_strconcat ("/", + file_name_from_path (filename), + NULL); + g_free (filename); + + fdata->original_path = fdata->full_path + 1; + fdata->link = NULL; + fdata->modified = get_file_mtime_for_path (comm->filename); + + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); +} + +/* xz let us known only the uncompressed size. To get it run: + * $ xz -l file.txt.xz + * Strms Blocks Compressed Uncompressed Ratio Check Filename + * 1 1 496.3 KiB 3,373.4 KiB 0.147 CRC64 file.txt.xz + */ +static void +fr_command_cfile_list__xz (FrCommand *comm) +{ + fr_process_set_out_line_func (FR_COMMAND (comm)->process, + list__process_line_xz, + comm); + + fr_process_begin_command (comm->process, "xz"); + fr_process_add_arg (comm->process, "-l"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + static void fr_command_cfile_list (FrCommand *comm) @@ -141,18 +355,13 @@ fr_command_cfile_list (FrCommand *comm) FrCommandCFile *comm_cfile = FR_COMMAND_CFILE (comm); if (is_mime_type (comm->mime_type, "application/x-gzip")) { - /* gzip let us known the uncompressed size */ - - fr_process_set_out_line_func (FR_COMMAND (comm)->process, - list__process_line, - comm); - - fr_process_begin_command (comm->process, "gzip"); - fr_process_add_arg (comm->process, "-l"); - fr_process_add_arg (comm->process, "-q"); - fr_process_add_arg (comm->process, comm->filename); - fr_process_end_command (comm->process); - fr_process_start (comm->process); + fr_command_cfile_list__gzip (comm); + } + else if (is_mime_type (comm->mime_type, "application/x-lzop")) { + fr_command_cfile_list__lzop (comm); + } + else if (is_mime_type (comm->mime_type, "application/x-xz")) { + fr_command_cfile_list__xz (comm); } else { /* ... other compressors do not support this feature so diff --git a/src/glib-utils.c b/src/glib-utils.c index 27d4548c2..d520cf81a 100644 --- a/src/glib-utils.c +++ b/src/glib-utils.c @@ -456,7 +456,12 @@ eat_void_chars (const char *line) return line; } - +/** + * Parse the line and return array of columns separated by space symbol and size of n_fields + * @param line + * @param n_fields how many colums/fields to parse + * @return NULL terminated array of fields with size of n_fields + */ char ** split_line (const char *line, int n_fields)