07c9e96b26db9c73c573433d11c70957a8dc979d
[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 #include <vis.h>
58 #include "pax.h"
59 #include "extern.h"
60
61 #include "strmode.h"
62
63 /*
64  * a collection of general purpose subroutines used by pax
65  */
66
67 /*
68  * constants used by ls_list() when printing out archive members
69  */
70 #define MODELEN 20
71 #define DATELEN 64
72 #define SIXMONTHS        ((DAYSPERNYEAR / 2) * SECSPERDAY)
73 #define CURFRMT         "%b %e %H:%M"
74 #define OLDFRMT         "%b %e  %Y"
75 #define NAME_WIDTH      8
76
77 /*
78  * ls_list()
79  *      list the members of an archive in ls format
80  */
81
82 void
83 ls_list(ARCHD *arcn, time_t now, FILE *fp)
84 {
85         struct stat *sbp;
86         char f_mode[MODELEN];
87         char f_date[DATELEN];
88         const char *timefrmt;
89         int term;
90
91         term = zeroflag ? '\0' : '\n';  /* path termination character */
92
93         /*
94          * if not verbose, just print the file name
95          */
96         if (!vflag) {
97                 if (zeroflag)
98                         (void)fputs(arcn->name, fp);
99                 else
100                         safe_print(arcn->name, fp);
101                 (void)putc(term, fp);
102                 (void)fflush(fp);
103                 return;
104         }
105
106         /*
107          * user wants long mode
108          */
109         sbp = &(arcn->sb);
110         strmode(sbp->st_mode, f_mode);
111
112         if (ltmfrmt == NULL) {
113                 /*
114                  * no locale specified format. time format based on age
115                  * compared to the time pax was started.
116                  */
117                 if ((sbp->st_mtime + SIXMONTHS) <= now)
118                         timefrmt = OLDFRMT;
119                 else
120                         timefrmt = CURFRMT;
121         } else
122                 timefrmt = ltmfrmt;
123
124         /*
125          * print file mode, link count, uid, gid and time
126          */
127         if (strftime(f_date,DATELEN,timefrmt,localtime(&(sbp->st_mtime))) == 0)
128                 f_date[0] = '\0';
129         (void)fprintf(fp, "%s%2u %-*.*s %-*.*s ", f_mode, sbp->st_nlink,
130                 NAME_WIDTH, UT_NAMESIZE, name_uid(sbp->st_uid, 1),
131                 NAME_WIDTH, UT_NAMESIZE, name_gid(sbp->st_gid, 1));
132
133         /*
134          * print device id's for devices, or sizes for other nodes
135          */
136         if ((arcn->type == PAX_CHR) || (arcn->type == PAX_BLK))
137 #               ifdef LONG_OFF_T
138                 (void)fprintf(fp, "%4u,%4u ", MAJOR(sbp->st_rdev),
139 #               else
140                 (void)fprintf(fp, "%4lu,%4lu ", (unsigned long)MAJOR(sbp->st_rdev),
141 #               endif
142                     (unsigned long)MINOR(sbp->st_rdev));
143         else {
144 #               ifdef LONG_OFF_T
145                 (void)fprintf(fp, "%9lu ", sbp->st_size);
146 #               else
147                 (void)fprintf(fp, "%9qu ", sbp->st_size);
148 #               endif
149         }
150
151         /*
152          * print name and link info for hard and soft links
153          */
154         (void)fputs(f_date, fp);
155         (void)putc(' ', fp);
156         safe_print(arcn->name, fp);
157         if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) {
158                 fputs(" == ", fp);
159                 safe_print(arcn->ln_name, fp);
160         } else if (arcn->type == PAX_SLK) {
161                 fputs(" -> ", fp);
162                 safe_print(arcn->ln_name, fp);
163         }
164         (void)putc(term, fp);
165         (void)fflush(fp);
166         return;
167 }
168
169 /*
170  * tty_ls()
171  *      print a short summary of file to tty.
172  */
173
174 void
175 ls_tty(ARCHD *arcn)
176 {
177         char f_date[DATELEN];
178         char f_mode[MODELEN];
179         const char *timefrmt;
180
181         if (ltmfrmt == NULL) {
182                 /*
183                  * no locale specified format
184                  */
185                 if ((arcn->sb.st_mtime + SIXMONTHS) <= time(NULL))
186                         timefrmt = OLDFRMT;
187                 else
188                         timefrmt = CURFRMT;
189         } else
190                 timefrmt = ltmfrmt;
191
192         /*
193          * convert time to string, and print
194          */
195         if (strftime(f_date, DATELEN, timefrmt,
196             localtime(&(arcn->sb.st_mtime))) == 0)
197                 f_date[0] = '\0';
198         strmode(arcn->sb.st_mode, f_mode);
199         tty_prnt("%s%s %s\n", f_mode, f_date, arcn->name);
200         return;
201 }
202
203 void
204 safe_print(const char *str, FILE *fp)
205 {
206         char visbuf[5];
207         const char *cp;
208
209         /*
210          * if printing to a tty, use vis(3) to print special characters.
211          */
212         if (isatty(fileno(fp))) {
213                 for (cp = str; *cp; cp++) {
214                         (void)vis(visbuf, cp[0], VIS_CSTYLE, cp[1]);
215                         (void)fputs(visbuf, fp);
216                 }
217         } else {
218                 (void)fputs(str, fp);
219         }
220 }
221
222 /*
223  * asc_ul()
224  *      convert hex/octal character string into a u_long. We do not have to
225  *      check for overflow! (the headers in all supported formats are not large
226  *      enough to create an overflow).
227  *      NOTE: strings passed to us are NOT TERMINATED.
228  * Return:
229  *      unsigned long value
230  */
231
232 u_long
233 asc_ul(char *str, int len, int base)
234 {
235         char *stop;
236         u_long tval = 0;
237
238         stop = str + len;
239
240         /*
241          * skip over leading blanks and zeros
242          */
243         while ((str < stop) && ((*str == ' ') || (*str == '0')))
244                 ++str;
245
246         /*
247          * for each valid digit, shift running value (tval) over to next digit
248          * and add next digit
249          */
250         if (base == HEX) {
251                 while (str < stop) {
252                         if ((*str >= '0') && (*str <= '9'))
253                                 tval = (tval << 4) + (*str++ - '0');
254                         else if ((*str >= 'A') && (*str <= 'F'))
255                                 tval = (tval << 4) + 10 + (*str++ - 'A');
256                         else if ((*str >= 'a') && (*str <= 'f'))
257                                 tval = (tval << 4) + 10 + (*str++ - 'a');
258                         else
259                                 break;
260                 }
261         } else {
262                 while ((str < stop) && (*str >= '0') && (*str <= '7'))
263                         tval = (tval << 3) + (*str++ - '0');
264         }
265         return(tval);
266 }
267
268 /*
269  * ul_asc()
270  *      convert an unsigned long into an hex/oct ascii string. pads with LEADING
271  *      ascii 0's to fill string completely
272  *      NOTE: the string created is NOT TERMINATED.
273  */
274
275 int
276 ul_asc(u_long val, char *str, int len, int base)
277 {
278         char *pt;
279         u_long digit;
280
281         /*
282          * WARNING str is not '\0' terminated by this routine
283          */
284         pt = str + len - 1;
285
286         /*
287          * do a tailwise conversion (start at right most end of string to place
288          * least significant digit). Keep shifting until conversion value goes
289          * to zero (all digits were converted)
290          */
291         if (base == HEX) {
292                 while (pt >= str) {
293                         if ((digit = (val & 0xf)) < 10)
294                                 *pt-- = '0' + (char)digit;
295                         else
296                                 *pt-- = 'a' + (char)(digit - 10);
297                         if ((val = (val >> 4)) == (u_long)0)
298                                 break;
299                 }
300         } else {
301                 while (pt >= str) {
302                         *pt-- = '0' + (char)(val & 0x7);
303                         if ((val = (val >> 3)) == (u_long)0)
304                                 break;
305                 }
306         }
307
308         /*
309          * pad with leading ascii ZEROS. We return -1 if we ran out of space.
310          */
311         while (pt >= str)
312                 *pt-- = '0';
313         if (val != (u_long)0)
314                 return(-1);
315         return(0);
316 }
317
318 #ifndef LONG_OFF_T
319 /*
320  * asc_uqd()
321  *      convert hex/octal character string into a u_quad_t. We do not have to
322  *      check for overflow! (the headers in all supported formats are not large
323  *      enough to create an overflow).
324  *      NOTE: strings passed to us are NOT TERMINATED.
325  * Return:
326  *      u_quad_t value
327  */
328
329 u_quad_t
330 asc_uqd(char *str, int len, int base)
331 {
332         char *stop;
333         u_quad_t tval = 0;
334
335         stop = str + len;
336
337         /*
338          * skip over leading blanks and zeros
339          */
340         while ((str < stop) && ((*str == ' ') || (*str == '0')))
341                 ++str;
342
343         /*
344          * for each valid digit, shift running value (tval) over to next digit
345          * and add next digit
346          */
347         if (base == HEX) {
348                 while (str < stop) {
349                         if ((*str >= '0') && (*str <= '9'))
350                                 tval = (tval << 4) + (*str++ - '0');
351                         else if ((*str >= 'A') && (*str <= 'F'))
352                                 tval = (tval << 4) + 10 + (*str++ - 'A');
353                         else if ((*str >= 'a') && (*str <= 'f'))
354                                 tval = (tval << 4) + 10 + (*str++ - 'a');
355                         else
356                                 break;
357                 }
358         } else {
359                 while ((str < stop) && (*str >= '0') && (*str <= '7'))
360                         tval = (tval << 3) + (*str++ - '0');
361         }
362         return(tval);
363 }
364
365 /*
366  * uqd_asc()
367  *      convert an u_quad_t into a hex/oct ascii string. pads with LEADING
368  *      ascii 0's to fill string completely
369  *      NOTE: the string created is NOT TERMINATED.
370  */
371
372 int
373 uqd_asc(u_quad_t val, char *str, int len, int base)
374 {
375         char *pt;
376         u_quad_t digit;
377
378         /*
379          * WARNING str is not '\0' terminated by this routine
380          */
381         pt = str + len - 1;
382
383         /*
384          * do a tailwise conversion (start at right most end of string to place
385          * least significant digit). Keep shifting until conversion value goes
386          * to zero (all digits were converted)
387          */
388         if (base == HEX) {
389                 while (pt >= str) {
390                         if ((digit = (val & 0xf)) < 10)
391                                 *pt-- = '0' + (char)digit;
392                         else
393                                 *pt-- = 'a' + (char)(digit - 10);
394                         if ((val = (val >> 4)) == (u_quad_t)0)
395                                 break;
396                 }
397         } else {
398                 while (pt >= str) {
399                         *pt-- = '0' + (char)(val & 0x7);
400                         if ((val = (val >> 3)) == (u_quad_t)0)
401                                 break;
402                 }
403         }
404
405         /*
406          * pad with leading ascii ZEROS. We return -1 if we ran out of space.
407          */
408         while (pt >= str)
409                 *pt-- = '0';
410         if (val != (u_quad_t)0)
411                 return(-1);
412         return(0);
413 }
414 #endif
415
416 /*
417  * Copy at max min(bufz, fieldsz) chars from field to buf, stopping
418  * at the first NUL char. NUL terminate buf if there is room left.
419  */
420 size_t
421 fieldcpy(char *buf, size_t bufsz, const char *field, size_t fieldsz)
422 {
423         char *p = buf;
424         const char *q = field;
425         size_t i = 0;
426
427         if (fieldsz > bufsz)
428                 fieldsz = bufsz;
429         while (i < fieldsz && *q != '\0') {
430                 *p++ = *q++;
431                 i++;
432         }
433         if (i < bufsz)
434                 *p = '\0';
435         return(i);
436 }