
/* 
 * ArgyllCMS.
 * Apply a device calibration to an ICC profile.
 *
 * Author:  Graeme W. Gill
 * Date:    2009/8/31
 * Version: 1.00
 *
 * Copyright 2009 Graeme W. Gill
 *
 * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
 * see the License.txt file for licencing details.
 */

/* TTBD:
 *
 *		Would be good if remove restores shared curves, rather than
 *		leaving duplicates.
 *
 * 		Could stash the whole cal file in a text tag.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include "copyright.h"
#include "aconfig.h"
#include "cgats.h"
#include "numlib.h"
#include "rspl.h"
#include "conv.h"
#include "xicc.h"

#undef DEBUG

#ifdef DEBUG
#undef DBG
#define DBG(xxx) printf xxx ;
#else
#undef DBG
#define DBG(xxx) 
#endif


typedef icmLut1 icmLut;			/* Will only work for icSigLut16Type & icSigLut8Type */

void usage(char *diag, ...) {
	int i;
	fprintf(stderr,"Apply device calibration to an ICC profile, Version %s\n",ARGYLL_VERSION_STR);
	fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
	if (diag != NULL) {
		va_list args;
		fprintf(stderr,"  Diagnostic: ");
		va_start(args, diag);
		vfprintf(stderr, diag, args);
		va_end(args);
		fprintf(stderr,"\n");
	}
	fprintf(stderr,"usage: %s [-options] [calfile.cal] inprof%s [outprof%s]\n","applycal",ICC_FILE_EXT,ICC_FILE_EXT);
	fprintf(stderr," -v              Verbose mode\n");
	fprintf(stderr," -a              Apply or re-apply calibration (default)\n");
	fprintf(stderr," -u              Remove calibration\n");
	fprintf(stderr," -c              Check calibration\n");
	fprintf(stderr," calfile.cal     Calibration file to apply\n");
	fprintf(stderr," inprof%s      ICC profile to read\n",ICC_FILE_EXT);
	fprintf(stderr," outprof%s     modified ICC profile to write\n",ICC_FILE_EXT);
	exit(2);
}

/* A primary signature and its backup */
struct _tagsigpair {
	icTagSignature prim;
	icTagSignature back;
	int chan;
	int dir;			/* 0 = none or out, 1 = in */
}; typedef struct _tagsigpair tagsigpair;

