1 /*
2  *  Copyright (C) 2008 Giuseppe Torelli - <colossus73@gmail.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17  *  MA 02110-1301 USA.
18  */
19 
20 #include <string.h>
21 #include <time.h>
22 #include <unistd.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include "gzip_et_al.h"
26 #include "interface.h"
27 #include "main.h"
28 #include "parser.h"
29 #include "string_utils.h"
30 #include "support.h"
31 #include "tar.h"
32 #include "window.h"
33 
34 #define compress (archive->type == XARCHIVETYPE_COMPRESS)
35 #define lrzip    (archive->type == XARCHIVETYPE_LRZIP)
36 #define lz4      (archive->type == XARCHIVETYPE_LZ4)
37 #define xz       (archive->type == XARCHIVETYPE_XZ)
38 #define zstd     (archive->type == XARCHIVETYPE_ZSTD)
39 
40 static gpointer item[7];
41 static gchar *filename;
42 static gboolean data_line, last_line;
43 static gboolean lrzip_can_password;
44 static gboolean zstd_can_list, zstd_can_test;
45 
xa_gzip_et_al_check_lrzip(const gchar * path)46 void xa_gzip_et_al_check_lrzip (const gchar *path)
47 {
48 	gchar *command, *output;
49 
50 	command = g_strconcat(path, " -h", NULL);
51 	g_spawn_command_line_sync(command, NULL, &output, NULL, NULL);
52 	g_free(command);
53 
54 	lrzip_can_password = (strstr(output, "-e, --encrypt[=pass") != NULL);
55 	g_free(output);
56 }
57 
xa_gzip_et_al_check_zstd(const gchar * compressor,const gchar * decompressor,gboolean * is_compressor)58 gchar *xa_gzip_et_al_check_zstd (const gchar *compressor, const gchar *decompressor, gboolean *is_compressor)
59 {
60 	gchar *path, *command, *output;
61 	gboolean found_compressor = FALSE;
62 
63 	path = g_find_program_in_path(compressor);
64 
65 	if (path)
66 		found_compressor = TRUE;
67 	else
68 		path = g_find_program_in_path(decompressor);
69 
70 	if (!path)
71 		return NULL;
72 
73 	command = g_strconcat(path, " -h", NULL);
74 	g_spawn_command_line_sync(command, &output, NULL, NULL, NULL);
75 	g_free(command);
76 
77 	/* check whether decompression is available */
78 	if (strstr(output, "\n -d "))
79 	{
80 		if (found_compressor)
81 			*is_compressor = (strstr(output, "\n -# ") != NULL);
82 
83 		zstd_can_list = (strstr(output, "\n -l ") || strstr(output, "\n--list "));
84 		zstd_can_test = (strstr(output, "\n -t ") || strstr(output, "\n--test "));
85 	}
86 	else   // useless
87 	{
88 		g_free(path);
89 		path = NULL;
90 	}
91 
92 	g_free(output);
93 
94 	return path;
95 }
96 
xa_gzip_et_al_password_str(const gchar * password,XArchiveType type)97 static gchar *xa_gzip_et_al_password_str (const gchar *password, XArchiveType type)
98 {
99 	if (password)
100 		if (type == XARCHIVETYPE_LRZIP)
101 			return g_strconcat(" --encrypt=", password, NULL);
102 
103 	return g_strdup("");
104 }
105 
xa_gzip_et_al_get_command(const gchar * program,gchar * workfile,gchar * archive,const gchar * password,XArchiveType type)106 gchar *xa_gzip_et_al_get_command (const gchar *program, gchar *workfile, gchar *archive, const gchar *password, XArchiveType type)
107 {
108 	gchar *password_str, *command;
109 
110 	password_str = xa_gzip_et_al_password_str(password, type);
111 	workfile = xa_escape_bad_chars(workfile, "\"");
112 	archive = xa_quote_shell_command(archive, TRUE);
113 	command = g_strconcat("sh -c \"", program, type == XARCHIVETYPE_LRZIP ? " " : " -c ", workfile, password_str, type == XARCHIVETYPE_LRZIP ? " -fo " : " > ", archive, "\"", NULL);
114 	g_free(archive);
115 	g_free(workfile);
116 	g_free(password_str);
117 
118 	return command;
119 }
120 
xa_gzip_et_al_can(XArchive * archive,gboolean can)121 static void xa_gzip_et_al_can (XArchive *archive, gboolean can)
122 {
123 	archive->can_test = (can && !compress && (!zstd || zstd_can_test));
124 	archive->can_extract = can;
125 	archive->can_password = (can && lrzip && lrzip_can_password);
126 	archive->can_overwrite = can;
127 	archive->can_move = can;
128 
129 	/* only if archive is new and empty */
130 	archive->can_add = (can && archiver[archive->type].is_compressor);
131 }
132 
xa_gzip_et_al_ask(XArchive * archive)133 void xa_gzip_et_al_ask (XArchive *archive)
134 {
135 	xa_gzip_et_al_can(archive, TRUE);
136 }
137 
xa_gzip_et_al_parse_output(gchar * line,XArchive * archive)138 static void xa_gzip_et_al_parse_output (gchar *line, XArchive *archive)
139 {
140 	XEntry *entry;
141 	guint idx0 = 0, idx1 = 1;
142 	const gchar *streams = NULL, *blocks = NULL;
143 
144 	USE_PARSER;
145 
146 	if (archive->type == XARCHIVETYPE_GZIP)
147 	{
148 		/* heading? */
149 		if (line[9] == 'c' || line[0] == 'c')   // genuine gzip or pigz
150 			return;
151 	}
152 	else if (archive->type == XARCHIVETYPE_LZIP)
153 	{
154 		/* heading? */
155 		if (line[3] == 'u')
156 			return;
157 		else
158 		{
159 			idx0 = 1;
160 			idx1 = 0;
161 		}
162 	}
163 	else if (archive->type == XARCHIVETYPE_LZOP)
164 	{
165 		/* heading? */
166 		if (line[12] == 'c')
167 			return;
168 		else
169 			/* method */
170 			NEXT_ITEM(item[5]);
171 	}
172 	else if (archive->type == XARCHIVETYPE_XZ)
173 	{
174 		/* heading? */
175 		if (*line == 't')
176 			return;
177 		else if (*line == 'n')
178 		{
179 			/* "name" */
180 			SKIP_ITEM;
181 			LAST_ITEM(filename);
182 
183 			if (g_str_has_suffix(filename, ".xz"))
184 				*(line - 4) = 0;
185 
186 			filename = g_path_get_basename(filename);
187 
188 			return;
189 		}
190 		else
191 		{
192 			/* "file" */
193 			SKIP_ITEM;
194 
195 			/* number of streams */
196 			NEXT_ITEM(streams);
197 
198 			/* number of blocks */
199 			NEXT_ITEM(blocks);
200 		}
201 	}
202 	else
203 		return;
204 
205 	/* compressed (uncompressed for lzip) */
206 	NEXT_ITEM(item[idx1]);
207 
208 	/* uncompressed (compressed for lzip) */
209 	NEXT_ITEM(item[idx0]);
210 
211 	/* ratio */
212 	NEXT_ITEM(item[2]);
213 
214 	if (archive->type == XARCHIVETYPE_XZ)
215 	{
216 		const gchar *padding;
217 
218 		/* check type */
219 		NEXT_ITEM(item[5]);
220 
221 		/* stream padding */
222 		NEXT_ITEM(padding);
223 
224 		item[6] = g_strconcat(streams, "/", blocks, "/", padding, NULL);
225 	}
226 	else
227 	{
228 		item[6] = NULL;
229 
230 		/* uncompressed_name */
231 		LAST_ITEM(filename);
232 
233 		if ((archive->type == XARCHIVETYPE_LZIP) && g_str_has_suffix(filename, ".lz"))
234 			*(line - 4) = 0;
235 
236 		filename = g_path_get_basename(filename);
237 	}
238 
239 	entry = xa_set_archive_entries_for_each_row(archive, filename, item);
240 
241 	if (entry)
242 	{
243 		archive->files = 1;
244 		archive->files_size = g_ascii_strtoull(item[0], NULL, 0);
245 	}
246 
247 	g_free(item[3]);
248 	g_free(item[4]);
249 	g_free(item[6]);
250 	g_free(filename);
251 }
252 
xa_gzip_et_al_parse_lrzip(gchar * line,XArchive * archive)253 static void xa_gzip_et_al_parse_lrzip (gchar *line, XArchive *archive)
254 {
255 	XEntry *entry;
256 
257 	USE_PARSER;
258 
259 	if (last_line)
260 		return;
261 
262 	if (!data_line)
263 	{
264 		LAST_ITEM(filename);
265 
266 		filename[strlen(filename) - 1] = 0;   // remove colon
267 
268 		if (g_str_has_suffix(filename, ".lrz"))
269 			*(line - 6) = 0;
270 
271 		filename = g_path_get_basename(filename);
272 		data_line = TRUE;
273 
274 		return;
275 	}
276 
277 	IF_ITEM_LINE("lrzip version:")
278 	{
279 		NEXT_ITEM(item[6]);
280 		item[6] = g_strdup(item[6]);
281 	}
282 	else IF_ITEM_LINE("Compression:")
283 		DUPE_ITEM(item[5]);
284 	else IF_ITEM_LINE("Decompressed file size:")
285 		DUPE_ITEM(item[0]);
286 	else IF_ITEM_LINE("Compressed file size:")
287 		DUPE_ITEM(item[1]);
288 	else IF_ITEM_LINE("Compression ratio:")
289 	{
290 		NEXT_ITEM(item[2]);
291 		item[2] = g_strconcat(item[2], ":1", NULL);
292 		last_line = TRUE;
293 	}
294 
295 	if (!last_line)
296 		return;
297 
298 	entry = xa_set_archive_entries_for_each_row(archive, filename, item);
299 
300 	if (entry)
301 	{
302 		entry->is_encrypted = archive->has_password;
303 
304 		archive->files = 1;
305 		archive->files_size = g_ascii_strtoull(item[0], NULL, 0);
306 	}
307 
308 	g_free(item[0]);
309 	g_free(item[1]);
310 	g_free(item[2]);
311 	g_free(item[3]);
312 	g_free(item[4]);
313 	g_free(item[5]);
314 	g_free(item[6]);
315 	g_free(filename);
316 }
317 
xa_gzip_et_al_parse_zstd(gchar * line,XArchive * archive)318 static void xa_gzip_et_al_parse_zstd (gchar *line, XArchive *archive)
319 {
320 	static gchar *zstandard;
321 	XEntry *entry;
322 	char *pos;
323 
324 	USE_PARSER;
325 
326 	if (last_line)
327 		return;
328 
329 	if (!data_line)
330 	{
331 		data_line = (strncmp(line, "Number of files listed:", 23) == 0);
332 		return;
333 	}
334 
335 	if ((pos = strstr(line, " (1/1):\n")))
336 	{
337 		*pos = 0;
338 		LAST_ITEM(filename);
339 
340 		if (g_str_has_suffix(filename, ".zst"))
341 			*(line - 4) = 0;
342 
343 		filename = g_path_get_basename(filename);
344 	}
345 	else IF_ITEM_LINE("# Zstandard Frames:")
346 		DUPE_ITEM(zstandard);
347 	else IF_ITEM_LINE("# Skippable Frames:")
348 	{
349 		NEXT_ITEM(item[6]);
350 		item[6] = g_strconcat(zstandard, "/", item[6], NULL);
351 		g_free(zstandard);
352 	}
353 	else IF_ITEM_LINE("Compressed Size:")
354 	{
355 		pos = strchr(line, '(');
356 
357 		item[1] = (pos ? g_strdup(pos + 1) : pos);
358 	}
359 	else IF_ITEM_LINE("Decompressed Size:")
360 	{
361 		pos = strchr(line, '(');
362 
363 		item[0] = (pos ? g_strdup(pos + 1) : pos);
364 	}
365 	else IF_ITEM_LINE("Ratio:")
366 	{
367 		NEXT_ITEM(item[2]);
368 		item[2] = g_strconcat(item[2], ":1", NULL);
369 	}
370 	else IF_ITEM_LINE("Check:")
371 	{
372 		DUPE_ITEM(item[5]);
373 		last_line = TRUE;
374 	}
375 
376 	if (!last_line)
377 		return;
378 
379 	entry = xa_set_archive_entries_for_each_row(archive, filename, item);
380 
381 	if (entry)
382 	{
383 		archive->files = 1;
384 		archive->files_size = g_ascii_strtoull(item[0], NULL, 0);
385 	}
386 
387 	g_free(item[0]);
388 	g_free(item[1]);
389 	g_free(item[2]);
390 	g_free(item[3]);
391 	g_free(item[4]);
392 	g_free(item[5]);
393 	g_free(item[6]);
394 	g_free(filename);
395 }
396 
xa_gzip_et_al_globally_stored_entry(gchar * line,XArchive * archive)397 static void xa_gzip_et_al_globally_stored_entry (gchar *line, XArchive *archive)
398 {
399 	XEntry *entry;
400 	gchar *filename;
401 	char *dot;
402 
403 	filename = g_path_get_basename(archive->path[0]);
404 	dot = strrchr(filename, '.');
405 
406 	if (dot)
407 		*dot = 0;
408 
409 	entry = xa_set_archive_entries_for_each_row(archive, filename, item);
410 
411 	if (entry)
412 	{
413 		entry->is_encrypted = archive->has_password;
414 
415 		archive->files = 1;
416 	}
417 
418 	g_free(item[0]);
419 	g_free(item[1]);
420 	g_free(item[2]);
421 	g_free(item[3]);
422 	g_free(item[4]);
423 	g_free(filename);
424 }
425 
xa_gzip_et_al_list(XArchive * archive)426 void xa_gzip_et_al_list (XArchive *archive)
427 {
428 	GType types[] = {GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_UINT64, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER};
429 	const gchar *titles[] = {_("Original Size"), _("Compressed"), _("Saving"), _("Date"), _("Time"), NULL, NULL};
430 	const gchar *decompfile = "xa-tmp.decompressed";
431 	gchar *password_str, *archive_path, *command, *workfile, buffer[12];
432 	FILE *file;
433 	struct stat st;
434 	guint i;
435 
436 	if (archive->type == XARCHIVETYPE_LRZIP)
437 	{
438 		file = fopen(archive->path[0], "r");
439 
440 		if (file)
441 		{
442 			char encrypted = 0;
443 
444 			if (fseek(file, 22, SEEK_SET) == 0)
445 				if (fread(&encrypted, sizeof(encrypted), 1, file) == 1)
446 					if (encrypted == 1)
447 						archive->has_password = TRUE;
448 
449 			fclose(file);
450 		}
451 
452 		if (archive->has_password && !lrzip_can_password)
453 		{
454 			command = g_strconcat("sh -c \"echo ", _("The archiver lacks necessary support for password protected decryption!"), " >&2; exit 1\"", NULL);
455 			xa_run_command(archive, command);
456 			g_free(command);
457 			return;
458 		}
459 	}
460 
461 	if (archive->has_password)
462 		if (!xa_check_password(archive))
463 			return;
464 
465 	if (!xa_create_working_directory(archive))
466 		return;
467 
468 	password_str = xa_gzip_et_al_password_str(archive->password, archive->type);
469 	archive_path = xa_quote_shell_command(archive->path[0], TRUE);
470 
471 	archive->child_dir = g_strdup(archive->working_dir);
472 	command = g_strconcat("sh -c \"", archiver[archive->type].program[0], lrzip ? " " : " -c ", "-d", password_str, " ", archive_path, lrzip ? " -fo " : " > ", decompfile, "\"", NULL);
473 	xa_run_command(archive, command);
474 	g_free(command);
475 
476 	g_free(archive->child_dir);
477 	archive->child_dir = NULL;
478 
479 	g_free(archive_path);
480 	g_free(password_str);
481 
482 	workfile = g_strconcat(archive->working_dir, "/", decompfile, NULL);
483 
484 	/* check if uncompressed file is tar archive */
485 
486 	file = fopen(workfile, "r");
487 
488 	if (file)
489 	{
490 		gboolean is_tar;
491 
492 		is_tar = isTar(file);
493 		fclose(file);
494 
495 		if (is_tar && (archiver[XARCHIVETYPE_TAR].ask == xa_tar_ask))
496 		{
497 			if (!xa_get_compressed_tar_type(&archive->type))
498 				return;
499 
500 			archive->path[2] = g_shell_quote(workfile);
501 			g_free(workfile);
502 
503 			xa_gzip_et_al_can(archive, FALSE);
504 
505 			archive->archiver = &archiver[XARCHIVETYPE_TAR];
506 
507 			(*archive->archiver->ask)(archive);
508 			(*archive->archiver->list)(archive);
509 
510 			return;
511 		}
512 	}
513 
514 	/* continue listing gzip et al. archive type */
515 
516 	archive->can_add = FALSE;
517 
518 	stat(archive->path[0], &st);
519 
520 	/* date */
521 	strftime(buffer, sizeof(buffer), "%Y-%m-%d", localtime(&st.st_mtime));
522 	item[3] = g_strdup(buffer);
523 
524 	/* time */
525 	strftime(buffer, sizeof(buffer), "%H:%M:%S", localtime(&st.st_mtime));
526 	item[4] = g_strdup(buffer);
527 
528 	archive->columns = 8;
529 	archive->parse_output = NULL;
530 
531 	switch (archive->type)
532 	{
533 		case XARCHIVETYPE_GZIP:
534 		case XARCHIVETYPE_LRZIP:
535 		case XARCHIVETYPE_LZIP:
536 		case XARCHIVETYPE_LZOP:
537 		case XARCHIVETYPE_XZ:
538 		case XARCHIVETYPE_ZSTD:
539 
540 			if (archive->type == XARCHIVETYPE_LRZIP)
541 			{
542 				if (archive->has_password)
543 					/* no further information will be available then */
544 					break;
545 
546 				data_line = FALSE;
547 				last_line = FALSE;
548 
549 				types[7] = G_TYPE_STRING;
550 				types[8] = G_TYPE_STRING;
551 
552 				titles[2] = _("Compression ratio");
553 				titles[5] = _("Method");
554 				titles[6] = _("Version");
555 
556 				archive->columns = 10;
557 			}
558 			else if (archive->type == XARCHIVETYPE_LZOP)
559 			{
560 				types[7] = G_TYPE_STRING;
561 
562 				titles[2] = _("Occupancy");
563 				titles[5] = _("Method");
564 
565 				archive->columns = 9;
566 			}
567 			else if (archive->type == XARCHIVETYPE_XZ)
568 			{
569 				types[7] = G_TYPE_STRING;
570 				types[8] = G_TYPE_STRING;
571 
572 				titles[2] = _("Ratio");
573 				titles[5] = _("Check Type");
574 				titles[6] = _("Streams/Blocks/Padding");
575 
576 				archive->columns = 10;
577 			}
578 			else if (archive->type == XARCHIVETYPE_ZSTD)
579 			{
580 				if (!zstd_can_list)
581 					break;
582 
583 				/* items potentially not listed */
584 				item[0] = NULL;
585 				item[2] = NULL;
586 
587 				data_line = FALSE;
588 				last_line = FALSE;
589 
590 				types[7] = G_TYPE_STRING;
591 				types[8] = G_TYPE_STRING;
592 
593 				titles[2] = _("Compression ratio");
594 				titles[5] = _("Check Type");
595 				titles[6] = _("Data Frames/Skippable Frames");
596 
597 				archive->columns = 10;
598 			}
599 
600 			command = g_strconcat(archiver[archive->type].program[0], lrzip ? " -i" : " -l", xz ? " --robot " : (zstd ? "v " : " "), archive->path[1], NULL);
601 
602 			if (archive->type == XARCHIVETYPE_LRZIP)
603 				archive->parse_output = xa_gzip_et_al_parse_lrzip;
604 			else if (archive->type == XARCHIVETYPE_ZSTD)
605 				archive->parse_output = xa_gzip_et_al_parse_zstd;
606 			else
607 				archive->parse_output = xa_gzip_et_al_parse_output;
608 
609 			break;
610 
611 		case XARCHIVETYPE_BZIP2:
612 		case XARCHIVETYPE_COMPRESS:
613 		case XARCHIVETYPE_LZ4:
614 		case XARCHIVETYPE_LZMA:
615 			break;
616 
617 		default:
618 			return;
619 	}
620 
621 	if (!archive->parse_output)
622 	{
623 		off_t compressed;
624 
625 		/* compressed */
626 		compressed = st.st_size;
627 		item[1] = g_strdup_printf("%" G_GUINT64_FORMAT, (guint64) compressed);
628 
629 		/* uncompressed */
630 		stat(workfile, &st);
631 		archive->files_size = (guint64) st.st_size;
632 		item[0] = g_strdup_printf("%" G_GUINT64_FORMAT, archive->files_size);
633 
634 		/* saving */
635 		if (st.st_size)
636 			item[2] = g_strdup_printf("%.1f%%", 100.0 - 100.0 * compressed / st.st_size);
637 		else
638 			item[2] = g_strdup("-");
639 
640 		/* trigger pseudo-parser once */
641 		command = g_strdup("sh -c echo");
642 		archive->parse_output = xa_gzip_et_al_globally_stored_entry;
643 	}
644 
645 	g_free(workfile);
646 
647 	xa_spawn_async_process(archive, command);
648 	g_free(command);
649 
650 	archive->size_column = 2;
651 	archive->column_types = g_malloc0(sizeof(types));
652 
653 	for (i = 0; i < archive->columns; i++)
654 		archive->column_types[i] = types[i];
655 
656 	xa_create_liststore(archive, titles);
657 }
658 
xa_gzip_et_al_test(XArchive * archive)659 void xa_gzip_et_al_test (XArchive *archive)
660 {
661 	gchar *password_str, *command;
662 
663 	password_str = xa_gzip_et_al_password_str(archive->password, archive->type);
664 	command = g_strconcat(archiver[archive->type].program[0], " -t", password_str, lrzip || lz4 ? " " : "v ", archive->path[1], NULL);
665 	g_free(password_str);
666 
667 	xa_run_command(archive, command);
668 	g_free(command);
669 }
670 
xa_gzip_et_al_extract(XArchive * archive,GSList * file_list)671 gboolean xa_gzip_et_al_extract (XArchive *archive, GSList *file_list)
672 {
673 	GString *files;
674 	gchar *command, *filename, *files_str, *out_dir, *out_file, *archive_path, *extraction_dir, *password_str;
675 	gboolean result;
676 
677 	files = xa_quote_filenames(file_list, NULL, TRUE);
678 
679 	if (*files->str)
680 	{
681 		filename = g_shell_unquote(files->str + 1, NULL);
682 		files_str = xa_quote_shell_command(files->str + 1, FALSE);
683 	}
684 	else
685 	{
686 		char *dot;
687 
688 		filename = g_path_get_basename(archive->path[0]);
689 		dot = strrchr(filename, '.');
690 
691 		if (dot)
692 			*dot = 0;
693 
694 		files_str = xa_quote_shell_command(filename, TRUE);
695 	}
696 
697 	out_dir = g_shell_unquote(archive->extraction_dir, NULL);
698 	out_file = g_strconcat(out_dir, "/", filename, NULL);
699 
700 	archive_path = xa_quote_shell_command(archive->path[0], TRUE);
701 	extraction_dir = xa_quote_shell_command(archive->extraction_dir, FALSE);
702 
703 	if (archive->do_overwrite || !g_file_test(out_file, G_FILE_TEST_EXISTS))
704 	{
705 		password_str = xa_gzip_et_al_password_str(archive->password, archive->type);
706 		command = g_strconcat("sh -c \"", archiver[archive->type].program[0], lrzip ? " " : " -c ", "-d", password_str, " ", archive_path, lrzip ? " -fo " : " > ", extraction_dir, "/", files_str, "\"", NULL);
707 		g_free(password_str);
708 	}
709 	else
710 		command = g_strdup("sh -c \"\"");
711 
712 	g_free(extraction_dir);
713 	g_free(archive_path);
714 	g_free(out_file);
715 	g_free(out_dir);
716 	g_free(filename);
717 	g_free(files_str);
718 	g_string_free(files, TRUE);
719 
720 	result = xa_run_command(archive, command);
721 	g_free(command);
722 
723 	return result;
724 }
725 
xa_gzip_et_al_add(XArchive * archive,GSList * file_list,gchar * compression)726 void xa_gzip_et_al_add (XArchive *archive, GSList *file_list, gchar *compression)
727 {
728 	GString *files;
729 	gchar *move, *files_str, *archive_path, *password_str, *command;
730 
731 	if (archive->location_path != NULL)
732 		archive->child_dir = g_strdup(archive->working_dir);
733 
734 	if (!compression)
735 	{
736 		switch (archive->type)
737 		{
738 			case XARCHIVETYPE_BZIP2:
739 				compression = "9";
740 				break;
741 
742 			case XARCHIVETYPE_COMPRESS:
743 				compression = "16";
744 				break;
745 
746 			case XARCHIVETYPE_GZIP:
747 			case XARCHIVETYPE_LZIP:
748 			case XARCHIVETYPE_XZ:
749 				compression = "6";
750 				break;
751 
752 			case XARCHIVETYPE_LZ4:
753 				compression = "1";
754 				break;
755 
756 			case XARCHIVETYPE_LRZIP:
757 			case XARCHIVETYPE_LZMA:
758 				compression = "7";
759 				break;
760 
761 			case XARCHIVETYPE_LZOP:
762 			case XARCHIVETYPE_ZSTD:
763 				compression = "3";
764 				break;
765 
766 			default:
767 				break;
768 		}
769 	}
770 
771 	files = xa_quote_filenames(file_list, NULL, TRUE);
772 	files_str = xa_escape_bad_chars(files->str, "\"");
773 	archive_path = xa_quote_shell_command(archive->path[0], TRUE);
774 
775 	if (archive->do_move)
776 		move = g_strconcat(" && rm", files_str, NULL);
777 	else
778 		move = g_strdup("");
779 
780 	password_str = xa_gzip_et_al_password_str(archive->password, archive->type);
781 	command = g_strconcat("sh -c \"", archiver[archive->type].program[0], lrzip ? " -" : " -c -", compress ? "b " : (lrzip ? "L " : ""), compression, password_str, files_str, lrzip ? " -fo " : " > ", archive_path, move, "\"", NULL);
782 	g_free(password_str);
783 
784 	g_free(move);
785 	g_free(archive_path);
786 	g_free(files_str);
787 	g_string_free(files, TRUE);
788 
789 	xa_run_command(archive, command);
790 	g_free(command);
791 }
792