/*
 * CIFwrite.c --
 *
 * Output of CIF.
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

#ifndef lint
static const char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/cif/CIFwrite.c,v 1.2 2010/06/24 12:37:15 tim Exp $";
#endif  /* not lint */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/utils.h"
#include "utils/hash.h"
#include "database/database.h"
#include "database/databaseInt.h"
#include "utils/tech.h"
#include "utils/stack.h"
#include "utils/undo.h"
#include "cif/cif.h"
#include "cif/CIFint.h"
#include "utils/signals.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "utils/styles.h"
#include "textio/textio.h"

    /* Forward declarations */
extern int cifWriteInitFunc(CellDef *def, ClientData cdata);	/* UNUSED */
extern int cifWriteMarkFunc(CellUse *use);
extern int cifWritePaintFunc(Tile *tile, TileType dinfo, FILE *f);
extern int cifWriteLabelFunc(Tile *tile, TileType dinfo, FILE *f);
extern int cifWriteUseFunc(CellUse *use, FILE *f);
extern void cifOutPreamble(FILE *outf, CellDef *cell);
extern void cifOut(FILE *outf);
extern void cifOutFunc(CellDef *def, FILE *f);
extern void GrClipTriangle(Rect *r, Rect *c, int clipped, TileType dinfo, Point *points, int *np);

/* Current cell number in CIF numbering scheme */

static int cifCellNum;

/* Stack of definitions left to process for generating CIF */

Stack *cifStack;

/* Scale factor for outputting paint: */

int cifPaintScale;

/* Current layer name.  If non-NULL, then cifWritePaintFunc outputs
 * this string to the file before outputting a rectangle, then nulls.
 * This way, the layer name only gets output when there's actually
 * stuff on that layer.
 */

char *cifPaintLayerName;

/* TRUE if area labels should be output */
bool CIFDoAreaLabels = FALSE;

/* TRUE if cell ids should be output */
bool CIFDoCellIdLabels = TRUE;

/* prefix string for pathnames */
char *CIFPathPrefix = NULL;

/* These are normally FALSE---enable at your own risk! */
bool CIFHierWriteDisable = FALSE;
bool CIFArrayWriteDisable = FALSE;


