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