Rizin
unix-like reverse engineering framework and cli tools
suffix.c File Reference

Checks filename suffix and creates the destination filename. More...

#include "private.h"

Go to the source code of this file.

Functions

static bool is_dir_sep (char c)
 Test if the char is a directory separator. More...
 
static bool has_dir_sep (const char *str)
 Test if the string contains a directory separator. More...
 
static size_t test_suffix (const char *suffix, const char *src_name, size_t src_len)
 Checks if src_name has given compressed_suffix. More...
 
static char * uncompressed_name (const char *src_name, const size_t src_len)
 Removes the filename suffix of the compressed file. More...
 
static void msg_suffix (const char *src_name, const char *suffix)
 
static char * compressed_name (const char *src_name, size_t src_len)
 Appends suffix to src_name. More...
 
char * suffix_get_dest_name (const char *src_name)
 Get the name of the destination file. More...
 
void suffix_set (const char *suffix)
 Set a custom filename suffix. More...
 

Variables

static char * custom_suffix = NULL
 

Detailed Description

Checks filename suffix and creates the destination filename.

Definition in file suffix.c.

Function Documentation

◆ compressed_name()

static char* compressed_name ( const char *  src_name,
size_t  src_len 
)
static

Appends suffix to src_name.

In contrast to uncompressed_name(), we check only suffixes that are valid for the specified file format.

Definition at line 196 of file suffix.c.

