10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
52016Sbasabi * Common Development and Distribution License (the "License").
62016Sbasabi * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*3404Snakanon * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
232016Sbasabi * Use is subject to license terms.
240Sstevel@tonic-gate *
250Sstevel@tonic-gate * logadm/fn.c -- "filename" string module
260Sstevel@tonic-gate *
270Sstevel@tonic-gate * this file contains routines for the manipulation of filenames.
280Sstevel@tonic-gate * they aren't particularly fast (at least they weren't designed
290Sstevel@tonic-gate * for performance), but they are simple and put all the malloc/free
300Sstevel@tonic-gate * stuff for these strings in a central place. most routines in
310Sstevel@tonic-gate * logadm that return filenames return a struct fn, and most routines
320Sstevel@tonic-gate * that return lists of strings return a struct fn_list.
330Sstevel@tonic-gate */
340Sstevel@tonic-gate
350Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
360Sstevel@tonic-gate
370Sstevel@tonic-gate #include <stdio.h>
380Sstevel@tonic-gate #include <libintl.h>
390Sstevel@tonic-gate #include <strings.h>
400Sstevel@tonic-gate #include <sys/types.h>
410Sstevel@tonic-gate #include <sys/stat.h>
420Sstevel@tonic-gate #include "err.h"
430Sstevel@tonic-gate #include "fn.h"
440Sstevel@tonic-gate
450Sstevel@tonic-gate #define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
460Sstevel@tonic-gate
470Sstevel@tonic-gate /*
480Sstevel@tonic-gate * constants controlling how we malloc space. bigger means fewer
490Sstevel@tonic-gate * calls to malloc. smaller means less wasted space.
500Sstevel@tonic-gate */
510Sstevel@tonic-gate #define FN_MIN 1024 /* initial size of string buffers */
520Sstevel@tonic-gate #define FN_MAX 10240 /* maximum size allowed before fatal "overflow" error */
530Sstevel@tonic-gate #define FN_INC 1024 /* increments in buffer size as strings grow */
540Sstevel@tonic-gate
550Sstevel@tonic-gate /* info created by fn_new(), private to this module */
560Sstevel@tonic-gate struct fn {
570Sstevel@tonic-gate char *fn_buf; /* first location in buf */
580Sstevel@tonic-gate char *fn_buflast; /* last location in buf */
590Sstevel@tonic-gate char *fn_rptr; /* read pointer (next unread character) */
600Sstevel@tonic-gate char *fn_wptr; /* write pointer (points at null terminator) */
610Sstevel@tonic-gate struct fn *fn_next; /* next in list */
620Sstevel@tonic-gate struct stat fn_stbuf;
630Sstevel@tonic-gate int fn_n;
640Sstevel@tonic-gate };
650Sstevel@tonic-gate
660Sstevel@tonic-gate /* info created by fn_list_new(), private to this module */
670Sstevel@tonic-gate struct fn_list {
680Sstevel@tonic-gate struct fn *fnl_first; /* first element of list */
690Sstevel@tonic-gate struct fn *fnl_last; /* last element of list */
700Sstevel@tonic-gate struct fn *fnl_rptr; /* read pointer for iterating through list */
710Sstevel@tonic-gate };
720Sstevel@tonic-gate
730Sstevel@tonic-gate /*
740Sstevel@tonic-gate * fn_new -- create a new filename buffer, possibly with initial contents
750Sstevel@tonic-gate *
760Sstevel@tonic-gate * use like this:
770Sstevel@tonic-gate * struct fn *fnp = fn_new("this is a string");
780Sstevel@tonic-gate */
790Sstevel@tonic-gate struct fn *
fn_new(const char * s)800Sstevel@tonic-gate fn_new(const char *s)
810Sstevel@tonic-gate {
820Sstevel@tonic-gate struct fn *fnp = MALLOC(sizeof (struct fn));
830Sstevel@tonic-gate
840Sstevel@tonic-gate fnp->fn_n = -1;
850Sstevel@tonic-gate bzero(&fnp->fn_stbuf, sizeof (fnp->fn_stbuf));
860Sstevel@tonic-gate fnp->fn_next = NULL;
870Sstevel@tonic-gate
880Sstevel@tonic-gate /* if passed-in string contains at least 1 non-null character... */
892397Sbasabi if (s != NULL && *s) {
900Sstevel@tonic-gate int len = strlen(s);
910Sstevel@tonic-gate int buflen = roundup(len + 1, FN_INC);
920Sstevel@tonic-gate
930Sstevel@tonic-gate /* start with buffer filled with passed-in string */
940Sstevel@tonic-gate fnp->fn_buf = MALLOC(buflen);
950Sstevel@tonic-gate fnp->fn_buflast = &fnp->fn_buf[buflen - 1];
960Sstevel@tonic-gate (void) strlcpy(fnp->fn_buf, s, buflen);
970Sstevel@tonic-gate fnp->fn_rptr = fnp->fn_buf;
980Sstevel@tonic-gate fnp->fn_wptr = &fnp->fn_buf[len];
990Sstevel@tonic-gate } else {
1000Sstevel@tonic-gate /* start with empty buffer */
1010Sstevel@tonic-gate fnp->fn_buf = MALLOC(FN_MIN);
1020Sstevel@tonic-gate fnp->fn_buflast = &fnp->fn_buf[FN_MIN - 1];
1030Sstevel@tonic-gate *fnp->fn_buf = '\0';
1040Sstevel@tonic-gate fnp->fn_rptr = fnp->fn_buf;
1050Sstevel@tonic-gate fnp->fn_wptr = fnp->fn_buf;
1060Sstevel@tonic-gate }
1070Sstevel@tonic-gate
1080Sstevel@tonic-gate return (fnp);
1090Sstevel@tonic-gate }
1100Sstevel@tonic-gate
1110Sstevel@tonic-gate /*
1120Sstevel@tonic-gate * fn_dup -- duplicate a filename buffer
1130Sstevel@tonic-gate */
1140Sstevel@tonic-gate struct fn *
fn_dup(struct fn * fnp)1150Sstevel@tonic-gate fn_dup(struct fn *fnp)
1160Sstevel@tonic-gate {
1170Sstevel@tonic-gate struct fn *ret = fn_new(fn_s(fnp));
1180Sstevel@tonic-gate
1190Sstevel@tonic-gate ret->fn_n = fnp->fn_n;
1200Sstevel@tonic-gate ret->fn_stbuf = fnp->fn_stbuf;
1210Sstevel@tonic-gate
1220Sstevel@tonic-gate return (ret);
1230Sstevel@tonic-gate }
1240Sstevel@tonic-gate
1250Sstevel@tonic-gate /*
1260Sstevel@tonic-gate * fn_dirname -- return the dirname part of a filename
1270Sstevel@tonic-gate */
1280Sstevel@tonic-gate struct fn *
fn_dirname(struct fn * fnp)1290Sstevel@tonic-gate fn_dirname(struct fn *fnp)
1300Sstevel@tonic-gate {
1312397Sbasabi char *ptr = NULL;
1320Sstevel@tonic-gate struct fn *ret;
1332397Sbasabi char *buf;
1340Sstevel@tonic-gate
1352397Sbasabi buf = fn_s(fnp);
1362397Sbasabi
1372397Sbasabi if (buf != NULL)
1382397Sbasabi ptr = strrchr(buf, '/');
1392397Sbasabi if (ptr == NULL || buf == NULL)
1400Sstevel@tonic-gate return (fn_new("."));
1410Sstevel@tonic-gate else {
1420Sstevel@tonic-gate *ptr = '\0';
1432397Sbasabi ret = fn_new(buf);
1440Sstevel@tonic-gate *ptr = '/';
1450Sstevel@tonic-gate return (ret);
1460Sstevel@tonic-gate }
1470Sstevel@tonic-gate }
1480Sstevel@tonic-gate
1490Sstevel@tonic-gate /*
1500Sstevel@tonic-gate * fn_setn -- set the "n" value for a filename
1510Sstevel@tonic-gate *
1520Sstevel@tonic-gate * the "n" value is initially -1, and is used by logadm to store
1530Sstevel@tonic-gate * the suffix for rotated log files. the function fn_list_popoldest()
1540Sstevel@tonic-gate * looks at these "n" values when sorting filenames to determine which
1550Sstevel@tonic-gate * old log file is the oldest and should be expired first.
1560Sstevel@tonic-gate */
1570Sstevel@tonic-gate void
fn_setn(struct fn * fnp,int n)1580Sstevel@tonic-gate fn_setn(struct fn *fnp, int n)
1590Sstevel@tonic-gate {
1600Sstevel@tonic-gate fnp->fn_n = n;
1610Sstevel@tonic-gate }
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate /*
1640Sstevel@tonic-gate * fn_setstat -- store a struct stat with a filename
1650Sstevel@tonic-gate *
1660Sstevel@tonic-gate * the glob functions typically fill in these struct stats since they
1670Sstevel@tonic-gate * have to stat while globbing anyway. just turned out to be a common
1680Sstevel@tonic-gate * piece of information that was conveniently stored with the associated
1690Sstevel@tonic-gate * filename.
1700Sstevel@tonic-gate */
1710Sstevel@tonic-gate void
fn_setstat(struct fn * fnp,struct stat * stp)1720Sstevel@tonic-gate fn_setstat(struct fn *fnp, struct stat *stp)
1730Sstevel@tonic-gate {
1740Sstevel@tonic-gate fnp->fn_stbuf = *stp;
1750Sstevel@tonic-gate }
1760Sstevel@tonic-gate
1770Sstevel@tonic-gate /*
1780Sstevel@tonic-gate * fn_getstat -- return a pointer to the stat info stored by fn_setstat()
1790Sstevel@tonic-gate */
1800Sstevel@tonic-gate struct stat *
fn_getstat(struct fn * fnp)1810Sstevel@tonic-gate fn_getstat(struct fn *fnp)
1820Sstevel@tonic-gate {
1830Sstevel@tonic-gate return (&fnp->fn_stbuf);
1840Sstevel@tonic-gate }
1850Sstevel@tonic-gate
1860Sstevel@tonic-gate /*
1870Sstevel@tonic-gate * fn_free -- free a filename buffer
1880Sstevel@tonic-gate */
1890Sstevel@tonic-gate void
fn_free(struct fn * fnp)1900Sstevel@tonic-gate fn_free(struct fn *fnp)
1910Sstevel@tonic-gate {
1920Sstevel@tonic-gate if (fnp) {
1930Sstevel@tonic-gate if (fnp->fn_buf)
1940Sstevel@tonic-gate FREE(fnp->fn_buf);
1950Sstevel@tonic-gate FREE(fnp);
1960Sstevel@tonic-gate }
1970Sstevel@tonic-gate }
1980Sstevel@tonic-gate
1990Sstevel@tonic-gate /*
2000Sstevel@tonic-gate * fn_renew -- reset a filename buffer
2010Sstevel@tonic-gate *
2020Sstevel@tonic-gate * calling fn_renew(fnp, s) is the same as calling:
2030Sstevel@tonic-gate * fn_free(fnp);
2040Sstevel@tonic-gate * fn_new(s);
2050Sstevel@tonic-gate */
2060Sstevel@tonic-gate void
fn_renew(struct fn * fnp,const char * s)2070Sstevel@tonic-gate fn_renew(struct fn *fnp, const char *s)
2080Sstevel@tonic-gate {
2090Sstevel@tonic-gate fnp->fn_rptr = fnp->fn_wptr = fnp->fn_buf;
2100Sstevel@tonic-gate fn_puts(fnp, s);
2110Sstevel@tonic-gate }
2120Sstevel@tonic-gate
2130Sstevel@tonic-gate /*
2140Sstevel@tonic-gate * fn_putc -- append a character to a filename
2150Sstevel@tonic-gate *
2160Sstevel@tonic-gate * this is the function that handles growing the filename buffer
2170Sstevel@tonic-gate * automatically and calling err() if it overflows.
2180Sstevel@tonic-gate */
2190Sstevel@tonic-gate void
fn_putc(struct fn * fnp,int c)2200Sstevel@tonic-gate fn_putc(struct fn *fnp, int c)
2210Sstevel@tonic-gate {
2220Sstevel@tonic-gate if (fnp->fn_wptr >= fnp->fn_buflast) {
2230Sstevel@tonic-gate int buflen = fnp->fn_buflast + 1 - fnp->fn_buf;
2240Sstevel@tonic-gate char *newbuf;
2250Sstevel@tonic-gate char *src;
2260Sstevel@tonic-gate char *dst;
2270Sstevel@tonic-gate
2280Sstevel@tonic-gate /* overflow, allocate more space or die if at FN_MAX */
2290Sstevel@tonic-gate if (buflen >= FN_MAX)
2300Sstevel@tonic-gate err(0, "fn buffer overflow");
2310Sstevel@tonic-gate buflen += FN_INC;
2320Sstevel@tonic-gate newbuf = MALLOC(buflen);
2330Sstevel@tonic-gate
2340Sstevel@tonic-gate /* copy string into new buffer */
2350Sstevel@tonic-gate src = fnp->fn_buf;
2360Sstevel@tonic-gate dst = newbuf;
2370Sstevel@tonic-gate
2380Sstevel@tonic-gate /* just copy up to wptr, rest is history anyway */
2390Sstevel@tonic-gate while (src < fnp->fn_wptr)
2400Sstevel@tonic-gate *dst++ = *src++;
2410Sstevel@tonic-gate fnp->fn_rptr = &newbuf[fnp->fn_rptr - fnp->fn_buf];
2420Sstevel@tonic-gate FREE(fnp->fn_buf);
2430Sstevel@tonic-gate fnp->fn_buf = newbuf;
2440Sstevel@tonic-gate fnp->fn_buflast = &fnp->fn_buf[buflen - 1];
2450Sstevel@tonic-gate fnp->fn_wptr = dst;
2460Sstevel@tonic-gate }
2470Sstevel@tonic-gate *fnp->fn_wptr++ = c;
2480Sstevel@tonic-gate *fnp->fn_wptr = '\0';
2490Sstevel@tonic-gate }
2500Sstevel@tonic-gate
2510Sstevel@tonic-gate /*
2520Sstevel@tonic-gate * fn_puts -- append a string to a filename
2530Sstevel@tonic-gate */
2540Sstevel@tonic-gate void
fn_puts(struct fn * fnp,const char * s)2550Sstevel@tonic-gate fn_puts(struct fn *fnp, const char *s)
2560Sstevel@tonic-gate {
2570Sstevel@tonic-gate /* non-optimal, but simple! */
2582397Sbasabi while (s != NULL && *s)
2590Sstevel@tonic-gate fn_putc(fnp, *s++);
2600Sstevel@tonic-gate }
2610Sstevel@tonic-gate
2620Sstevel@tonic-gate /*
2630Sstevel@tonic-gate * fn_putfn -- append a filename buffer to a filename
2640Sstevel@tonic-gate */
2650Sstevel@tonic-gate void
fn_putfn(struct fn * fnp,struct fn * srcfnp)2660Sstevel@tonic-gate fn_putfn(struct fn *fnp, struct fn *srcfnp)
2670Sstevel@tonic-gate {
2680Sstevel@tonic-gate int c;
2690Sstevel@tonic-gate
2700Sstevel@tonic-gate fn_rewind(srcfnp);
2710Sstevel@tonic-gate while (c = fn_getc(srcfnp))
2720Sstevel@tonic-gate fn_putc(fnp, c);
2730Sstevel@tonic-gate }
2740Sstevel@tonic-gate
2750Sstevel@tonic-gate /*
2760Sstevel@tonic-gate * fn_rewind -- reset the "read pointer" to the beginning of a filename
2770Sstevel@tonic-gate */
2780Sstevel@tonic-gate void
fn_rewind(struct fn * fnp)2790Sstevel@tonic-gate fn_rewind(struct fn *fnp)
2800Sstevel@tonic-gate {
2810Sstevel@tonic-gate fnp->fn_rptr = fnp->fn_buf;
2820Sstevel@tonic-gate }
2830Sstevel@tonic-gate
2840Sstevel@tonic-gate /*
2850Sstevel@tonic-gate * fn_getc -- "read" the next character of a filename
2860Sstevel@tonic-gate */
2870Sstevel@tonic-gate int
fn_getc(struct fn * fnp)2880Sstevel@tonic-gate fn_getc(struct fn *fnp)
2890Sstevel@tonic-gate {
2900Sstevel@tonic-gate if (fnp->fn_rptr > fnp->fn_buflast || *fnp->fn_rptr == '\0')
2910Sstevel@tonic-gate return (0);
2920Sstevel@tonic-gate
2930Sstevel@tonic-gate return (*fnp->fn_rptr++);
2940Sstevel@tonic-gate }
2950Sstevel@tonic-gate
2960Sstevel@tonic-gate /*
2970Sstevel@tonic-gate * fn_peekc -- "peek" at the next character of a filename
2980Sstevel@tonic-gate */
2990Sstevel@tonic-gate int
fn_peekc(struct fn * fnp)3000Sstevel@tonic-gate fn_peekc(struct fn *fnp)
3010Sstevel@tonic-gate {
3020Sstevel@tonic-gate if (fnp->fn_rptr > fnp->fn_buflast || *fnp->fn_rptr == '\0')
3030Sstevel@tonic-gate return (0);
3040Sstevel@tonic-gate
3050Sstevel@tonic-gate return (*fnp->fn_rptr);
3060Sstevel@tonic-gate }
3070Sstevel@tonic-gate
3080Sstevel@tonic-gate /*
3090Sstevel@tonic-gate * fn_s -- return a pointer to a null-terminated string containing the filename
3100Sstevel@tonic-gate */
3110Sstevel@tonic-gate char *
fn_s(struct fn * fnp)3120Sstevel@tonic-gate fn_s(struct fn *fnp)
3130Sstevel@tonic-gate {
3140Sstevel@tonic-gate return (fnp->fn_buf);
3150Sstevel@tonic-gate }
3160Sstevel@tonic-gate
3170Sstevel@tonic-gate /*
3182876Snakanon * fn_isgz -- return true if filename is *.gz
3192876Snakanon */
3202876Snakanon boolean_t
fn_isgz(struct fn * fnp)3212876Snakanon fn_isgz(struct fn *fnp)
3222876Snakanon {
3232876Snakanon size_t len;
3242876Snakanon char *name;
3252876Snakanon
3262876Snakanon name = fnp->fn_buf;
3272876Snakanon len = strlen(name);
3282876Snakanon if (len > 3 && strcmp(name + len - 3, ".gz") == 0)
3292876Snakanon return (B_TRUE);
3302876Snakanon else
3312876Snakanon return (B_FALSE);
3322876Snakanon }
3332876Snakanon
3342876Snakanon /*
3350Sstevel@tonic-gate * fn_list_new -- create a new list of filenames
3360Sstevel@tonic-gate *
3370Sstevel@tonic-gate * by convention, an empty list is represented by an allocated
3380Sstevel@tonic-gate * struct fn_list which contains a NULL linked list, rather than
3390Sstevel@tonic-gate * by a NULL fn_list pointer. in other words:
3400Sstevel@tonic-gate *
3410Sstevel@tonic-gate * struct fn_list *fnlp = some_func_returning_a_list();
3420Sstevel@tonic-gate * if (fn_list_empty(fnlp))
3430Sstevel@tonic-gate * ...
3440Sstevel@tonic-gate *
3450Sstevel@tonic-gate * is preferable to checking if the fnlp returned is NULL.
3460Sstevel@tonic-gate */
3470Sstevel@tonic-gate struct fn_list *
fn_list_new(const char * const * slist)3480Sstevel@tonic-gate fn_list_new(const char * const *slist)
3490Sstevel@tonic-gate {
3500Sstevel@tonic-gate struct fn_list *fnlp = MALLOC(sizeof (struct fn_list));
3510Sstevel@tonic-gate
3520Sstevel@tonic-gate fnlp->fnl_first = fnlp->fnl_last = fnlp->fnl_rptr = NULL;
3530Sstevel@tonic-gate
3540Sstevel@tonic-gate while (slist && *slist)
3550Sstevel@tonic-gate fn_list_adds(fnlp, *slist++);
3560Sstevel@tonic-gate
3570Sstevel@tonic-gate return (fnlp);
3580Sstevel@tonic-gate }
3590Sstevel@tonic-gate
3600Sstevel@tonic-gate /*
3610Sstevel@tonic-gate * fn_list_dup -- duplicate a list of filenames
3620Sstevel@tonic-gate */
3630Sstevel@tonic-gate struct fn_list *
fn_list_dup(struct fn_list * fnlp)3640Sstevel@tonic-gate fn_list_dup(struct fn_list *fnlp)
3650Sstevel@tonic-gate {
3660Sstevel@tonic-gate struct fn_list *ret = fn_list_new(NULL);
3670Sstevel@tonic-gate struct fn *fnp;
3680Sstevel@tonic-gate
3690Sstevel@tonic-gate fn_list_rewind(fnlp);
3700Sstevel@tonic-gate while ((fnp = fn_list_next(fnlp)) != NULL)
3710Sstevel@tonic-gate fn_list_addfn(ret, fn_dup(fnp));
3720Sstevel@tonic-gate
3730Sstevel@tonic-gate return (ret);
3740Sstevel@tonic-gate }
3750Sstevel@tonic-gate
3760Sstevel@tonic-gate /*
3770Sstevel@tonic-gate * fn_list_free -- free a list of filenames
3780Sstevel@tonic-gate */
3790Sstevel@tonic-gate void
fn_list_free(struct fn_list * fnlp)3800Sstevel@tonic-gate fn_list_free(struct fn_list *fnlp)
3810Sstevel@tonic-gate {
3820Sstevel@tonic-gate struct fn *fnp;
3830Sstevel@tonic-gate
3840Sstevel@tonic-gate fn_list_rewind(fnlp);
3850Sstevel@tonic-gate while ((fnp = fn_list_next(fnlp)) != NULL)
3860Sstevel@tonic-gate fn_free(fnp);
3870Sstevel@tonic-gate FREE(fnlp);
3880Sstevel@tonic-gate }
3890Sstevel@tonic-gate
3900Sstevel@tonic-gate /*
3910Sstevel@tonic-gate * fn_list_adds -- add a string to a list of filenames
3920Sstevel@tonic-gate */
3930Sstevel@tonic-gate void
fn_list_adds(struct fn_list * fnlp,const char * s)3940Sstevel@tonic-gate fn_list_adds(struct fn_list *fnlp, const char *s)
3950Sstevel@tonic-gate {
3960Sstevel@tonic-gate fn_list_addfn(fnlp, fn_new(s));
3970Sstevel@tonic-gate }
3980Sstevel@tonic-gate
3990Sstevel@tonic-gate /*
4000Sstevel@tonic-gate * fn_list_addfn -- add a filename (i.e. struct fn *) to a list of filenames
4010Sstevel@tonic-gate */
4020Sstevel@tonic-gate void
fn_list_addfn(struct fn_list * fnlp,struct fn * fnp)4030Sstevel@tonic-gate fn_list_addfn(struct fn_list *fnlp, struct fn *fnp)
4040Sstevel@tonic-gate {
4050Sstevel@tonic-gate fnp->fn_next = NULL;
4060Sstevel@tonic-gate if (fnlp->fnl_first == NULL)
4070Sstevel@tonic-gate fnlp->fnl_first = fnlp->fnl_last = fnlp->fnl_rptr = fnp;
4080Sstevel@tonic-gate else {
4090Sstevel@tonic-gate fnlp->fnl_last->fn_next = fnp;
4100Sstevel@tonic-gate fnlp->fnl_last = fnp;
4110Sstevel@tonic-gate }
4120Sstevel@tonic-gate }
4130Sstevel@tonic-gate
4140Sstevel@tonic-gate /*
4150Sstevel@tonic-gate * fn_list_rewind -- reset the "read pointer" to the beginning of the list
4160Sstevel@tonic-gate */
4170Sstevel@tonic-gate void
fn_list_rewind(struct fn_list * fnlp)4180Sstevel@tonic-gate fn_list_rewind(struct fn_list *fnlp)
4190Sstevel@tonic-gate {
4200Sstevel@tonic-gate fnlp->fnl_rptr = fnlp->fnl_first;
4210Sstevel@tonic-gate }
4220Sstevel@tonic-gate
4230Sstevel@tonic-gate /*
4240Sstevel@tonic-gate * fn_list_next -- return the filename at the read pointer and advance it
4250Sstevel@tonic-gate */
4260Sstevel@tonic-gate struct fn *
fn_list_next(struct fn_list * fnlp)4270Sstevel@tonic-gate fn_list_next(struct fn_list *fnlp)
4280Sstevel@tonic-gate {
4290Sstevel@tonic-gate struct fn *ret = fnlp->fnl_rptr;
4300Sstevel@tonic-gate
4310Sstevel@tonic-gate if (fnlp->fnl_rptr == fnlp->fnl_last)
4320Sstevel@tonic-gate fnlp->fnl_rptr = NULL;
4330Sstevel@tonic-gate else if (fnlp->fnl_rptr != NULL)
4340Sstevel@tonic-gate fnlp->fnl_rptr = fnlp->fnl_rptr->fn_next;
4350Sstevel@tonic-gate
4360Sstevel@tonic-gate return (ret);
4370Sstevel@tonic-gate }
4380Sstevel@tonic-gate
4390Sstevel@tonic-gate /*
4400Sstevel@tonic-gate * fn_list_addfn_list -- move filenames from fnlp2 to end of fnlp
4410Sstevel@tonic-gate *
4420Sstevel@tonic-gate * frees fnlp2 after moving all the filenames off of it.
4430Sstevel@tonic-gate */
4440Sstevel@tonic-gate void
fn_list_addfn_list(struct fn_list * fnlp,struct fn_list * fnlp2)4450Sstevel@tonic-gate fn_list_addfn_list(struct fn_list *fnlp, struct fn_list *fnlp2)
4460Sstevel@tonic-gate {
4470Sstevel@tonic-gate struct fn *fnp2 = fnlp2->fnl_first;
4480Sstevel@tonic-gate struct fn *nextfnp2;
4490Sstevel@tonic-gate
4500Sstevel@tonic-gate /* for each fn in the second list... */
4510Sstevel@tonic-gate while (fnp2) {
4520Sstevel@tonic-gate if (fnp2 == fnlp2->fnl_last)
4530Sstevel@tonic-gate nextfnp2 = NULL;
4540Sstevel@tonic-gate else
4550Sstevel@tonic-gate nextfnp2 = fnp2->fn_next;
4560Sstevel@tonic-gate
4570Sstevel@tonic-gate /* append it to the first list */
4580Sstevel@tonic-gate fn_list_addfn(fnlp, fnp2);
4590Sstevel@tonic-gate
4600Sstevel@tonic-gate fnp2 = nextfnp2;
4610Sstevel@tonic-gate }
4620Sstevel@tonic-gate /* all the fn's were moved off the second list */
4630Sstevel@tonic-gate fnlp2->fnl_first = fnlp2->fnl_last = fnlp2->fnl_rptr = NULL;
4640Sstevel@tonic-gate
4650Sstevel@tonic-gate /* done with the second list */
4660Sstevel@tonic-gate fn_list_free(fnlp2);
4670Sstevel@tonic-gate }
4680Sstevel@tonic-gate
4690Sstevel@tonic-gate /*
4700Sstevel@tonic-gate * fn_list_appendrange -- append a range of characters to each filename in list
4710Sstevel@tonic-gate *
4720Sstevel@tonic-gate * range of characters appended is the character at *s up to but not including
4730Sstevel@tonic-gate * the character at *limit. NULL termination is not required.
4740Sstevel@tonic-gate */
4750Sstevel@tonic-gate void
fn_list_appendrange(struct fn_list * fnlp,const char * s,const char * limit)4760Sstevel@tonic-gate fn_list_appendrange(struct fn_list *fnlp, const char *s, const char *limit)
4770Sstevel@tonic-gate {
4780Sstevel@tonic-gate struct fn *fnp = fnlp->fnl_first;
4790Sstevel@tonic-gate struct fn *nextfnp;
4800Sstevel@tonic-gate const char *ptr;
4810Sstevel@tonic-gate
4820Sstevel@tonic-gate /* for each fn in the list... */
4832397Sbasabi while (fnp != NULL) {
4840Sstevel@tonic-gate if (fnp == fnlp->fnl_last)
4850Sstevel@tonic-gate nextfnp = NULL;
4860Sstevel@tonic-gate else
4870Sstevel@tonic-gate nextfnp = fnp->fn_next;
4880Sstevel@tonic-gate
4890Sstevel@tonic-gate /* append the range */
4900Sstevel@tonic-gate for (ptr = s; ptr < limit; ptr++)
4910Sstevel@tonic-gate fn_putc(fnp, *ptr);
4920Sstevel@tonic-gate
4930Sstevel@tonic-gate fnp = nextfnp;
4940Sstevel@tonic-gate }
4950Sstevel@tonic-gate }
4960Sstevel@tonic-gate
4970Sstevel@tonic-gate /*
4980Sstevel@tonic-gate * fn_list_totalsize -- sum up all the st_size fields in the stat structs
4990Sstevel@tonic-gate */
5002016Sbasabi off_t
fn_list_totalsize(struct fn_list * fnlp)5010Sstevel@tonic-gate fn_list_totalsize(struct fn_list *fnlp)
5020Sstevel@tonic-gate {
5030Sstevel@tonic-gate struct fn *fnp;
5042016Sbasabi off_t ret = 0;
5050Sstevel@tonic-gate
5060Sstevel@tonic-gate fn_list_rewind(fnlp);
5070Sstevel@tonic-gate while ((fnp = fn_list_next(fnlp)) != NULL)
5080Sstevel@tonic-gate ret += fnp->fn_stbuf.st_size;
5090Sstevel@tonic-gate
5100Sstevel@tonic-gate return (ret);
5110Sstevel@tonic-gate }
5120Sstevel@tonic-gate
5130Sstevel@tonic-gate /*
5140Sstevel@tonic-gate * fn_list_popoldest -- remove oldest file from list and return it
5150Sstevel@tonic-gate *
5160Sstevel@tonic-gate * this function uses the "n" values (set by fn_setn()) to determine
5170Sstevel@tonic-gate * which file is oldest, or when there's a tie it turns to the modification
5180Sstevel@tonic-gate * times in the stat structs, or when there's still a tie lexical sorting.
5190Sstevel@tonic-gate */
5200Sstevel@tonic-gate struct fn *
fn_list_popoldest(struct fn_list * fnlp)5210Sstevel@tonic-gate fn_list_popoldest(struct fn_list *fnlp)
5220Sstevel@tonic-gate {
5230Sstevel@tonic-gate struct fn *fnp;
5240Sstevel@tonic-gate struct fn *ret = NULL;
5250Sstevel@tonic-gate
5260Sstevel@tonic-gate fn_list_rewind(fnlp);
5270Sstevel@tonic-gate while ((fnp = fn_list_next(fnlp)) != NULL)
5280Sstevel@tonic-gate if (ret == NULL)
5290Sstevel@tonic-gate ret = fnp;
5300Sstevel@tonic-gate else if (fnp->fn_n > ret->fn_n ||
5310Sstevel@tonic-gate (fnp->fn_n == ret->fn_n &&
5320Sstevel@tonic-gate (fnp->fn_stbuf.st_mtime < ret->fn_stbuf.st_mtime ||
5330Sstevel@tonic-gate ((fnp->fn_stbuf.st_mtime == ret->fn_stbuf.st_mtime &&
5340Sstevel@tonic-gate strcmp(fnp->fn_buf, ret->fn_buf) > 0)))))
5350Sstevel@tonic-gate ret = fnp;
5360Sstevel@tonic-gate
5370Sstevel@tonic-gate if (ret == NULL)
5380Sstevel@tonic-gate return (NULL);
5390Sstevel@tonic-gate
5400Sstevel@tonic-gate /* oldest file is ret, remove it from list */
541*3404Snakanon if (fnlp->fnl_first == ret) {
5420Sstevel@tonic-gate fnlp->fnl_first = ret->fn_next;
543*3404Snakanon } else {
5440Sstevel@tonic-gate fn_list_rewind(fnlp);
545*3404Snakanon while ((fnp = fn_list_next(fnlp)) != NULL) {
5460Sstevel@tonic-gate if (fnp->fn_next == ret) {
5470Sstevel@tonic-gate fnp->fn_next = ret->fn_next;
548*3404Snakanon if (fnlp->fnl_last == ret)
549*3404Snakanon fnlp->fnl_last = fnp;
5500Sstevel@tonic-gate break;
5510Sstevel@tonic-gate }
552*3404Snakanon }
5530Sstevel@tonic-gate }
5540Sstevel@tonic-gate
5550Sstevel@tonic-gate ret->fn_next = NULL;
5560Sstevel@tonic-gate return (ret);
5570Sstevel@tonic-gate }
5580Sstevel@tonic-gate
5590Sstevel@tonic-gate /*
5600Sstevel@tonic-gate * fn_list_empty -- true if the list is empty
5610Sstevel@tonic-gate */
5620Sstevel@tonic-gate boolean_t
fn_list_empty(struct fn_list * fnlp)5630Sstevel@tonic-gate fn_list_empty(struct fn_list *fnlp)
5640Sstevel@tonic-gate {
5650Sstevel@tonic-gate return (fnlp->fnl_first == NULL);
5660Sstevel@tonic-gate }
5670Sstevel@tonic-gate
5680Sstevel@tonic-gate /*
5690Sstevel@tonic-gate * fn_list_count -- return number of filenames in list
5700Sstevel@tonic-gate */
5710Sstevel@tonic-gate int
fn_list_count(struct fn_list * fnlp)5720Sstevel@tonic-gate fn_list_count(struct fn_list *fnlp)
5730Sstevel@tonic-gate {
5740Sstevel@tonic-gate int ret = 0;
5750Sstevel@tonic-gate
5760Sstevel@tonic-gate /*
5770Sstevel@tonic-gate * if this operation were more common, we'd cache the count
5780Sstevel@tonic-gate * in the struct fn_list, but it isn't very common so we just
5790Sstevel@tonic-gate * count 'em up here
5800Sstevel@tonic-gate */
5810Sstevel@tonic-gate fn_list_rewind(fnlp);
5820Sstevel@tonic-gate while (fn_list_next(fnlp) != NULL)
5830Sstevel@tonic-gate ret++;
5840Sstevel@tonic-gate
5850Sstevel@tonic-gate return (ret);
5860Sstevel@tonic-gate }
587