OLD | NEW |
(Empty) | |
| 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 } |
OLD | NEW |