#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "cvs.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);
RCSNUM *
rcsnum_alloc(void)
{
RCSNUM *rnp;
rnp = xcalloc(1, sizeof(*rnp));
rnp->rn_len = 0;
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') {
free(num);
num = NULL;
}
return (num);
}
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)
fatal("rcsnum_tostr: truncation");
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)
fatal("rcsnum_tostr: truncation");
}
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);
memcpy(ndst->rn_id, nsrc->rn_id, len * sizeof(*(nsrc->rn_id)));
}
int
rcsnum_cmp(RCSNUM *n1, RCSNUM *n2, u_int depth)
{
int res;
u_int i;
size_t slen;
if (!rcsnum_differ(n1, n2))
return (0);
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;
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)
goto rcsnum_aton_failed;
nump->rn_len++;
nump->rn_id[nump->rn_len] = 0;
continue;
}
val = (nump->rn_id[nump->rn_len] * 10) + (*sp - '0');
if (val > RCSNUM_MAXNUM)
fatal("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) {
if ((s = strrchr(str, '.')) != NULL) {
s--;
while (*s != '.')
s--;
if (!strncmp(s, RCS_MAGIC_BRANCH,
sizeof(RCS_MAGIC_BRANCH) - 1)) {
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[nump->rn_len] = 0;
}
nump->rn_len++;
return (nump->rn_len);
rcsnum_aton_failed:
nump->rn_len = 0;
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_dec(RCSNUM *num)
{
if (num->rn_id[num->rn_len - 1] <= 1)
return (num);
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);
}
RCSNUM *
rcsnum_new_branch(RCSNUM *rev)
{
RCSNUM *branch;
if (rev->rn_len > RCSNUM_MAXLEN - 1)
return NULL;
branch = rcsnum_alloc();
rcsnum_cpy(rev, branch, 0);
rcsnum_setsize(branch, rev->rn_len + 1);
branch->rn_id[branch->rn_len - 1] = 2;
return branch;
}
RCSNUM *
rcsnum_branch_root(RCSNUM *brev)
{
RCSNUM *root;
if (!RCSNUM_ISBRANCHREV(brev))
fatal("rcsnum_branch_root: no revision on branch specified");
root = rcsnum_alloc();
rcsnum_cpy(brev, root, 0);
root->rn_len -= 2;
return (root);
}
static void
rcsnum_setsize(RCSNUM *num, u_int len)
{
num->rn_len = len;
}
int
rcsnum_differ(RCSNUM *r1, RCSNUM *r2)
{
int i, len;
if (r1->rn_len != r2->rn_len)
return (1);
len = MINIMUM(r1->rn_len, r2->rn_len);
for (i = 0; i < len; i++) {
if (r1->rn_id[i] != r2->rn_id[i])
return (1);
}
return (0);
}