1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * logadm/fn.c -- "filename" string module 26 * 27 * this file contains routines for the manipulation of filenames. 28 * they aren't particularly fast (at least they weren't designed 29 * for performance), but they are simple and put all the malloc/free 30 * stuff for these strings in a central place. most routines in 31 * logadm that return filenames return a struct fn, and most routines 32 * that return lists of strings return a struct fn_list. 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 #include <stdio.h> 38 #include <libintl.h> 39 #include <strings.h> 40 #include <sys/types.h> 41 #include <sys/stat.h> 42 #include "err.h" 43 #include "fn.h" 44 45 #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) 46 47 /* 48 * constants controlling how we malloc space. bigger means fewer 49 * calls to malloc. smaller means less wasted space. 50 */ 51 #define FN_MIN 1024 /* initial size of string buffers */ 52 #define FN_MAX 10240 /* maximum size allowed before fatal "overflow" error */ 53 #define FN_INC 1024 /* increments in buffer size as strings grow */ 54 55 /* info created by fn_new(), private to this module */ 56 struct fn { 57 char *fn_buf; /* first location in buf */ 58 char *fn_buflast; /* last location in buf */ 59 char *fn_rptr; /* read pointer (next unread character) */ 60 char *fn_wptr; /* write pointer (points at null terminator) */ 61 struct fn *fn_next; /* next in list */ 62 struct stat fn_stbuf; 63 int fn_n; 64 }; 65 66 /* info created by fn_list_new(), private to this module */ 67 struct fn_list { 68 struct fn *fnl_first; /* first element of list */ 69 struct fn *fnl_last; /* last element of list */ 70 struct fn *fnl_rptr; /* read pointer for iterating through list */ 71 }; 72 73 /* 74 * fn_new -- create a new filename buffer, possibly with initial contents 75 * 76 * use like this: 77 * struct fn *fnp = fn_new("this is a string"); 78 */ 79 struct fn * 80 fn_new(const char *s) 81 { 82 struct fn *fnp = MALLOC(sizeof (struct fn)); 83 84 fnp->fn_n = -1; 85 bzero(&fnp->fn_stbuf, sizeof (fnp->fn_stbuf)); 86 fnp->fn_next = NULL; 87 88 /* if passed-in string contains at least 1 non-null character... */ 89 if (s && *s) { 90 int len = strlen(s); 91 int buflen = roundup(len + 1, FN_INC); 92 93 /* start with buffer filled with passed-in string */ 94 fnp->fn_buf = MALLOC(buflen); 95 fnp->fn_buflast = &fnp->fn_buf[buflen - 1]; 96 (void) strlcpy(fnp->fn_buf, s, buflen); 97 fnp->fn_rptr = fnp->fn_buf; 98 fnp->fn_wptr = &fnp->fn_buf[len]; 99 } else { 100 /* start with empty buffer */ 101 fnp->fn_buf = MALLOC(FN_MIN); 102 fnp->fn_buflast = &fnp->fn_buf[FN_MIN - 1]; 103 *fnp->fn_buf = '\0'; 104 fnp->fn_rptr = fnp->fn_buf; 105 fnp->fn_wptr = fnp->fn_buf; 106 } 107 108 return (fnp); 109 } 110 111 /* 112 * fn_dup -- duplicate a filename buffer 113 */ 114 struct fn * 115 fn_dup(struct fn *fnp) 116 { 117 struct fn *ret = fn_new(fn_s(fnp)); 118 119 ret->fn_n = fnp->fn_n; 120 ret->fn_stbuf = fnp->fn_stbuf; 121 122 return (ret); 123 } 124 125 /* 126 * fn_dirname -- return the dirname part of a filename 127 */ 128 struct fn * 129 fn_dirname(struct fn *fnp) 130 { 131 char *ptr; 132 struct fn *ret; 133 134 if ((ptr = strrchr(fn_s(fnp), '/')) == NULL) 135 return (fn_new(".")); 136 else { 137 *ptr = '\0'; 138 ret = fn_new(fn_s(fnp)); 139 *ptr = '/'; 140 return (ret); 141 } 142 } 143 144 /* 145 * fn_setn -- set the "n" value for a filename 146 * 147 * the "n" value is initially -1, and is used by logadm to store 148 * the suffix for rotated log files. the function fn_list_popoldest() 149 * looks at these "n" values when sorting filenames to determine which 150 * old log file is the oldest and should be expired first. 151 */ 152 void 153 fn_setn(struct fn *fnp, int n) 154 { 155 fnp->fn_n = n; 156 } 157 158 /* 159 * fn_setstat -- store a struct stat with a filename 160 * 161 * the glob functions typically fill in these struct stats since they 162 * have to stat while globbing anyway. just turned out to be a common 163 * piece of information that was conveniently stored with the associated 164 * filename. 165 */ 166 void 167 fn_setstat(struct fn *fnp, struct stat *stp) 168 { 169 fnp->fn_stbuf = *stp; 170 } 171 172 /* 173 * fn_getstat -- return a pointer to the stat info stored by fn_setstat() 174 */ 175 struct stat * 176 fn_getstat(struct fn *fnp) 177 { 178 return (&fnp->fn_stbuf); 179 } 180 181 /* 182 * fn_free -- free a filename buffer 183 */ 184 void 185 fn_free(struct fn *fnp) 186 { 187 if (fnp) { 188 if (fnp->fn_buf) 189 FREE(fnp->fn_buf); 190 FREE(fnp); 191 } 192 } 193 194 /* 195 * fn_renew -- reset a filename buffer 196 * 197 * calling fn_renew(fnp, s) is the same as calling: 198 * fn_free(fnp); 199 * fn_new(s); 200 */ 201 void 202 fn_renew(struct fn *fnp, const char *s) 203 { 204 fnp->fn_rptr = fnp->fn_wptr = fnp->fn_buf; 205 fn_puts(fnp, s); 206 } 207 208 /* 209 * fn_putc -- append a character to a filename 210 * 211 * this is the function that handles growing the filename buffer 212 * automatically and calling err() if it overflows. 213 */ 214 void 215 fn_putc(struct fn *fnp, int c) 216 { 217 if (fnp->fn_wptr >= fnp->fn_buflast) { 218 int buflen = fnp->fn_buflast + 1 - fnp->fn_buf; 219 char *newbuf; 220 char *src; 221 char *dst; 222 223 /* overflow, allocate more space or die if at FN_MAX */ 224 if (buflen >= FN_MAX) 225 err(0, "fn buffer overflow"); 226 buflen += FN_INC; 227 newbuf = MALLOC(buflen); 228 229 /* copy string into new buffer */ 230 src = fnp->fn_buf; 231 dst = newbuf; 232 233 /* just copy up to wptr, rest is history anyway */ 234 while (src < fnp->fn_wptr) 235 *dst++ = *src++; 236 fnp->fn_rptr = &newbuf[fnp->fn_rptr - fnp->fn_buf]; 237 FREE(fnp->fn_buf); 238 fnp->fn_buf = newbuf; 239 fnp->fn_buflast = &fnp->fn_buf[buflen - 1]; 240 fnp->fn_wptr = dst; 241 } 242 *fnp->fn_wptr++ = c; 243 *fnp->fn_wptr = '\0'; 244 } 245 246 /* 247 * fn_puts -- append a string to a filename 248 */ 249 void 250 fn_puts(struct fn *fnp, const char *s) 251 { 252 /* non-optimal, but simple! */ 253 while (s && *s) 254 fn_putc(fnp, *s++); 255 } 256 257 /* 258 * fn_putfn -- append a filename buffer to a filename 259 */ 260 void 261 fn_putfn(struct fn *fnp, struct fn *srcfnp) 262 { 263 int c; 264 265 fn_rewind(srcfnp); 266 while (c = fn_getc(srcfnp)) 267 fn_putc(fnp, c); 268 } 269 270 /* 271 * fn_rewind -- reset the "read pointer" to the beginning of a filename 272 */ 273 void 274 fn_rewind(struct fn *fnp) 275 { 276 fnp->fn_rptr = fnp->fn_buf; 277 } 278 279 /* 280 * fn_getc -- "read" the next character of a filename 281 */ 282 int 283 fn_getc(struct fn *fnp) 284 { 285 if (fnp->fn_rptr > fnp->fn_buflast || *fnp->fn_rptr == '\0') 286 return (0); 287 288 return (*fnp->fn_rptr++); 289 } 290 291 /* 292 * fn_peekc -- "peek" at the next character of a filename 293 */ 294 int 295 fn_peekc(struct fn *fnp) 296 { 297 if (fnp->fn_rptr > fnp->fn_buflast || *fnp->fn_rptr == '\0') 298 return (0); 299 300 return (*fnp->fn_rptr); 301 } 302 303 /* 304 * fn_s -- return a pointer to a null-terminated string containing the filename 305 */ 306 char * 307 fn_s(struct fn *fnp) 308 { 309 return (fnp->fn_buf); 310 } 311 312 /* 313 * fn_list_new -- create a new list of filenames 314 * 315 * by convention, an empty list is represented by an allocated 316 * struct fn_list which contains a NULL linked list, rather than 317 * by a NULL fn_list pointer. in other words: 318 * 319 * struct fn_list *fnlp = some_func_returning_a_list(); 320 * if (fn_list_empty(fnlp)) 321 * ... 322 * 323 * is preferable to checking if the fnlp returned is NULL. 324 */ 325 struct fn_list * 326 fn_list_new(const char * const *slist) 327 { 328 struct fn_list *fnlp = MALLOC(sizeof (struct fn_list)); 329 330 fnlp->fnl_first = fnlp->fnl_last = fnlp->fnl_rptr = NULL; 331 332 while (slist && *slist) 333 fn_list_adds(fnlp, *slist++); 334 335 return (fnlp); 336 } 337 338 /* 339 * fn_list_dup -- duplicate a list of filenames 340 */ 341 struct fn_list * 342 fn_list_dup(struct fn_list *fnlp) 343 { 344 struct fn_list *ret = fn_list_new(NULL); 345 struct fn *fnp; 346 347 fn_list_rewind(fnlp); 348 while ((fnp = fn_list_next(fnlp)) != NULL) 349 fn_list_addfn(ret, fn_dup(fnp)); 350 351 return (ret); 352 } 353 354 /* 355 * fn_list_free -- free a list of filenames 356 */ 357 void 358 fn_list_free(struct fn_list *fnlp) 359 { 360 struct fn *fnp; 361 362 fn_list_rewind(fnlp); 363 while ((fnp = fn_list_next(fnlp)) != NULL) 364 fn_free(fnp); 365 FREE(fnlp); 366 } 367 368 /* 369 * fn_list_adds -- add a string to a list of filenames 370 */ 371 void 372 fn_list_adds(struct fn_list *fnlp, const char *s) 373 { 374 fn_list_addfn(fnlp, fn_new(s)); 375 } 376 377 /* 378 * fn_list_addfn -- add a filename (i.e. struct fn *) to a list of filenames 379 */ 380 void 381 fn_list_addfn(struct fn_list *fnlp, struct fn *fnp) 382 { 383 fnp->fn_next = NULL; 384 if (fnlp->fnl_first == NULL) 385 fnlp->fnl_first = fnlp->fnl_last = fnlp->fnl_rptr = fnp; 386 else { 387 fnlp->fnl_last->fn_next = fnp; 388 fnlp->fnl_last = fnp; 389 } 390 } 391 392 /* 393 * fn_list_rewind -- reset the "read pointer" to the beginning of the list 394 */ 395 void 396 fn_list_rewind(struct fn_list *fnlp) 397 { 398 fnlp->fnl_rptr = fnlp->fnl_first; 399 } 400 401 /* 402 * fn_list_next -- return the filename at the read pointer and advance it 403 */ 404 struct fn * 405 fn_list_next(struct fn_list *fnlp) 406 { 407 struct fn *ret = fnlp->fnl_rptr; 408 409 if (fnlp->fnl_rptr == fnlp->fnl_last) 410 fnlp->fnl_rptr = NULL; 411 else if (fnlp->fnl_rptr != NULL) 412 fnlp->fnl_rptr = fnlp->fnl_rptr->fn_next; 413 414 return (ret); 415 } 416 417 /* 418 * fn_list_addfn_list -- move filenames from fnlp2 to end of fnlp 419 * 420 * frees fnlp2 after moving all the filenames off of it. 421 */ 422 void 423 fn_list_addfn_list(struct fn_list *fnlp, struct fn_list *fnlp2) 424 { 425 struct fn *fnp2 = fnlp2->fnl_first; 426 struct fn *nextfnp2; 427 428 /* for each fn in the second list... */ 429 while (fnp2) { 430 if (fnp2 == fnlp2->fnl_last) 431 nextfnp2 = NULL; 432 else 433 nextfnp2 = fnp2->fn_next; 434 435 /* append it to the first list */ 436 fn_list_addfn(fnlp, fnp2); 437 438 fnp2 = nextfnp2; 439 } 440 /* all the fn's were moved off the second list */ 441 fnlp2->fnl_first = fnlp2->fnl_last = fnlp2->fnl_rptr = NULL; 442 443 /* done with the second list */ 444 fn_list_free(fnlp2); 445 } 446 447 /* 448 * fn_list_appendrange -- append a range of characters to each filename in list 449 * 450 * range of characters appended is the character at *s up to but not including 451 * the character at *limit. NULL termination is not required. 452 */ 453 void 454 fn_list_appendrange(struct fn_list *fnlp, const char *s, const char *limit) 455 { 456 struct fn *fnp = fnlp->fnl_first; 457 struct fn *nextfnp; 458 const char *ptr; 459 460 /* for each fn in the list... */ 461 while (fnp) { 462 if (fnp == fnlp->fnl_last) 463 nextfnp = NULL; 464 else 465 nextfnp = fnp->fn_next; 466 467 /* append the range */ 468 for (ptr = s; ptr < limit; ptr++) 469 fn_putc(fnp, *ptr); 470 471 fnp = nextfnp; 472 } 473 } 474 475 /* 476 * fn_list_totalsize -- sum up all the st_size fields in the stat structs 477 */ 478 off_t 479 fn_list_totalsize(struct fn_list *fnlp) 480 { 481 struct fn *fnp; 482 off_t ret = 0; 483 484 fn_list_rewind(fnlp); 485 while ((fnp = fn_list_next(fnlp)) != NULL) 486 ret += fnp->fn_stbuf.st_size; 487 488 return (ret); 489 } 490 491 /* 492 * fn_list_popoldest -- remove oldest file from list and return it 493 * 494 * this function uses the "n" values (set by fn_setn()) to determine 495 * which file is oldest, or when there's a tie it turns to the modification 496 * times in the stat structs, or when there's still a tie lexical sorting. 497 */ 498 struct fn * 499 fn_list_popoldest(struct fn_list *fnlp) 500 { 501 struct fn *fnp; 502 struct fn *ret = NULL; 503 504 fn_list_rewind(fnlp); 505 while ((fnp = fn_list_next(fnlp)) != NULL) 506 if (ret == NULL) 507 ret = fnp; 508 else if (fnp->fn_n > ret->fn_n || 509 (fnp->fn_n == ret->fn_n && 510 (fnp->fn_stbuf.st_mtime < ret->fn_stbuf.st_mtime || 511 ((fnp->fn_stbuf.st_mtime == ret->fn_stbuf.st_mtime && 512 strcmp(fnp->fn_buf, ret->fn_buf) > 0))))) 513 ret = fnp; 514 515 if (ret == NULL) 516 return (NULL); 517 518 /* oldest file is ret, remove it from list */ 519 if (fnlp->fnl_first == ret) 520 fnlp->fnl_first = ret->fn_next; 521 else { 522 fn_list_rewind(fnlp); 523 while ((fnp = fn_list_next(fnlp)) != NULL) 524 if (fnp->fn_next == ret) { 525 fnp->fn_next = ret->fn_next; 526 break; 527 } 528 } 529 530 ret->fn_next = NULL; 531 return (ret); 532 } 533 534 /* 535 * fn_list_empty -- true if the list is empty 536 */ 537 boolean_t 538 fn_list_empty(struct fn_list *fnlp) 539 { 540 return (fnlp->fnl_first == NULL); 541 } 542 543 /* 544 * fn_list_count -- return number of filenames in list 545 */ 546 int 547 fn_list_count(struct fn_list *fnlp) 548 { 549 int ret = 0; 550 551 /* 552 * if this operation were more common, we'd cache the count 553 * in the struct fn_list, but it isn't very common so we just 554 * count 'em up here 555 */ 556 fn_list_rewind(fnlp); 557 while (fn_list_next(fnlp) != NULL) 558 ret++; 559 560 return (ret); 561 } 562