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