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