197 {
198  // The order of these must match the order in args.h.
199  static const char *const all_suffixes[][4] = {
200  {
201  ".xz",
202  ".txz",
203  NULL
204  }, {
205  ".lzma",
206 #ifdef __DJGPP__
207  ".lzm",
208 #endif
209  ".tlz",
210  NULL
211 /*
212  }, {
213  ".gz",
214  ".tgz",
215  NULL
216 */
217  }, {
218  // --format=raw requires specifying the suffix
219  // manually or using stdout.
220  NULL
221  }
222  };
223 
224  // args.c ensures this.
226 
227  const size_t format = opt_format - 1;
228  const char *const *suffixes = all_suffixes[format];
229 
230  // Look for known filename suffixes and refuse to compress them.
231  for (size_t i = 0; suffixes[i] != NULL; ++i) {
232  if (test_suffix(suffixes[i], src_name, src_len) != 0) {
233  msg_suffix(src_name, suffixes[i]);
234  return NULL;
235  }
236  }
237 
238 #ifdef __DJGPP__
239  // Recognize also the special suffix that is used when long
240  // filename (LFN) support isn't available. This suffix is
241  // recognized on LFN systems too.
242  if (opt_format == FORMAT_XZ && has_sfn_suffix(src_name, src_len)) {
243  msg_suffix(src_name, "-");
244  return NULL;
245  }
246 #endif
247 
248  if (custom_suffix != NULL) {
249  if (test_suffix(custom_suffix, src_name, src_len) != 0) {
250  msg_suffix(src_name, custom_suffix);
251  return NULL;
252  }
253  }
254 
255  // TODO: Hmm, maybe it would be better to validate this in args.c,
256  // since the suffix handling when decoding is weird now.
257  if (opt_format == FORMAT_RAW && custom_suffix == NULL) {
258  message_error(_("%s: With --format=raw, "
259  "--suffix=.SUF is required unless "
260  "writing to stdout"), src_name);
261  return NULL;
262  }
263 
264  const char *suffix = custom_suffix != NULL
265  ? custom_suffix : suffixes[0];
266  size_t suffix_len = strlen(suffix);
267 
268 #ifdef __DJGPP__
269  if (!_use_lfn(src_name)) {
270  // Long filename (LFN) support isn't available and we are
271  // limited to 8.3 short filenames (SFN).
272  //
273  // Look for suffix separator from the filename, and make sure
274  // that it is in the filename, not in a directory name.
275  const char *sufsep = strrchr(src_name, '.');
276  if (sufsep == NULL || sufsep[1] == '\0'
277  || has_dir_sep(sufsep)) {
278  // src_name has no filename extension.
279  //
280  // Examples:
281  // xz foo -> foo.xz
282  // xz -F lzma foo -> foo.lzm
283  // xz -S x foo -> foox
284  // xz -S x foo. -> foo.x
285  // xz -S x.y foo -> foox.y
286  // xz -S .x foo -> foo.x
287  // xz -S .x foo. -> foo.x
288  //
289  // Avoid double dots:
290  if (sufsep != NULL && sufsep[1] == '\0'
291  && suffix[0] == '.')
292  --src_len;
293 
294  } else if (custom_suffix == NULL
295  && strcasecmp(sufsep, ".tar") == 0) {
296  // ".tar" is handled specially.
297  //
298  // Examples:
299  // xz foo.tar -> foo.txz
300  // xz -F lzma foo.tar -> foo.tlz
301  static const char *const tar_suffixes[] = {
302  ".txz",
303  ".tlz",
304  // ".tgz",
305  };
306  suffix = tar_suffixes[format];
307  suffix_len = 4;
308  src_len -= 4;
309 
310  } else {
311  if (custom_suffix == NULL && opt_format == FORMAT_XZ) {
312  // Instead of the .xz suffix, use a single
313  // character at the end of the filename
314  // extension. This is to minimize name
315  // conflicts when compressing multiple files
316  // with the same basename. E.g. foo.txt and
317  // foo.exe become foo.tx- and foo.ex-. Dash
318  // is rare as the last character of the
319  // filename extension, so it seems to be
320  // quite safe choice and it stands out better
321  // in directory listings than e.g. x. For
322  // comparison, gzip uses z.
323  suffix = "-";
324  suffix_len = 1;
325  }
326 
327  if (suffix[0] == '.') {
328  // The first character of the suffix is a dot.
329  // Throw away the original filename extension
330  // and replace it with the new suffix.
331  //
332  // Examples:
333  // xz -F lzma foo.txt -> foo.lzm
334  // xz -S .x foo.txt -> foo.x
335  src_len = sufsep - src_name;
336 
337  } else {
338  // The first character of the suffix is not
339  // a dot. Preserve the first 0-2 characters
340  // of the original filename extension.
341  //
342  // Examples:
343  // xz foo.txt -> foo.tx-
344  // xz -S x foo.c -> foo.cx
345  // xz -S ab foo.c -> foo.cab
346  // xz -S ab foo.txt -> foo.tab
347  // xz -S abc foo.txt -> foo.abc
348  //
349  // Truncate the suffix to three chars:
350  if (suffix_len > 3)
351  suffix_len = 3;
352 
353  // If needed, overwrite 1-3 characters.
354  if (strlen(sufsep) > 4 - suffix_len)
355  src_len = sufsep - src_name
356  + 4 - suffix_len;
357  }
358  }
359  }
360 #endif
361 
362  char *dest_name = xmalloc(src_len + suffix_len + 1);
363 
364  memcpy(dest_name, src_name, src_len);
365  memcpy(dest_name + src_len, suffix, suffix_len);
366  dest_name[src_len + suffix_len] = '\0';
367 
368  return dest_name;
369 }
lzma_index ** i
Definition: index.h:629
enum format_type opt_format
Definition: coder.c:25
@ FORMAT_RAW
Definition: coder.h:27
@ FORMAT_AUTO
Definition: coder.h:23
@ FORMAT_XZ
Definition: coder.h:24
#define NULL
Definition: cris-opc.c:27
#define xmalloc
Definition: disas-asm.h:43
unsigned char suffix[65536]
Definition: gun.c:164
memcpy(mem, inblock.get(), min(CONTAINING_RECORD(inblock.get(), MEMBLOCK, data) ->size, size))
assert(limit<=UINT32_MAX/2)
void message_error(const char *fmt,...)
Definition: message.c:764
#define _(String)
Definition: opintl.h:53
static char * custom_suffix
Definition: suffix.c:25
static void msg_suffix(const char *src_name, const char *suffix)
Definition: suffix.c:183
static bool has_dir_sep(const char *str)
Test if the string contains a directory separator.
Definition: suffix.c:42
static size_t test_suffix(const char *suffix, const char *src_name, size_t src_len)
Checks if src_name has given compressed_suffix.
Definition: suffix.c:87

