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