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