aboutsummaryrefslogblamecommitdiff
path: root/src/lzma/suffix.c
blob: 460acee2134a35509da03a540d0e51de21f8be3d (plain) (tree)





















                                                                               
                    

                                 


































                                                                               
                                                             
 








                                                                               
                                    

                           















                                                                             



























                                                                          


                                                                             
             
                                                           
 



























                                                                        










                                                                          


                                                                          


                                                                   


                            




















                                                              





                                                                          
                                        

                                                               
 
///////////////////////////////////////////////////////////////////////////////
//
/// \file       suffix.c
/// \brief      Checks filename suffix and creates the destination filename
//
//  Copyright (C) 2007 Lasse Collin
//
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
///////////////////////////////////////////////////////////////////////////////

#include "private.h"


struct suffix_pair {
	const char *compressed;
	const char *uncompressed;
};


/// \brief      Checks if src_name has given compressed_suffix
///
/// \param      suffix      Filename suffix to look for
/// \param      src_name    Input filename
/// \param      src_len     strlen(src_name)
///
/// \return     If src_name has the suffix, src_len - strlen(suffix) is
///             returned. It's always a positive integer. Otherwise zero
///             is returned.
static size_t
test_suffix(const char *suffix, const char *src_name, size_t src_len)
{
	const size_t suffix_len = strlen(suffix);

	// The filename must have at least one character in addition to
	// the suffix. src_name may contain path to the filename, so we
	// need to check for directory separator too.
	if (src_len <= suffix_len || src_name[src_len - suffix_len - 1] == '/')
		return 0;

	if (strcmp(suffix, src_name + src_len - suffix_len) == 0)
		return src_len - suffix_len;

	return 0;
}


/// \brief      Removes the filename suffix of the compressed file
///
/// \return     Name of the uncompressed file, or NULL if file has unknown
///             suffix.
static char *
uncompressed_name(const char *src_name, const size_t src_len)
{
	static const struct suffix_pair suffixes[] = {
		{ ".xz",    "" },
		{ ".txz",   ".tar" }, // .txz abbreviation for .txt.gz is rare.
		{ ".lzma",  "" },
		{ ".tlz",   ".tar" },
		// { ".gz",    "" },
		// { ".tgz",   ".tar" },
	};

	const char *new_suffix = "";
	size_t new_len = 0;

	if (opt_format == FORMAT_RAW) {
		// Don't check for known suffixes when --format=raw was used.
		if (opt_suffix == NULL) {
			errmsg(V_ERROR, _("%s: With --format=raw, "
					"--suffix=.SUF is required unless "
					"writing to stdout"), src_name);
			return NULL;
		}
	} else {
		for (size_t i = 0; i < ARRAY_SIZE(suffixes); ++i) {
			new_len = test_suffix(suffixes[i].compressed,
					src_name, src_len);
			if (new_len != 0) {
				new_suffix = suffixes[i].uncompressed;
				break;
			}
		}
	}

	if (new_len == 0 && opt_suffix != NULL)
		new_len = test_suffix(opt_suffix, src_name, src_len);

	if (new_len == 0) {
		errmsg(V_WARNING, _("%s: Filename has an unknown suffix, "
				"skipping"), src_name);
		return NULL;
	}

	const size_t new_suffix_len = strlen(new_suffix);
	char *dest_name = malloc(new_len + new_suffix_len + 1);
	if (dest_name == NULL) {
		out_of_memory();
		return NULL;
	}

	memcpy(dest_name, src_name, new_len);
	memcpy(dest_name + new_len, new_suffix, new_suffix_len);
	dest_name[new_len + new_suffix_len] = '\0';

	return dest_name;
}


/// \brief      Appends suffix to src_name
///
/// In contrast to uncompressed_name(), we check only suffixes that are valid
/// for the specified file format.
static char *
compressed_name(const char *src_name, const size_t src_len)
{
	// The order of these must match the order in args.h.
	static const struct suffix_pair all_suffixes[][3] = {
		{
			{ ".xz",    "" },
			{ ".txz",   ".tar" },
			{ NULL, NULL }
		}, {
			{ ".lzma",  "" },
			{ ".tlz",   ".tar" },
			{ NULL,     NULL }
/*
		}, {
			{ ".gz",    "" },
			{ ".tgz",   ".tar" },
			{ NULL,     NULL }
*/
		}, {
			// --format=raw requires specifying the suffix
			// manually or using stdout.
			{ NULL,     NULL }
		}
	};

	// args.c ensures this.
	assert(opt_format != FORMAT_AUTO);

	const size_t format = opt_format - 1;
	const struct suffix_pair *const suffixes = all_suffixes[format];

	for (size_t i = 0; suffixes[i].compressed != NULL; ++i) {
		if (test_suffix(suffixes[i].compressed, src_name, src_len)
				!= 0) {
			errmsg(V_WARNING, _("%s: File already has `%s' "
					"suffix, skipping"), src_name,
					suffixes[i].compressed);
			return NULL;
		}
	}

	// TODO: Hmm, maybe it would be better to validate this in args.c,
	// since the suffix handling when decoding is weird now.
	if (opt_format == FORMAT_RAW && opt_suffix == NULL) {
		errmsg(V_ERROR, _("%s: With --format=raw, "
				"--suffix=.SUF is required unless "
				"writing to stdout"), src_name);
		return NULL;
	}

	const char *suffix = opt_suffix != NULL
			? opt_suffix : suffixes[0].compressed;
	const size_t suffix_len = strlen(suffix);

	char *dest_name = malloc(src_len + suffix_len + 1);
	if (dest_name == NULL) {
		out_of_memory();
		return NULL;
	}

	memcpy(dest_name, src_name, src_len);
	memcpy(dest_name + src_len, suffix, suffix_len);
	dest_name[src_len + suffix_len] = '\0';

	return dest_name;
}


extern char *
get_dest_name(const char *src_name)
{
	assert(src_name != NULL);

	// Length of the name is needed in all cases to locate the end of
	// the string to compare the suffix, so calculate the length here.
	const size_t src_len = strlen(src_name);

	return opt_mode == MODE_COMPRESS
			? compressed_name(src_name, src_len)
			: uncompressed_name(src_name, src_len);
}