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