1 /******************************************************************************
2 *
3 *  NSSDC/CDF                                        CDF `create' operations.
4 *
5 *  Version 1.5b, 20-Oct-97, Hughes STX.
6 *
7 *  Modification history:
8 *
9 *   V1.0  20-May-92, J Love     Original version (was part of `cdflib.c').
10 *   V1.1  29-Sep-92, J Love     CDF V2.3 (shareable/NeXT/zVar).
11 *   V1.2  24-Jan-94, J Love     CDF V2.4.  Added readonly mode.
12 *   V1.3  15-Dec-94, J Love     CDF V2.5.
13 *   V1.3a  9-Jan-95, J Love	Encode/decode changes.  More cache-residency.
14 *   V1.3b 24-Feb-95, J Love	Solaris 2.3 IDL i/f.
15 *   V1.4  21-Mar-95, J Love	POSIX.
16 *   V1.4a  7-Sep-95, J Love	CDFexport-related changes.  Fixed cleanup when
17 *				a CDF is aborted.
18 *   V1.5  15-Aug-96, J Love	CDF V2.6.
19 *   V1.5a 21-Feb-97, J Love	Removed RICE.
20 *   V1.5b 11-Sep-97, J Love	Magic numbers are now uInt32.
21 *   V1.5c 20-Oct-97, J Love	Properly cast the uInt32 magic numbers.
22 *   V1.6   8-Apr-04, M Liu      Save the currently created variable's offset.
23 *   V1.7  13-Oct-06, M Liu      Changed to allow upper and lower case CDF
24 *                               name to be used on win32.
25 *
26 ******************************************************************************/
27 
28 #include "cdflib.h"
29 #include "cdflib64.h"
30 #include "cdfrev.h"
31 
32 /******************************************************************************
33 * CDFcre.
34 ******************************************************************************/
35 
CDFcre(Va,Cur)36 STATICforIDL CDFstatus CDFcre (Va, Cur)
37 struct VAstruct *Va;
38 struct CurStruct *Cur;
39 {
40 CDFstatus tStatus, pStatus = CDF_OK;
41 
42 switch (Va->item) {
43   /****************************************************************************
44   * CDF_, create a new CDF.
45   ****************************************************************************/
46   case CDF_: {
47     long numDims, *dimSizes;
48     char CDFnameT[CDF_PATHNAME_LEN+1], CDFnameX[DU_MAX_PATH_LEN+1], *CDFnameP;
49     char CDFnameTx[CDF_PATHNAME_LEN+1];
50     char copyRight[CDF_COPYRIGHT_LEN+1];
51     CDFid *id;
52     struct CDFstruct *CDF;
53     struct CDRstruct CDR;
54     struct GDRstruct GDR;
55     vFILE *dotFp;
56     int dimN;
57 #if defined(vms) || defined(dos)
58     Logical upper_case_ext = TRUE;
59 #else /* Unix, POSIX, & Macintosh */
60     Logical upper_case_ext = FALSE;
61 #endif
62     Logical version_numbers = FALSE;
63     Logical no_append = FALSE;
64     uInt32 magicNumber1 = V2magicNUMBER_1;
65     uInt32 magicNumber2u = V2magicNUMBER_2u;
66     /**************************************************************************
67     * Get arguments for this operation/item.
68     **************************************************************************/
69     CDFnameP = va_arg (Va->ap, char *);
70     numDims = va_arg (Va->ap, long);
71     dimSizes = va_arg (Va->ap, long *);
72     id = va_arg (Va->ap, CDFid *);
73     *id = (CDFid) NULL;
74     /**************************************************************************
75     * Validate arguments.
76     **************************************************************************/
77 #if BUILD_READ_ONLY_DISTRIBUTION
78     if (!sX(READ_ONLY_DISTRIBUTION,&pStatus)) return pStatus;
79 #endif
80     if (numDims < 0 || numDims > CDF_MAX_DIMS) return BAD_NUM_DIMS;
81     for (dimN = 0; dimN < numDims; dimN++) {
82        if (dimSizes[dimN] < 1) return BAD_DIM_SIZE;
83     }
84     if (strlen(CDFnameP) > (size_t) CDF_PATHNAME_LEN) {
85       if (!sX(CDF_NAME_TRUNC,&pStatus)) return pStatus;
86     }
87     strcpyX (CDFnameT, CDFnameP, CDF_PATHNAME_LEN);
88 #if STRIP_TRAILING_BLANKS_FROM_CDFPATH
89     StripTrailingBlanks (CDFnameT);
90 #endif
91 #if defined(vms) || defined(dos)
92     MakeUpperString (CDFnameT);
93 #endif
94     RemoveCDFFileExtension(CDFnameT, CDFnameTx);
95     if (!ValidCDFname(CDFnameTx)) return BAD_CDF_NAME;
96     BuildFilePath (CDFt, CDFnameTx, no_append, upper_case_ext, version_numbers,
97 		   INT32_ZERO, CDFnameX);
98     if (IsReg(CDFnameX)) return CDF_EXISTS;
99     /**************************************************************************
100     * Create CDF file.
101     **************************************************************************/
102     dotFp = V_open (CDFnameX, WRITE_PLUS_a_mode);
103     if (dotFp == NULL) return CDF_CREATE_ERROR;
104     /**************************************************************************
105     * Allocate/initialize CDF structure.
106     **************************************************************************/
107     CDF = (struct CDFstruct *) cdf_AllocateMemory (sizeof(struct CDFstruct), NULL);
108     if (CDF == NULL) {
109       V_close (dotFp, NULL, NULL);
110       DeleteFile (CDFnameX);
111       return BAD_MALLOC;
112     }
113     CDF->CDFname = (char *) cdf_AllocateMemory (strlen(CDFnameTx) + 1, NULL);
114     if (CDF->CDFname == NULL) {
115       V_close (dotFp, NULL, NULL);
116       DeleteFile (CDFnameX);
117       cdf_FreeMemory (CDF, NULL);
118       return BAD_MALLOC;
119     }
120     else
121       strcpyX (CDF->CDFname, CDFnameTx, 0);
122     CDF->magic = VALIDid_MAGIC_NUMBER;
123     CDF->largeFile = FALSE;
124     CDF->CDRoffset = V2_CDR_OFFSET;
125     CDF->GDRoffset = NO_OFFSET;		/* Reset below when GDR is written. */
126     CDF->readOnly = FALSE;
127     CDF->zMode = zMODEoff;
128     CDF->decoding = HOST_DECODING;
129     CDF->negToPosFp0 = FALSE;
130     CDF->status = READ_WRITE;
131     CDF->dotFp = dotFp;
132     CDF->uDotFp = NULL;
133     CDF->fp = dotFp;
134     CDF->pseudo_clock = 0;
135     CDF->upper_case_ext = upper_case_ext;
136     CDF->version_numbers = version_numbers;
137     CDF->no_append = no_append;
138     CDF->fakeEPOCH = FALSE;
139     CDF->wastedSpace = FALSE;
140     CDF->badEOF = FALSE;
141     CDF->badTerminatingOffsets = FALSE;
142     CDF->assumedScopes = FALSE;
143     CDF->NrVars = 0;
144     CDF->NzVars = 0;
145     CDF->MAXrVars = 0;
146     CDF->MAXzVars = 0;
147     CDF->rVars = NULL;
148     CDF->zVars = NULL;
149     CDF->encoding = BOO(DEFAULT_TO_HOST_ENCODING,
150 			HostEncoding(),NETWORK_ENCODING);
151     CDF->rowMajor = DEFAULT_TO_ROW_MAJOR;
152     CDF->singleFile = DEFAULT_TO_SINGLE_FILE;
153     CDF->rMaxRec = NO_RECORD;
154     CDF->rNumDims = numDims;
155     for (dimN = 0; dimN < numDims; dimN++) {
156        CDF->rDimSizes[dimN] = dimSizes[dimN];
157     }
158     CDF->stage.fp = NULL;
159     CDF->stage.mark = ZERO_OFFSET;
160     CDF->stage.cacheSize = NUMcacheSTAGE;
161     CDF->compressFp = NULL;
162     CDF->scratchDir = NULL;
163     CDF->workingCacheSize = BOO(CDF->singleFile,NUMcacheSINGLE,NUMcacheMULTI);
164     CDF->compressCacheSize = NUMcacheCOMPRESS;
165     CDF->checksum = BOO(CDFgetChecksumEnvVar()<=0,NONE_CHECKSUM,MD5_CHECKSUM);
166     InitCURobjectsStates (CDF);
167     AddTOvStats (&CDF->dotCDFvStats, NULL);
168     AddTOvStats (&CDF->uDotCDFvStats, NULL);
169     /**************************************************************************
170     * Set number of cache buffers based on the CDF's format.
171     **************************************************************************/
172     if (!CACHEv(CDF->fp,CDF->workingCacheSize)) {
173       AbortAccess (CDF, noUPDATE, DELETE);
174       cdf_FreeMemory (CDF, NULL);
175       return BAD_CACHE_SIZE;
176     }
177     /**************************************************************************
178     * Write magic numbers.
179     **************************************************************************/
180     if (!Write32(CDF->fp,(Int32 *)&magicNumber1)) {
181       AbortAccess (CDF, noUPDATE, DELETE);
182       cdf_FreeMemory (CDF, NULL);
183       return CDF_WRITE_ERROR;
184     }
185     if (!Write32(CDF->fp,(Int32 *)&magicNumber2u)) {
186       AbortAccess (CDF, noUPDATE, DELETE);
187       cdf_FreeMemory (CDF, NULL);
188       return CDF_WRITE_ERROR;
189     }
190     /**************************************************************************
191     * Write CDR.
192     **************************************************************************/
193     CDR.RecordSize = CDR_BASE_SIZE + CDF_COPYRIGHT_LEN;
194     CDR.RecordType = CDR_;
195     CDR.GDRoffset = CDF->CDRoffset + CDR.RecordSize;
196     if (CDFgetFileBackward()) {
197       CDR.Version = 2;
198       CDR.Release = 7;
199       CDR.Increment = 2;
200     } else {
201       CDR.Version = CDF_LIBRARY_VERSION;
202       CDR.Release = CDF_LIBRARY_RELEASE;
203       CDR.Increment = CDF_LIBRARY_INCREMENT;
204     }
205     CDR.Encoding = CDF->encoding;
206     CDR.Flags = 0;
207     if (CDF->rowMajor) SetBit32 (&(CDR.Flags), CDR_MAJORITY_BIT);
208     if (CDF->singleFile) SetBit32 (&(CDR.Flags), CDR_FORMAT_BIT);
209     CDR.rfuA = 0;
210     CDR.rfuB = 0;
211     CDR.rfuD = -1;
212     CDR.rfuE = -1;
213     CDFcopyRight (copyRight);
214     NulPad (copyRight, CDF_COPYRIGHT_LEN);
215     if (!sX(WriteCDR(CDF->fp,V2_CDR_OFFSET,
216 		     CDR_RECORD,&CDR,copyRight,
217 		     CDR_NULL),&pStatus)) {
218       AbortAccess (CDF, noUPDATE, DELETE);
219       cdf_FreeMemory (CDF, NULL);
220       return pStatus;
221     }
222     /**************************************************************************
223     * Write GDR.
224     **************************************************************************/
225     CDF->GDRoffset = CDR.GDRoffset;
226     GDR.RecordSize = GDR_BASE_SIZE + (numDims * sizeof(Int32));
227     GDR.RecordType = GDR_;
228     GDR.rVDRhead = 0;
229     GDR.zVDRhead = 0;
230     GDR.ADRhead = 0;
231     GDR.eof = CDR.GDRoffset + GDR.RecordSize;
232     GDR.NrVars = CDF->NrVars;
233     GDR.NumAttr = 0;
234     GDR.rMaxRec = CDF->rMaxRec;
235     GDR.rNumDims = CDF->rNumDims;
236     GDR.NzVars = CDF->NzVars;
237     GDR.UIRhead = 0;
238     GDR.rfuC = 0;
239     GDR.rfuD = -1;
240     GDR.rfuE = -1;
241     for (dimN = 0; dimN < CDF->rNumDims; dimN++) {
242        GDR.rDimSizes[dimN] = CDF->rDimSizes[dimN];
243     }
244     if (!sX(WriteGDR(CDF->fp,CDF->GDRoffset,
245 		     GDR_RECORD,&GDR,
246 		     GDR_NULL),&pStatus)) {
247       AbortAccess (CDF, noUPDATE, DELETE);
248       cdf_FreeMemory (CDF, NULL);
249       return pStatus;
250     }
251     /**************************************************************************
252     * Select current CDF and pass back CDFid.
253     **************************************************************************/
254     Cur->cdf = CDF;
255     *id = (CDFid) CDF;
256     if (CDF->checksum > 0) {
257         if (!sX(CDFsetChecksum(*id, CDF->checksum),&pStatus)) return pStatus;
258     }
259     break;
260   }
261 
262   /****************************************************************************
263   * rVAR_/zVAR_, create a new variable.
264   ****************************************************************************/
265 
266   case rVAR_:
267   case zVAR_: {
268     Logical zOp = (Va->item == zVAR_);
269     long *numOut, zNumDims, *zDimSizes, recVariance,
270 	 *dimVariances, newVarNum, numDims;
271     char *name, Tname[CDF_VAR_NAME_LEN+1], pathnameX[DU_MAX_PATH_LEN+1];
272     struct CDFstruct *CDF;
273     struct VDRstruct VDR;
274     struct VarStruct ***vars;
275     vFILE *varFp;
276     Int32 offset, ntlOffset, VDRhead, nVars;
277     Int32 dataType, numElements;
278     int dimN, *max;
279     name = va_arg (Va->ap, char *);
280     dataType = (Int32) va_arg (Va->ap, long);
281     numElements = (Int32) va_arg (Va->ap, long);
282     if (zOp) {
283       zNumDims = va_arg (Va->ap, long);
284       zDimSizes = va_arg (Va->ap, long *);
285     }
286     recVariance = va_arg (Va->ap, long);
287     dimVariances = va_arg (Va->ap, long *);
288     numOut = va_arg (Va->ap, long *);
289     /**************************************************************************
290     * Get current CDF.
291     **************************************************************************/
292     SelectCDF (Cur->cdf, CDF)
293     /**************************************************************************
294     * Read GDR fields.
295     **************************************************************************/
296     if (!sX(ReadGDR(CDF->fp,CDF->GDRoffset,
297 		    BOO(zOp,GDR_zVDRHEAD,GDR_rVDRHEAD),&VDRhead,
298 		    BOO(zOp,GDR_NzVARS,GDR_NrVARS),&nVars,
299 		    GDR_NULL),&pStatus)) {
300       AbortAccess (CDF, UPDATE, noDELETE);
301       return pStatus;
302     }
303     /**************************************************************************
304     * Validate arguments.
305     **************************************************************************/
306     if (BADzOP(CDF,!zOp)) return ILLEGAL_IN_zMODE;
307     if (!ValidDataType(dataType)) return BAD_DATA_TYPE;
308     if (numElements < 1) return BAD_NUM_ELEMS;
309     if (!STRINGdataType(dataType) && numElements != 1) return BAD_NUM_ELEMS;
310     if (zOp) {
311       if (zNumDims < 0 || zNumDims > CDF_MAX_DIMS) return BAD_NUM_DIMS;
312       for (dimN = 0; dimN < zNumDims; dimN++)
313 	 if (zDimSizes[dimN] < 1) return BAD_DIM_SIZE;
314     }
315     if (strlen(name) > (size_t) CDF_VAR_NAME_LEN) {
316       if (!sX(VAR_NAME_TRUNC,&pStatus)) return pStatus;
317     }
318     strcpyX (Tname, name, CDF_VAR_NAME_LEN);
319 #if LIMITof64K
320     if (TOObigIBMpc(CDFelemSize(dataType)*numElements)) return IBM_PC_OVERFLOW;
321 #endif
322     if (!ValidVarName(Tname)) return BAD_VAR_NAME;
323     tStatus = FindVarByName (CDF, Tname, NULL, NULL, NULL);
324     switch (tStatus) {
325       case NO_SUCH_VAR:
326 	break;
327       case CDF_OK:
328 	return VAR_EXISTS;
329       default:
330 	AbortAccess (CDF, UPDATE, noDELETE);
331 	return tStatus;
332     }
333     newVarNum = nVars;
334     nVars++;
335 #if defined(dos)
336     if (!CDF->singleFile && newVarNum > 99) return TOO_MANY_VARS;
337 #endif
338     /**************************************************************************
339     * Switch to read-write access if necessary.
340     **************************************************************************/
341     if (!WriteAccess(CDF,FALSE,&pStatus)) return pStatus;
342     /**************************************************************************
343     * Allocate initial/additional pointers to variable data structures.
344     **************************************************************************/
345     max = BOO(zOp,&(CDF->MAXzVars),&(CDF->MAXrVars));
346     vars = BOO(zOp,&(CDF->zVars),&(CDF->rVars));
347     if (newVarNum >= *max) {
348       int newMaxVars = (int) (VARs_INCREMENT * ((newVarNum/VARs_INCREMENT)+1));
349       int varN;
350       size_t nBytes = newMaxVars * sizeof(struct VarStruct *);
351       void *ptr;
352       if (*max > 0)
353 	ptr = (struct VarStruct **) cdf_ReallocateMemory (*vars, nBytes, NULL);
354       else
355 	ptr = (struct VarStruct **) cdf_AllocateMemory (nBytes, NULL);
356       if (ptr == NULL) return BAD_MALLOC;
357       *vars = ptr;
358       for (varN = *max; varN < newMaxVars; varN++) (*vars)[varN] = NULL;
359       *max = newMaxVars;
360     }
361     /**************************************************************************
362     * Create variable file (if multi-file CDF).
363     **************************************************************************/
364     if (!CDF->singleFile) {
365       BuildFilePath (BOO(zOp,Zt,Vt), CDF->CDFname, CDF->no_append,
366 		     CDF->upper_case_ext, CDF->version_numbers,
367 		     (Int32) newVarNum, pathnameX);
368       varFp = V_open (pathnameX, WRITE_PLUS_a_mode);
369       if (varFp == NULL) {
370 	if (!sX(CloseLRUvar(CDF),&pStatus)) return pStatus;
371 	varFp = V_open (pathnameX, WRITE_PLUS_a_mode);
372 	if (varFp == NULL) return VAR_CREATE_ERROR;
373       }
374       if (!CLOSEv(varFp,NULL, NULL)) {
375 	DeleteFile (pathnameX);
376 	return VAR_CREATE_ERROR;
377       }
378     }
379     /**************************************************************************
380     * Write rVDR/zVDR.
381     **************************************************************************/
382     VDR.RecordSize = BOO(zOp,zVDR_BASE_SIZE,rVDR_BASE_SIZE) +
383 		     BOO(zOp,zNumDims,0)*sizeof(Int32) +          /*DimSizes.*/
384 		     BOO(zOp,zNumDims,
385 			     CDF->rNumDims)*sizeof(Int32);	  /*DimVarys.*/
386     VDR.RecordType = BOO(zOp,zVDR_,rVDR_);
387     VDR.VDRnext = 0;
388     VDR.DataType = dataType;
389     VDR.MaxRec = NO_RECORD;
390     VDR.VXRhead = 0;
391     VDR.VXRtail = 0;
392     VDR.Flags = 0;
393     if (recVariance) SetBit32 (&(VDR.Flags), VDR_RECVARY_BIT);
394     VDR.sRecords = NO_SPARSERECORDS;
395     VDR.rfuB = 0;
396     VDR.rfuC = -1;
397     VDR.rfuF = -1;
398     VDR.NumElems = numElements;
399     VDR.Num = newVarNum;
400     VDR.CPRorSPRoffset = NO_OFFSET;
401     VDR.blockingFactor = 0;
402     strcpyX (VDR.Name, Tname, CDF_VAR_NAME_LEN);
403     NulPad (VDR.Name, CDF_VAR_NAME_LEN);
404     if (zOp) {
405       VDR.zNumDims = zNumDims;
406       for (dimN = 0; dimN < zNumDims; dimN++)
407 	 VDR.zDimSizes[dimN] = zDimSizes[dimN];
408     }
409     for (dimN = 0, numDims = BOO(zOp,zNumDims,CDF->rNumDims);
410 	 dimN < numDims; dimN++) {
411        VDR.DimVarys[dimN] = BOO(dimVariances[dimN],VARY,NOVARY);
412     }
413     if (!sX(AllocateIR(CDF,VDR.RecordSize,&offset),&pStatus)) {
414       DeleteFile (pathnameX);
415       AbortAccess (CDF, UPDATE, noDELETE);
416       return pStatus;
417     }
418     if (!sX(WriteVDR(CDF,CDF->fp,offset,zOp,
419 		     VDR_RECORD,&VDR,NULL,
420 		     VDR_NULL),&pStatus)) {
421       DeleteFile (pathnameX);
422       AbortAccess (CDF, UPDATE, noDELETE);
423       return pStatus;
424     }
425     /**************************************************************************
426     * Point next-to-last rVDR/zVDR (or GDR) to this rVDR/zVDR.
427     **************************************************************************/
428     if (newVarNum != 0) {
429       if (!sX(FindVarByNumber(CDF,(Int32)(newVarNum-1),
430 			      zOp,&ntlOffset),&pStatus)) {
431 	AbortAccess (CDF, UPDATE, noDELETE);
432 	return pStatus;
433       }
434       if (!sX(WriteVDR(CDF,CDF->fp,ntlOffset,zOp,
435 		       VDR_VDRNEXT,&offset,
436 		       VDR_NULL),&pStatus)) {
437 	AbortAccess (CDF, UPDATE, noDELETE);
438 	return pStatus;
439       }
440     }
441     else
442       VDRhead = offset;
443     /**************************************************************************
444     * Update GDR fields that may have been modified.
445     **************************************************************************/
446     if (!sX(WriteGDR(CDF->fp,CDF->GDRoffset,
447 		     BOO(zOp,GDR_zVDRHEAD,GDR_rVDRHEAD),&VDRhead,
448 		     BOO(zOp,GDR_NzVARS,GDR_NrVARS),&nVars,
449 		     GDR_NULL),&pStatus)) {
450       AbortAccess (CDF, UPDATE, noDELETE);
451       return pStatus;
452     }
453     /**************************************************************************
454     * Update the appropriate variable count held in memory for efficiency.
455     **************************************************************************/
456     if (zOp)
457       CDF->NzVars = nVars;
458     else
459       CDF->NrVars = nVars;
460     /**************************************************************************
461     * Select current variable and determine variable number to be passed back
462     * (based on zMode).
463     **************************************************************************/
464     if (zModeON(CDF)) {
465       long varN = CDF->NrVars + newVarNum;
466       CDF->CURzVarNum = varN;
467       CDF->CURzVarOffset = offset;
468       *numOut = varN;
469     }
470     else {
471       if (zOp) {
472 	CDF->CURzVarNum = newVarNum;
473         CDF->CURzVarOffset = offset;
474       } else {
475 	CDF->CURrVarNum = newVarNum;
476         CDF->CURrVarOffset = offset;
477       }
478       *numOut = newVarNum;
479     }
480     break;
481   }
482   /****************************************************************************
483   * ATTR_,
484   ****************************************************************************/
485   case ATTR_: {
486     long *attrNumOut, scope;
487     char *attrName, truncName[CDF_ATTR_NAME_LEN+1];
488     struct CDFstruct *CDF;
489     struct ADRstruct ADR;
490     Int32 offset, numAttr, ADRhead;
491     /**************************************************************************
492     * Get arguments for this operation/item.
493     **************************************************************************/
494     attrName = va_arg (Va->ap, char *);
495     scope = va_arg (Va->ap, long);
496     attrNumOut = va_arg (Va->ap, long *);
497     /**************************************************************************
498     * Select current CDF.
499     **************************************************************************/
500     SelectCDF (Cur->cdf, CDF)
501     /**************************************************************************
502     * Validate arguments.
503     **************************************************************************/
504     if (!ValidAttrScope((Int32)scope)) return BAD_SCOPE;
505     if (strlen(attrName) > (size_t) CDF_ATTR_NAME_LEN) {
506       if (!sX(ATTR_NAME_TRUNC,&pStatus)) return pStatus;
507     }
508     strcpyX (truncName, attrName, CDF_ATTR_NAME_LEN);
509     if (!ValidAttrName(truncName)) return BAD_ATTR_NAME;
510     tStatus = FindAttrByName (CDF, truncName, NULL);
511     switch (tStatus) {
512       case NO_SUCH_ATTR:
513 	break;
514       case CDF_OK:
515 	return ATTR_EXISTS;
516       default:
517 	AbortAccess (CDF, UPDATE, noDELETE);
518 	return tStatus;
519     }
520     /**************************************************************************
521     * Switch to read-write access if necessary.
522     **************************************************************************/
523     if (!WriteAccess(CDF,FALSE,&pStatus)) return pStatus;
524     /**************************************************************************
525     * Read GDR fields.
526     **************************************************************************/
527     if (!sX(ReadGDR(CDF->fp,CDF->GDRoffset,
528 		    GDR_NUMATTR,&numAttr,
529 		    GDR_ADRHEAD,&ADRhead,
530 		    GDR_NULL),&pStatus)) return pStatus;
531     /**************************************************************************
532     * Write ADR.
533     **************************************************************************/
534     ADR.RecordSize = ADR_BASE_SIZE;
535     ADR.RecordType = ADR_;
536     ADR.ADRnext = 0;
537     ADR.AgrEDRhead = 0;
538     ADR.Scope = scope;
539     ADR.Num = numAttr;
540     ADR.NgrEntries = 0;
541     ADR.MAXgrEntry = NO_ENTRY;
542     ADR.rfuA = 0;
543     ADR.AzEDRhead = 0;
544     ADR.NzEntries = 0;
545     ADR.MAXzEntry = NO_ENTRY;
546     ADR.rfuE = -1;
547     strcpyX (ADR.Name, truncName, CDF_ATTR_NAME_LEN);
548     NulPad (ADR.Name, CDF_ATTR_NAME_LEN);
549     if (!sX(AllocateIR(CDF,ADR.RecordSize,&offset),&pStatus)) {
550       AbortAccess (CDF, UPDATE, noDELETE);
551       return pStatus;
552     }
553     if (!sX(WriteADR(CDF->fp,offset,
554 		     ADR_RECORD,&ADR,
555 		     ADR_NULL),&pStatus)) {
556       AbortAccess (CDF, UPDATE, noDELETE);
557       return pStatus;
558     }
559     /**************************************************************************
560     * Point last ADR (or GDR) to this ADR.
561     **************************************************************************/
562     if (numAttr == 0)
563       ADRhead = offset;
564     else {
565       Int32 lastOffset;
566       if (!sX(FindLastAttr(CDF,&lastOffset),&pStatus)) {
567 	AbortAccess (CDF, UPDATE, noDELETE);
568 	return pStatus;
569       }
570       if (!sX(WriteADR(CDF->fp,lastOffset,
571 		       ADR_ADRNEXT,&offset,
572 		       ADR_NULL),&pStatus)) {
573 	AbortAccess (CDF, UPDATE, noDELETE);
574 	return pStatus;
575       }
576     }
577     /**************************************************************************
578     * Update GDR fields.
579     **************************************************************************/
580     numAttr++;
581     if (!sX(WriteGDR(CDF->fp,CDF->GDRoffset,
582 		     GDR_NUMATTR,&numAttr,
583 		     GDR_ADRHEAD,&ADRhead,
584 		     GDR_NULL),&pStatus)) {
585       AbortAccess (CDF, UPDATE, noDELETE);
586       return pStatus;
587     }
588     /**************************************************************************
589     * Select current attribute and entry offsets and pass back attribute
590     * number.
591     **************************************************************************/
592     CDF->CURattrOffset = offset;
593     CDF->CURgrEntryOffset = RESERVED_ENTRYOFFSET;
594     CDF->CURzEntryOffset = RESERVED_ENTRYOFFSET;
595     *attrNumOut = ADR.Num;
596     break;
597   }
598   /****************************************************************************
599   * Unknown item, must be the next function.
600   ****************************************************************************/
601   default: {
602     Va->fnc = Va->item;
603     break;
604   }
605 }
606 return pStatus;
607 }
608