1 ///////////////////////////////////////////////////////////////////////////////
4 /// \brief Checks filename suffix and creates the destination filename
6 // Copyright (C) 2007 Lasse Collin
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
18 ///////////////////////////////////////////////////////////////////////////////
22 // For case-insensitive filename suffix on case-insensitive systems
24 # define strcmp strcasecmp
28 static char *custom_suffix = NULL;
32 const char *compressed;
33 const char *uncompressed;
37 /// \brief Checks if src_name has given compressed_suffix
39 /// \param suffix Filename suffix to look for
40 /// \param src_name Input filename
41 /// \param src_len strlen(src_name)
43 /// \return If src_name has the suffix, src_len - strlen(suffix) is
44 /// returned. It's always a positive integer. Otherwise zero
47 test_suffix(const char *suffix, const char *src_name, size_t src_len)
49 const size_t suffix_len = strlen(suffix);
51 // The filename must have at least one character in addition to
52 // the suffix. src_name may contain path to the filename, so we
53 // need to check for directory separator too.
54 if (src_len <= suffix_len || src_name[src_len - suffix_len - 1] == '/')
57 if (strcmp(suffix, src_name + src_len - suffix_len) == 0)
58 return src_len - suffix_len;
64 /// \brief Removes the filename suffix of the compressed file
66 /// \return Name of the uncompressed file, or NULL if file has unknown
69 uncompressed_name(const char *src_name, const size_t src_len)
71 static const struct suffix_pair suffixes[] = {
73 { ".txz", ".tar" }, // .txz abbreviation for .txt.gz is rare.
77 // { ".tgz", ".tar" },
80 const char *new_suffix = "";
83 if (opt_format == FORMAT_RAW) {
84 // Don't check for known suffixes when --format=raw was used.
85 if (custom_suffix == NULL) {
86 message_error(_("%s: With --format=raw, "
87 "--suffix=.SUF is required unless "
88 "writing to stdout"), src_name);
92 for (size_t i = 0; i < ARRAY_SIZE(suffixes); ++i) {
93 new_len = test_suffix(suffixes[i].compressed,
96 new_suffix = suffixes[i].uncompressed;
102 if (new_len == 0 && custom_suffix != NULL)
103 new_len = test_suffix(custom_suffix, src_name, src_len);
106 message_warning(_("%s: Filename has an unknown suffix, "
107 "skipping"), src_name);
111 const size_t new_suffix_len = strlen(new_suffix);
112 char *dest_name = xmalloc(new_len + new_suffix_len + 1);
114 memcpy(dest_name, src_name, new_len);
115 memcpy(dest_name + new_len, new_suffix, new_suffix_len);
116 dest_name[new_len + new_suffix_len] = '\0';
122 /// \brief Appends suffix to src_name
124 /// In contrast to uncompressed_name(), we check only suffixes that are valid
125 /// for the specified file format.
127 compressed_name(const char *src_name, const size_t src_len)
129 // The order of these must match the order in args.h.
130 static const struct suffix_pair all_suffixes[][3] = {
146 // --format=raw requires specifying the suffix
147 // manually or using stdout.
152 // args.c ensures this.
153 assert(opt_format != FORMAT_AUTO);
155 const size_t format = opt_format - 1;
156 const struct suffix_pair *const suffixes = all_suffixes[format];
158 for (size_t i = 0; suffixes[i].compressed != NULL; ++i) {
159 if (test_suffix(suffixes[i].compressed, src_name, src_len)
161 message_warning(_("%s: File already has `%s' "
162 "suffix, skipping"), src_name,
163 suffixes[i].compressed);
168 // TODO: Hmm, maybe it would be better to validate this in args.c,
169 // since the suffix handling when decoding is weird now.
170 if (opt_format == FORMAT_RAW && custom_suffix == NULL) {
171 message_error(_("%s: With --format=raw, "
172 "--suffix=.SUF is required unless "
173 "writing to stdout"), src_name);
177 const char *suffix = custom_suffix != NULL
178 ? custom_suffix : suffixes[0].compressed;
179 const size_t suffix_len = strlen(suffix);
181 char *dest_name = xmalloc(src_len + suffix_len + 1);
183 memcpy(dest_name, src_name, src_len);
184 memcpy(dest_name + src_len, suffix, suffix_len);
185 dest_name[src_len + suffix_len] = '\0';
192 suffix_get_dest_name(const char *src_name)
194 assert(src_name != NULL);
196 // Length of the name is needed in all cases to locate the end of
197 // the string to compare the suffix, so calculate the length here.
198 const size_t src_len = strlen(src_name);
200 return opt_mode == MODE_COMPRESS
201 ? compressed_name(src_name, src_len)
202 : uncompressed_name(src_name, src_len);
207 suffix_set(const char *suffix)
209 // Empty suffix and suffixes having a slash are rejected. Such
210 // suffixes would break things later.
211 if (suffix[0] == '\0' || strchr(suffix, '/') != NULL)
212 message_fatal(_("%s: Invalid filename suffix"), optarg);
214 // Replace the old custom_suffix (if any) with the new suffix.
216 custom_suffix = xstrdup(suffix);