References _, assert(), custom_suffix, FORMAT_AUTO, FORMAT_RAW, FORMAT_XZ, has_dir_sep(), i, memcpy(), message_error(), msg_suffix(), NULL, opt_format, suffix, test_suffix(), and xmalloc.

Referenced by suffix_get_dest_name().

◆ has_dir_sep()

static bool has_dir_sep ( const char *  str)
static

Test if the string contains a directory separator.

Definition at line 42 of file suffix.c.

43 {
44 #ifdef TUKLIB_DOSLIKE
45  return strpbrk(str, "/\\:") != NULL;
46 #else
47  return strchr(str, '/') != NULL;
48 #endif
49 }

References NULL, and cmd_descs_generate::str.

Referenced by compressed_name(), and suffix_set().

◆ is_dir_sep()

static bool is_dir_sep ( char  c)
static

Test if the char is a directory separator.

Definition at line 30 of file suffix.c.

31 {
32 #ifdef TUKLIB_DOSLIKE
33  return c == '/' || c == '\\' || c == ':';
34 #else
35  return c == '/';
36 #endif
37 }
#define c(i)
Definition: sha256.c:43

References c.

Referenced by test_suffix().

◆ msg_suffix()

static void msg_suffix ( const char *  src_name,
const char *  suffix 
)
static

This message is needed in multiple places in compressed_name(), so the message has been put into its own function.

Definition at line 183 of file suffix.c.

184 {
185  message_warning(_("%s: File already has `%s' suffix, skipping"),
186  src_name, suffix);
187  return;
188 }
void message_warning(const char *fmt,...)
Definition: message.c:751

References _, message_warning(), and suffix.

Referenced by compressed_name().

◆ suffix_get_dest_name()

char* suffix_get_dest_name ( const char *  src_name)

Get the name of the destination file.

Depending on the global variable opt_mode, this tries to find a matching counterpart for src_name. If the name can be constructed, it is allocated and returned (caller must free it). On error, a message is printed and NULL is returned.

Definition at line 373 of file suffix.c.

374 {
375  assert(src_name != NULL);
376 
377  // Length of the name is needed in all cases to locate the end of
378  // the string to compare the suffix, so calculate the length here.
379  const size_t src_len = strlen(src_name);
380 
381  return opt_mode == MODE_COMPRESS
382  ? compressed_name(src_name, src_len)
383  : uncompressed_name(src_name, src_len);
384 }
enum operation_mode opt_mode
Definition: coder.c:24
@ MODE_COMPRESS
Definition: coder.h:14
static char * compressed_name(const char *src_name, size_t src_len)
Appends suffix to src_name.
Definition: suffix.c:196
static char * uncompressed_name(const char *src_name, const size_t src_len)
Removes the filename suffix of the compressed file.
Definition: suffix.c:110

References assert(), compressed_name(), MODE_COMPRESS, NULL, opt_mode, and uncompressed_name().

Referenced by io_open_dest_real().

◆ suffix_set()

void suffix_set ( const char *  suffix)

Set a custom filename suffix.

This function calls xstrdup() for the given suffix, thus the caller doesn't need to keep the memory allocated. There can be only one custom suffix, thus if this is called multiple times, the old suffixes are freed and forgotten.

Definition at line 388 of file suffix.c.

389 {
390  // Empty suffix and suffixes having a directory separator are
391  // rejected. Such suffixes would break things later.
392  if (suffix[0] == '\0' || has_dir_sep(suffix))
393  message_fatal(_("%s: Invalid filename suffix"), suffix);
394 
395  // Replace the old custom_suffix (if any) with the new suffix.
398  return;
399 }
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
char * xstrdup(const char *) ATTRIBUTE_MALLOC
Definition: util.c:48
void message_fatal(const char *fmt,...)
Definition: message.c:777

References _, custom_suffix, free(), has_dir_sep(), message_fatal(), suffix, and xstrdup().

Referenced by parse_real().

◆ test_suffix()

static size_t test_suffix ( const char *  suffix,
const char *  src_name,
size_t  src_len 
)
static

