enable use of vis, copying code in from OpenBSD CVS
[debian/pax] / gen_subs.c
1 /*      $OpenBSD: gen_subs.c,v 1.19 2007/04/04 21:55:10 millert Exp $   */
2 /*      $NetBSD: gen_subs.c,v 1.5 1995/03/21 09:07:26 cgd Exp $ */
3
4 /*-
5  * Copyright (c) 1992 Keith Muller.
6  * Copyright (c) 1992, 1993
7  *      The Regents of the University of California.  All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Keith Muller of the University of California, San Diego.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #ifndef lint
38 #if 0
39 static const char sccsid[] = "@(#)gen_subs.c    8.1 (Berkeley) 5/31/93";
40 #else
41 static const char rcsid[] = "$OpenBSD: gen_subs.c,v 1.19 2007/04/04 21:55:10 millert Exp $";
42 #endif
43 #endif /* not lint */
44
45 #include <sys/types.h>
46 #include <sys/sysmacros.h>
47 #include <sys/time.h>
48 #include <time.h>
49 #include <sys/stat.h>
50 #include <sys/param.h>
51 #include <stdio.h>
52 #include "tzfile.h"
53 #include <utmp.h>
54 #include <unistd.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #ifndef DEBIAN
58 # include <vis.h>
59 #endif  /* DEBIAN */
60 #include "pax.h"
61 #include "extern.h"
62
63 #include "strmode.h"
64
65 /*
66  * a collection of general purpose subroutines used by pax
67  */
68
69 /*
70  * constants used by ls_list() when printing out archive members
71  */
72 #define MODELEN 20
73 #define DATELEN 64
74 #define DAYSPERNYEAR 365
75 #define SECSPERDAY 86400
76 /* #define VIS_CSTYLE 0 */
77 #define SIXMONTHS        ((DAYSPERNYEAR / 2) * SECSPERDAY)
78 #define CURFRMT         "%b %e %H:%M"
79 #define OLDFRMT         "%b %e  %Y"
80 #define NAME_WIDTH      8
81
82 /*
83  * ls_list()
84  *      list the members of an archive in ls format
85  */
86
87 void
88 ls_list(ARCHD *arcn, time_t now, FILE *fp)
89 {
90         struct stat *sbp;
91         char f_mode[MODELEN];
92         char f_date[DATELEN];
93         const char *timefrmt;
94         int term;
95
96         term = zeroflag ? '\0' : '\n';  /* path termination character */
97
98         /*
99          * if not verbose, just print the file name
100          */
101         if (!vflag) {
102                 if (zeroflag)
103                         (void)fputs(arcn->name, fp);
104                 else
105                         safe_print(arcn->name, fp);
106                 (void)putc(term, fp);
107                 (void)fflush(fp);
108                 return;
109         }
110
111         /*
112          * user wants long mode
113          */
114         sbp = &(arcn->sb);
115         strmode(sbp->st_mode, f_mode);
116
117         if (ltmfrmt == NULL) {
118                 /*
119                  * no locale specified format. time format based on age
120                  * compared to the time pax was started.
121                  */
122                 if ((sbp->st_mtime + SIXMONTHS) <= now)
123                         timefrmt = OLDFRMT;
124                 else
125                         timefrmt = CURFRMT;
126         } else
127                 timefrmt = ltmfrmt;
128
129         /*
130          * print file mode, link count, uid, gid and time
131          */
132         if (strftime(f_date,DATELEN,timefrmt,localtime(&(sbp->st_mtime))) == 0)
133                 f_date[0] = '\0';
134         (void)fprintf(fp, "%s%2u %-*.*s %-*.*s ", f_mode, sbp->st_nlink,
135                 NAME_WIDTH, UT_NAMESIZE, name_uid(sbp->st_uid, 1),
136                 NAME_WIDTH, UT_NAMESIZE, name_gid(sbp->st_gid, 1));
137
138         /*
139          * print device id's for devices, or sizes for other nodes
140          */
141         if ((arcn->type == PAX_CHR) || (arcn->type == PAX_BLK))
142 #               ifdef LONG_OFF_T
143                 (void)fprintf(fp, "%4u,%4u ", MAJOR(sbp->st_rdev),
144 #               else
145                 (void)fprintf(fp, "%4lu,%4lu ", (unsigned long)MAJOR(sbp->st_rdev),
146 #               endif
147                     (unsigned long)MINOR(sbp->st_rdev));
148         else {
149 #               ifdef LONG_OFF_T
150                 (void)fprintf(fp, "%9lu ", sbp->st_size);
151 #               else
152                 (void)fprintf(fp, "%9qu ", sbp->st_size);
153 #               endif
154         }
155
156         /*
157          * print name and link info for hard and soft links
158          */
159         (void)fputs(f_date, fp);
160         (void)putc(' ', fp);
161         safe_print(arcn->name, fp);
162         if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) {
163                 fputs(" == ", fp);
164                 safe_print(arcn->ln_name, fp);
165         } else if (arcn->type == PAX_SLK) {
166                 fputs(" -> ", fp);
167                 safe_print(arcn->ln_name, fp);
168         }
169         (void)putc(term, fp);
170         (void)fflush(fp);
171         return;
172 }
173
174 /*
175  * tty_ls()
176  *      print a short summary of file to tty.
177  */
178
179 void
180 ls_tty(ARCHD *arcn)
181 {
182         char f_date[DATELEN];
183         char f_mode[MODELEN];
184         const char *timefrmt;
185
186         if (ltmfrmt == NULL) {
187                 /*
188                  * no locale specified format
189                  */
190                 if ((arcn->sb.st_mtime + SIXMONTHS) <= time(NULL))
191                         timefrmt = OLDFRMT;
192                 else
193                         timefrmt = CURFRMT;
194         } else
195                 timefrmt = ltmfrmt;
196
197         /*
198          * convert time to string, and print
199          */
200         if (strftime(f_date, DATELEN, timefrmt,
201             localtime(&(arcn->sb.st_mtime))) == 0)
202                 f_date[0] = '\0';
203         strmode(arcn->sb.st_mode, f_mode);
204         tty_prnt("%s%s %s\n", f_mode, f_date, arcn->name);
205         return;
206 }
207
208 void
209 safe_print(const char *str, FILE *fp)
210 {
211         char visbuf[5];
212         const char *cp;
213
214         /*
215          * if printing to a tty, use vis(3) to print special characters.
216          */
217         if (0 && isatty(fileno(fp))) {
218                 for (cp = str; *cp; cp++) {
219                         (void)vis(visbuf, cp[0], VIS_CSTYLE, cp[1]);
220                         (void)fputs(visbuf, fp);
221                 }
222         } else {
223                 (void)fputs(str, fp);
224         }
225 }
226
227 /*
228  * asc_ul()
229  *      convert hex/octal character string into a u_long. We do not have to
230  *      check for overflow! (the headers in all supported formats are not large
231  *      enough to create an overflow).
232  *      NOTE: strings passed to us are NOT TERMINATED.
233  * Return:
234  *      unsigned long value
235  */
236
237 u_long
238 asc_ul(char *str, int len, int base)
239 {
240         char *stop;
241         u_long tval = 0;
242
243         stop = str + len;
244
245         /*
246          * skip over leading blanks and zeros
247          */
248         while ((str < stop) && ((*str == ' ') || (*str == '0')))
249                 ++str;
250
251         /*
252          * for each valid digit, shift running value (tval) over to next digit
253          * and add next digit
254          */
255         if (base == HEX) {
256                 while (str < stop) {
257                         if ((*str >= '0') && (*str <= '9'))
258                                 tval = (tval << 4) + (*str++ - '0');
259                         else if ((*str >= 'A') && (*str <= 'F'))
260                                 tval = (tval << 4) + 10 + (*str++ - 'A');
261                         else if ((*str >= 'a') && (*str <= 'f'))
262                                 tval = (tval << 4) + 10 + (*str++ - 'a');
263                         else
264                                 break;
265                 }
266         } else {
267                 while ((str < stop) && (*str >= '0') && (*str <= '7'))
268                         tval = (tval << 3) + (*str++ - '0');
269         }
270         return(tval);
271 }
272
273 /*
274  * ul_asc()
275  *      convert an unsigned long into an hex/oct ascii string. pads with LEADING
276  *      ascii 0's to fill string completely
277  *      NOTE: the string created is NOT TERMINATED.
278  */
279
280 int
281 ul_asc(u_long val, char *str, int len, int base)
282 {
283         char *pt;
284         u_long digit;
285
286         /*
287          * WARNING str is not '\0' terminated by this routine
288          */
289         pt = str + len - 1;
290
291         /*
292          * do a tailwise conversion (start at right most end of string to place
293          * least significant digit). Keep shifting until conversion value goes
294          * to zero (all digits were converted)
295          */
296         if (base == HEX) {
297                 while (pt >= str) {
298                         if ((digit = (val & 0xf)) < 10)
299                                 *pt-- = '0' + (char)digit;
300                         else
301                                 *pt-- = 'a' + (char)(digit - 10);
302                         if ((val = (val >> 4)) == (u_long)0)
303                                 break;
304                 }
305         } else {
306                 while (pt >= str) {
307                         *pt-- = '0' + (char)(val & 0x7);
308                         if ((val = (val >> 3)) == (u_long)0)
309                                 break;
310                 }
311         }
312
313         /*
314          * pad with leading ascii ZEROS. We return -1 if we ran out of space.
315          */
316         while (pt >= str)
317                 *pt-- = '0';
318         if (val != (u_long)0)
319                 return(-1);
320         return(0);
321 }
322
323 #ifndef LONG_OFF_T
324 /*
325  * asc_uqd()
326  *      convert hex/octal character string into a u_quad_t. We do not have to
327  *      check for overflow! (the headers in all supported formats are not large
328  *      enough to create an overflow).
329  *      NOTE: strings passed to us are NOT TERMINATED.
330  * Return:
331  *      u_quad_t value
332  */
333
334 u_quad_t
335 asc_uqd(char *str, int len, int base)
336 {
337         char *stop;
338         u_quad_t tval = 0;
339
340         stop = str + len;
341
342         /*
343          * skip over leading blanks and zeros
344          */
345         while ((str < stop) && ((*str == ' ') || (*str == '0')))
346                 ++str;
347
348         /*
349          * for each valid digit, shift running value (tval) over to next digit
350          * and add next digit
351          */
352         if (base == HEX) {
353                 while (str < stop) {
354                         if ((*str >= '0') && (*str <= '9'))
355                                 tval = (tval << 4) + (*str++ - '0');
356                         else if ((*str >= 'A') && (*str <= 'F'))
357                                 tval = (tval << 4) + 10 + (*str++ - 'A');
358                         else if ((*str >= 'a') && (*str <= 'f'))
359                                 tval = (tval << 4) + 10 + (*str++ - 'a');
360                         else
361                                 break;
362                 }
363         } else {
364                 while ((str < stop) && (*str >= '0') && (*str <= '7'))
365                         tval = (tval << 3) + (*str++ - '0');
366         }
367         return(tval);
368 }
369
370 /*
371  * uqd_asc()
372  *      convert an u_quad_t into a hex/oct ascii string. pads with LEADING
373  *      ascii 0's to fill string completely
374  *      NOTE: the string created is NOT TERMINATED.
375  */
376
377 int
378 uqd_asc(u_quad_t val, char *str, int len, int base)
379 {
380         char *pt;
381         u_quad_t digit;
382
383         /*
384          * WARNING str is not '\0' terminated by this routine
385          */
386         pt = str + len - 1;
387
388         /*
389          * do a tailwise conversion (start at right most end of string to place
390          * least significant digit). Keep shifting until conversion value goes
391          * to zero (all digits were converted)
392          */
393         if (base == HEX) {
394                 while (pt >= str) {
395                         if ((digit = (val & 0xf)) < 10)
396                                 *pt-- = '0' + (char)digit;
397                         else
398                                 *pt-- = 'a' + (char)(digit - 10);
399                         if ((val = (val >> 4)) == (u_quad_t)0)
400                                 break;
401                 }
402         } else {
403                 while (pt >= str) {
404                         *pt-- = '0' + (char)(val & 0x7);
405                         if ((val = (val >> 3)) == (u_quad_t)0)
406                                 break;
407                 }
408         }
409
410         /*
411          * pad with leading ascii ZEROS. We return -1 if we ran out of space.
412          */
413         while (pt >= str)
414                 *pt-- = '0';
415         if (val != (u_quad_t)0)
416                 return(-1);
417         return(0);
418 }
419 #endif
420
421 /*
422  * Copy at max min(bufz, fieldsz) chars from field to buf, stopping
423  * at the first NUL char. NUL terminate buf if there is room left.
424  */
425 size_t
426 fieldcpy(char *buf, size_t bufsz, const char *field, size_t fieldsz)
427 {
428         char *p = buf;
429         const char *q = field;
430         size_t i = 0;
431
432         if (fieldsz > bufsz)
433                 fieldsz = bufsz;
434         while (i < fieldsz && *q != '\0') {
435                 *p++ = *q++;
436                 i++;
437         }
438         if (i < bufsz)
439                 *p = '\0';
440         return(i);
441 }