Files
argyllcms/target/alphix.c
T
2026-08-20 20:28:40 +01:00

477 lines
11 KiB
C

/*
* Alphabetic index class.
*/
/*
* Argyll Color Correction System
*
* Author: Graeme W. Gill
* Date: 22/8/2005
*
* Copyright 2005, Graeme W. Gill
* All rights reserved.
*
* This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
* see the License.txt file for licencing details.
*/
/* TTBD:
It would be nice if it was possible to add a delimeter between
the X and Y identifiers so that number + number or alpha + alpha
identification was possible.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "numsup.h"
#include "alphix.h"
/* Return the alpha index for the given raw index number */
/* Return NULL on error */
static char *torawix(alphix *p, int ix) {
int i, j, n;
int z = 0;
char *rv, *v;
if (ix < 0 || ix >= p->rmct)
return NULL; /* Index range error */
if ((rv = malloc((p->nd+1) * sizeof(char))) == NULL)
return NULL;
for (v = rv, j = 0, i = p->nd-1; i >= 0; j++, i--) {
char c;
n = ix / p->ds[i].b;
ix -= n * p->ds[i].b;
c = p->ds[i].seq[n];
if (p->ds[i].z && z == 0 && c == '0')
c = ' ';
if (z != 0 || c != ' ') {
*v++ = p->ds[i].seq[n];
z = 1;
}
}
*v = '\000';
return rv;
}
/* Return the the raw index number given the alpha index */
/* return -1 on error */
static int fromanat(alphix *p, char *ax) {
char *v, *tb, _tb[11];
int cl;
int i, k, rv = 0;
cl = strlen(ax);
if (cl > p->nd) /* String is too long to be our index */
return -1;
if (p->nd > 10) {
if ((tb = malloc((p->nd+1) * sizeof(char))) == NULL)
return -1; /* Malloc error */
} else
tb = _tb;
/* Pack the string out to the right number of digits with spaces. */
for (v = tb; cl < p->nd; v++, cl++)
*v = ' ';
strcpy(v, ax);
/* For each digit, convert to numerical and accumulate */
for (v = tb, i = p->nd-1; i >= 0; v++, i--) {
for (k = 0; k < p->ds[i].n; k++) {
if (*v == p->ds[i].seq[k]
|| (p->ds[i].z && *v == ' ' && p->ds[i].seq[k] == '0')) {
rv += k * p->ds[i].b;
break;
}
}
if (k >= p->ds[i].n) {
if (tb != _tb)
free(tb);
return -1; /* Unknown digit */
}
}
if (tb != _tb)
free(tb);
return rv;
}
/* Working from the end of the string, */
/* return a pointer to the maximum number of characters */
/* that are legaly part of this alpha index. */
static char *find_start(alphix *p, char *ax) {
int i, k;
char *v;
v = ax + strlen(ax) - 1; /* start at last character */
for (i = 0; v >= ax && i < p->nd; v--, i++) { /* working backwards */
for (k = 0; k < p->ds[i].n; k++) { /* Locate */
if (*v == p->ds[i].seq[k])
break; /* Found */
}
if (k >= p->ds[i].n) /* Not found, so we are at the start */
break;
}
return v+1;
}
/* Convert from cooked index to raw index. */
/* Return -1 if out of range */
static int cookedtoraw(alphix *p, int ix) {
int i;
if (p->nr == 0)
return ix;
for (i = 0; i < p->nr; i++) {
if (ix >= p->rs[i].c0 && ix <= p->rs[i].c1) {
ix = ix - p->rs[i].c0 + p->rs[i].r0;
return ix;
}
}
return -1;
}
/* Convert from raw index to cooked index. */
/* Return -1 if out of range */
static int rawtocooked(alphix *p, int ix) {
int i;
if (p->nr == 0)
return ix;
for (i = 0; i < p->nr; i++) {
if (ix >= p->rs[i].r0 && ix <= p->rs[i].r1) {
ix = ix - p->rs[i].r0 + p->rs[i].c0;
return ix;
}
}
return -1;
}
/* Return the maximum possible length of count */
static int alphix_maxlen(alphix *p) {
return p->cmct;
}
/* Return the alpha index for the given index number (0 based) */
/* Return NULL on error */
/* (Free after use) */
static char *alphix_aix(alphix *p, int ix) {
if ((ix = cookedtoraw(p, ix)) < 0)
return NULL;
return torawix(p, ix);
}
/* Return the index number for the given alpha index */
int alphix_nix(alphix *p, char *ax) {
int rv;
if ((rv = fromanat(p, ax)) < 0)
return -1;
return rawtocooked(p, rv);
}
/* Destroy ourselves */
static void
alphix_del(alphix *p) {
int i;
for (i = 0; i < p->nd; i++)
free(p->ds[i].seq);
free (p->ds);
free (p->rs);
free (p);
}
/* Constructor: */
alphix *new_alphix(char *pattern) {
alphix *p;
char *pp = pattern;
int i;
if ((p = (alphix *)calloc(1,sizeof(alphix))) == NULL)
error("alphix: malloc failed");
p->maxlen = alphix_maxlen;
p->aix = alphix_aix;
p->nix = alphix_nix;
p->del = alphix_del;
/* We now need to parse the pattern to setup our index sequence: */
/* For all digits */
for (p->nd = 0; ; p->nd++) {
if (*pp == '\000' || *pp == ';') {
break;
}
/* We are starting a new digit sequence */
if (p->nd >= p->_nd) { /* Allocate space for current digit */
p->_nd = 2 + p->_nd;
if ((p->ds = (dig *)realloc(p->ds, p->_nd * sizeof(dig))) == NULL)
error ("alphix: realloc failed");
}
p->ds[p->nd].n = 0;
p->ds[p->nd].seq = NULL;
p->ds[p->nd]._n = 0;
p->ds[p->nd].z = 0;
/* For all symbols in this digit */
for (p->ds[p->nd].n = 0; ;) {
char c, c1, c2;
/* We are adding another character to the sequence */
if (*pp == '\000' || *pp == ';' || *pp == ',')
break; /* Done this digit */
if (pp[1] == '-' && pp[2] != '\000' && pp[2] != ';' && pp[2] != ',') {
c1 = pp[0];
c2 = pp[2];
pp += 3;
} else {
c1 = c2 = *pp;
pp++;
}
if (c1 == '@') {
c1 = '0';
p->ds[p->nd].z = 1;
}
if (c2 == '@') {
c2 = '0';
p->ds[p->nd].z = 1;
}
/* Expand digit sequence */
for (c = c1; c <= c2; c++) {
if (p->ds[p->nd].n >= p->ds[p->nd]._n) { /* Allocate space for next character */
p->ds[p->nd]._n = 20 + p->ds[p->nd]._n;
if ((p->ds[p->nd].seq = (char *)realloc(p->ds[p->nd].seq, p->ds[p->nd]._n * sizeof(char))) == NULL)
error ("alphix: realloc failed");
}
p->ds[p->nd].seq[p->ds[p->nd].n++] = c;
}
}
if (*pp == '\000' || *pp == ';')
continue;
pp++;
}
/* Compute the native maximum index count */
for (p->rmct = 1, i = 0; i < p->nd; i++) {
p->ds[i].b = p->rmct;
p->rmct *= p->ds[i].n;
}
/* Search for valid ranges */
if (*pp == ';') {
char *v, *tb, _tb[11];
pp++;
if (p->nd > 10) {
if ((tb = malloc((p->nd+1) * sizeof(char))) == NULL)
error ("alphix: malloc failed");
} else
tb = _tb;
/* For each range */
for (p->nr = 0; ; p->nr++) {
if (*pp == '\000' || *pp == ';')
break;
/* We are adding a new range */
if (p->nr >= p->_nr) { /* Allocate space for current range */
p->_nr = 2 + p->_nr;
if ((p->rs = (rngsq *)realloc(p->rs, p->_nr * sizeof(rngsq))) == NULL)
error ("alphix: realloc failed");
}
/* Locate the end of the range start */
for(v = tb; *pp != '\000' && *pp != '-' && *pp != ','; v++, pp++)
*v = *pp;
*v = '\000';
p->rs[p->nr].r0 = p->rs[p->nr].r1 = fromanat(p, tb);
if (p->rs[p->nr].r0 < 0)
error("alphix: range start definition error on '%s'",tb);
if (*pp != '-') { /* oops - bad definition */
error("alphix: range definition error - missing '-'");
}
/* Locate the end of the range end */
for(v = tb, pp++; *pp != '\000' && *pp != ','; v++, pp++)
*v = *pp;
*v = '\000';
p->rs[p->nr].r1 = fromanat(p, tb);
if (p->rs[p->nr].r1 < 0)
error("alphix: range end definition error on '%s'",tb);
if (p->rs[p->nr].r1 < p->rs[p->nr].r0) /* Hmm */
error("alphix: range definition error, end < start ");
/* Compute cooked index range */
p->rs[p->nr].c0 = 0;
p->rs[p->nr].c1 = p->rs[p->nr].r1 - p->rs[p->nr].r0;
if (p->nr > 0) {
int ofs = p->rs[p->nr-1].c1+1;
p->rs[p->nr].c0 += ofs;
p->rs[p->nr].c1 += ofs;
}
if (*pp == '\000' || *pp == ';')
continue;
pp++;
}
if (tb != _tb)
free(tb);
}
/* Compute cooked actual range */
p->cmct = p->rmct;
if (p->nr > 0) {
p->cmct = p->rs[p->nr-1].c1+1;
}
#ifdef NEVER
// ###########################################################
/* Debug stuff */
printf("~1 There are %d digits, %d ranges, raw max = %d, cooked max = %d\n",
p->nd,p->nr,p->rmct, p->maxlen(p));
/* Digit sequences */
for (i = 0; i < p->nd; i++) {
printf(" ~1 Digit %d has %d symbols\n",i,p->ds[i].n);
for (j = 0; j < p->ds[i].n; j++)
printf(" ~1 symbol %d = '%c'\n",j,p->ds[i].seq[j]);
}
/* Ranges */
for (i = 0; i < p->nr; i++) {
printf(" ~1 Range %d is raw %d - %d, cooked %d - %d\n",
i,p->rs[i].r0, p->rs[i].r1, p->rs[i].c0, p->rs[i].c1);
}
/* Itterate the sequence */
for (i = 0; i < p->cmct; i++) {
char *v;
v = p->aix(p, i);
printf("ix %d -> '%s'\n",i,v);
free(v);
}
// ###########################################################
#endif /* NEVER */
return p;
}
/* ==================================================================== */
/* Utility function: */
/* Given the strip and patch alphix objects, and order flag, */
/* Return a patch location. Free the returned string after use. */
/* Return NULL on error */
char *patch_location(
alphix *saix, /* Strip alpha index object */
alphix *paix, /* Patch alpha index object */
int ixord, /* Index order, 0 = strip then patch */
int six, /* Strip index */
int pix /* Patch index */
) {
char *sl, *pl, *rv;
int ll;
if ((sl = saix->aix(saix, six)) == NULL)
return NULL;
if ((pl = paix->aix(paix, pix)) == NULL) {
free (sl);
return NULL;
}
ll = strlen(sl) + strlen(pl) + 1;
if ((rv = malloc(ll * sizeof(char))) == NULL) {
free (pl);
free (sl);
return NULL;
}
if (ixord == 0) {
strcpy (rv, sl);
strcat (rv, pl);
} else {
strcpy (rv, pl);
strcat (rv, sl);
}
return rv;
}
/* ==================================================================== */
/* Utility function: */
/* Given the strip and patch alphix objects, and order flag, */
/* and a corresonding patch location string, return an index */
/* number suitable for sorting location strings. */
/* The sort order is in order of strips, then patches within each strip */
/* Return -1 on error */
int patch_location_order(
alphix *saix, /* Strip alpha index object */
alphix *paix, /* Patch alpha index object */
int ixord, /* Index order, 0 = strip then patch */
char *_ax /* Patch location string */
) {
char *ax; /* Copy of input string */
char *v;
alphix *rh; /* Least significant, right hand alphix */
alphix *lh; /* Most significant, left hand alphix */
int ri, li; /* Right hand and left hand index numbers */
if ((ax = malloc(strlen(_ax)+1)) == NULL)
return -1;
strcpy(ax,_ax);
if (ixord == 0) {
lh = saix; /* Strip is left hand */
rh = paix; /* Patch is right hand */
} else {
rh = saix; /* Strip is right hand */
lh = paix; /* Patch is left hand */
}
/* We need to identify the boundary between */
/* the right hand and left hand indexes. */
/* We assume that the sequences are distinguishable ... */
v = find_start(rh, ax);
if (*v == '\000') /* Nothing looks like a alphix */
return -1;
ri = rh->nix(rh, v);
*v = '\000';
li = lh->nix(lh, ax);
free(ax);
if (ri < 0 || li < 0)
return -1;
if (ixord == 0) /* Strip is left hand */
return li * rh->cmct + ri;
else
return ri * lh->cmct + li;
}