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