/*
 * ----------------------------------------------------------------------------
 *
 * CIFWrite --
 *
 * Write out the entire tree rooted at the supplied CellDef in CIF format,
 * to the specified file.
 *
 * Results:
 *	TRUE if the cell could be written successfully, FALSE otherwise.
 *
 * Side effects:
 *	Writes a file to disk.
 *	In the event of an error while writing out the cell,
 *	the external integer errno is set to the UNIX error
 *	encountered.
 *
 * Algorithm:
 *	We make a depth-first traversal of the entire design tree,
 *	marking each cell with a CIF symbol number and then outputting
 *	it to the CIF file.  If a given cell is not read in when we
 *	visit it, we read it in.
 *
 *	No hierarchical design rule checking or bounding box computation
 *	occur during this traversal -- both are explicitly avoided.
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFWrite(
    CellDef *rootDef,	/* Pointer to CellDef to be written */
    FILE *f)		/* Open output file */
{
    bool good;
    int oldCount = DBWFeedbackCount;
    CellDef *err_def;
    CellUse dummy;

    /*
     * Make sure that the entire hierarchy rooted at rootDef is
     * read into memory and that timestamp mismatches are resolved
     * (this is needed so that we know that bounding boxes are OK).
     */

    dummy.cu_def = rootDef;
    err_def = DBCellReadArea(&dummy, &rootDef->cd_bbox, TRUE);
    if (err_def != NULL)
    {
	TxError("Failure to read in entire subtree of the cell.\n");
	TxError("Failed on cell %s.\n", err_def->cd_name);
	return (FALSE);
    }
    DBFixMismatch();

    if (CIFCurStyle->cs_reducer == 0)
    {
	TxError("The current CIF output style can only be used for writing\n");
	TxError("Calma output.  Try picking another output style.\n");
	return (FALSE);
    }

    /*
     * Go through all cells currently having CellDefs in the
     * def symbol table and mark them with negative symbol numbers.
     */
    (void) DBCellSrDefs(0, cifWriteInitFunc, (ClientData) NULL);
    cifCellNum = (-2);
    rootDef->cd_client = (ClientData) -1;

    /*
     * Start by pushing the root def on the stack of cell defs
     * to be processed.
     */
    cifStack = StackNew(100);
    StackPush((ClientData) rootDef, cifStack);
    cifOutPreamble(f, rootDef);
    cifOut(f);
    StackFree(cifStack);
    if ((int) CD2INT(rootDef->cd_client) < 0)
	rootDef->cd_client = INT2CD(- (int) CD2INT(rootDef->cd_client));

    /* See if any problems occurred. */

    if (DBWFeedbackCount != oldCount)
    {
	TxPrintf("%d problems occurred.  See feedback entries.\n",
	    DBWFeedbackCount - oldCount);
    }

    /*
     * Now we are almost done.
     * Just output a call on the root cell
     */
    fprintf(f, "C %d;\nEnd\n", (int) CD2INT(rootDef->cd_client));
    good = !ferror(f);
    return (good);
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifWriteInitFunc --
 *
 * Filter function called on behalf of CIFWrite() above.
 * Responsible for setting the cif number of each cell to zero.
 *
 * Results:
 *	Returns 0 to indicate that the search should continue.
 *
 * Side effects:
 *	Modify the cif numbers of the cells they are passed.
 *
 * ----------------------------------------------------------------------------
 */

/*ARGSUSED*/
int
cifWriteInitFunc(
    CellDef *def,
    ClientData cdata)	/* UNUSED */
{
    def->cd_client = (ClientData) 0;
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifWriteMarkFunc --
 *
 * Called to add each cell def in the subcell plane of a parent
 * to the stack of cells to be processed.
 *
 * Results:
 *	Returns 0 to indicate that DBCellEnum() should continue.
 *
 * Side effects:
 *	Pushes the cell def on the stack.
 * ----------------------------------------------------------------------------
 */

int
cifWriteMarkFunc(
    CellUse *use)
{
    if (use->cu_def->cd_client != (ClientData) 0) return 0;
    use->cu_def->cd_client = INT2CD(cifCellNum);
    cifCellNum -= 1;
    StackPush((ClientData) use->cu_def, cifStack);
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifOutPreamble --
 *
 * Write preamble in front of a CIF file for version control.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Preamble in CIF comment style - () are send to output file.
 * ----------------------------------------------------------------------------
 */

void
cifOutPreamble(
    FILE *outf,
    CellDef *cell)
{
    extern char *MagicVersion;
    extern char *MagicCompileTime;
    char *now, *t;
    struct tm *clock;
    time_t t_stamp = time((time_t *) NULL);

    clock = localtime(&t_stamp);
    now = ctime(&t_stamp);
    /* remove the '\n' at the end of the string... */
    now[strlen(now)-1] = '\0';

    fprintf(outf,"( @@user : %s );\n", (t = getenv("USER")) ? t : "?");
    fprintf(outf,"( @@machine : %s );\n", (t = getenv("HOSTNAME")) ? t : "?");
    fprintf(outf,"( @@source : %s );\n",(cell->cd_file) ? cell->cd_file : "?");
    fprintf(outf,"( @@tool : Magic %s.%s );\n", MagicVersion, MagicRevision);
    fprintf(outf,"( @@compiled : %s );\n", MagicCompileTime);
    fprintf(outf,"( @@technology : %s );\n", DBTechName);
    if (DBTechVersion)
	fprintf(outf,"( @@version : %s );\n", DBTechVersion);
    else
	fprintf(outf,"( @@version : unknown );\n");
    if (DBTechDescription)
	fprintf(outf,"( @@techdesc : %s );\n", DBTechDescription);
    fprintf(outf,"( @@style : %s );\n", CIFCurStyle->cs_name);
    fprintf(outf,"( @@date : %s );\n", now);
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifOut --
 *
 * Main loop of CIF generation.  Pull a cell def from the stack
 * and process it.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Causes CIF to be output.
 *	Returns when the stack is empty.
 * ----------------------------------------------------------------------------
 */

void
cifOut(
    FILE *outf)
{
    CellDef *def;
    bool needHier;

    while (!StackEmpty(cifStack))
    {
	def = (CellDef *) StackPop(cifStack);
	if ((int) CD2INT(def->cd_client) >= 0) continue;	/* Already output */
	if (SigInterruptPending) continue;

	def->cd_client = INT2CD(- (int) CD2INT(def->cd_client));

	/* Read the cell in if it is not already available. */
	if ((def->cd_flags & CDAVAILABLE) == 0)
	    if (!DBCellRead(def, TRUE, TRUE, NULL))
		continue;

	/* Add any subcells to the stack.  This must be done before
	 * outputting CIF to make sure that the subcells all have
	 * CIF numbers.
	 */

	(void) DBCellEnum(def, cifWriteMarkFunc, (ClientData) 0);

	/* Output CIF for this cell */

	cifOutFunc(def, outf);
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifOutFunc --
 *
 * Write out the definition for a single cell as CIF.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Appends to the open CIF output file.
 *
 * ----------------------------------------------------------------------------
 */

void
cifOutFunc(
    CellDef *def,	/* Pointer to cell def to be written */
    FILE *f)		/* Open output file */
{
    Rect bigArea;
    Label *lab;
    int type;
    CIFLayer *layer;

    fprintf(f, "DS %d %d %d;\n", (int) CD2INT(def->cd_client),
	CIFCurStyle->cs_reducer, 2 * CIFCurStyle->cs_expander);

    if (def->cd_name != (char *) NULL)
	if (def->cd_name[0] != '\0')
	{
	    if (strcmp(def->cd_name, "(UNNAMED)") == 0)
		fprintf(f, "9 UNNAMED;\n");
	    else {
	        if (CIFPathPrefix && CIFPathPrefix[0])
		  fprintf(f, "9 %s/%s;\n", CIFPathPrefix, def->cd_name);
		else
		  fprintf(f, "9 %s;\n", def->cd_name);
	    }
	}

    /*
     * Output all the tiles associated with this cell.  Skip temporary
     * layers.
     */

    GEO_EXPAND(&def->cd_bbox, CIFCurStyle->cs_radius, &bigArea);
    CIFErrorDef = def;
    CIFGen(def, def, &bigArea, CIFPlanes, &DBAllTypeBits, TRUE, TRUE, FALSE,
		(ClientData)NULL);

    if (!CIFHierWriteDisable)
        CIFGenSubcells(def, &bigArea, CIFPlanes);
    if (!CIFArrayWriteDisable)
        CIFGenArrays(def, &bigArea, CIFPlanes);

    for (type = 0; type < CIFCurStyle->cs_nLayers; type++)
    {
	layer = CIFCurStyle->cs_layers[type];
	if (layer->cl_flags & CIF_TEMP) continue;
	cifPaintLayerName = layer->cl_name;
	cifPaintScale = 1;
	if (layer->cl_flags & CIF_LABEL)
	    DBSrPaintArea((Tile *) NULL, CIFPlanes[type],
		    &TiPlaneRect, &CIFSolidBits, cifWriteLabelFunc,
		    (ClientData) f);
	else
	    DBSrPaintArea((Tile *) NULL, CIFPlanes[type],
		    &TiPlaneRect, &CIFSolidBits, cifWritePaintFunc,
		    (ClientData) f);
    }

    /* Output labels */
    for (lab = def->cd_labels; lab; lab = lab->lab_next)
    {
	int type = CIFCurStyle->cs_labelLayer[lab->lab_type];
	Point center, size;

	center.p_x = lab->lab_rect.r_xbot + lab->lab_rect.r_xtop;
	center.p_y = lab->lab_rect.r_ybot + lab->lab_rect.r_ytop;
	center.p_x *= CIFCurStyle->cs_scaleFactor;
	center.p_x /= CIFCurStyle->cs_reducer;
	center.p_y *= CIFCurStyle->cs_scaleFactor;
	center.p_y /= CIFCurStyle->cs_reducer;

	if (CIFDoAreaLabels)
	{
	    size.p_x = lab->lab_rect.r_xtop - lab->lab_rect.r_xbot;
	    size.p_y = lab->lab_rect.r_ytop - lab->lab_rect.r_ybot;
	    size.p_x *= 2 * CIFCurStyle->cs_scaleFactor;
	    size.p_x /= CIFCurStyle->cs_reducer;
	    size.p_y *= 2 * CIFCurStyle->cs_scaleFactor;
	    size.p_y /= CIFCurStyle->cs_reducer;
	    if (type >= 0)
	    {
		fprintf(f, "95 %s %d %d %d %d %s;\n",
		    lab->lab_text, size.p_x, size.p_y, center.p_x, center.p_y,
		    CIFCurStyle->cs_layers[type]->cl_name);
	    }
	    else
	    {
		fprintf(f, "95 %s %d %d %d %d;\n",
		    lab->lab_text, size.p_x, size.p_y, center.p_x, center.p_y);
	    }
	}
	else
	{
	    if (type >= 0)
	    {
		fprintf(f, "94 %s %d %d %s;\n",
		    lab->lab_text, center.p_x, center.p_y,
		    CIFCurStyle->cs_layers[type]->cl_name);
	    }
	    else
	    {
		fprintf(f, "94 %s %d %d;\n",
		    lab->lab_text, center.p_x, center.p_y);
	    }
	}
    }

    /*
     * Output the calls that the child makes to its children.  For
     * arrays it is necessary to output one call for each instance.
     */
    (void) DBCellEnum(def, cifWriteUseFunc, (ClientData) f);
    fprintf(f, "DF;\n");
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifWriteUseFunc --
 *
 * Filter function, called by DBCellEnum on behalf of cifOutFunc above,
 * to write out each CellUse called by the CellDef being output.
 *
 * Results:
 *	Always return 0
 *
 * Side effects:
 *	Appends to the open CIF output file.
 *
 * ----------------------------------------------------------------------------
 */

int
cifWriteUseFunc(
    CellUse *use,
    FILE *f)
{
    int x, y, topx, topy;
    int realx, realy;
    Transform *t;
    int cifnum;

    cifnum = (int) CD2INT(use->cu_def->cd_client);
    if (cifnum < 0) cifnum = (-cifnum);
    topx = use->cu_xhi - use->cu_xlo;
    if (topx < 0) topx = -topx;
    topy = use->cu_yhi - use->cu_ylo;
    if (topy < 0) topy = -topy;
    realx = use->cu_xlo;
    for (x = 0; x <= topx; x++)
    {
        realy = use->cu_ylo;
	for (y = 0; y <= topy; y++)
	{
	    /*
	     * We eventually want to tag each use with its unique
	     * use identifier, which should include array subscripting
	     * information.
	     */

  	    /*
	     * Insert a 91 user command to label the next cell
	     */
	    if (CIFDoCellIdLabels && use->cu_id && use->cu_id[0]) {
	      fprintf (f, "91 %s", use->cu_id);
	      if (topx > 0 || topy > 0) {
		if (topx > 0 && topy > 0)
		  fprintf (f, "(%d,%d)", realy, realx);
		else {
		  if (topx > 0)
		    fprintf (f, "(%d)", realx);
		  else
		    fprintf (f, "(%d)", realy);
		}
	      }
	      fprintf (f, ";\n");
	    }
	    fprintf(f, "C %d", cifnum);

	    /*
	     * The following translates from the abcdef transforms
	     * that we use internally to the rotate and mirror
	     * specs used in CIF.  It only works because
	     * orientations are orthogonal in magic.  Check all
	     * 8 possible positions if you don't believe this.
	     */

	    t = &use->cu_transform;
	    if ((t->t_a != t->t_e) || ((t->t_a == 0) && (t->t_b == t->t_d)))
		fprintf(f, " MX R %d %d", -(t->t_a), -(t->t_d));
	    else
		fprintf(f, " R %d %d", t->t_a, t->t_d);

	    fprintf(f, " T %d %d;\n",
		((t->t_c + t->t_a*(use->cu_xsep)*x + t->t_b*(use->cu_ysep)*y)
		    * 2 * CIFCurStyle->cs_scaleFactor) / CIFCurStyle->cs_reducer,
		((t->t_f + t->t_d*(use->cu_xsep)*x + t->t_e*(use->cu_ysep)*y)
		    * 2 * CIFCurStyle->cs_scaleFactor) / CIFCurStyle->cs_reducer);

	    if (use->cu_yhi > use->cu_ylo)
	      realy++;
	    else
	      realy--;
	}
	if (use->cu_xhi > use->cu_xlo)
	  realx++;
	else
	  realx--;
    }
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifWriteLabelFunc --
 *
 * Filter function used to write out a label corresponding to a
 * single paint tile.  The CIF layer name is used as the label to
 * output.
 *
 * Results:
 *	Always return 0
 *
 * Side effects:
 *	Writes to the disk file.
 *
 * ----------------------------------------------------------------------------
 */

int
cifWriteLabelFunc(
    Tile *tile,		/* Tile to be written out. */
    TileType dinfo,	/* Split tile information (unused) */
    FILE *f)		/* File in which to write. */
{
    Rect r;
    int type;
    Point center, size;

    if (IsSplit(tile)) return 0;	/* Ignore non-manhattan tiles */

    if (cifPaintLayerName == NULL) return 0;	/* Shouldn't happen */

    TiToRect(tile, &r);

    type = CIFCurStyle->cs_labelLayer[TiGetType(tile)];

    center.p_x = r.r_xbot + r.r_xtop;
    center.p_y = r.r_ybot + r.r_ytop;
    center.p_x *= CIFCurStyle->cs_scaleFactor;
    center.p_x /= CIFCurStyle->cs_reducer;
    center.p_y *= CIFCurStyle->cs_scaleFactor;
    center.p_y /= CIFCurStyle->cs_reducer;

    if (CIFDoAreaLabels)
    {
	size.p_x = r.r_xtop - r.r_xbot;
	size.p_y = r.r_ytop - r.r_ybot;
	size.p_x *= 2 * CIFCurStyle->cs_scaleFactor;
	size.p_x /= CIFCurStyle->cs_reducer;
	size.p_y *= 2 * CIFCurStyle->cs_scaleFactor;
	size.p_y /= CIFCurStyle->cs_reducer;
	fprintf(f, "95 %s %d %d %d %d;\n",
		cifPaintLayerName, size.p_x, size.p_y, center.p_x, center.p_y);
    }
    else
    {
	fprintf(f, "94 %s %d %d;\n",
		    cifPaintLayerName, center.p_x, center.p_y);
    }
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifWritePaintFunc --
 *
 * Filter function used to write out a single paint tile.
 *
 * Results:
 *	Always return 0
 *
 * Side effects:
 *	Writes to the disk file.
 *
 * ----------------------------------------------------------------------------
 */

int
cifWritePaintFunc(
    Tile *tile,		/* Tile to be written out. */
    TileType dinfo,	/* Split tile information */
    FILE *f)		/* File in which to write. */
{
    Rect r;

    /* Output the layer name if it hasn't been done already. */

    if (cifPaintLayerName != NULL)
    {
	fprintf(f, "L %s;\n", cifPaintLayerName);
	cifPaintLayerName = NULL;
    }

    TiToRect(tile, &r);

    if (IsSplit(tile))
    {
	Point points[5];
	int i, np;

	GrClipTriangle(&r, NULL, FALSE, TiGetTypeExact(tile) | dinfo, points, &np);

	/* Write triangle as a CIF polygon */

	fprintf(f, "    P");
	for (i = 0; i < np; i++)
	{
	    fprintf(f, " %d %d",
		(2*cifPaintScale*(points[i].p_x))/CIFCurStyle->cs_reducer,
		(2*cifPaintScale*(points[i].p_y))/CIFCurStyle->cs_reducer);
	}
	fprintf(f, ";\n");
    }
    else

	/* The only tricky thing here is that we MUST scale the rectangle
	 * up by a factor of two to avoid round-off errors in computing
	 * its center point (what a bogosity in CIF!!).  This is compensated
	 * by shrinking by a factor of two in the "DS" statement.
	 */

	fprintf(f, "    B %d %d %d %d;\n",
		(2*cifPaintScale*(r.r_xtop - r.r_xbot))/CIFCurStyle->cs_reducer,
		(2*cifPaintScale*(r.r_ytop - r.r_ybot))/CIFCurStyle->cs_reducer,
		(cifPaintScale*(r.r_xtop + r.r_xbot))/CIFCurStyle->cs_reducer,
		(cifPaintScale*(r.r_ytop + r.r_ybot))/CIFCurStyle->cs_reducer);
    CIFRects += 1;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFWriteFlat --
 *
 * Write out the entire tree rooted at the supplied CellDef in CIF format,
 * to the specified file, but write non-hierarchical CIF.
 *
 * Results:
 *	TRUE if the cell could be written successfully, FALSE otherwise.
 *
 * Side effects:
 *	Writes a file to disk.
 *	In the event of an error while writing out the cell,
 *	the external integer errno is set to the UNIX error
 *	encountered.
 *
 * Algorithm:
 * 	We operate on the cell in chunks chosen to keep the memory utilization
 * reasonable.  Foreach chunk, we use DBTreeSrTiles and cifHierCopyFunc to
 * flatten the
 * chunk into a yank buffer ("eliminating" the subcell problem), then use
 * cifOut to generate the CIF.
 *	No hierarchical design rule checking or bounding box computation
 *	occur during this operation -- both are explicitly avoided.
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFWriteFlat(
    CellDef *rootDef,	/* Pointer to CellDef to be written */
    FILE *f)		/* Open output file */
{
    bool good;
    int oldCount = DBWFeedbackCount;
    TileTypeBitMask cifMask;
    SearchContext scx;

    cifStack = StackNew(1);
    CIFInitCells();
    UndoDisable();
    CIFDummyUse->cu_def = rootDef;
    /*
     * Write CIF preamble out first.
     */
    cifOutPreamble(f, rootDef);

    /*
     * Now process each chunk.  We cheat and use cifOut(), so we need to have
     * a stack for the single flattened "component" to be on.
     */

    {
	scx.scx_use = CIFDummyUse;
	scx.scx_trans = GeoIdentityTransform;
	GEO_EXPAND(&rootDef->cd_bbox, CIFCurStyle->cs_radius, &scx.scx_area);
	(void) DBTreeSrTiles(&scx, &DBAllButSpaceAndDRCBits, 0,
			     cifHierCopyFunc, (ClientData) CIFComponentDef);
	DBReComputeBbox(CIFComponentDef);
	cifCellNum = (-2);
	CIFComponentDef->cd_client = (ClientData) -1;
	StackPush((ClientData) CIFComponentDef, cifStack);
	cifOut(f);
	/* cifStack SHould be empty now */
	if(!StackEmpty(cifStack))
	{
	    TxPrintf("Stack error in CIFWriteInverted()!!  "
			"Your CIF is probably corrupted.\n");
	    StackFree(cifStack);
	    return FALSE;
	}
	DBCellClearDef(CIFComponentDef);
    }
    StackFree(cifStack);

    /*
     * Now we are almost done.
     * Just output a call on the root cell
     */

    fprintf(f, "C %d;\nEnd\n", (int) CD2INT(CIFComponentDef->cd_client));
    DBCellClearDef(CIFComponentDef);
    good = !ferror(f);

    /* Report any errors that occurred. */

    if (DBWFeedbackCount != oldCount)
    {
	TxPrintf("%d problems occurred.  See feedback entries.\n",
		 DBWFeedbackCount-oldCount);
    }
    return good;
}
