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.
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.
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]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * logadm/fn.c -- "filename" string module
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.
35 #pragma ident "%Z%%M% %I% %E% SMI"
40 #include <sys/types.h>
45 #define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
48 * constants controlling how we malloc space. bigger means fewer
49 * calls to malloc. smaller means less wasted space.
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 */
55 /* info created by fn_new(), private to this module */
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 */
66 /* info created by fn_list_new(), private to this module */
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 */
74 * fn_new -- create a new filename buffer, possibly with initial contents
77 * struct fn *fnp = fn_new("this is a string");
82 struct fn
*fnp
= MALLOC(sizeof (struct fn
));
85 bzero(&fnp
->fn_stbuf
, sizeof (fnp
->fn_stbuf
));
88 /* if passed-in string contains at least 1 non-null character... */
89 if (s
!= NULL
&& *s
) {
91 int buflen
= roundup(len
+ 1, FN_INC
);
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
];
100 /* start with empty buffer */
101 fnp
->fn_buf
= MALLOC(FN_MIN
);
102 fnp
->fn_buflast
= &fnp
->fn_buf
[FN_MIN
- 1];
104 fnp
->fn_rptr
= fnp
->fn_buf
;
105 fnp
->fn_wptr
= fnp
->fn_buf
;
112 * fn_dup -- duplicate a filename buffer
115 fn_dup(struct fn
*fnp
)
117 struct fn
*ret
= fn_new(fn_s(fnp
));
119 ret
->fn_n
= fnp
->fn_n
;
120 ret
->fn_stbuf
= fnp
->fn_stbuf
;
126 * fn_dirname -- return the dirname part of a filename
129 fn_dirname(struct fn
*fnp
)
138 ptr
= strrchr(buf
, '/');
139 if (ptr
== NULL
|| buf
== NULL
)
140 return (fn_new("."));
150 * fn_setn -- set the "n" value for a filename
152 * the "n" value is initially -1, and is used by logadm to store
153 * the suffix for rotated log files. the function fn_list_popoldest()
154 * looks at these "n" values when sorting filenames to determine which
155 * old log file is the oldest and should be expired first.
158 fn_setn(struct fn
*fnp
, int n
)
164 * fn_setstat -- store a struct stat with a filename
166 * the glob functions typically fill in these struct stats since they
167 * have to stat while globbing anyway. just turned out to be a common
168 * piece of information that was conveniently stored with the associated
172 fn_setstat(struct fn
*fnp
, struct stat
*stp
)
174 fnp
->fn_stbuf
= *stp
;
178 * fn_getstat -- return a pointer to the stat info stored by fn_setstat()
181 fn_getstat(struct fn
*fnp
)
183 return (&fnp
->fn_stbuf
);
187 * fn_free -- free a filename buffer
190 fn_free(struct fn
*fnp
)
200 * fn_renew -- reset a filename buffer
202 * calling fn_renew(fnp, s) is the same as calling:
207 fn_renew(struct fn
*fnp
, const char *s
)
209 fnp
->fn_rptr
= fnp
->fn_wptr
= fnp
->fn_buf
;
214 * fn_putc -- append a character to a filename
216 * this is the function that handles growing the filename buffer
217 * automatically and calling err() if it overflows.
220 fn_putc(struct fn
*fnp
, int c
)
222 if (fnp
->fn_wptr
>= fnp
->fn_buflast
) {
223 int buflen
= fnp
->fn_buflast
+ 1 - fnp
->fn_buf
;
228 /* overflow, allocate more space or die if at FN_MAX */
229 if (buflen
>= FN_MAX
)
230 err(0, "fn buffer overflow");
232 newbuf
= MALLOC(buflen
);
234 /* copy string into new buffer */
238 /* just copy up to wptr, rest is history anyway */
239 while (src
< fnp
->fn_wptr
)
241 fnp
->fn_rptr
= &newbuf
[fnp
->fn_rptr
- fnp
->fn_buf
];
243 fnp
->fn_buf
= newbuf
;
244 fnp
->fn_buflast
= &fnp
->fn_buf
[buflen
- 1];
248 *fnp
->fn_wptr
= '\0';
252 * fn_puts -- append a string to a filename
255 fn_puts(struct fn
*fnp
, const char *s
)
257 /* non-optimal, but simple! */
258 while (s
!= NULL
&& *s
)
263 * fn_putfn -- append a filename buffer to a filename
266 fn_putfn(struct fn
*fnp
, struct fn
*srcfnp
)
271 while (c
= fn_getc(srcfnp
))
276 * fn_rewind -- reset the "read pointer" to the beginning of a filename
279 fn_rewind(struct fn
*fnp
)
281 fnp
->fn_rptr
= fnp
->fn_buf
;
285 * fn_getc -- "read" the next character of a filename
288 fn_getc(struct fn
*fnp
)
290 if (fnp
->fn_rptr
> fnp
->fn_buflast
|| *fnp
->fn_rptr
== '\0')
293 return (*fnp
->fn_rptr
++);
297 * fn_peekc -- "peek" at the next character of a filename
300 fn_peekc(struct fn
*fnp
)
302 if (fnp
->fn_rptr
> fnp
->fn_buflast
|| *fnp
->fn_rptr
== '\0')
305 return (*fnp
->fn_rptr
);
309 * fn_s -- return a pointer to a null-terminated string containing the filename
314 return (fnp
->fn_buf
);
318 * fn_isgz -- return true if filename is *.gz
321 fn_isgz(struct fn
*fnp
)
328 if (len
> 3 && strcmp(name
+ len
- 3, ".gz") == 0)
335 * fn_list_new -- create a new list of filenames
337 * by convention, an empty list is represented by an allocated
338 * struct fn_list which contains a NULL linked list, rather than
339 * by a NULL fn_list pointer. in other words:
341 * struct fn_list *fnlp = some_func_returning_a_list();
342 * if (fn_list_empty(fnlp))
345 * is preferable to checking if the fnlp returned is NULL.
348 fn_list_new(const char * const *slist
)
350 struct fn_list
*fnlp
= MALLOC(sizeof (struct fn_list
));
352 fnlp
->fnl_first
= fnlp
->fnl_last
= fnlp
->fnl_rptr
= NULL
;
354 while (slist
&& *slist
)
355 fn_list_adds(fnlp
, *slist
++);
361 * fn_list_dup -- duplicate a list of filenames
364 fn_list_dup(struct fn_list
*fnlp
)
366 struct fn_list
*ret
= fn_list_new(NULL
);
369 fn_list_rewind(fnlp
);
370 while ((fnp
= fn_list_next(fnlp
)) != NULL
)
371 fn_list_addfn(ret
, fn_dup(fnp
));
377 * fn_list_free -- free a list of filenames
380 fn_list_free(struct fn_list
*fnlp
)
384 fn_list_rewind(fnlp
);
385 while ((fnp
= fn_list_next(fnlp
)) != NULL
)
391 * fn_list_adds -- add a string to a list of filenames
394 fn_list_adds(struct fn_list
*fnlp
, const char *s
)
396 fn_list_addfn(fnlp
, fn_new(s
));
400 * fn_list_addfn -- add a filename (i.e. struct fn *) to a list of filenames
403 fn_list_addfn(struct fn_list
*fnlp
, struct fn
*fnp
)
406 if (fnlp
->fnl_first
== NULL
)
407 fnlp
->fnl_first
= fnlp
->fnl_last
= fnlp
->fnl_rptr
= fnp
;
409 fnlp
->fnl_last
->fn_next
= fnp
;
410 fnlp
->fnl_last
= fnp
;
415 * fn_list_rewind -- reset the "read pointer" to the beginning of the list
418 fn_list_rewind(struct fn_list
*fnlp
)
420 fnlp
->fnl_rptr
= fnlp
->fnl_first
;
424 * fn_list_next -- return the filename at the read pointer and advance it
427 fn_list_next(struct fn_list
*fnlp
)
429 struct fn
*ret
= fnlp
->fnl_rptr
;
431 if (fnlp
->fnl_rptr
== fnlp
->fnl_last
)
432 fnlp
->fnl_rptr
= NULL
;
433 else if (fnlp
->fnl_rptr
!= NULL
)
434 fnlp
->fnl_rptr
= fnlp
->fnl_rptr
->fn_next
;
440 * fn_list_addfn_list -- move filenames from fnlp2 to end of fnlp
442 * frees fnlp2 after moving all the filenames off of it.
445 fn_list_addfn_list(struct fn_list
*fnlp
, struct fn_list
*fnlp2
)
447 struct fn
*fnp2
= fnlp2
->fnl_first
;
450 /* for each fn in the second list... */
452 if (fnp2
== fnlp2
->fnl_last
)
455 nextfnp2
= fnp2
->fn_next
;
457 /* append it to the first list */
458 fn_list_addfn(fnlp
, fnp2
);
462 /* all the fn's were moved off the second list */
463 fnlp2
->fnl_first
= fnlp2
->fnl_last
= fnlp2
->fnl_rptr
= NULL
;
465 /* done with the second list */
470 * fn_list_appendrange -- append a range of characters to each filename in list
472 * range of characters appended is the character at *s up to but not including
473 * the character at *limit. NULL termination is not required.
476 fn_list_appendrange(struct fn_list
*fnlp
, const char *s
, const char *limit
)
478 struct fn
*fnp
= fnlp
->fnl_first
;
482 /* for each fn in the list... */
483 while (fnp
!= NULL
) {
484 if (fnp
== fnlp
->fnl_last
)
487 nextfnp
= fnp
->fn_next
;
489 /* append the range */
490 for (ptr
= s
; ptr
< limit
; ptr
++)
498 * fn_list_totalsize -- sum up all the st_size fields in the stat structs
501 fn_list_totalsize(struct fn_list
*fnlp
)
506 fn_list_rewind(fnlp
);
507 while ((fnp
= fn_list_next(fnlp
)) != NULL
)
508 ret
+= fnp
->fn_stbuf
.st_size
;
514 * fn_list_popoldest -- remove oldest file from list and return it
516 * this function uses the "n" values (set by fn_setn()) to determine
517 * which file is oldest, or when there's a tie it turns to the modification
518 * times in the stat structs, or when there's still a tie lexical sorting.
521 fn_list_popoldest(struct fn_list
*fnlp
)
524 struct fn
*ret
= NULL
;
526 fn_list_rewind(fnlp
);
527 while ((fnp
= fn_list_next(fnlp
)) != NULL
)
530 else if (fnp
->fn_n
> ret
->fn_n
||
531 (fnp
->fn_n
== ret
->fn_n
&&
532 (fnp
->fn_stbuf
.st_mtime
< ret
->fn_stbuf
.st_mtime
||
533 ((fnp
->fn_stbuf
.st_mtime
== ret
->fn_stbuf
.st_mtime
&&
534 strcmp(fnp
->fn_buf
, ret
->fn_buf
) > 0)))))
540 /* oldest file is ret, remove it from list */
541 if (fnlp
->fnl_first
== ret
) {
542 fnlp
->fnl_first
= ret
->fn_next
;
544 fn_list_rewind(fnlp
);
545 while ((fnp
= fn_list_next(fnlp
)) != NULL
) {
546 if (fnp
->fn_next
== ret
) {
547 fnp
->fn_next
= ret
->fn_next
;
548 if (fnlp
->fnl_last
== ret
)
549 fnlp
->fnl_last
= fnp
;
560 * fn_list_empty -- true if the list is empty
563 fn_list_empty(struct fn_list
*fnlp
)
565 return (fnlp
->fnl_first
== NULL
);
569 * fn_list_count -- return number of filenames in list
572 fn_list_count(struct fn_list
*fnlp
)
577 * if this operation were more common, we'd cache the count
578 * in the struct fn_list, but it isn't very common so we just
581 fn_list_rewind(fnlp
);
582 while (fn_list_next(fnlp
) != NULL
)