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