Checks if src_name has given compressed_suffix.

Parameters
suffixFilename suffix to look for
src_nameInput filename
src_lenstrlen(src_name)
Returns
If src_name has the suffix, src_len - strlen(suffix) is returned. It's always a positive integer. Otherwise zero is returned.

Definition at line 87 of file suffix.c.

88 {
89  const size_t suffix_len = strlen(suffix);
90 
91  // The filename must have at least one character in addition to
92  // the suffix. src_name may contain path to the filename, so we
93  // need to check for directory separator too.
94  if (src_len <= suffix_len
95  || is_dir_sep(src_name[src_len - suffix_len - 1]))
96  return 0;
97 
98  if (strcmp(suffix, src_name + src_len - suffix_len) == 0)
99  return src_len - suffix_len;
100 
101  return 0;
102 }
static bool is_dir_sep(char c)
Test if the char is a directory separator.
Definition: suffix.c:30

References is_dir_sep(), and suffix.

Referenced by compressed_name(), and uncompressed_name().

◆ uncompressed_name()

static char* uncompressed_name ( const char *  src_name,
const size_t  src_len 
)
static

Removes the filename suffix of the compressed file.

Returns
Name of the uncompressed file, or NULL if file has unknown suffix.

Definition at line 110 of file suffix.c.

111 {
112  static const struct {
113  const char *compressed;
114  const char *uncompressed;
115  } suffixes[] = {
116  { ".xz", "" },
117  { ".txz", ".tar" }, // .txz abbreviation for .txt.gz is rare.
118  { ".lzma", "" },
119 #ifdef __DJGPP__
120  { ".lzm", "" },
121 #endif
122  { ".tlz", ".tar" },
123  // { ".gz", "" },
124  // { ".tgz", ".tar" },
125  };
126 
127  const char *new_suffix = "";
128  size_t new_len = 0;
129 
130  if (opt_format == FORMAT_RAW) {
131  // Don't check for known suffixes when --format=raw was used.
132  if (custom_suffix == NULL) {
133  message_error(_("%s: With --format=raw, "
134  "--suffix=.SUF is required unless "
135  "writing to stdout"), src_name);
136  return NULL;
137  }
138  } else {
139  for (size_t i = 0; i < ARRAY_SIZE(suffixes); ++i) {
140  new_len = test_suffix(suffixes[i].compressed,
141  src_name, src_len);
142  if (new_len != 0) {
143  new_suffix = suffixes[i].uncompressed;
144  break;
145  }
146  }
147 
148 #ifdef __DJGPP__
149  // Support also *.?- -> *.? and *.??- -> *.?? on DOS.
150  // This is done also when long filenames are available
151  // to keep it easy to decompress files created when
152  // long filename support wasn't available.
153  if (new_len == 0 && has_sfn_suffix(src_name, src_len)) {
154  new_suffix = "";
155  new_len = src_len - 1;
156  }
157 #endif
158  }
159 
160  if (new_len == 0 && custom_suffix != NULL)
161  new_len = test_suffix(custom_suffix, src_name, src_len);
162 
163  if (new_len == 0) {
164  message_warning(_("%s: Filename has an unknown suffix, "
165  "skipping"), src_name);
166  return NULL;
167  }
168 
169  const size_t new_suffix_len = strlen(new_suffix);
170  char *dest_name = xmalloc(new_len + new_suffix_len + 1);
171 
172  memcpy(dest_name, src_name, new_len);
173  memcpy(dest_name + new_len, new_suffix, new_suffix_len);
174  dest_name[new_len + new_suffix_len] = '\0';
175 
176  return dest_name;
177 }
#define ARRAY_SIZE(a)
static uint8_t compressed[1024]
in[] after compression

References _, ARRAY_SIZE, compressed, custom_suffix, FORMAT_RAW, i, memcpy(), message_error(), message_warning(), NULL, opt_format, test_suffix(), and xmalloc.

Referenced by suffix_get_dest_name().

Variable Documentation

◆ custom_suffix

char* custom_suffix = NULL
static

Definition at line 25 of file suffix.c.

Referenced by compressed_name(), suffix_set(), and uncompressed_name().