]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/xz/src/xz/suffix.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / xz / src / xz / suffix.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       suffix.c
4 /// \brief      Checks filename suffix and creates the destination filename
5 //
6 //  Author:     Lasse Collin
7 //
8 //  This file has been put into the public domain.
9 //  You can do whatever you want with this file.
10 //
11 ///////////////////////////////////////////////////////////////////////////////
12
13 #include "private.h"
14
15 // For case-insensitive filename suffix on case-insensitive systems
16 #if defined(TUKLIB_DOSLIKE) || defined(__VMS)
17 #       define strcmp strcasecmp
18 #endif
19
20
21 static char *custom_suffix = NULL;
22
23
24 /// \brief      Test if the char is a directory separator
25 static bool
26 is_dir_sep(char c)
27 {
28 #ifdef TUKLIB_DOSLIKE
29         return c == '/' || c == '\\' || c == ':';
30 #else
31         return c == '/';
32 #endif
33 }
34
35
36 /// \brief      Test if the string contains a directory separator
37 static bool
38 has_dir_sep(const char *str)
39 {
40 #ifdef TUKLIB_DOSLIKE
41         return strpbrk(str, "/\\:") != NULL;
42 #else
43         return strchr(str, '/') != NULL;
44 #endif
45 }
46
47
48 /// \brief      Checks if src_name has given compressed_suffix
49 ///
50 /// \param      suffix      Filename suffix to look for
51 /// \param      src_name    Input filename
52 /// \param      src_len     strlen(src_name)
53 ///
54 /// \return     If src_name has the suffix, src_len - strlen(suffix) is
55 ///             returned. It's always a positive integer. Otherwise zero
56 ///             is returned.
57 static size_t
58 test_suffix(const char *suffix, const char *src_name, size_t src_len)
59 {
60         const size_t suffix_len = strlen(suffix);
61
62         // The filename must have at least one character in addition to
63         // the suffix. src_name may contain path to the filename, so we
64         // need to check for directory separator too.
65         if (src_len <= suffix_len
66                         || is_dir_sep(src_name[src_len - suffix_len - 1]))
67                 return 0;
68
69         if (strcmp(suffix, src_name + src_len - suffix_len) == 0)
70                 return src_len - suffix_len;
71
72         return 0;
73 }
74
75
76 /// \brief      Removes the filename suffix of the compressed file
77 ///
78 /// \return     Name of the uncompressed file, or NULL if file has unknown
79 ///             suffix.
80 static char *
81 uncompressed_name(const char *src_name, const size_t src_len)
82 {
83         static const struct {
84                 const char *compressed;
85                 const char *uncompressed;
86         } suffixes[] = {
87                 { ".xz",    "" },
88                 { ".txz",   ".tar" }, // .txz abbreviation for .txt.gz is rare.
89                 { ".lzma",  "" },
90                 { ".tlz",   ".tar" },
91                 // { ".gz",    "" },
92                 // { ".tgz",   ".tar" },
93         };
94
95         const char *new_suffix = "";
96         size_t new_len = 0;
97
98         if (opt_format == FORMAT_RAW) {
99                 // Don't check for known suffixes when --format=raw was used.
100                 if (custom_suffix == NULL) {
101                         message_error(_("%s: With --format=raw, "
102                                         "--suffix=.SUF is required unless "
103                                         "writing to stdout"), src_name);
104                         return NULL;
105                 }
106         } else {
107                 for (size_t i = 0; i < ARRAY_SIZE(suffixes); ++i) {
108                         new_len = test_suffix(suffixes[i].compressed,
109                                         src_name, src_len);
110                         if (new_len != 0) {
111                                 new_suffix = suffixes[i].uncompressed;
112                                 break;
113                         }
114                 }
115         }
116
117         if (new_len == 0 && custom_suffix != NULL)
118                 new_len = test_suffix(custom_suffix, src_name, src_len);
119
120         if (new_len == 0) {
121                 message_warning(_("%s: Filename has an unknown suffix, "
122                                 "skipping"), src_name);
123                 return NULL;
124         }
125
126         const size_t new_suffix_len = strlen(new_suffix);
127         char *dest_name = xmalloc(new_len + new_suffix_len + 1);
128
129         memcpy(dest_name, src_name, new_len);
130         memcpy(dest_name + new_len, new_suffix, new_suffix_len);
131         dest_name[new_len + new_suffix_len] = '\0';
132
133         return dest_name;
134 }
135
136
137 /// \brief      Appends suffix to src_name
138 ///
139 /// In contrast to uncompressed_name(), we check only suffixes that are valid
140 /// for the specified file format.
141 static char *
142 compressed_name(const char *src_name, const size_t src_len)
143 {
144         // The order of these must match the order in args.h.
145         static const char *const all_suffixes[][3] = {
146                 {
147                         ".xz",
148                         ".txz",
149                         NULL
150                 }, {
151                         ".lzma",
152                         ".tlz",
153                         NULL
154 /*
155                 }, {
156                         ".gz",
157                         ".tgz",
158                         NULL
159 */
160                 }, {
161                         // --format=raw requires specifying the suffix
162                         // manually or using stdout.
163                         NULL
164                 }
165         };
166
167         // args.c ensures this.
168         assert(opt_format != FORMAT_AUTO);
169
170         const size_t format = opt_format - 1;
171         const char *const *suffixes = all_suffixes[format];
172
173         for (size_t i = 0; suffixes[i] != NULL; ++i) {
174                 if (test_suffix(suffixes[i], src_name, src_len) != 0) {
175                         message_warning(_("%s: File already has `%s' "
176                                         "suffix, skipping"), src_name,
177                                         suffixes[i]);
178                         return NULL;
179                 }
180         }
181
182         if (custom_suffix != NULL) {
183                 if (test_suffix(custom_suffix, src_name, src_len) != 0) {
184                         message_warning(_("%s: File already has `%s' "
185                                         "suffix, skipping"), src_name,
186                                         custom_suffix);
187                         return NULL;
188                 }
189         }
190
191         // TODO: Hmm, maybe it would be better to validate this in args.c,
192         // since the suffix handling when decoding is weird now.
193         if (opt_format == FORMAT_RAW && custom_suffix == NULL) {
194                 message_error(_("%s: With --format=raw, "
195                                 "--suffix=.SUF is required unless "
196                                 "writing to stdout"), src_name);
197                 return NULL;
198         }
199
200         const char *suffix = custom_suffix != NULL
201                         ? custom_suffix : suffixes[0];
202         const size_t suffix_len = strlen(suffix);
203
204         char *dest_name = xmalloc(src_len + suffix_len + 1);
205
206         memcpy(dest_name, src_name, src_len);
207         memcpy(dest_name + src_len, suffix, suffix_len);
208         dest_name[src_len + suffix_len] = '\0';
209
210         return dest_name;
211 }
212
213
214 extern char *
215 suffix_get_dest_name(const char *src_name)
216 {
217         assert(src_name != NULL);
218
219         // Length of the name is needed in all cases to locate the end of
220         // the string to compare the suffix, so calculate the length here.
221         const size_t src_len = strlen(src_name);
222
223         return opt_mode == MODE_COMPRESS
224                         ? compressed_name(src_name, src_len)
225                         : uncompressed_name(src_name, src_len);
226 }
227
228
229 extern void
230 suffix_set(const char *suffix)
231 {
232         // Empty suffix and suffixes having a directory separator are
233         // rejected. Such suffixes would break things later.
234         if (suffix[0] == '\0' || has_dir_sep(suffix))
235                 message_fatal(_("%s: Invalid filename suffix"), optarg);
236
237         // Replace the old custom_suffix (if any) with the new suffix.
238         free(custom_suffix);
239         custom_suffix = xstrdup(suffix);
240         return;
241 }