| 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 |