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