#include <ctype.h>
#include <err.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "rcs.h"
#include "xmalloc.h"
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
static void rcsnum_setsize(RCSNUM *, u_int);
static char *rcsnum_itoa(u_int16_t, char *, size_t);
int rcsnum_flags;
RCSNUM *
rcsnum_alloc(void)
{
RCSNUM *rnp;
rnp = xmalloc(sizeof(*rnp));
rnp->rn_len = 0;
rnp->rn_id = NULL;
return (rnp);
}
int
rcsnum_addmagic(RCSNUM *rn)
{
if (!rn->rn_len || rn->rn_len > RCSNUM_MAXLEN - 1)
return -1;
rcsnum_setsize(rn, rn->rn_len + 1);
rn->rn_id[rn->rn_len - 1] = rn->rn_id[rn->rn_len - 2];
rn->rn_id[rn->rn_len - 2] = 0;
return 0;
}
RCSNUM *
rcsnum_parse(const char *str)
{
const char *ep;
RCSNUM *num;
num = rcsnum_alloc();
if (rcsnum_aton(str, &ep, num) < 0 || *ep != '\0') {
rcsnum_free(num);
num = NULL;
if (*ep != '\0')
rcs_errno = RCS_ERR_BADNUM;
}
return (num);
}
void
rcsnum_free(RCSNUM *rn)
{
if (rn == NULL)
return;
free(rn->rn_id);
free(rn);
}
char *
rcsnum_tostr(const RCSNUM *nump, char *buf, size_t blen)
{
u_int i;
char tmp[8];
if (nump == NULL || nump->rn_len == 0) {
buf[0] = '\0';
return (buf);
}
if (strlcpy(buf, rcsnum_itoa(nump->rn_id[0], buf, blen), blen) >= blen)
errx(1, "rcsnum_tostr: string truncated");
for (i = 1; i < nump->rn_len; i++) {
const char *str;
str = rcsnum_itoa(nump->rn_id[i], tmp, sizeof(tmp));
if (strlcat(buf, ".", blen) >= blen ||
strlcat(buf, str, blen) >= blen)
errx(1, "rcsnum_tostr: string truncated");
}
return (buf);
}
static char *
rcsnum_itoa(u_int16_t num, char *buf, size_t len)
{
u_int16_t i;
char *p;
if (num == 0)
return "0";
p = buf + len - 1;
i = num;
bzero(buf, len);
while (i) {
*--p = '0' + (i % 10);
i /= 10;
}
return (p);
}
void
rcsnum_cpy(const RCSNUM *nsrc, RCSNUM *ndst, u_int depth)
{
u_int len;
len = nsrc->rn_len;
if (depth != 0 && len > depth)
len = depth;
rcsnum_setsize(ndst, len);
(void)memcpy(ndst->rn_id, nsrc->rn_id,
len * sizeof(*(nsrc->rn_id)));
}
int
rcsnum_cmp(const RCSNUM *n1, const RCSNUM *n2, u_int depth)
{
int res;
u_int i;
size_t slen;
slen = MINIMUM(n1->rn_len, n2->rn_len);
if (depth != 0 && slen > depth)
slen = depth;
for (i = 0; i < slen; i++) {
res = n1->rn_id[i] - n2->rn_id[i];
if (res < 0)
return (1);
else if (res > 0)
return (-1);
}
if (depth != 0 && slen == depth)
return (0);
else if (n1->rn_len > n2->rn_len)
return (-1);
else if (n2->rn_len > n1->rn_len)
return (1);
return (0);
}
int
rcsnum_aton(const char *str, const char **ep, RCSNUM *nump)
{
u_int32_t val;
const char *sp;
char *s;
if (nump->rn_id == NULL)
nump->rn_id = xmalloc(sizeof(*(nump->rn_id)));
nump->rn_len = 0;
nump->rn_id[0] = 0;
for (sp = str;; sp++) {
if (!isdigit((unsigned char)*sp) && (*sp != '.'))
break;
if (*sp == '.') {
if (nump->rn_len >= RCSNUM_MAXLEN - 1) {
rcs_errno = RCS_ERR_BADNUM;
goto rcsnum_aton_failed;
}
nump->rn_len++;
nump->rn_id = xreallocarray(nump->rn_id,
nump->rn_len + 1, sizeof(*(nump->rn_id)));
nump->rn_id[nump->rn_len] = 0;
continue;
}
val = (nump->rn_id[nump->rn_len] * 10) + (*sp - '0');
if (val > RCSNUM_MAXNUM)
errx(1, "RCSNUM overflow!");
nump->rn_id[nump->rn_len] = val;
}
if (ep != NULL)
*ep = sp;
if (nump->rn_len > 2 && nump->rn_id[nump->rn_len - 1] == 0
&& !(rcsnum_flags & RCSNUM_NO_MAGIC)) {
if ((s = strrchr(str, '.')) != NULL) {
s--;
while (*s != '.')
s--;
if (!strncmp(s, RCS_MAGIC_BRANCH,
strlen(RCS_MAGIC_BRANCH))) {
nump->rn_id[nump->rn_len - 1] =
nump->rn_id[nump->rn_len];
nump->rn_len--;
}
}
}
if (nump->rn_len == 0) {
nump->rn_len++;
nump->rn_id = xreallocarray(nump->rn_id,
nump->rn_len + 1, sizeof(*(nump->rn_id)));
nump->rn_id[nump->rn_len] = 0;
}
nump->rn_len++;
return (nump->rn_len);
rcsnum_aton_failed:
nump->rn_len = 0;
free(nump->rn_id);
nump->rn_id = NULL;
return (-1);
}
RCSNUM *
rcsnum_inc(RCSNUM *num)
{
if (num->rn_id[num->rn_len - 1] == RCSNUM_MAXNUM)
return (NULL);
num->rn_id[num->rn_len - 1]++;
return (num);
}
RCSNUM *
rcsnum_revtobr(const RCSNUM *num)
{
RCSNUM *brnum;
if (num->rn_len < 2)
return (NULL);
brnum = rcsnum_alloc();
rcsnum_cpy(num, brnum, 0);
if (!RCSNUM_ISBRANCH(brnum))
brnum->rn_len--;
return (brnum);
}
RCSNUM *
rcsnum_brtorev(const RCSNUM *brnum)
{
RCSNUM *num;
if (!RCSNUM_ISBRANCH(brnum)) {
return (NULL);
}
num = rcsnum_alloc();
rcsnum_setsize(num, brnum->rn_len + 1);
rcsnum_cpy(brnum, num, brnum->rn_len);
num->rn_id[num->rn_len++] = 1;
return (num);
}
static void
rcsnum_setsize(RCSNUM *num, u_int len)
{
num->rn_id = xreallocarray(num->rn_id, len, sizeof(*(num->rn_id)));
num->rn_len = len;
}