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