#include <stdio.h>
#include <libintl.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "err.h"
#include "fn.h"
#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
#define FN_MIN 1024
#define FN_MAX 10240
#define FN_INC 1024
struct fn {
char *fn_buf;
char *fn_buflast;
char *fn_rptr;
char *fn_wptr;
struct fn *fn_next;
struct stat fn_stbuf;
int fn_n;
};
struct fn_list {
struct fn *fnl_first;
struct fn *fnl_last;
struct fn *fnl_rptr;
};
struct fn *
fn_new(const char *s)
{
struct fn *fnp = MALLOC(sizeof (struct fn));
fnp->fn_n = -1;
bzero(&fnp->fn_stbuf, sizeof (fnp->fn_stbuf));
fnp->fn_next = NULL;
if (s != NULL && *s) {
int len = strlen(s);
int buflen = roundup(len + 1, FN_INC);
fnp->fn_buf = MALLOC(buflen);
fnp->fn_buflast = &fnp->fn_buf[buflen - 1];
(void) strlcpy(fnp->fn_buf, s, buflen);
fnp->fn_rptr = fnp->fn_buf;
fnp->fn_wptr = &fnp->fn_buf[len];
} else {
fnp->fn_buf = MALLOC(FN_MIN);
fnp->fn_buflast = &fnp->fn_buf[FN_MIN - 1];
*fnp->fn_buf = '\0';
fnp->fn_rptr = fnp->fn_buf;
fnp->fn_wptr = fnp->fn_buf;
}
return (fnp);
}
struct fn *
fn_dup(struct fn *fnp)
{
struct fn *ret = fn_new(fn_s(fnp));
ret->fn_n = fnp->fn_n;
ret->fn_stbuf = fnp->fn_stbuf;
return (ret);
}
struct fn *
fn_dirname(struct fn *fnp)
{
char *ptr = NULL;
struct fn *ret;
char *buf;
buf = fn_s(fnp);
if (buf != NULL)
ptr = strrchr(buf, '/');
if (ptr == NULL || buf == NULL)
return (fn_new("."));
else {
*ptr = '\0';
ret = fn_new(buf);
*ptr = '/';
return (ret);
}
}
void
fn_setn(struct fn *fnp, int n)
{
fnp->fn_n = n;
}
void
fn_setstat(struct fn *fnp, struct stat *stp)
{
fnp->fn_stbuf = *stp;
}
struct stat *
fn_getstat(struct fn *fnp)
{
return (&fnp->fn_stbuf);
}
void
fn_free(struct fn *fnp)
{
if (fnp) {
if (fnp->fn_buf)
FREE(fnp->fn_buf);
FREE(fnp);
}
}
void
fn_renew(struct fn *fnp, const char *s)
{
fnp->fn_rptr = fnp->fn_wptr = fnp->fn_buf;
fn_puts(fnp, s);
}
void
fn_putc(struct fn *fnp, int c)
{
if (fnp->fn_wptr >= fnp->fn_buflast) {
int buflen = fnp->fn_buflast + 1 - fnp->fn_buf;
char *newbuf;
char *src;
char *dst;
if (buflen >= FN_MAX)
err(0, "fn buffer overflow");
buflen += FN_INC;
newbuf = MALLOC(buflen);
src = fnp->fn_buf;
dst = newbuf;
while (src < fnp->fn_wptr)
*dst++ = *src++;
fnp->fn_rptr = &newbuf[fnp->fn_rptr - fnp->fn_buf];
FREE(fnp->fn_buf);
fnp->fn_buf = newbuf;
fnp->fn_buflast = &fnp->fn_buf[buflen - 1];
fnp->fn_wptr = dst;
}
*fnp->fn_wptr++ = c;
*fnp->fn_wptr = '\0';
}
void
fn_puts(struct fn *fnp, const char *s)
{
while (s != NULL && *s)
fn_putc(fnp, *s++);
}
void
fn_putfn(struct fn *fnp, struct fn *srcfnp)
{
int c;
fn_rewind(srcfnp);
while (c = fn_getc(srcfnp))
fn_putc(fnp, c);
}
void
fn_rewind(struct fn *fnp)
{
fnp->fn_rptr = fnp->fn_buf;
}
int
fn_getc(struct fn *fnp)
{
if (fnp->fn_rptr > fnp->fn_buflast || *fnp->fn_rptr == '\0')
return (0);
return (*fnp->fn_rptr++);
}
int
fn_peekc(struct fn *fnp)
{
if (fnp->fn_rptr > fnp->fn_buflast || *fnp->fn_rptr == '\0')
return (0);
return (*fnp->fn_rptr);
}
char *
fn_s(struct fn *fnp)
{
return (fnp->fn_buf);
}
boolean_t
fn_isgz(struct fn *fnp)
{
size_t len;
char *name;
name = fnp->fn_buf;
len = strlen(name);
if (len > 3 && strcmp(name + len - 3, ".gz") == 0)
return (B_TRUE);
else
return (B_FALSE);
}
struct fn_list *
fn_list_new(const char * const *slist)
{
struct fn_list *fnlp = MALLOC(sizeof (struct fn_list));
fnlp->fnl_first = fnlp->fnl_last = fnlp->fnl_rptr = NULL;
while (slist && *slist)
fn_list_adds(fnlp, *slist++);
return (fnlp);
}
struct fn_list *
fn_list_dup(struct fn_list *fnlp)
{
struct fn_list *ret = fn_list_new(NULL);
struct fn *fnp;
fn_list_rewind(fnlp);
while ((fnp = fn_list_next(fnlp)) != NULL)
fn_list_addfn(ret, fn_dup(fnp));
return (ret);
}
void
fn_list_free(struct fn_list *fnlp)
{
struct fn *fnp;
fn_list_rewind(fnlp);
while ((fnp = fn_list_next(fnlp)) != NULL)
fn_free(fnp);
FREE(fnlp);
}
void
fn_list_adds(struct fn_list *fnlp, const char *s)
{
fn_list_addfn(fnlp, fn_new(s));
}
void
fn_list_addfn(struct fn_list *fnlp, struct fn *fnp)
{
fnp->fn_next = NULL;
if (fnlp->fnl_first == NULL)
fnlp->fnl_first = fnlp->fnl_last = fnlp->fnl_rptr = fnp;
else {
fnlp->fnl_last->fn_next = fnp;
fnlp->fnl_last = fnp;
}
}
void
fn_list_rewind(struct fn_list *fnlp)
{
fnlp->fnl_rptr = fnlp->fnl_first;
}
struct fn *
fn_list_next(struct fn_list *fnlp)
{
struct fn *ret = fnlp->fnl_rptr;
if (fnlp->fnl_rptr == fnlp->fnl_last)
fnlp->fnl_rptr = NULL;
else if (fnlp->fnl_rptr != NULL)
fnlp->fnl_rptr = fnlp->fnl_rptr->fn_next;
return (ret);
}
void
fn_list_addfn_list(struct fn_list *fnlp, struct fn_list *fnlp2)
{
struct fn *fnp2 = fnlp2->fnl_first;
struct fn *nextfnp2;
while (fnp2) {
if (fnp2 == fnlp2->fnl_last)
nextfnp2 = NULL;
else
nextfnp2 = fnp2->fn_next;
fn_list_addfn(fnlp, fnp2);
fnp2 = nextfnp2;
}
fnlp2->fnl_first = fnlp2->fnl_last = fnlp2->fnl_rptr = NULL;
fn_list_free(fnlp2);
}
void
fn_list_appendrange(struct fn_list *fnlp, const char *s, const char *limit)
{
struct fn *fnp = fnlp->fnl_first;
struct fn *nextfnp;
const char *ptr;
while (fnp != NULL) {
if (fnp == fnlp->fnl_last)
nextfnp = NULL;
else
nextfnp = fnp->fn_next;
for (ptr = s; ptr < limit; ptr++)
fn_putc(fnp, *ptr);
fnp = nextfnp;
}
}
off_t
fn_list_totalsize(struct fn_list *fnlp)
{
struct fn *fnp;
off_t ret = 0;
fn_list_rewind(fnlp);
while ((fnp = fn_list_next(fnlp)) != NULL)
ret += fnp->fn_stbuf.st_size;
return (ret);
}
struct fn *
fn_list_popoldest(struct fn_list *fnlp)
{
struct fn *fnp;
struct fn *ret = NULL;
fn_list_rewind(fnlp);
while ((fnp = fn_list_next(fnlp)) != NULL)
if (ret == NULL)
ret = fnp;
else if (fnp->fn_n > ret->fn_n ||
(fnp->fn_n == ret->fn_n &&
(fnp->fn_stbuf.st_mtime < ret->fn_stbuf.st_mtime ||
((fnp->fn_stbuf.st_mtime == ret->fn_stbuf.st_mtime &&
strcmp(fnp->fn_buf, ret->fn_buf) > 0)))))
ret = fnp;
if (ret == NULL)
return (NULL);
if (fnlp->fnl_first == ret) {
fnlp->fnl_first = ret->fn_next;
} else {
fn_list_rewind(fnlp);
while ((fnp = fn_list_next(fnlp)) != NULL) {
if (fnp->fn_next == ret) {
fnp->fn_next = ret->fn_next;
if (fnlp->fnl_last == ret)
fnlp->fnl_last = fnp;
break;
}
}
}
ret->fn_next = NULL;
return (ret);
}
boolean_t
fn_list_empty(struct fn_list *fnlp)
{
return (fnlp->fnl_first == NULL);
}
int
fn_list_count(struct fn_list *fnlp)
{
int ret = 0;
fn_list_rewind(fnlp);
while (fn_list_next(fnlp) != NULL)
ret++;
return (ret);
}