654 lines
15 KiB
C
654 lines
15 KiB
C
|
|
/*
|
|
* Argyll Color Management System
|
|
* Colorimeter Correction Matrix
|
|
*/
|
|
|
|
/*
|
|
* Author: Graeme W. Gill
|
|
* Date: 9/8/2010
|
|
*
|
|
* Copyright 2010 Graeme W. Gill
|
|
* All rights reserved.
|
|
*
|
|
* This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
|
|
* see the License2.txt file for licencing details.
|
|
*/
|
|
|
|
|
|
/*
|
|
* TTBD:
|
|
*/
|
|
|
|
#undef DEBUG
|
|
|
|
#define verbo stdout
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#ifndef SALONEINSTLIB
|
|
# include "aconfig.h"
|
|
# include "numlib.h"
|
|
# include "icc.h"
|
|
# include "plot.h" /* For debugging */
|
|
#else
|
|
# include "numsup.h"
|
|
# include "sa_conv.h"
|
|
#endif
|
|
#include "cgats.h"
|
|
#include "xspect.h"
|
|
#include "disptechs.h"
|
|
#include "ccss.h"
|
|
|
|
#ifdef NT /* You'd think there might be some standards.... */
|
|
# ifndef __BORLANDC__
|
|
# define stricmp _stricmp
|
|
# endif
|
|
#else
|
|
# define stricmp strcasecmp
|
|
#endif
|
|
|
|
/* Forward declarations */
|
|
static void free_ccss(ccss *p);
|
|
|
|
/* Utilities */
|
|
|
|
/* Method implimentations */
|
|
|
|
/* Write out the ccss to a CGATS format object */
|
|
/* Return nz on error */
|
|
static int create_ccss_cgats(
|
|
ccss *p, /* This */
|
|
cgats **pocg /* return CGATS structure */
|
|
) {
|
|
int i, j;
|
|
time_t clk = time(0);
|
|
struct tm *tsp = localtime(&clk);
|
|
char *atm = asctime(tsp); /* Ascii time */
|
|
cgats *ocg; /* CGATS structure */
|
|
int nsetel = 0;
|
|
cgats_set_elem *setel; /* Array of set value elements */
|
|
char buf[100];
|
|
|
|
atm[strlen(atm)-1] = '\000'; /* Remove \n from end */
|
|
|
|
/* Setup output cgats file */
|
|
ocg = new_cgats(); /* Create a CGATS structure */
|
|
ocg->add_other(ocg, "CCSS"); /* Type is Colorimeter Calibration Spectral Set */
|
|
ocg->add_table(ocg, tt_other, 0); /* Start the first table */
|
|
|
|
if (p->desc != NULL)
|
|
ocg->add_kword(ocg, 0, "DESCRIPTOR", p->desc,NULL);
|
|
|
|
if (p->orig != NULL)
|
|
ocg->add_kword(ocg, 0, "ORIGINATOR", p->orig, NULL);
|
|
else
|
|
ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll ccss", NULL);
|
|
if (p->crdate != NULL)
|
|
ocg->add_kword(ocg, 0, "CREATED",p->crdate, NULL);
|
|
else
|
|
ocg->add_kword(ocg, 0, "CREATED",atm, NULL);
|
|
|
|
if (p->disp)
|
|
ocg->add_kword(ocg, 0, "DISPLAY",p->disp, NULL);
|
|
|
|
if ((p->tech = strdup(disptech_get_id(p->dtech)->strid)) == NULL) {
|
|
sprintf(p->e.m, "strdup of tech string faile!");
|
|
ocg->del(ocg); /* Clean up */
|
|
return 2;
|
|
}
|
|
|
|
ocg->add_kword(ocg, 0, "TECHNOLOGY", p->tech, NULL);
|
|
|
|
if (p->disp == NULL && p->tech == NULL) {
|
|
sprintf(p->e.m, "write_ccss: ccss doesn't contain display or techology strings");
|
|
ocg->del(ocg);
|
|
return 1;
|
|
}
|
|
if (p->refrmode >= 0)
|
|
ocg->add_kword(ocg, 0, "DISPLAY_TYPE_REFRESH", p->refrmode ? "YES" : "NO", NULL);
|
|
if (p->sel != NULL)
|
|
ocg->add_kword(ocg, 0, "UI_SELECTORS", p->sel, NULL);
|
|
if (p->ref != NULL)
|
|
ocg->add_kword(ocg, 0, "REFERENCE",p->ref, NULL);
|
|
if (p->oem != 0)
|
|
ocg->add_kword(ocg, 0, "OEM","YES", NULL);
|
|
|
|
sprintf(buf,"%d", p->samples[0].spec_n);
|
|
ocg->add_kword(ocg, 0, "SPECTRAL_BANDS",buf, NULL);
|
|
sprintf(buf,"%f", p->samples[0].spec_wl_short);
|
|
ocg->add_kword(ocg, 0, "SPECTRAL_START_NM",buf, NULL);
|
|
sprintf(buf,"%f", p->samples[0].spec_wl_long);
|
|
ocg->add_kword(ocg, 0, "SPECTRAL_END_NM",buf, NULL);
|
|
sprintf(buf,"%f", p->samples[0].norm);
|
|
ocg->add_kword(ocg, 0, "SPECTRAL_NORM",buf, NULL);
|
|
|
|
/* Fields we want */
|
|
if (ocg->add_field(ocg, 0, "SAMPLE_ID", nqcs_t) < 0) {
|
|
sprintf(p->e.m, "cgats add_field SAMPLE_ID failed with '%s'!",ocg->e.m);
|
|
ocg->del(ocg); /* Clean up */
|
|
return 2;
|
|
}
|
|
nsetel += 1; /* For id */
|
|
|
|
/* Generate fields for spectral values */
|
|
for (i = 0; i < p->samples[0].spec_n; i++) {
|
|
int nm;
|
|
|
|
/* Compute nearest integer wavelength */
|
|
nm = (int)(p->samples[0].spec_wl_short + ((double)i/(p->samples[0].spec_n-1.0))
|
|
* (p->samples[0].spec_wl_long - p->samples[0].spec_wl_short) + 0.5);
|
|
|
|
sprintf(buf,"SPEC_%03d",nm);
|
|
if (ocg->add_field(ocg, 0, buf, r_t) < 0) {
|
|
sprintf(p->e.m, "cgats add_field %s failed with '%s'",buf,ocg->e.m);
|
|
ocg->del(ocg); /* Clean up */
|
|
return 2;
|
|
}
|
|
}
|
|
nsetel += p->samples[0].spec_n; /* Spectral values */
|
|
|
|
if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * nsetel)) == NULL) {
|
|
strcpy(p->e.m, "Malloc failed!");
|
|
ocg->del(ocg); /* Clean up */
|
|
return 2;
|
|
}
|
|
|
|
/* Write out the patch info to the output CGATS file */
|
|
for (i = 0; i < p->no_samp; i++) {
|
|
int k = 0;
|
|
|
|
sprintf(buf,"%d",i+1);
|
|
setel[k++].c = buf;
|
|
|
|
for (j = 0; j < p->samples[i].spec_n; j++)
|
|
setel[k++].d = p->samples[i].spec[j];
|
|
|
|
ocg->add_setarr(ocg, 0, setel);
|
|
}
|
|
free(setel);
|
|
|
|
if (pocg != NULL)
|
|
*pocg = ocg;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Write out the ccss to a CGATS format .ccss file */
|
|
/* Return nz on error */
|
|
static int write_ccss(
|
|
ccss *p, /* This */
|
|
char *outname /* Filename to write to */
|
|
) {
|
|
int rv;
|
|
cgats *ocg; /* CGATS structure */
|
|
|
|
if (p->no_samp < 3) {
|
|
strcpy(p->e.m, "Need at least three spectral samples");
|
|
return 1;
|
|
}
|
|
|
|
/* Create CGATS elements */
|
|
if ((rv = create_ccss_cgats(p, &ocg)) != 0) {
|
|
return rv;
|
|
}
|
|
|
|
/* Write it to file */
|
|
if (ocg->write_name(ocg, outname)) {
|
|
strcpy(p->e.m, ocg->e.m);
|
|
ocg->del(ocg); /* Clean up */
|
|
return 1;
|
|
}
|
|
ocg->del(ocg); /* Clean up */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* write to a CGATS .ccss file to a memory buffer. */
|
|
/* return nz on error, with message in err[] */
|
|
static int buf_write_ccss(
|
|
ccss *p,
|
|
unsigned char **buf, /* Return allocated buffer */
|
|
size_t *len /* Return length */
|
|
) {
|
|
int rv;
|
|
cgats *ocg; /* CGATS structure */
|
|
cgatsFile *fp;
|
|
|
|
if (p->no_samp < 3) {
|
|
strcpy(p->e.m, "Need at least three spectral samples");
|
|
return 1;
|
|
}
|
|
|
|
/* Create CGATS elements */
|
|
if ((rv = create_ccss_cgats(p, &ocg)) != 0) {
|
|
return rv;
|
|
}
|
|
|
|
if ((fp = new_cgatsFileMem(NULL, 0)) == NULL) {
|
|
strcpy(p->e.m, "new_cgatsFileMem failed");
|
|
return 2;
|
|
}
|
|
|
|
/* Write it to file */
|
|
if (ocg->write(ocg, fp)) {
|
|
strcpy(p->e.m, ocg->e.m);
|
|
ocg->del(ocg); /* Clean up */
|
|
fp->del(fp);
|
|
return 1;
|
|
}
|
|
|
|
/* Get the buffer the ccss has been written to */
|
|
if (fp->get_buf(fp, buf, len)) {
|
|
strcpy(p->e.m, "cgatsFileMem get_buf failed");
|
|
return 2;
|
|
}
|
|
|
|
ocg->del(ocg); /* Clean up */
|
|
fp->del(fp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Read in the ccss CGATS .ccss file from cgats */
|
|
/* Return nz on error */
|
|
static int read_ccss_cgats(
|
|
ccss *p, /* This */
|
|
cgats *icg /* input cgats structure */
|
|
) {
|
|
int i, j;
|
|
int ti, ii; /* Temporary CGATs index */
|
|
int spi[XSPECT_MAX_BANDS]; /* CGATS indexes for each wavelength */
|
|
xspect sp;
|
|
|
|
if (icg->ntables == 0 || icg->t[0].tt != tt_other || icg->t[0].oi != 0) {
|
|
sprintf(p->e.m, "read_ccss: Input file isn't a CCSS format file");
|
|
return 1;
|
|
}
|
|
if (icg->ntables != 1) {
|
|
sprintf(p->e.m, "Input file doesn't contain exactly one table");
|
|
return 1;
|
|
}
|
|
|
|
free_ccss(p);
|
|
|
|
if ((ti = icg->find_kword(icg, 0, "DESCRIPTOR")) >= 0) {
|
|
if ((p->desc = strdup(icg->t[0].kdata[ti])) == NULL) {
|
|
sprintf(p->e.m, "read_ccss: malloc failed");
|
|
return 2;
|
|
}
|
|
}
|
|
if ((ti = icg->find_kword(icg, 0, "ORIGINATOR")) >= 0) {
|
|
if ((p->orig = strdup(icg->t[0].kdata[ti])) == NULL) {
|
|
sprintf(p->e.m, "read_ccss: malloc failed");
|
|
return 2;
|
|
}
|
|
}
|
|
if ((ti = icg->find_kword(icg, 0, "CREATED")) >= 0) {
|
|
if ((p->crdate = strdup(icg->t[0].kdata[ti])) == NULL) {
|
|
sprintf(p->e.m, "read_ccss: malloc failed");
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
if ((ti = icg->find_kword(icg, 0, "DISPLAY")) >= 0) {
|
|
if ((p->disp = strdup(icg->t[0].kdata[ti])) == NULL) {
|
|
sprintf(p->e.m, "read_ccss: malloc failed");
|
|
return 2;
|
|
}
|
|
}
|
|
if ((ti = icg->find_kword(icg, 0, "TECHNOLOGY")) >= 0) {
|
|
if ((p->tech = strdup(icg->t[0].kdata[ti])) == NULL) {
|
|
sprintf(p->e.m, "read_ccss: malloc failed");
|
|
return 2;
|
|
}
|
|
/* Get disptech enum from standard TECHNOLOGY string */
|
|
p->dtech = disptech_get_strid(p->tech)->dtech;
|
|
}
|
|
if (p->disp == NULL && p->tech == NULL) {
|
|
sprintf(p->e.m, "read_ccss: Input file doesn't contain keyword DISPLAY or TECHNOLOGY");
|
|
return 1;
|
|
}
|
|
p->refrmode = -1;
|
|
if ((ti = icg->find_kword(icg, 0, "DISPLAY_TYPE_REFRESH")) >= 0) {
|
|
if (stricmp(icg->t[0].kdata[ti], "YES") == 0)
|
|
p->refrmode = 1;
|
|
else if (stricmp(icg->t[0].kdata[ti], "NO") == 0)
|
|
p->refrmode = 0;
|
|
}
|
|
|
|
if ((ti = icg->find_kword(icg, 0, "UI_SELECTORS")) >= 0) {
|
|
if ((p->sel = strdup(icg->t[0].kdata[ti])) == NULL) {
|
|
sprintf(p->e.m, "read_ccss: malloc failed");
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
if ((ti = icg->find_kword(icg, 0, "REFERENCE")) >= 0) {
|
|
if ((p->ref = strdup(icg->t[0].kdata[ti])) == NULL) {
|
|
sprintf(p->e.m, "read_ccss: malloc failed");
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
if ((ti = icg->find_kword(icg, 0, "OEM")) >= 0) {
|
|
if (stricmp(icg->t[0].kdata[ti], "YES") == 0)
|
|
p->oem = 1;
|
|
else if (stricmp(icg->t[0].kdata[ti], "NO") == 0)
|
|
p->oem = 0;
|
|
} else {
|
|
p->oem = 0;
|
|
}
|
|
|
|
if ((ii = icg->find_kword(icg, 0, "SPECTRAL_BANDS")) < 0) {
|
|
sprintf(p->e.m,"Input file doesn't contain keyword SPECTRAL_BANDS");
|
|
return 1;
|
|
}
|
|
sp.spec_n = atoi(icg->t[0].kdata[ii]);
|
|
if ((ii = icg->find_kword(icg, 0, "SPECTRAL_START_NM")) < 0) {
|
|
sprintf(p->e.m,"Input file doesn't contain keyword SPECTRAL_START_NM");
|
|
return 1;
|
|
}
|
|
sp.spec_wl_short = atof(icg->t[0].kdata[ii]);
|
|
if ((ii = icg->find_kword(icg, 0, "SPECTRAL_END_NM")) < 0) {
|
|
sprintf(p->e.m,"Input file doesn't contain keyword SPECTRAL_END_NM");
|
|
return 1;
|
|
}
|
|
sp.spec_wl_long = atof(icg->t[0].kdata[ii]);
|
|
|
|
if ((ii = icg->find_kword(icg, 0, "SPECTRAL_NORM")) < 0) {
|
|
sp.norm = 1.0; /* Older versions don't have SPECTRAL_NORM */
|
|
} else {
|
|
sp.norm = atof(icg->t[0].kdata[ii]);
|
|
}
|
|
|
|
/* Find the fields for spectral values */
|
|
for (j = 0; j < sp.spec_n; j++) {
|
|
char buf[100];
|
|
int nm;
|
|
|
|
/* Compute nearest integer wavelength */
|
|
nm = (int)(sp.spec_wl_short + ((double)j/(sp.spec_n-1.0))
|
|
* (sp.spec_wl_long - sp.spec_wl_short) + 0.5);
|
|
|
|
sprintf(buf,"SPEC_%03d",nm);
|
|
|
|
if ((spi[j] = icg->find_field(icg, 0, buf)) < 0) {
|
|
sprintf(p->e.m,"Input file doesn't contain field %s",buf);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if ((p->no_samp = icg->t[0].nsets) < 3) {
|
|
sprintf(p->e.m, "Input file doesn't contain at least three spectral samples");
|
|
p->no_samp = 0;
|
|
return 1;
|
|
}
|
|
|
|
/* Allocate spectral array */
|
|
if ((p->samples = (xspect *)malloc(sizeof(xspect) * p->no_samp)) == NULL) {
|
|
strcpy(p->e.m, "Malloc failed!");
|
|
p->no_samp = 0;
|
|
return 2;
|
|
}
|
|
|
|
/* Fill sample array with the data */
|
|
for (i = 0; i < p->no_samp; i++) {
|
|
|
|
XSPECT_COPY_INFO(&p->samples[i], &sp);
|
|
|
|
/* Read the spectral values for this patch */
|
|
for (j = 0; j < sp.spec_n; j++) {
|
|
p->samples[i].spec[j] = *((double *)icg->t[0].fdata[i][spi[j]]);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Read in the ccss CGATS .ccss file */
|
|
/* Return nz on error */
|
|
static int read_ccss(
|
|
ccss *p, /* This */
|
|
char *inname /* Filename to read from */
|
|
) {
|
|
int rv;
|
|
cgats *icg; /* input cgats structure */
|
|
|
|
/* Open and look at the .ccss file */
|
|
if ((icg = new_cgats()) == NULL) { /* Create a CGATS structure */
|
|
sprintf(p->e.m, "read_ccss: new_cgats() failed");
|
|
return 2;
|
|
}
|
|
icg->add_other(icg, "CCSS"); /* our special type is Model Printer Profile */
|
|
|
|
if (icg->read_name(icg, inname)) {
|
|
strcpy(p->e.m, icg->e.m);
|
|
icg->del(icg);
|
|
return 1;
|
|
}
|
|
|
|
if ((rv = read_ccss_cgats(p, icg)) != 0) {
|
|
icg->del(icg); /* Clean up */
|
|
return rv;
|
|
}
|
|
|
|
icg->del(icg); /* Clean up */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Read in the ccss CGATS .ccss file from a memory buffer */
|
|
/* Return nz on error */
|
|
static int buf_read_ccss(
|
|
ccss *p, /* This */
|
|
unsigned char *buf,
|
|
size_t len
|
|
) {
|
|
int rv;
|
|
cgatsFile *fp;
|
|
cgats *icg; /* input cgats structure */
|
|
|
|
if ((fp = new_cgatsFileMem(buf, len)) == NULL) {
|
|
strcpy(p->e.m, "new_cgatsFileMem failed");
|
|
return 2;
|
|
}
|
|
|
|
/* Open and look at the .ccss file */
|
|
if ((icg = new_cgats()) == NULL) { /* Create a CGATS structure */
|
|
sprintf(p->e.m, "read_ccss: new_cgats() failed");
|
|
fp->del(fp);
|
|
return 2;
|
|
}
|
|
icg->add_other(icg, "CCSS"); /* our special type is Model Printer Profile */
|
|
|
|
if (icg->read(icg, fp)) {
|
|
strcpy(p->e.m, icg->e.m);
|
|
icg->del(icg);
|
|
fp->del(fp);
|
|
return 1;
|
|
}
|
|
fp->del(fp);
|
|
|
|
if ((rv = read_ccss_cgats(p, icg)) != 0) {
|
|
icg->del(icg); /* Clean up */
|
|
return rv;
|
|
}
|
|
|
|
icg->del(icg); /* Clean up */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Set the contents of the ccss. return nz on error. */
|
|
/* (Copy all the values) */
|
|
static int set_ccss(ccss *p,
|
|
char *orig, /* Originator (May be NULL) */
|
|
char *crdate, /* Creation date in ctime() format (May be NULL) */
|
|
char *desc, /* General description (optional) */
|
|
char *disp, /* Display make and model (optional) */
|
|
disptech dtech, /* Display technology enum */
|
|
int refrmode, /* Display refresh mode, -1 = unknown, 0 = n, 1 = yes */
|
|
char *sel, /* UI selector characters - NULL for none */
|
|
char *refd, /* Reference spectrometer description (optional) */
|
|
int oem, /* NZ if OEM source */
|
|
xspect *samples, /* Arry of spectral samples. All assumed to be same dim as first */
|
|
int no_samp /* Number of spectral samples */
|
|
) {
|
|
int i;
|
|
|
|
free_ccss(p);
|
|
if (orig != NULL) {
|
|
if ((p->orig = strdup(orig)) == NULL) {
|
|
sprintf(p->e.m, "set_ccss: malloc orig failed");
|
|
return 2;
|
|
}
|
|
}
|
|
if (desc != NULL) {
|
|
if ((p->desc = strdup(desc)) == NULL) {
|
|
sprintf(p->e.m, "set_ccss: malloc desc failed");
|
|
return 2;
|
|
}
|
|
}
|
|
if (crdate != NULL) {
|
|
if ((p->crdate = strdup(crdate)) == NULL) {
|
|
sprintf(p->e.m, "set_ccss: malloc crdate failed");
|
|
return 2;
|
|
}
|
|
}
|
|
if (disp != NULL) {
|
|
if ((p->disp = strdup(disp)) == NULL) {
|
|
sprintf(p->e.m, "set_ccss: malloc disp failed");
|
|
return 2;
|
|
}
|
|
}
|
|
p->dtech = dtech;
|
|
p->refrmode = refrmode;
|
|
if (sel != NULL) {
|
|
if ((p->sel = strdup(sel)) == NULL) {
|
|
sprintf(p->e.m, "set_ccss: malloc sel failed");
|
|
return 2;
|
|
}
|
|
}
|
|
if (refd != NULL) {
|
|
if ((p->ref = strdup(refd)) == NULL) {
|
|
sprintf(p->e.m, "set_ccss: malloc ref failed");
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
p->oem = oem;
|
|
|
|
if (p->samples != NULL) {
|
|
free(p->samples);
|
|
p->samples = NULL;
|
|
}
|
|
|
|
if ((p->no_samp = no_samp) < 3) {
|
|
strcpy(p->e.m, "Must be at least three spectral samples");
|
|
p->no_samp = 0;
|
|
return 1;
|
|
}
|
|
|
|
/* Allocate spectral array */
|
|
if ((p->samples = (xspect *)malloc(sizeof(xspect) * p->no_samp)) == NULL) {
|
|
strcpy(p->e.m, "Malloc failed!");
|
|
p->no_samp = 0;
|
|
return 2;
|
|
}
|
|
|
|
/* And copy all the values */
|
|
for (i = 0; i < p->no_samp; i++) {
|
|
p->samples[i] = samples[i];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Free the contents */
|
|
static void free_ccss(ccss *p) {
|
|
if (p != NULL) {
|
|
if (p->desc != NULL)
|
|
free(p->desc);
|
|
p->desc = NULL;
|
|
if (p->orig != NULL)
|
|
free(p->orig);
|
|
p->orig = NULL;
|
|
if (p->crdate != NULL)
|
|
free(p->crdate);
|
|
p->crdate = NULL;
|
|
if (p->disp != NULL)
|
|
free(p->disp);
|
|
p->disp = NULL;
|
|
if (p->tech != NULL)
|
|
free(p->tech);
|
|
p->tech = NULL;
|
|
if (p->sel != NULL)
|
|
free(p->sel);
|
|
p->sel = NULL;
|
|
if (p->ref != NULL)
|
|
free(p->ref);
|
|
p->ref = NULL;
|
|
if (p->samples != NULL)
|
|
free(p->samples);
|
|
p->samples = NULL;
|
|
p->no_samp = 0;
|
|
}
|
|
}
|
|
|
|
/* Delete it */
|
|
static void del_ccss(ccss *p) {
|
|
if (p != NULL) {
|
|
free_ccss(p);
|
|
free(p);
|
|
}
|
|
}
|
|
|
|
/* Allocate a new, uninitialised ccss */
|
|
/* Note thate black and white points aren't allocated */
|
|
ccss *new_ccss(void) {
|
|
ccss *p;
|
|
|
|
if ((p = (ccss *)calloc(1, sizeof(ccss))) == NULL)
|
|
return NULL;
|
|
|
|
/* Init method pointers */
|
|
p->del = del_ccss;
|
|
p->set_ccss = set_ccss;
|
|
p->write_ccss = write_ccss;
|
|
p->buf_write_ccss = buf_write_ccss;
|
|
p->read_ccss = read_ccss;
|
|
p->buf_read_ccss = buf_read_ccss;
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|