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