1 /* util.c --
2
3 This file is part of the lzop file compressor.
4
5 Copyright (C) 1996-2017 Markus Franz Xaver Johannes Oberhumer
6 All Rights Reserved.
7
8 lzop and the LZO library are free software; you can redistribute them
9 and/or modify them under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of
11 the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; see the file COPYING.
20 If not, write to the Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
23 Markus F.X.J. Oberhumer
24 <markus@oberhumer.com>
25 http://www.oberhumer.com/opensource/lzop/
26 */
27
28
29 #include "conf.h"
30
31
32 /*************************************************************************
33 // filename util
34 **************************************************************************/
35
36 static const char dir_sep[] = DIR_SEP;
37
38 #define fn_is_sep(c) (strchr(dir_sep,c) != NULL)
39
40 #if defined(DOSISH)
41 #define fn_is_drive(n) (n[0] && n[1] == ':')
42 #define fn_skip_drive(n) (fn_is_drive(n) ? (n) + 2 : (n))
43 #else
44 #define fn_is_drive(n) (0)
45 #define fn_skip_drive(n) (n)
46 #endif
47
48
fn_baseindex(const char * name)49 unsigned fn_baseindex(const char *name)
50 {
51 const char *n, *nn;
52
53 n = fn_skip_drive(name);
54 for (nn = n; *nn; nn++)
55 if (fn_is_sep(*nn))
56 n = nn + 1;
57 return (unsigned) (n - name);
58 }
59
60
fn_basename(const char * name)61 const char *fn_basename(const char *name)
62 {
63 return name + fn_baseindex(name);
64 }
65
66
fn_addslash(char * name,lzo_bool slash)67 void fn_addslash(char *name, lzo_bool slash)
68 {
69 char *p;
70
71 name = fn_skip_drive(name);
72 p = name + strlen(name);
73 while (p > name && fn_is_sep(p[-1]))
74 *p-- = 0;
75 if (p > name)
76 {
77 if (slash)
78 *p++ = dir_sep[0];
79 *p = 0;
80 }
81 }
82
83
fn_strlwr(char * n)84 char *fn_strlwr(char *n)
85 {
86 char *p;
87 for (p = n; *p; p++)
88 *p = (char) fn_tolower(*p);
89 return n;
90 }
91
92
fn_strcmp(const char * n1,const char * n2)93 int fn_strcmp(const char *n1, const char *n2)
94 {
95 for (;;)
96 {
97 if (*n1 != *n2)
98 {
99 int c = fn_tolower(*n1) - fn_tolower(*n2);
100 if (c)
101 return c;
102 }
103 if (*n1 == 0)
104 return 0;
105 n1++; n2++;
106 }
107 }
108
109
fn_is_same_file(const char * n1,const char * n2)110 lzo_bool fn_is_same_file(const char *n1, const char *n2)
111 {
112 /* very simple... */
113 if (fn_strcmp(n1,n2) == 0)
114 return 1;
115 return 0;
116 }
117
118
fn_has_suffix(const char * name)119 int fn_has_suffix(const char *name)
120 {
121 size_t l;
122 size_t s;
123
124 name = fn_skip_drive(name);
125 l = strlen(name);
126 if (l > 4 && name[l-4] == '.')
127 {
128 if (strcasecmp(&name[l-3],"lzo") == 0)
129 return SUFF_LZO;
130 if (strcasecmp(&name[l-3],"nrv") == 0)
131 return SUFF_NRV;
132 if (strcasecmp(&name[l-3],"tar") == 0)
133 return SUFF_TAR;
134 if (strcasecmp(&name[l-3],"tnv") == 0)
135 return SUFF_TNV;
136 if (strcasecmp(&name[l-3],"tzo") == 0)
137 return SUFF_TZO;
138 }
139
140 if (l > 5 && name[l-5] == '.')
141 {
142 if (strcasecmp(&name[l-4],"lzop") == 0)
143 return SUFF_LZOP;
144 }
145
146 s = strlen(opt_suffix);
147 if (s > 0 && l > s)
148 {
149 if (strcasecmp(&name[l-s],opt_suffix) == 0)
150 return SUFF_USER;
151 }
152
153 return SUFF_NONE;
154 }
155
156
fn_cleanpath(const char * name,char * newname,size_t size,int flags)157 int fn_cleanpath(const char *name, char *newname, size_t size, int flags)
158 {
159 size_t l = 0;
160 size_t n = 0;
161 int slashes = 0;
162 #define add(x) if (l >= size) return -1; else newname[l++] = (x)
163
164 name = fn_skip_drive(name);
165 while (*name && fn_is_sep(*name))
166 name++;
167 while (name[n])
168 {
169 size_t last_n;
170 assert(!fn_is_sep(name[n]));
171 last_n = n++;
172 while (name[n] && !fn_is_sep(name[n]))
173 n++;
174 if (n - last_n == 2 && name[n-2] == '.' && name[n-1] == '.')
175 {
176 if (flags & 1)
177 return -2;
178 while (l > 0) {
179 if (newname[--l] == '/')
180 {
181 --slashes;
182 break;
183 }
184 }
185 /* newname[l] = 0; printf("del %s\n", newname); */
186 }
187 else if (n - last_n == 1 && name[n-1] == '.')
188 {
189 if (flags & 2)
190 return -3;
191 }
192 else
193 {
194 if (l > 0)
195 { ++slashes; add('/'); }
196 while (last_n < n)
197 { add(name[last_n++]); }
198 /* newname[l] = 0; printf("add %s\n", newname); */
199 }
200 while (name[n] && fn_is_sep(name[n]))
201 n++;
202 }
203 add('\0');
204 return slashes;
205 #undef add
206 }
207
208
209 /*************************************************************************
210 // time util
211 **************************************************************************/
212
fix_time(time_t t)213 time_t fix_time(time_t t)
214 {
215 if (t == (time_t) -1)
216 t = 0;
217 return t;
218 }
219
get_mtime(const header_t * h)220 time_t get_mtime(const header_t *h)
221 {
222 lzop_ulong_t t;
223 t = h->mtime_high;
224 t <<= 16; t <<= 16;
225 t |= h->mtime_low;
226 return t == (lzop_ulong_t)(time_t)t ? (time_t)t : (time_t)0;
227 }
228
229
230 #if defined(HAVE_LOCALTIME)
tm2str(char * s,size_t size,const struct tm * tmp)231 void tm2str(char *s, size_t size, const struct tm *tmp)
232 {
233 assert(size >= 18);
234 #if defined(HAVE_SNPRINTF)
235 snprintf(s, size, "%04d-%02d-%02d %02d:%02d:%02d",
236 (int) tmp->tm_year + 1900, (int) tmp->tm_mon + 1,
237 (int) tmp->tm_mday,
238 (int) tmp->tm_hour, (int) tmp->tm_min, (int) tmp->tm_sec);
239 #else
240 sprintf(s, "%04d-%02d-%02d %02d:%02d:%02d",
241 (int) tmp->tm_year + 1900, (int) tmp->tm_mon + 1,
242 (int) tmp->tm_mday,
243 (int) tmp->tm_hour, (int) tmp->tm_min, (int) tmp->tm_sec);
244 #endif
245 }
246 #endif
247
248
time2str(char * s,size_t size,const time_t * t)249 void time2str(char *s, size_t size, const time_t *t)
250 {
251 #if defined(HAVE_LOCALTIME)
252 tm2str(s, size, localtime(t));
253 #elif defined(HAVE_CTIME)
254 const char *p = ctime(t);
255 assert(size >= 18);
256 memset(s, ' ', 16);
257 memcpy(s + 2, p + 4, 6);
258 memcpy(s + 11, p + 11, 5);
259 s[16] = 0;
260 #else
261 s[0] = 0;
262 UNUSED(size);
263 #endif
264 }
265
266
time2ls(char * s,size_t size,const time_t * t)267 void time2ls(char *s, size_t size, const time_t *t)
268 {
269 #if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
270 const char *fmt = "%b %e %Y";
271 #if defined(HAVE_DIFFTIME)
272 # if (ACC_OS_DOS16)
273 /* do not introduce any floating point dependencies */
274 long d = (long)current_time - (long)*t;
275 # else
276 const double d = difftime(current_time, *t);
277 # endif
278 if (d <= 6 * 30 * 24 * 3600L && d >= -3600L)
279 fmt = "%b %e %H:%M";
280 #endif
281 assert(size >= 13);
282 if (strftime(s, 13, fmt, localtime(t)) == 13)
283 s[0] = 0;
284 #else
285 s[0] = 0;
286 UNUSED(size);
287 UNUSED(t);
288 #endif
289 }
290
291
292 /*************************************************************************
293 //
294 **************************************************************************/
295
file_exists(const char * name)296 lzo_bool file_exists(const char *name)
297 {
298 int fd, r;
299 struct stat st;
300
301 /* return true if we can open it */
302 fd = open(name, O_RDONLY, 0);
303 if (fd >= 0)
304 {
305 (void) close(fd);
306 return 1;
307 }
308
309 /* return true if we can stat it */
310 r = stat(name, &st);
311 if (r != -1)
312 return 1;
313
314 /* return true if we can lstat it */
315 #if defined(HAVE_LSTAT)
316 r = lstat(name, &st);
317 if (r != -1)
318 return 1;
319 #endif
320
321 return 0;
322 }
323
324
325 /*************************************************************************
326 // Some systems have very exotic mode values.
327 // Convert them to standard values for portable use in our header.
328 **************************************************************************/
329
fix_mode_for_header(lzo_uint32 mode)330 lzo_uint32 fix_mode_for_header(lzo_uint32 mode)
331 {
332 lzo_uint32 m = mode;
333
334 if (mode == 0)
335 return 0;
336
337 /* This function can only deal with S_ISREG and S_ISDIR modes. */
338 assert(S_ISREG(mode) || S_ISDIR(mode));
339
340 #if defined(ACC_OS_TOS) && defined(__PUREC__)
341 m = 0444;
342 if (mode & S_IWRITE)
343 m |= 0200;
344 if (mode & S_IEXEC)
345 m |= 0111;
346 if (S_ISREG(mode))
347 m |= 0100000 | 0400;
348 else if (S_ISDIR(mode))
349 m |= 0040000 | 0700;
350 #elif defined(DOSISH)
351 m &= 0777;
352 if (S_ISREG(mode))
353 m |= 0100000 | 0400;
354 else if (S_ISDIR(mode))
355 m |= 0040000 | 0700;
356 #else
357 m &= 07777;
358 if (S_ISREG(mode))
359 m |= 0100000;
360 else if (S_ISDIR(mode))
361 m |= 0040000 | 0700;
362 #endif
363
364 if ((m & 0777) == 0)
365 m |= (0644 & ~u_mask);
366 return m;
367 }
368
369
fix_mode_for_chmod(lzo_uint32 mode)370 MODE_T fix_mode_for_chmod(lzo_uint32 mode)
371 {
372 MODE_T m = (MODE_T) (mode & 07777);
373 if ((m & 0777) == 0)
374 m |= (MODE_T) (0644 & ~u_mask);
375 return m;
376 }
377
378
fix_mode_for_ls(lzo_uint32 mode)379 MODE_T fix_mode_for_ls(lzo_uint32 mode)
380 {
381 MODE_T m = (MODE_T) mode;
382 if ((m & 0777) == 0)
383 m |= (MODE_T) (0644 & ~u_mask);
384 return m;
385 }
386
387
fix_mode_for_open(MODE_T mode)388 MODE_T fix_mode_for_open(MODE_T mode)
389 {
390 MODE_T m = (MODE_T) (mode & 0666);
391 #if defined(ACC_OS_TOS) && defined(__PUREC__)
392 m = S_IWRITE | S_IREAD;
393 #else
394 if ((m & 0777) == 0)
395 m |= (MODE_T) (0644 & ~u_mask);
396 m |= 0600;
397 #endif
398 return m;
399 }
400
401
402 /*************************************************************************
403 // ls util - adapted from GNU fileutils 3.16
404 **************************************************************************/
405
406 /* Return a character indicating the type of file described by
407 file mode BITS:
408 'd' for directories
409 'b' for block special files
410 'c' for character special files
411 'm' for multiplexor files
412 'l' for symbolic links
413 's' for sockets
414 'p' for fifos
415 '-' for regular files
416 '?' for any other file type. */
417
ftypelet(unsigned mode)418 static char ftypelet(unsigned mode)
419 {
420 if (S_ISREG(mode)) return '-';
421 if (S_ISDIR(mode)) return 'd';
422 #ifdef S_ISBLK
423 if (S_ISBLK(mode)) return 'b';
424 #endif
425 #ifdef S_ISCHR
426 if (S_ISCHR(mode)) return 'c';
427 #endif
428 #ifdef S_ISFIFO
429 if (S_ISFIFO(mode)) return 'p';
430 #endif
431 #ifdef S_ISLNK
432 if (S_ISLNK(mode)) return 'l';
433 #endif
434 #ifdef S_ISSOCK
435 if (S_ISSOCK(mode)) return 's';
436 #endif
437 #ifdef S_ISMPC
438 if (S_ISMPC(mode)) return 'm';
439 #endif
440 #ifdef S_ISNWK
441 if (S_ISNWK(mode)) return 'n';
442 #endif
443 #ifdef S_ISOFD
444 if (S_ISOFD(mode)) return 'M'; /* Cray migrated dmf file. */
445 #endif
446 #ifdef S_ISOFL
447 if (S_ISOFL(mode)) return 'M'; /* Cray migrated dmf file. */
448 #endif
449 return '?';
450 }
451
452
453 /* Set the read, write, and execute flags. */
set_rwx(unsigned mode,char * str)454 static void set_rwx(unsigned mode, char *str)
455 {
456 str[0] = (char) ((mode & 4) ? 'r' : '-');
457 str[1] = (char) ((mode & 2) ? 'w' : '-');
458 str[2] = (char) ((mode & 1) ? 'x' : '-');
459 }
460
461
462 /* Set the 's' and 't' flags in file attributes string. */
set_st(unsigned mode,char * str)463 static void set_st(unsigned mode, char *str)
464 {
465 #ifdef S_ISUID
466 if (mode & S_ISUID)
467 str[3] = (char) (str[3] == 'x' ? 's' : 'S');
468 #endif
469 #ifdef S_ISGID
470 if (mode & S_ISGID)
471 str[6] = (char) (str[6] == 'x' ? 's' : 'S');
472 #endif
473 #ifdef S_ISVTX
474 if (mode & S_ISVTX)
475 str[9] = (char) (str[9] == 'x' ? 't' : 'T');
476 #endif
477 UNUSED(mode);
478 UNUSED(str);
479 }
480
481
mode_string(MODE_T m,char * str)482 void mode_string(MODE_T m, char *str)
483 {
484 unsigned mode = (unsigned) m;
485 str[0] = ftypelet(mode);
486 set_rwx((mode & 0700) >> 6, &str[1]);
487 set_rwx((mode & 0070) >> 3, &str[4]);
488 set_rwx((mode & 0007) >> 0, &str[7]);
489 set_st(mode, str);
490 }
491
492
493 /*************************************************************************
494 // adapted from the djgpp port of cpio 2.4.2 by Eli Zaretskii
495 **************************************************************************/
496
497 #if defined(DOSISH)
498
499 /* If the original file name includes characters illegal for MS-DOS and
500 MS-Windows, massage it to make it suitable and return a pointer to
501 static storage with a new name. If the original name is legit,
502 return it instead. Return NULL if the rename failed (shouldn't happen).
503
504 The file name changes are: (1) any name that is reserved by a DOS
505 device driver (such as 'prn.txt' or 'aux.c') is prepended with a '_';
506 and (2) illegal characters are replaced with '_' or '-' (well, almost;
507 look at the code below for details). */
508
509 #define is_slash(x) fn_is_sep(x)
510
maybe_rename_file(const char * original_name)511 char *maybe_rename_file(const char *original_name)
512 {
513 static char dosified_name[PATH_MAX+1];
514 static const char illegal_chars_dos[] = ".+, ;=[]|<>\":?*";
515 const char *illegal_chars = illegal_chars_dos;
516 int idx, dot_idx;
517 const char *s = original_name;
518 char *d = dosified_name;
519
520 /* Support for Win32 VFAT systems, when available. */
521 #if defined(__DJGPP__)
522 if (_use_lfn(original_name))
523 illegal_chars = illegal_chars_dos + 8;
524 #elif (ACC_OS_WIN32 || ACC_OS_WIN64 || ACC_OS_CYGWIN)
525 illegal_chars = illegal_chars_dos + 8;
526 #endif
527
528 /* Get past the drive letter, if any. */
529 if (fn_is_drive(s))
530 {
531 *d++ = *s++;
532 *d++ = *s++;
533 }
534
535 for (idx = 0, dot_idx = -1; *s; s++, d++)
536 {
537 if (strchr(illegal_chars, *s))
538 {
539 /* Dots are special on DOS: it doesn't allow them as the leading
540 character, and a file name cannot have more than a single dot.
541 We leave the first non-leading dot alone, unless it comes too
542 close to the beginning of the name: we want sh.lex.c to become
543 sh_lex.c, not sh.lex-c. */
544 if (*s == '.')
545 {
546 if (idx == 0
547 && (is_slash(s[1]) || s[1] == '\0'
548 || (s[1] == '.' && (is_slash(s[2]) || s[2] == '\0'))))
549 {
550 /* Copy "./" and "../" verbatim. */
551 *d++ = *s++;
552 if (*s == '.')
553 *d++ = *s++;
554 *d = *s;
555 }
556 else if (idx == 0)
557 *d = '_';
558 else if (dot_idx >= 0)
559 {
560 if (dot_idx < 5) /* 5 is merely a heuristic ad-hoc'ery */
561 {
562 d[dot_idx - idx] = '_'; /* replace previous dot */
563 *d = '.';
564 }
565 else
566 *d = '-';
567 }
568 else
569 *d = '.';
570
571 if (*s == '.')
572 dot_idx = idx;
573 }
574 else if (*s == '+' && s[1] == '+')
575 {
576 if (idx - 2 == dot_idx) /* .c++, .h++ etc. */
577 {
578 *d++ = 'x';
579 *d = 'x';
580 }
581 else
582 {
583 /* libg++ etc. */
584 memcpy (d, "plus", 4);
585 d += 3;
586 }
587 s++;
588 idx++;
589 }
590 else
591 *d = '_';
592 }
593 else
594 *d = *s;
595 if (is_slash(*s))
596 {
597 idx = 0;
598 dot_idx = -1;
599 }
600 else
601 idx++;
602 }
603
604 *d = '\0';
605
606 #if defined(S_ISCHR)
607 /* We could have a file in an archive whose name is reserved
608 on MS-DOS by a device driver. Trying to extract such a
609 file would fail at best and wedge us at worst. We need to
610 rename such files. */
611
612 if (idx > 0)
613 {
614 struct stat st_buf;
615 char *base = d - idx;
616 int i = 0;
617
618 /* The list of character devices is not constant: it depends on
619 what device drivers did they install in their CONFIG.SYS.
620 'stat' will tell us if the basename of the file name is a
621 characer device. */
622 while (stat(base, &st_buf) == 0 && S_ISCHR(st_buf.st_mode))
623 {
624 size_t blen = strlen(base);
625
626 /* I don't believe any DOS character device names begin with a
627 '_'. But in case they invent such a device, let us try twice. */
628 if (++i > 2)
629 return (char *)0;
630
631 /* Prepend a '_'. */
632 memmove(base + 1, base, blen + 1);
633 base[0] = '_';
634 }
635 }
636 #endif
637
638 return dosified_name;
639 }
640
641 #endif /* DOSISH */
642
643
644 /*************************************************************************
645 //
646 **************************************************************************/
647
648 #if (ACC_CC_MSC && (_MSC_VER >= 1000 && _MSC_VER < 1200))
649 /* avoid '-W4' warnings in <windows.h> */
650 # pragma warning(disable: 4201 4214 4514)
651 #endif
652 #if (ACC_CC_MSC && (_MSC_VER >= 1300))
653 /* avoid '-Wall' warnings in <windows.h> */
654 # pragma warning(disable: 4255)
655 #endif
656
657 #define ACC_WANT_ACC_INCI_H 1
658 #define ACC_WANT_ACCLIB_GETOPT 1
659 #define ACC_WANT_ACCLIB_HALLOC 1
660 #define ACC_WANT_ACCLIB_HMEMCPY 1
661 #define ACC_WANT_ACCLIB_HSREAD 1
662 #define ACC_WANT_ACCLIB_MISC 1
663 #define ACC_WANT_ACCLIB_WILDARGV 1
664 #if (ACC_HAVE_MM_HUGE_PTR)
665 # define ACC_WANT_ACCLIB_HREAD 1
666 #endif
667 #include "miniacc.h"
668 #undef ACC_WANT_ACC_INCI_H
669 #undef ACC_WANT_ACCLIB_HALLOC
670 #undef ACC_WANT_ACCLIB_MISC
671 #undef ACC_WANT_ACCLIB_WILDARGV
672 #undef ACC_WANT_ACCLIB_HREAD
673
674 #if (__ACCLIB_REQUIRE_HMEMCPY_CH) && !defined(__ACCLIB_HMEMCPY_CH_INCLUDED)
675 # define ACC_WANT_ACCLIB_HMEMCPY 1
676 # include "acc/acclib/hmemcpy.ch"
677 # undef ACC_WANT_ACCLIB_HMEMCPY
678 #endif
679
680
681 /* vim:set ts=4 sw=4 et: */
682