xref: /onnv-gate/usr/src/cmd/logadm/fn.c (revision 3404:a0ad82bad007)
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