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