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