int
main(int argc, char *argv[]) {
	int fa,nfa;				/* argument we're looking at */
	char cal_name[MAXNAMEL+1];
	char in_name[MAXNAMEL+1];
	char out_name[MAXNAMEL+1];
	xcal *cal = NULL;			/* Calibration to apply */
	icmFile *rd_fp = NULL, *wr_fp = NULL;
	icmErr err = { 0, { '\000'} };
	icc *icco;
	int apply = 1;
	int remove = 0;
	int check = 0;
	int verb = 0;
	int found = -1;
	int rv;


	if (argc < 3)
		usage("Too few arguments");

	/* Process the arguments */
	for(fa = 1;fa < argc;fa++) {
		nfa = fa;					/* skip to nfa if next argument is used */
		if (argv[fa][0] == '-')	{	/* Look for any flags */
			char *na = NULL;		/* next argument after flag, null if none */

			if (argv[fa][2] != '\000')
				na = &argv[fa][2];		/* next is directly after flag */
			else {
				if ((fa+1) < argc) {
					if (argv[fa+1][0] != '-') {
						nfa = fa + 1;
						na = argv[nfa];		/* next is seperate non-flag argument */
					}
				}
			}

			if (argv[fa][1] == '?')
				usage("Usage requested");

			else if (argv[fa][1] == 'v' || argv[fa][1] == 'V')
				verb = 1;

			else if (argv[fa][1] == 'a' || argv[fa][1] == 'A') {
				apply = 1;
				remove = 0;
				check = 0;
			}

			else if (argv[fa][1] == 'u' || argv[fa][1] == 'U') {
				apply = 0;
				remove = 1;
				check = 0;
			}

			else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') {
				apply = 0;
				remove = 0;
				check = 1;
			}

			else
				usage("Unknown option '%s'",argv[fa][1]);

		} else
			break;
	}

	if (apply) {
		if (fa >= argc || argv[fa][0] == '-') usage("Missing calibration filename");
		strncpy(cal_name,argv[fa++],MAXNAMEL); cal_name[MAXNAMEL] = '\000';
	}

	if (fa >= argc || argv[fa][0] == '-') usage("Missing input profile filename");
	strncpy(in_name,argv[fa++],MAXNAMEL); in_name[MAXNAMEL] = '\000';


	if (apply || remove) {
		if (fa >= argc || argv[fa][0] == '-') usage("Missing output profile name");
		strncpy(out_name,argv[fa++],MAXNAMEL); out_name[MAXNAMEL] = '\000';
	}
	if (fa < argc) usage("Extra argument '%s'",argv[fa]);

	DBG(("apply %d, remove %d, check %d, calfile '%s', infile '%s', outfile '%s'\n",apply,remove,check,cal_name,in_name,out_name));

	if (apply) {
		/* Open up the calibration file */
		if ((cal = new_xcal()) == NULL)
			error("new_xcal failed");
		if ((cal->read(cal, cal_name)) != 0)
			error("%s",cal->e.m);
	}

	/* Open up the profile for reading */
	if ((rd_fp = new_icmFileStd_name(&err,in_name,"r")) == NULL)
		error ("Can't open file '%s' (0x%x, '%s')",in_name,err.c,err.m);

	if ((icco = new_icc(&err)) == NULL)
		error ("Creation of ICC object failed (0x%x, '%s')",err.c,err.m);

	/* Read header etc. */
	if ((rv = icco->read(icco,rd_fp,0)) != 0)
		error ("%d, %s",rv,icco->e.m);

	/* Read every tag */
	if (icco->read_all_tags(icco) != 0) {
		error("Unable to read all tags: %d, %s",icco->e.c,icco->e.m);
	}

	rd_fp->del(rd_fp);

	/* ======================================= */
	{
		tagsigpair sigs[] = {			/* Signatures to modify and their backups */
			{ icSigProfileDescriptionTag, 	icmMakeTag('A','R','0','T'), 0, 0 },
			{ icSigAToB0Tag,				icmMakeTag('A','R','1','0'), 0, 1 },
			{ icSigAToB1Tag,				icmMakeTag('A','R','2','0'), 0, 1 },
			{ icSigAToB2Tag,				icmMakeTag('A','R','3','0'), 0, 1 },
			{ icSigBToA0Tag,				icmMakeTag('A','R','4','0'), 0, 0 },
			{ icSigBToA1Tag,				icmMakeTag('A','R','5','0'), 0, 0 },
			{ icSigBToA2Tag,				icmMakeTag('A','R','6','0'), 0, 0 },
			{ icSigRedTRCTag,				icmMakeTag('A','R','7','0'), 0, 0 },
			{ icSigGreenTRCTag,				icmMakeTag('A','R','7','1'), 1, 0 },
			{ icSigBlueTRCTag,				icmMakeTag('A','R','7','2'), 2, 0 },
			{ icSigGrayTRCTag, 				icmMakeTag('A','R','8','0'), 0, 0 },
			{ 0, 0, 0 }
		};
		tagsigpair linksigs[] = {		/* Signatures to modify */
			{ icSigProfileDescriptionTag, 	icmMakeTag('A','R','0','T'), 0 },
			{ icSigAToB0Tag,				icmMakeTag('A','R','9','1'), 1 },
			{ icSigAToB0Tag,				icmMakeTag('A','R','9','0'), 0 },
			{ 0, 0, 0 }
		};
		tagsigpair *ssigp, *sigp;
		int ntags = 0;						/* Number of tags done */
		icmBase *dtags[50];					/* Address of tags done */
		int inp = 0;						/* NZ for input calibration */
		unsigned int i, j;

		if (icco->header->deviceClass == icSigInputClass && cal->devclass != icSigInputClass) {
			warning("Non-input calibration being applied to an input profile");
		}


		if (check) {
			DBG(("Checking...\n"));

			for (sigp = linksigs; sigp->prim != 0; sigp++) {
				icmBase *primt;

				if (sigp->prim != icSigProfileDescriptionTag)
					continue;

				if ((primt = icco->read_tag(icco, sigp->prim)) == NULL)
					error("Can't find icSigProfileDescriptionTag in profile"); 

					/* See if we have a backup */
				if (icco->read_tag(icco, sigp->back) != NULL)
					found = 1;
				else
					found = 0;
			}
			DBG(("found = %d\n",found));
			if (found < 0)
				error("Internal, didn't look for ProfileDescriptionTag");

			if (verb) {
				if (found)
					printf("Profile has had calibration applied\n");
				else
					printf("Profile has NOT had calibration applied\n");
			}
		} else if (apply) {
			DBG(("Applying...\n"));

			if (icco->header->deviceClass == icSigInputClass
			 || icco->header->deviceClass == icSigDisplayClass
			 || icco->header->deviceClass == icSigOutputClass) {

				DBG(("Input, display or output profile\n"));

				/* Check colorspace is compatible */
				if (cal->colspace != icco->header->colorSpace)
					error("Calibration space %s doesn't match profile %s",
					             icm2str(icmColorSpaceSig, cal->colspace),
					             icm2str(icmColorSpaceSig, icco->header->colorSpace));

				ssigp = sigs;
				/* Note the cal direction */
				if (cal->devclass == icSigInputClass)
					inp = 1;
				else
					inp = 0;

			} else if (icco->header->deviceClass == icSigLinkClass) {
				DBG(("Device link profile\n"));

				/* Check colorspace is compatible */
				if (cal->colspace != icco->header->pcs)
					error("Calibration space %s doesn't match profile %s",
					             icm2str(icmColorSpaceSig, cal->colspace),
					             icm2str(icmColorSpaceSig, icco->header->pcs));
				ssigp = linksigs;
				/* Note the cal direction */
				if (cal->devclass == icSigInputClass)
					inp = 1;
				else
					inp = 0;
			} else {
				error("Can't apply calibration to profile of class %s",
				      icm2str(icmProfileClassSig, icco->header->deviceClass));
			}
			DBG(("input calibration = %d\n",inp));

			/* First pass is to duplicate any linked TRCTags */
			for (sigp = ssigp; sigp->prim != 0; sigp++) { /* Process each tag */
				icmBase *primt;

				DBG(("looking for tag '%s'\n",icm2str(icmTagSig, sigp->prim)));
				if ((primt = icco->read_tag(icco, sigp->prim)) == NULL) {
					if (sigp->prim == icSigProfileDescriptionTag)
						error("Can't find icSigProfileDescriptionTag in profile"); 
					continue;		/* Don't have this tag */
				}

				/* XXXXTRCTag */
			    if (primt->ttype == icSigCurveType) {
					icmCurve *wo, *ro = (icmCurve *)primt;

					/* See if we have a backup */
					if ((wo = (icmCurve *)icco->read_tag(icco, sigp->back)) == NULL) {

						/* If tag is shared, we need to separated it */
						if (ro->refcount > 1) {
							DBG(("tag is shared, so separate it\n"));
							if ((wo = (icmCurve *)icco->add_tag(
							           icco, sigp->back, icSigCurveType)) == NULL) 
								error("Failed to create tag '%s'\n", icm2str(icmTagSig, sigp->back));
							wo->ctype = ro->ctype;
							wo->count = ro->count;
							wo->allocate(wo);	/* Allocate space */
							for (i = 0; i < wo->count; i++)	/* Copy the curve */
								wo->data[i] = ro->data[i];

							if (icco->delete_tag(icco, sigp->prim))
								error("Failed to delete tag '%s'",icm2str(icmTagSig, sigp->prim));
							if (icco->rename_tag(icco, sigp->back, sigp->prim))
								error("Failed to rename tag '%s' to '%s'",
								          icm2str(icmTagSig, sigp->back),
								          icm2str(icmTagSig, sigp->prim));
						}
					} else {
						if (ro->refcount > 1)
							error("Found tag %s has backup, but is shared",icm2str(icmTagSig,sigp->prim));
					}
				}
			}

			/* Second pass we create backups and calibrated curves */
			for (sigp = ssigp; sigp->prim != 0; sigp++) { /* Process each tag */
				icmBase *primt;

				DBG(("looking for tag '%s'\n",icm2str(icmTagSig, sigp->prim)));
				if ((primt = icco->read_tag(icco, sigp->prim)) == NULL) {
					if (sigp->prim == icSigProfileDescriptionTag)
						error("Can't find icSigProfileDescriptionTag in profile"); 
					continue;		/* Don't have this tag */
				}

				/* icSigProfileDescriptionTag type */
				if (ISCOMMONTEXT(primt->ttype)) {
					icmCommonTextDescription *wo, *ro = (icmCommonTextDescription *)primt;
					char *extra = NULL;

					/* See if we've done this tag before due to links */
					for (i = 0; i < ntags; i++) {
						if (dtags[i] == primt)
							break;				/* Yes */
					}
					if (i < ntags) {
						DBG(("Found this tag before (link)\n"));
						continue;				/* Skip tag */
					}
					if (ntags >= 50)			/* Impossible */
						error("Internal, run out of previuos tags space");
					dtags[ntags++] = primt;		/* Remember this one */

					DBG(("ProfileDescriptionTag\n"));
					DBG(("Looking for backup tag '%s'\n",icm2str(icmTagSig, sigp->back)));

					/* See if we have a backup */
					if ((wo = (icmCommonTextDescription *)icco->read_tag(icco, sigp->back)) == NULL) {
						DBG(("No backup, creating one\n"));
						/* No, so create one */
						if ((wo = (icmCommonTextDescription *)icco->add_tag(
						           icco, sigp->back, icmSigCommonTextDescriptionType)) == NULL) 
							error("Failed to create tag '%s' type '%s'\n", icm2str(icmTagSig, sigp->back),icm2str(icmTypeSig,icmSigCommonTextDescriptionType));

						wo->count = ro->count;
						wo->allocate(wo);	/* Allocate space */
						strcpy(wo->desc, ro->desc);		/* Copy the string in */
						/* Hmm. what should we do with Unicode and script ? */
					} else {
						DBG(("Found backup\n"));
					}

					if (cal->xpi.profDesc != NULL)
						extra = cal->xpi.profDesc;
					else
						extra = cal_name;

					ro->count = strlen(wo->desc) + 3 + strlen(extra) + 3;
					ro->allocate(ro);	/* Allocate space */
					strcpy(ro->desc, wo->desc);
					strcat(ro->desc, " [ ");
					strcat(ro->desc, extra);
					strcat(ro->desc, " ]");

					DBG(("Set tag contents to '%s' len %" PFSTPREC "d size %d\n",ro->desc,strlen(ro->desc)+1,ro->count));

				/* icSigAToBXTag or icSigBToAXTag */
				} else if (primt->ttype == icSigLut8Type
				        || primt->ttype == icSigLut16Type) {
					icmLut *ro = (icmLut *)primt;	/* Modified Lut */

					/* See if we've done this tag before due to links */
					for (i = 0; i < ntags; i++) {
						if (dtags[i] == primt)
							break;				/* Yes */
					}
					if (i < ntags) {
						DBG(("Found this tag before (link)\n"));
						continue;				/* Skip tag */
					}
					if (ntags >= 50)			/* Impossible */
						error("Internal, run out of previous tags space");
					dtags[ntags++] = primt;		/* Remember this one */

					DBG(("Lut8 or Lut16\n"));

					if (sigp->dir) {
						/* Apply calibration to the input table */
						for (j = 0; j < ro->inputChan; j++) {
							icTagSignature bsig = sigp->back;
							icmCurve *wo;					/* Backup of original */

							/* Create a tag per channel */
							bsig += j; 

							DBG(("Looking for backup tag '%s'\n",icm2str(icmTagSig, bsig)));

							/* See if we have a backup */
							if ((wo = (icmCurve *)icco->read_tag(icco, bsig)) == NULL) {
								DBG(("No backup, creating one\n"));
								/* No, so create one */
								if ((wo = (icmCurve *)icco->add_tag(
								           icco, bsig, icSigCurveType)) == NULL) 
									error("Failed to create tag '%s'\n", icm2str(icmTagSig, bsig));
								wo->ctype = icmCurveSpec; 		/* Specified version */
								wo->count = ro->inputEnt;
								wo->allocate(wo);	/* Allocate space */
								for (i = 0; i < wo->count; i++)	/* Copy the curve */
									wo->data[i] = ro->pe_ic[j]->data[i];
							} else {
								DBG(("Found backup\n"));
							}

							/* Create new input curve from inv cal + orginal curve */
							for (i = 0; i < ro->inputEnt; i++) {
								double val;
								val = i/(ro->inputEnt-1.0);
								if (inp)
									val = cal->interp_ch(cal, j, val);		/* Do calibration */
								else
									val = cal->inv_interp_ch(cal, j, val);	/* Undo calibration */
								wo->lookup_fwd(wo, &val, &val);		/* Original curve */
								ro->pe_ic[j]->data[i] = val;
							}
							DBG(("Created calibrated input curve\n"));
						}
					} else {
						/* Apply calibration to the output table */
						for (j = 0; j < ro->outputChan; j++) {
							icTagSignature bsig = sigp->back;
							icmCurve *wo;					/* Backup of original */

							/* Create a tag per channel */
							bsig += j; 

							DBG(("Looking for backup tag '%s'\n",icm2str(icmTagSig, bsig)));
							/* See if we have a backup */
							if ((wo = (icmCurve *)icco->read_tag(icco, bsig)) == NULL) {
								DBG(("No backup, creating one\n"));
								/* No, so create one */
								if ((wo = (icmCurve *)icco->add_tag(
								           icco, bsig, icSigCurveType)) == NULL) 
									error("Failed to create tag '%s'\n", icm2str(icmTagSig, bsig));
						
								wo->ctype = icmCurveSpec; 		/* Specified version */
								wo->count = ro->outputEnt;
								wo->allocate(wo);	/* Allocate space */
								for (i = 0; i < wo->count; i++)	/* Copy the curve */
									wo->data[i] = ro->pe_oc[j]->data[i];
							} else {
								DBG(("Found backup\n"));
							}

							/* Create new output curve from original + cal */
							for (i = 0; i < ro->outputEnt; i++) {
								double val;
								val = i/(ro->outputEnt-1.0);
								wo->lookup_fwd(wo, &val, &val);			/* Original curve */
								if (inp)
									val = cal->interp_ch(cal, j, val);		/* Undo calibration */
								else
									val = cal->interp_ch(cal, j, val);		/* Do calibration */
								ro->pe_oc[j]->data[i] = val;
							}
							DBG(("Created calibrated output curve\n"));
						}
					}

				/* XXXXTRCTag */
				} else if (primt->ttype == icSigCurveType) {
					icmCurve *wo, *ro = (icmCurve *)primt;

					DBG(("CurveType\n"));

					DBG(("Looking for backup tag '%s'\n",icm2str(icmTagSig, sigp->back)));

					/* See if we have a backup */
					if ((wo = (icmCurve *)icco->read_tag(icco, sigp->back)) == NULL) {

						DBG(("No backup, creating one\n"));
						/* No, so create one */
						if ((wo = (icmCurve *)icco->add_tag(
						           icco, sigp->back, icSigCurveType)) == NULL) 
							error("Failed to create tag '%s'\n", icm2str(icmTagSig, sigp->back));
				
						wo->ctype = ro->ctype;
						wo->count = ro->count;
						wo->allocate(wo);	/* Allocate space */
						for (i = 0; i < wo->count; i++)	/* Copy the curve */
							wo->data[i] = ro->data[i];

						/* Change type & size of ro if necessary */
						if (ro->ctype != icmCurveSpec || wo->count < 256) {
							ro->ctype = icmCurveSpec;
							ro->count = 256;
							ro->allocate(ro);	/* Allocate space */
						} 
					} else {
						DBG(("Found backup\n"));
					}

					/* Create new forward direction curve from cal + orginal curve */
					j = sigp->chan;
					for (i = 0; i < ro->count; i++) {
						double val;
						val = i/(ro->count-1.0);
						val = cal->inv_interp_ch(cal, j, val);	/* Inverse output calibration */
						wo->lookup_fwd(wo, &val, &val);			/* Original curve */
						ro->data[i] = val;
					}
					DBG(("Created calibrated %s curve for chan %d\n",inp ? "input" : "output",j));
				} else {
					error("Tag %s is type %s we don't know how to handle",
					      icm2str(icmTagSig, sigp->prim),
					      icm2str(icmTypeSig, primt->ttype));
				}
			}

		} else if (remove) {
			int k;
			DBG(("Removing...\n"));
			for (k = 0; k < 2; k++) {
				if (k == 0)
					ssigp = sigs;
				else if (k == 1)
					ssigp = linksigs;

				for (sigp = ssigp; sigp->prim != 0; sigp++) { /* Process each tag */
					icmBase *backt, *primt;

					DBG(("Looking for baclup tag '%s'\n",icm2str(icmTagSig, sigp->back)));
					if ((backt = icco->read_tag(icco, sigp->back)) == NULL)
						continue;		/* Don't have this backup tag */
					
					DBG(("Looking for primary tag '%s'\n",icm2str(icmTagSig, sigp->prim)));
					if ((primt = icco->read_tag(icco, sigp->prim)) == NULL) {
						error("Can't find primary tag %s for backup %s",
						                  icm2str(icmTagSig, sigp->prim),
						                  icm2str(icmTagSig, sigp->back));
					}

					/* icSigProfileDescriptionTag type */
					if (ISCOMMONTEXT(primt->ttype)) {
						icmCommonTextDescription *wo, *ro = (icmCommonTextDescription *)primt;

						DBG(("ProfileDescriptionTag\n"));

						wo = (icmCommonTextDescription *)backt;

						/* Restore primary table */
						ro->count = wo->count;
						ro->allocate(ro);	/* Reallocate space */
						strcpy(ro->desc, wo->desc);		/* Restore description */

						/* delete backup */
						if (icco->delete_tag(icco, sigp->back))
							error("Failed to delete tag '%s'",icm2str(icmTagSig, sigp->prim));
						DBG(("Restored primary and deleted backup\n"));

					/* icSigAToBXTag or icSigBToAXTag */
					} else if (primt->ttype == icSigLut8Type
					        || primt->ttype == icSigLut16Type) {
						icmLut *ro = (icmLut *)primt;	/* Modified Lut */

						if (sigp->dir) {
							/* Restore the input table */
							for (j = 0; j < ro->inputChan; j++) {
								icTagSignature bsig = sigp->back;
								icmCurve *wo;					/* Backup of original */

								/* Create a tag per channel */
								bsig += j; 

								DBG(("Looking for backup tag '%s'\n",icm2str(icmTagSig, bsig)));

								/* See if we have a backup */
								if ((wo = (icmCurve *)icco->read_tag(icco, bsig)) == NULL)
									error("Can't find original table data in tag %s",
									                 icm2str(icmTagSig, sigp->back));

								/* Restore primary table */
								for (i = 0; i < wo->count; i++)	/* Copy the curve */
									ro->pe_ic[j]->data[i] = wo->data[i];
			
								/* delete backup */
								if (icco->delete_tag(icco, bsig))
									error("Failed to delete tag '%s'",icm2str(icmTagSig, bsig));
								DBG(("Restored primary and deleted backup\n"));
							}
						} else {
							/* Restore the output table */
							for (j = 0; j < ro->outputChan; j++) {
								icTagSignature bsig = sigp->back;
								icmCurve *wo;					/* Backup of original */

								/* Create a tag per channel */
								bsig += j; 

								DBG(("Looking for backup tag '%s'\n",icm2str(icmTagSig, bsig)));

								/* See if we have a backup */
								if ((wo = (icmCurve *)icco->read_tag(icco, bsig)) == NULL)
									error("Can't find original table data in tag %s",
									                 icm2str(icmTagSig, sigp->back));

								/* Restore primary table */
								for (i = 0; i < wo->count; i++)	/* Copy the curve */
									ro->pe_oc[j]->data[i] = wo->data[i];
			
								/* delete backup */
								if (icco->delete_tag(icco, bsig))
									error("Failed to delete tag '%s'",icm2str(icmTagSig, bsig));
								DBG(("Restored primary and deleted backup\n"));
							}
						}

					/* XXXXTRCTag */
					} else if (primt->ttype == icSigCurveType) {
						icmCurve *wo, *ro = (icmCurve *)primt;

						DBG(("CurveType\n"));
						DBG(("Looking for backup tag '%s'\n",icm2str(icmTagSig, sigp->back)));

						/* See if we have a backup */
						wo = (icmCurve *)backt;

						/* Restore primary table */
						ro->ctype = wo->ctype;
						ro->count = wo->count;
						ro->allocate(ro);	/* Allocate space */
						for (i = 0; i < wo->count; i++)	/* Copy the curve */
							ro->data[i] = wo->data[i];

						/* delete backup */
						if (icco->delete_tag(icco, sigp->back))
							error("Failed to delete tag '%s'",icm2str(icmTagSig, sigp->back));
						DBG(("Restored primary and deleted backup\n"));

					} else {
						error("Tag %s is type %s we don't know how to handle",
						      icm2str(icmTagSig, sigp->prim),
						      icm2str(icmTypeSig, primt->ttype));
					}
				}
			}
		}
	}
	/* ======================================= */
	
	if (apply || remove) {
		/* Open up the other profile for writing */
		if ((wr_fp = new_icmFileStd_name(&err,out_name,"w")) == NULL)
			error ("Can't open file '%s' (0x%x, '%s')",out_name,err.c,err.m);
	
		if ((rv = icco->write(icco,wr_fp,0)) != 0)
			error ("Write file: %d, %s",rv,icco->e.m);
		wr_fp->del(wr_fp);
	}
	
	if (cal != NULL)
		cal->del(cal);
	icco->del(icco);

	if (found == 1)
		return 1;
	return 0;
}



