int EXP_LVL7 CS_ips (csFILE *strm,short rs,long32_t eofPos,int (*comp)(Const void *pp,Const void *qq)) { int st; size_t rec_cnt; /* Record count */ size_t buf_siz; /* Buffer size */ size_t rd_cnt; /* Read check. */ size_t wr_cnt; /* Write check. */ long32_t beg; /* File position of the beginning of the portion of the file which we are to sort but remains unsorted. */ long32_t end; /* File position of the end of the portion of the file which we are to sort but remains unsorted. */ void *buff; /* Pointer to malloc'ed sort buffer. */ /* Prepare for a possible error. */ buff = NULL; /* Establish the extrema of the sort. We support sorting only a portion of a file. */ beg = CS_ftell (strm); if (beg < 0L) { CS_erpt (cs_IOERR); goto error; } if (eofPos <= 0L) { st = CS_fseek (strm,0L,SEEK_END); if (st != 0) { CS_erpt (cs_IOERR); goto error; } end = CS_ftell (strm); if (end < 0L) { CS_erpt (cs_IOERR); goto error; } st = CS_fseek (strm,beg,SEEK_SET); if (st != 0) { CS_erpt (cs_IOERR); goto error; } } else { end = eofPos; } /* If there is nothing to sort, we done. */ if (end <= beg) { return 0; } /* Set up our buffer area. */ buf_siz = (size_t)(end - beg); buff = malloc (buf_siz); if (buff == NULL) { CS_erpt (cs_NO_MEM); goto error; } /* Compute some basic parameters for the sort. */ rec_cnt = buf_siz / (unsigned short)rs; /* Read in the entire file. */ rd_cnt = CS_fread (buff,1,buf_siz,strm); if (rd_cnt != buf_siz) { if (ferror (strm)) CS_erpt (cs_IOERR); else CS_erpt (cs_INV_FILE); goto error; } /* Sort it. */ qsort (buff,rec_cnt,rs,(int (_cdecl *)(Const void *pp,Const void *qq))comp); /*lint !e732 */ /* Write it back out. */ st = CS_fseek (strm,beg,SEEK_SET); if (st != 0) { CS_erpt (cs_IOERR); goto error; } wr_cnt = CS_fwrite (buff,1,buf_siz,strm); if (wr_cnt != buf_siz) { if (ferror (strm)) CS_erpt (cs_IOERR); else CS_erpt (cs_DISK_FULL); goto error; } /* Clean up */ CS_fseek (strm,beg,SEEK_SET); free (buff); return (1); error: if (buff != NULL) free (buff); return (-1); }
int EXP_LVL7 CS_bins (csFILE *strm,long32_t start,long32_t eofPos,int rs,Const void *rec,int (*comp)(Const void *pp,Const void *qq)) { int st; int flag = -1; /* initialization to keep gcc happy */ size_t rd_cnt; long32_t nrecs; long32_t hi_rec; long32_t lo_rec; long32_t test_rec; long32_t test_pos; char *buff; /* Prepare for a possible error. */ buff = NULL; /* Get a buffer which we can use for record I/O. */ buff = (char *)CS_malc ((unsigned)rs); if (buff == NULL) { CS_erpt (cs_NO_MEM); goto error; } /* Compute the portion of the file which is to be searched. Quite often, we don't want to search the whole thing. */ if (start < 0L) { start = CS_ftell (strm); if (start < 0L) { CS_erpt (cs_IOERR); goto error; } } if (eofPos <= 0L) { st = CS_fseek (strm,0L,SEEK_END); if (st != 0) { CS_erpt (cs_IOERR); goto error; } eofPos = CS_ftell (strm); if (eofPos < 0L) { CS_erpt (cs_IOERR); goto error; } } /* Compute the number of records in the file which are to be searched. */ nrecs = (eofPos - start) / rs; /* If there are no records, we don't have a match, any any record would appear at the start. */ if (nrecs <= 0) { st = CS_fseek (strm,start,SEEK_SET); if (st != 0) { CS_erpt (cs_IOERR); goto error; } CS_free (buff); buff = NULL; return (0); } /* Set the starting and ending record numbers, where the first record is numbered zero. */ hi_rec = nrecs - 1; lo_rec = 0; /* Continue to search the records until we have exhausted all possibilities. The equal part of this test is nescessary so that the last record in the search gets compared for a match or not. */ test_pos = start; /* initialization to keep gcc happy */ while (lo_rec <= hi_rec) { /* Select the next record to be tested in terms of record number. */ test_rec = (hi_rec + lo_rec) / 2; /* Convert this to a file position. */ test_pos = (long32_t)(test_rec * rs) + start; /* Read in the new test record. */ st = CS_fseek (strm,test_pos,SEEK_SET); if (st != 0) { CS_erpt (cs_IOERR); goto error; } rd_cnt = CS_fread (buff,1,(unsigned)rs,strm); if (rd_cnt != (size_t)rs) { if (ferror (strm)) CS_erpt (cs_IOERR); else CS_erpt (cs_INV_FILE); goto error; } /* Compare with our search record. */ flag = (*comp)(buff,rec); if (flag > 0) { /* Here if our test record is past where our search record will be if it does indeed exist. We can exclude this record (that we just read) and all records past it from the search. */ hi_rec = test_rec - 1; } else if (flag < 0) { /* Here if the test record is before where our search record will be if it does indeed exist. We can exclude this record and all which precede it from the search. */ lo_rec = test_rec + 1; } else { /* We have found a matching record. Before we return, we need to make sure that if there are duplicate records in the file, we are returning with the file positioned to read the first of such duplicates. */ do { test_pos -= (long32_t)rs; if (test_pos < start) { /* The last record tested is the first in the search area. Seek to search and break, we're all done. */ st = CS_fseek (strm,start,SEEK_SET); if (st != 0) { CS_erpt (cs_IOERR); goto error; } break; } /* Seek to the record just prior to the last one tested and read it in. */ st = CS_fseek (strm,test_pos,SEEK_SET); if (st != 0) { CS_erpt (cs_IOERR); goto error; } rd_cnt = CS_fread (buff,1,(unsigned)rs,strm); if (rd_cnt != (size_t)rs) { if (ferror (strm)) CS_erpt (cs_IOERR); else CS_erpt (cs_INV_FILE); goto error; } /* See if this record also matches our key. NOTE, that the read positions the file back to the last record tested, which is the desired file position if the match test fails. */ flag = (*comp)(buff,rec); } while (flag == 0); /* Reset flag to zero, i.e. we got here because we found a matching record. */ flag = 0; /* Now we can return. */ break; } } /* If flag is negative here, the file is correctly positioned since we have read the record in and the file is positioned for the next. If flag is greater than zero, we need to move the file pointer back one record so that the user will be able to re-read the current record. */ if (flag > 0) { st = CS_fseek (strm,test_pos,SEEK_SET); if (st != 0) { CS_erpt (cs_IOERR); goto error; } } /* OK, return and tell the user if we found the desired record or not. */ CS_free (buff); buff = NULL; return (flag == 0); error: if (buff != NULL) CS_free (buff); return (-1); }
/***************************************************************************** Constructor */ struct csGeoid99GridFile_* CSnewGeoid99GridFile (Const char *path,long32_t bufferSize,ulong32_t flags,double density) { extern double cs_K360; extern char cs_DirsepC; extern char cs_ExtsepC; extern char csErrnam []; size_t readCount; long lngTmp; double lngMin, lngMax; char *cp1, *cp2; csFILE *fstr; struct csGeoid99GridFile_* __This; char cTemp [MAXPATH]; struct csGeoid99Hdr_ geoid99Hdr; /* Prepare for an error. */ __This = NULL; fstr = NULL; /* Malloc and initialize */ __This = CS_malc (sizeof (struct csGeoid99GridFile_)); if (__This == NULL) { CS_erpt (cs_NO_MEM); goto error; } CSinitGeoid99 (__This); /* Set default values for all members. */ __This->bufferSize = bufferSize; if (__This->bufferSize < 0) __This->bufferSize = 0; /* Save file path/name. */ CS_stncp (__This->filePath,path,sizeof (__This->filePath)); CS_stncp (cTemp,path,sizeof (cTemp)); /* Set up the type of file. Get cp1 to point at the file name, and cp2 to point at the extension. We consider it an error if we are not given a full path name. Note, we care not about the format of the drive specification. But there must be at least one directory and there must be an extension. */ cp1 = strrchr (cTemp,cs_DirsepC); if (cp1 == NULL) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_INV_FILE); goto error; } cp1 += 1; cp2 = strchr (cp1,cs_ExtsepC); if (cp2 == NULL) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_INV_FILE); goto error; } *cp2++ = '\0'; CS_stncp (__This->fileName,cp1,sizeof (__This->fileName)); /* The thing should have a .bin extension to be processed by us. */ if (CS_stricmp (cp2,"bin")) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_INV_FILE); goto error; } /* Get the file information header. */ fstr = CS_fopen (__This->filePath,_STRM_BINRD); if (fstr == NULL) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_DTC_FILE); goto error; } readCount = CS_fread (&geoid99Hdr,1,sizeof (geoid99Hdr),fstr); if (readCount != sizeof (geoid99Hdr)) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_INV_FILE); goto error; } if (CS_ferror (fstr)) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_IOERR); goto error; } /* Determine the size of the file. */ if (CS_fseek (fstr,0L,SEEK_END)) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_IOERR); goto error; } __This->fileSize = CS_ftell (fstr); if (__This->fileSize < 0L) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_IOERR); goto error; } /* This constructor doesn't need the file any more. */ CS_fclose (fstr); fstr = NULL; /* Swap the bytes if necessary. In all the data files I've seen, iKind is 32 bit 1. If it is a one in the structure, than the file is in the same byte order as the machine we are running on, and swapping is unnecessary. If iKind is not a one, we assume that is because of byte order, and we do a swap. Note, however, that we preserve iKind as it was before swapping so that we know whether to swap or not in the other functions in this code module. */ __This->iKind = geoid99Hdr.iKind; if (geoid99Hdr.iKind != 1L) { /* CSbswap is a variation on CS_bswap. CSbswap always swaps. */ CSbswap (&geoid99Hdr,cs_BSWP_Geoid99Hdr); } /* At this point, geoid99Hdr.iKind should be a one, or either the file is corrupted, or we don't understand the file format properly. */ if (geoid99Hdr.iKind != 1L) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_INV_FILE); goto error; } /* Extract the important stuff from the header. Note that the longitude in the header is a zero thru 360 value, proceeding east from Greenwich. This makes a lot of sense since Alaska crosses the 190 degree crack. However, it is inconsistent with all the other stuff. So, we do the following kludge, which is consistent with the NADCON data files. */ lngMin = geoid99Hdr.lngMin; lngMax = lngMin + (geoid99Hdr.lngDelta * (geoid99Hdr.lngCount - 1)); if (lngMin >= 180.0 || lngMax > 180.0) { __This->coverage.southWest [LNG] = geoid99Hdr.lngMin - cs_K360; } else { __This->coverage.southWest [LNG] = geoid99Hdr.lngMin; } __This->coverage.southWest [LAT] = geoid99Hdr.latMin; __This->deltaLng = geoid99Hdr.lngDelta; __This->deltaLat = geoid99Hdr.latDelta; __This->coverage.northEast [LNG] = __This->coverage.southWest [LNG] + (geoid99Hdr.lngDelta * (geoid99Hdr.lngCount - 1)); __This->coverage.northEast [LAT] = __This->coverage.southWest [LAT] + (geoid99Hdr.latDelta * (geoid99Hdr.latCount - 1)); __This->coverage.density = (__This->deltaLng < __This->deltaLat) ? __This->deltaLng : __This->deltaLat; if (density != 0.0) __This->coverage.density = density; __This->elementCount = geoid99Hdr.lngCount; __This->recordCount = geoid99Hdr.latCount; __This->recordSize = geoid99Hdr.lngCount * (int)sizeof (float); /* Verify the integrity of the file. */ lngTmp = __This->recordCount * __This->recordSize + sizeof (geoid99Hdr); if (lngTmp != __This->fileSize) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_INV_FILE); goto error; } /* Now that we know recordSize, we can adjust the bufferSize for maximum efficiency. */ if (__This->bufferSize > __This->fileSize) { __This->bufferSize = __This->fileSize; } else { if (__This->bufferSize > (3 * __This->recordSize)) { /* Maximum efficiency is obtained with a buffer size whch is a multiple of the record size. */ __This->bufferSize = (__This->bufferSize / __This->recordSize) * __This->recordSize; } else { /* We require a minimum buffer size of 3 records. */ __This->bufferSize = 3 * __This->recordSize; } } return (__This); error: CSdeleteGeoid99GridFile (__This); return NULL; }
/***************************************************************************** Constructor */ struct csGeoid96GridFile_* CSnewGeoid96GridFile (Const char *path,long32_t bufferSize,ulong32_t flags,double density) { extern char cs_DirsepC; extern char cs_ExtsepC; extern char csErrnam []; size_t readCount; long lngTmp; char *cp1, *cp2; csFILE *fstr; struct csGeoid96GridFile_* __This; char cTemp [MAXPATH]; struct csNadconFileHdr_ nadconHdr; /* Prepare for an error. */ __This = NULL; fstr = NULL; /* Malloc and initialize */ __This = CS_malc (sizeof (struct csGeoid96GridFile_)); if (__This == NULL) { CS_erpt (cs_NO_MEM); goto error; } CSinitGeoid96 (__This); /* Set default values for all members. */ __This->bufferSize = bufferSize; if (__This->bufferSize < 0) __This->bufferSize = 0; /* Save file path/name. */ CS_stncp (__This->filePath,path,sizeof (__This->filePath)); CS_stncp (cTemp,path,sizeof (cTemp)); /* Set up the type of file. Get cp1 to point at the file name, and cp2 to point at the extension. We consider it an error if we are not given a full path name. Note, we care not about the format of the drive specification. But there must be at least one directory and there must be an extension. */ cp1 = strrchr (cTemp,cs_DirsepC); if (cp1 == NULL) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_INV_FILE); goto error; } cp1 += 1; cp2 = strchr (cp1,cs_ExtsepC); if (cp2 == NULL) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_INV_FILE); goto error; } *cp2++ = '\0'; CS_stncp (__This->fileName,cp1,sizeof (__This->fileName)); /* The thing should have a .geo extension to be processed by us. */ if (CS_stricmp (cp2,"geo")) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_INV_FILE); goto error; } /* Get the file information header. */ fstr = CS_fopen (__This->filePath,_STRM_BINRD); if (fstr == NULL) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_DTC_FILE); goto error; } readCount = CS_fread (&nadconHdr,1,sizeof (nadconHdr),fstr); if (readCount != sizeof (nadconHdr)) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_INV_FILE); goto error; } if (CS_ferror (fstr)) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_IOERR); goto error; } /* Determine the size of the file. */ if (CS_fseek (fstr,0L,SEEK_END)) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_IOERR); goto error; } __This->fileSize = CS_ftell (fstr); if (__This->fileSize < 0L) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_IOERR); goto error; } /* This constructor doesn't need the file any more. */ CS_fclose (fstr); fstr = NULL; /* Swap the bytes if necessary. */ CS_bswap (&nadconHdr,cs_BSWP_NadconFileHdr); /* The extra stuff here is required as conversions of floats to doubles does not always provide precise results. To get the precise results we require, we assume that the value (which is in degrees) is an intergal number of seconds. */ lngTmp = (long)(((double)nadconHdr.del_lng * 3600.0) + 0.4); __This->deltaLng = ((double)lngTmp / 3600.0); lngTmp = (long)(((double)nadconHdr.del_lat * 3600.0) + 0.4); __This->deltaLat = ((double)lngTmp / 3600.0); /* Now we can do the rest of this stuff. */ __This->coverage.southWest [LNG] = nadconHdr.min_lng; __This->coverage.southWest [LAT] = nadconHdr.min_lat; __This->coverage.northEast [LNG] = nadconHdr.min_lng + (__This->deltaLng * (nadconHdr.ele_cnt - 1)); __This->coverage.northEast [LAT] = nadconHdr.min_lat + (__This->deltaLat * (nadconHdr.rec_cnt - 1)); __This->coverage.density = (__This->deltaLng < __This->deltaLat) ? __This->deltaLng : __This->deltaLat; if (density != 0.0) __This->coverage.density = density; __This->elementCount = nadconHdr.ele_cnt; __This->recordCount = nadconHdr.rec_cnt; __This->recordSize = nadconHdr.ele_cnt * (int)sizeof (float) + (int)sizeof (long); /* Verify the integrity of the file. */ lngTmp = (__This->recordCount + 1) * __This->recordSize; if (lngTmp != __This->fileSize) { CS_stncp (csErrnam,__This->filePath,MAXPATH); CS_erpt (cs_INV_FILE); goto error; } /* Now that we know recordSize, we can adjust the bufferSize for maximum efficiency. */ if (__This->bufferSize > __This->fileSize) { __This->bufferSize = __This->fileSize; } else { if (__This->bufferSize > (3 * __This->recordSize)) { /* Maximum efficiency is obtained with a buffer size whch is a multiple of the record size. */ __This->bufferSize = (__This->bufferSize / __This->recordSize) * __This->recordSize; } else { /* We require a minimum buffer size of 3 records. */ __This->bufferSize = 3 * __This->recordSize; } } return (__This); error: CSdeleteGeoid96GridFile (__This); return NULL; }
/* Interpolation Calculator The comment below is stale now that we store the entire grid file in memory, but is interesting nonetheless as it documents unusual properties of NTv2 grids. ==== Due to a bust in the file format, we do not buffer up grid cells and stuff. There are a couple of sub-grids which overlap other grids in such a way that buffering can cause errors. So, at least until (if ever) the data file is corrected, we do no buffering of the grid cells. Also, this file format is being adopted by others, such as the Australians. We don't know what they are going to do. So to be safe, NO BUFFERING OF GRID CELLS. Also, due to the sub-grid nature of the data file, we do not buffer the data file in any special way; we simply use normal stream buffering. We do, however, use a normal stream buffer of the size specified in the main object. */ int CScalcNTv2 (struct cs_NTv2_* thisPtr,double deltaLL [2],Const double source [2]) { extern double cs_Zero; /* 0.0 */ extern double cs_LlNoise; /* 1.0E-12 */ extern char csErrnam [MAXPATH]; short onLimit; unsigned short eleNbr, rowNbr; int rtnValue; int swapping; csFILE* stream = NULL; size_t readCnt; long32_t filePosition; struct csNTv2SubGrid_ *cvtPtr; double wpLL [2]; double seCell [2]; double nwCell [2]; struct TcsCaNTv2Data southEast; struct TcsCaNTv2Data southWest; struct TcsCaNTv2Data northEast; struct TcsCaNTv2Data northWest; /* Until we know differently. */ rtnValue = csGRIDI_ST_SYSTEM; thisPtr->CellIsValid = FALSE; /* In case of an error. This saves duplication of this many many times. */ CS_stncp (csErrnam,thisPtr->FilePath,MAXPATH); /* Remember, source is East Positive. All NTv2 files are West Positive. */ /* Locate the appropriate sub-grid. If there is none, than there is no coverage. There are two algorithms: the original one and one invented to cater to the Spaniards (and maybe some others in the future). In the original algorithm, we search through the top level of parent grids looking for coverage. The top level parents are those which have no parent. If none is found, there is no coverage. If we locate a parent which provides coverage, we examine all children of that parent looking for a sub-grid; and so on. In the Spanish algorithm, we search all grids, and choose the grid which produces the smallest cell size. This is necessary as the grids are allowed to overlap in the Spanish variation. */ cvtPtr = CSlocateSubNTv2 (thisPtr,source); /* OK, if cvtPtr is not NULL, its a pointer to the appropriate sub grid for this conversion. */ if (cvtPtr != NULL) { /* NTv2 files consider west longitude to be positive. */ wpLL [LNG] = -source [LNG]; wpLL [LAT] = source [LAT]; /* Determine the status of onLimit. This indicates if the point to be converted actually resides on the northern or western edge of the grid cell. */ onLimit = 0; if (fabs (wpLL [LAT] - cvtPtr->NwReference [LAT]) <= cs_LlNoise) onLimit |= 1; if (fabs (wpLL [LNG] - cvtPtr->NwReference [LNG]) <= cs_LlNoise) onLimit |= 2; if (thisPtr->fileImage == NULL) { stream = CS_fopen (thisPtr->FilePath,_STRM_BINRD); if (stream == NULL) { CS_stncp (csErrnam,thisPtr->FilePath,MAXPATH); CS_erpt (cs_DTC_FILE); goto error; } setvbuf (stream,NULL,_IOFBF,(size_t)thisPtr->BufferSize); // Determine the size of the file. if (CS_fseek (stream,0L,SEEK_END)) { CS_stncp (csErrnam,thisPtr->FilePath,MAXPATH); CS_erpt (cs_IOERR); goto error; } thisPtr->fileImageSize = CS_ftell (stream); if (thisPtr->fileImageSize < 0L) { CS_stncp (csErrnam,thisPtr->FilePath,MAXPATH); CS_erpt (cs_IOERR); goto error; } if (CS_fseek (stream, 0L, SEEK_SET)) { CS_stncp (csErrnam,thisPtr->FilePath,MAXPATH); CS_erpt (cs_IOERR); goto error; } // Prepare memory thisPtr->fileImage = (char*)CS_malc(thisPtr->fileImageSize); if (thisPtr->fileImage == NULL) { CS_erpt (cs_NO_MEM); goto error; } // Copy everything into the memory readCnt = CS_fread(thisPtr->fileImage, 1, thisPtr->fileImageSize, stream); if (CS_ferror(stream)) { CS_erpt (cs_IOERR); goto error; } CS_fclose (stream); stream = NULL; } /* Compute onLimit for this point and the selected sub-grid regardless of how we got here. This should now only occur at the extreme edges of the entire file coverage. */ onLimit = 0; if (fabs (wpLL [LAT] - cvtPtr->NwReference [LAT]) <= cs_LlNoise) onLimit |= 1; if (fabs (wpLL [LNG] - cvtPtr->NwReference [LNG]) <= cs_LlNoise) onLimit |= 2; /* Compute the elements required for the file access. This is common to all cases of "onLimit". */ eleNbr = (unsigned short)(((wpLL [LNG] - cvtPtr->SeReference [LNG]) / cvtPtr->DeltaLng) + cs_LlNoise); rowNbr = (unsigned short)(((wpLL [LAT] - cvtPtr->SeReference [LAT]) / cvtPtr->DeltaLat) + cs_LlNoise); /* Compute the boundaries of the specific cell we dealing with, assuming onLimit is zero (which is the case 99.999% of the time). */ seCell [LNG] = cvtPtr->SeReference [LNG] + cvtPtr->DeltaLng * (double)eleNbr; seCell [LAT] = cvtPtr->SeReference [LAT] + cvtPtr->DeltaLat * (double)rowNbr; nwCell [LNG] = seCell [LNG] + cvtPtr->DeltaLng; nwCell [LAT] = seCell [LAT] + cvtPtr->DeltaLng; /* Build the extent portions of the grid cells. */ thisPtr->longitudeCell.seCorner [LNG] = seCell [LNG]; thisPtr->longitudeCell.seCorner [LAT] = seCell [LAT]; thisPtr->longitudeCell.nwCorner [LNG] = nwCell [LNG]; thisPtr->longitudeCell.nwCorner [LAT] = nwCell [LAT]; thisPtr->longitudeCell.deltaLng = cvtPtr->DeltaLng; thisPtr->longitudeCell.deltaLat = cvtPtr->DeltaLat; thisPtr->longitudeCell.density = cvtPtr->Density; thisPtr->latitudeCell.seCorner [LNG] = seCell [LNG]; thisPtr->latitudeCell.seCorner [LAT] = seCell [LAT]; thisPtr->latitudeCell.nwCorner [LNG] = nwCell [LNG]; thisPtr->latitudeCell.nwCorner [LAT] = nwCell [LAT]; thisPtr->latitudeCell.deltaLng = cvtPtr->DeltaLng; thisPtr->latitudeCell.deltaLat = cvtPtr->DeltaLat; thisPtr->latitudeCell.density = cvtPtr->Density; /* We could reduce the code complexity here by getting smart with the onLimit thing. However, this gets very tricky. My excuse here is that what is code below emulates the way the Canadians did it in FORTRAN as best we can do in C. */ if (onLimit == 0) { /* The normal case, probably about 99.9999 percent of the time. Read the data into my record buffer. */ filePosition = cvtPtr->FirstRecord + rowNbr * cvtPtr->RowSize + eleNbr * thisPtr->RecSize; if ((filePosition + sizeof(southEast) + sizeof(southWest)) > thisPtr->fileImageSize) { CS_erpt (cs_INV_FILE); goto error; } memcpy(&southEast, thisPtr->fileImage + filePosition, sizeof(southEast)); /* Read southwest shifts. */ memcpy(&southWest, thisPtr->fileImage + filePosition + sizeof(southEast), sizeof(southWest)); /* Read northeast shifts. */ filePosition += cvtPtr->RowSize; if ((filePosition + sizeof(northEast) + sizeof(northWest)) > thisPtr->fileImageSize) { CS_erpt (cs_INV_FILE); goto error; } memcpy(&northEast, thisPtr->fileImage + filePosition, sizeof(northEast)); /* Read northwest shifts. */ memcpy(&northWest, thisPtr->fileImage + filePosition + sizeof(northEast), sizeof(northWest)); /* Swap as necessary. */ swapping = CS_bswap (&southEast,cs_BSWP_NTv2Data); if (swapping) { CS_bswap (&southWest,cs_BSWP_NTv2Data); CS_bswap (&northEast,cs_BSWP_NTv2Data); CS_bswap (&northWest,cs_BSWP_NTv2Data); } /* Build the grid cell AA, BB, CC, and DD values. */ thisPtr->longitudeCell.currentAA = southEast.del_lng; thisPtr->longitudeCell.currentBB = southWest.del_lng - southEast.del_lng; thisPtr->longitudeCell.currentCC = northEast.del_lng - southEast.del_lng; thisPtr->longitudeCell.currentDD = northWest.del_lng - southWest.del_lng - northEast.del_lng + southEast.del_lng; thisPtr->latitudeCell.currentAA = southEast.del_lat; thisPtr->latitudeCell.currentBB = southWest.del_lat - southEast.del_lat; thisPtr->latitudeCell.currentCC = northEast.del_lat - southEast.del_lat; thisPtr->latitudeCell.currentDD = northWest.del_lat - southWest.del_lat - northEast.del_lat + southEast.del_lat; } else if (onLimit == 1) { /* Point is on the extreme northern edge of the sub-grid. This occurs ocassionally. In this case, the "northern" boundary of the grid cell doesn't exist, and we must manufacture such. This is called a virtual cell in the Canadian documentation. */ filePosition = cvtPtr->FirstRecord + rowNbr * cvtPtr->RowSize + eleNbr * thisPtr->RecSize; if ((filePosition + sizeof(southEast) + sizeof(southWest)) > thisPtr->fileImageSize) { CS_erpt (cs_INV_FILE); goto error; } memcpy(&southEast, thisPtr->fileImage + filePosition, sizeof(southEast)); /* Read southwest shifts. */ memcpy(&southWest, thisPtr->fileImage + filePosition + sizeof(southEast), sizeof(southWest)); /* Swap as necessary. */ swapping = CS_bswap (&southEast,cs_BSWP_NTv2Data); if (swapping) { CS_bswap (&southWest,cs_BSWP_NTv2Data); } /* Do not attempt to read the northern boundary, it ain't there. Compute the AA, BB, CC, DD values. */ thisPtr->longitudeCell.currentAA = southEast.del_lng; thisPtr->longitudeCell.currentBB = southWest.del_lng - southEast.del_lng; thisPtr->longitudeCell.currentCC = cs_Zero; thisPtr->longitudeCell.currentDD = cs_Zero; thisPtr->latitudeCell.currentAA = southEast.del_lat; thisPtr->latitudeCell.currentBB = southWest.del_lat - southEast.del_lat; thisPtr->latitudeCell.currentCC = cs_Zero; thisPtr->latitudeCell.currentDD = cs_Zero; /* Adjust the grid cell boundaries to indicate that the northern limits are the same as the southern limits. I.e. a grid cell that has zero height. */ thisPtr->longitudeCell.nwCorner [LAT] = thisPtr->longitudeCell.seCorner [LAT] + cs_LlNoise; thisPtr->latitudeCell.nwCorner [LAT] = thisPtr->latitudeCell.seCorner [LAT] + cs_LlNoise; } else if (onLimit == 2) { /* Point is on the extreme western edge of the sub-grid. */ filePosition = cvtPtr->FirstRecord + rowNbr * cvtPtr->RowSize + eleNbr * thisPtr->RecSize; if ((filePosition + sizeof(southEast)) > thisPtr->fileImageSize) { CS_erpt (cs_INV_FILE); goto error; } memcpy(&southEast, thisPtr->fileImage + filePosition, sizeof(southEast)); /* Don't read the south west, it ain't there. */ filePosition += cvtPtr->RowSize; if ((filePosition + sizeof(northEast)) > thisPtr->fileImageSize) { CS_erpt (cs_INV_FILE); goto error; } memcpy(&northEast, thisPtr->fileImage + filePosition, sizeof(northEast)); /* Don't read the northwest, it ain't there. */ swapping = CS_bswap (&southEast,cs_BSWP_NTv2Data); if (swapping) { CS_bswap (&northEast,cs_BSWP_NTv2Data); } thisPtr->longitudeCell.currentAA = southEast.del_lng; thisPtr->longitudeCell.currentBB = cs_Zero; thisPtr->longitudeCell.currentCC = northEast.del_lng - southEast.del_lng; thisPtr->longitudeCell.currentDD = cs_Zero; thisPtr->latitudeCell.currentAA = southEast.del_lat; thisPtr->latitudeCell.currentBB = cs_Zero; thisPtr->latitudeCell.currentCC = northEast.del_lat - southEast.del_lat; thisPtr->latitudeCell.currentDD = cs_Zero; /* Adjust the grid cell boundaries to indicate that the eastern limits are the same as the western limits. I.e. a grid cell that has zero width. */ thisPtr->longitudeCell.nwCorner [LNG] = thisPtr->longitudeCell.seCorner [LNG] + cs_LlNoise; thisPtr->latitudeCell.nwCorner [LNG] = thisPtr->latitudeCell.seCorner [LNG] + cs_LlNoise; } else /* onLimit == 3 */ { /* Point is actually the northwestern corner of the sub-grid. */ filePosition = cvtPtr->FirstRecord + rowNbr * cvtPtr->RowSize + eleNbr * thisPtr->RecSize; if ((filePosition + sizeof(southEast)) > thisPtr->fileImageSize) { CS_erpt (cs_INV_FILE); goto error; } memcpy(&southEast, thisPtr->fileImage + filePosition, sizeof(southEast)); /* Don't read anything else. There's nothing there. */ CS_bswap (&southEast,cs_BSWP_NTv2Data); /* Compute the AA, BB, CC, DD values. */ thisPtr->longitudeCell.currentAA = southEast.del_lng; thisPtr->longitudeCell.currentBB = cs_Zero; thisPtr->longitudeCell.currentCC = cs_Zero; thisPtr->longitudeCell.currentDD = cs_Zero; thisPtr->latitudeCell.currentAA = southEast.del_lat; thisPtr->latitudeCell.currentBB = cs_Zero; thisPtr->latitudeCell.currentCC = cs_Zero; thisPtr->latitudeCell.currentDD = cs_Zero; /* Adjust the grid cell boundaries to indicate that the northeastern limits are the same as the southwestern limits. I.e. a grid cell that has zero width and zero height. */ thisPtr->longitudeCell.nwCorner [LNG] = thisPtr->longitudeCell.seCorner [LNG] + cs_LlNoise; thisPtr->latitudeCell.nwCorner [LNG] = thisPtr->latitudeCell.seCorner [LNG] + cs_LlNoise; thisPtr->longitudeCell.nwCorner [LAT] = thisPtr->longitudeCell.seCorner [LAT] + cs_LlNoise; thisPtr->latitudeCell.nwCorner [LAT] = thisPtr->latitudeCell.seCorner [LAT] + cs_LlNoise; } /* The cells are now valid, maybe. We now work around a bust in the Canadian NTV2_0.gsb grid data file. */ thisPtr->CellIsValid = TRUE; /* Perform the interpolation calculation. */ deltaLL [LNG] = CScalcNTv2GridCell (&thisPtr->longitudeCell,source); deltaLL [LAT] = CScalcNTv2GridCell (&thisPtr->latitudeCell,source); rtnValue = csGRIDI_ST_OK; } else { /* We didn't find a sub-grid. The return value is +1 to indicate no coverage. */ deltaLL [LNG] = cs_Zero; deltaLL [LAT] = cs_Zero; rtnValue = csGRIDI_ST_COVERAGE; } csErrnam [0] = '\0'; return rtnValue; error: if (stream != NULL) { CS_fclose (stream); stream = NULL; } return csGRIDI_ST_SYSTEM; }
int CSinitNTv2 (struct cs_NTv2_* thisPtr,Const char *filePath,long32_t bufferSize, ulong32_t flags, double density) { extern double cs_Sec2Deg; extern char cs_DirsepC; extern char csErrnam []; short idx; short parIdx; int overlap; int seekStat; size_t readCnt; size_t readCntRq; size_t malcCnt; long32_t skipAmount; char *cp; struct csNTv2SubGrid_* subPtr; struct csNTv2SubGrid_* kidPtr; struct csNTv2SubGrid_* parPtr; union csNtv2Hdrs_ fileHdr; struct csNTv2SubHdr_ fileSubHdr; char ctemp [MAXPATH]; csFILE* stream = NULL; /* Try to prevent a likely crash. */ if (thisPtr == NULL) { CS_stncp (csErrnam,"CS_ntv2::1",MAXPATH); CS_erpt (cs_ISER); return -1; } /* In the event of an error; this eliminates duplicating this many many times. */ CS_stncp (csErrnam,filePath,MAXPATH); /* Initialize the structure to harmless values. */ thisPtr->SubGridDir = NULL; thisPtr->fileImage = NULL; thisPtr->fileImageSize = 0; thisPtr->HdrRecCnt = 0; thisPtr->SubCount = 0; thisPtr->RecSize = 16; thisPtr->CellIsValid = FALSE; thisPtr->SubOverlap = (short)((flags & 0x01) != 0); thisPtr->IntType = csNTv2TypeNone; thisPtr->BufferSize = bufferSize; thisPtr->sourceId [0] = '\0'; if (thisPtr->BufferSize <= 0) thisPtr->BufferSize = csNTv2BufrSz; if (thisPtr->BufferSize <= 4096) thisPtr->BufferSize = 4096; CSinitNTv2GridCell (&thisPtr->longitudeCell); CSinitNTv2GridCell (&thisPtr->latitudeCell); /* Deal with the file path. */ CS_stncp (thisPtr->FilePath,filePath,sizeof (thisPtr->FilePath)); /* Extract and save last 15 characters of the data file name. */ cp = strrchr (thisPtr->FilePath,cs_DirsepC); if (cp == NULL) cp = thisPtr->FilePath; CS_stncp (ctemp,cp,sizeof (ctemp)); cp = strrchr (ctemp,'.'); if (cp != NULL) *cp = '\0'; cp = ctemp; if (strlen (ctemp) > 15) { cp = ctemp + strlen (ctemp) - 15; } CS_stncp (thisPtr->FileName,cp,sizeof (thisPtr->FileName)); /* Open the file. */ stream = CS_fopen (thisPtr->FilePath,_STRM_BINRD); if (stream == NULL) { CS_erpt (cs_DTC_FILE); goto error; } setvbuf (stream,NULL,_IOFBF,(size_t)thisPtr->BufferSize); /* We've got a file. Read the header. */ readCnt = CS_fread (&fileHdr,1,sizeof (fileHdr),stream); if (CS_ferror (stream)) { CS_erpt (cs_IOERR); goto error; } if (readCnt != sizeof (fileHdr)) { CS_erpt (cs_INV_FILE); goto error; } /* Verify that this is the kind of file we know how to deal with. */ if (strncmp (fileHdr.Canadian.titl01,"NUM_OREC",8)) { /* Opps!!! Not a CaNTv2 file. */ CS_erpt (cs_INV_FILE); goto error; } /* Determine the type/source of the file. */ if (fileHdr.Canadian.titl02 [0] == 'N' && fileHdr.Canadian.titl02 [1] == 'U') { /* It appears that the file is Canadian. */ thisPtr->IntType = csNTv2TypeCanada; skipAmount = sizeof (struct csNTv2HdrCa_); CS_bswap (&fileHdr.Canadian,cs_BSWP_NTv2HdrCa); } else if (fileHdr.Australian.titl02 [0] == 'N' && fileHdr.Australian.titl02 [1] == 'U') { /* It appears to be an Australian file. */ thisPtr->IntType = csNTv2TypeAustralia; skipAmount = sizeof (struct csNTv2HdrAu_); CS_bswap (&fileHdr.Australian,cs_BSWP_NTv2HdrAu); } else { /* Opps!!! Don't know what kind of file it is. */ CS_erpt (cs_INV_FILE); goto error; } /* Reposition the input file as is appropriate due to the type of file. A little hoeky, but it should be portable. */ seekStat = CS_fseek (stream,skipAmount,SEEK_SET); if (seekStat != 0) { CS_erpt (cs_INV_FILE); goto error; } /* Extract the valuable stuff. */ if (thisPtr->IntType == csNTv2TypeCanada) { thisPtr->HdrRecCnt = fileHdr.Canadian.num_orec; thisPtr->SubCount = fileHdr.Canadian.num_file; thisPtr->SubHdrRecCnt = fileHdr.Canadian.num_srec; } else { thisPtr->HdrRecCnt = fileHdr.Australian.num_orec; thisPtr->SubCount = fileHdr.Australian.num_file; thisPtr->SubHdrRecCnt = fileHdr.Australian.num_srec; } /* The rest of the header is pretty much useless. */ /* Now, we deal with the sub-directories. THese are very important. */ malcCnt = sizeof (struct csNTv2SubHdr_) * (ulong32_t)thisPtr->SubCount; thisPtr->SubGridDir = (struct csNTv2SubGrid_ *)CS_malc (malcCnt); if (thisPtr->SubGridDir == NULL) { CS_erpt (cs_NO_MEM); goto error; } /* Initialize (i.e. construct) each of the sub-grid items we just allocated. */ for (idx = 0;idx < thisPtr->SubCount;idx += 1) { subPtr = &thisPtr->SubGridDir [idx]; /* Initialize to a boundary which will not match anything. */ subPtr->SouthWest [LNG] = 180.0; subPtr->SouthWest [LAT] = 90.0; subPtr->NorthEast [LNG] = -180.0; subPtr->NorthEast [LAT] = -90.0; /* Remember, these values as extracted from the file itself are WEST positive. */ subPtr->SeReference [LNG] = 180.0; subPtr->SeReference [LAT] = 90.0; subPtr->NwReference [LNG] = -180.0; subPtr->NwReference [LAT] = -90.0; subPtr->DeltaLng = 0.0; subPtr->DeltaLat = 0.0; subPtr->Density = 0.0; subPtr->FirstRecord = -1; subPtr->GridRecCnt = 0; subPtr->ParentIndex = -1; subPtr->ChildIndex = -1; subPtr->RowCount = 0; subPtr->ElementCount = 0; subPtr->RowSize = 0; subPtr->Cacheable = FALSE; subPtr->Name [0] = '\0'; subPtr->Parent [0] = '\0'; } /* Once for each sub-grid in the file; read in the header. At this point, we just read them in. Later on, we peruse the array and figure out who the mamas and the papas are. */ for (idx = 0;idx < thisPtr->SubCount;idx += 1) { /* Kludge to handle the variation in format. Doing this right would require duplication of a whole bunch of code. So . . . */ readCntRq = sizeof (fileSubHdr); if (thisPtr->IntType == csNTv2TypeAustralia) readCntRq -= 4; readCnt = CS_fread (&fileSubHdr,1,readCntRq,stream); if (CS_ferror (stream)) { CS_erpt (cs_IOERR); goto error; } if (readCnt != readCntRq) { CS_erpt (cs_INV_FILE); goto error; } if (strncmp (fileSubHdr.titl01,"SUB_NAME",8)) { CS_erpt (cs_INV_FILE); goto error; } if (thisPtr->IntType == csNTv2TypeCanada) { CS_bswap (&fileSubHdr,cs_BSWP_NTv2SubHdrCA); } else { CS_bswap (&fileSubHdr,cs_BSWP_NTv2SubHdrAU); } /* Collect the useful stuff. */ subPtr = &thisPtr->SubGridDir [idx]; /* Data for each sub-grid immediately follows the sub-grid header. */ subPtr->FirstRecord = CS_ftell (stream); /* These boundaries are rational east positive boundaries. */ subPtr->SouthWest [LNG] = -fileSubHdr.w_long * cs_Sec2Deg; subPtr->SouthWest [LAT] = fileSubHdr.s_lat * cs_Sec2Deg; subPtr->NorthEast [LNG] = -fileSubHdr.e_long * cs_Sec2Deg; subPtr->NorthEast [LAT] = fileSubHdr.n_lat * cs_Sec2Deg; /* These boundaries are the screwy west positive ones used in the NTv2 format. */ subPtr->SeReference [LNG] = fileSubHdr.e_long * cs_Sec2Deg; subPtr->SeReference [LAT] = fileSubHdr.s_lat * cs_Sec2Deg; subPtr->NwReference [LNG] = fileSubHdr.w_long * cs_Sec2Deg; subPtr->NwReference [LAT] = fileSubHdr.n_lat * cs_Sec2Deg; /* The remainder of this is pretty rational. */ subPtr->DeltaLng = fileSubHdr.long_inc * cs_Sec2Deg; subPtr->DeltaLat = fileSubHdr.lat_inc * cs_Sec2Deg; /* We do not use Density in the calculations. It is only used to select one sub-grid over another in the case of overlap. Yes, I know. The sub-grids at the same level are not suppoded to overlap; but they do. Call it job security for you an me. */ subPtr->Density = (subPtr->DeltaLat < subPtr->DeltaLng) ? subPtr->DeltaLat : subPtr->DeltaLng; /* If the user has specified a default density value, we use it. */ if (density != 0.0) { subPtr->Density = density; } /* Save the name for reporting purposes. */ CS_stncp (subPtr->Name,fileSubHdr.sub_name,9); CS_stncp (subPtr->Parent,fileSubHdr.parent,9); subPtr->GridRecCnt = fileSubHdr.gs_count; /* WEST Positive, dummy. The extra .01 is to eliminate possible fuzz in the double portion of the calculations. */ subPtr->RowCount = (unsigned short)(((subPtr->NwReference [LAT] - subPtr->SeReference [LAT]) / subPtr->DeltaLat) + 1.01); subPtr->ElementCount = (unsigned short)(((subPtr->NwReference [LNG] - subPtr->SeReference [LNG]) / subPtr->DeltaLng) + 1.01); subPtr->RowSize = (unsigned short)(subPtr->ElementCount * thisPtr->RecSize); /* Certain sub grids are not cacheable. In the Canadian file, the region which is not cacheable is rather small. We use the csCaNTv2KludgeTable to handle it. The one Austrailian sub-grid we've seen is screwed up, so we disable cacheing (at least for now), for all Australian files. Australian, in this context, means file in the old Australian format, not necessarily data files covering Australian geography. In the case of the Spanish variation, parent grids overlap, and therefore none of the sub-grids are cacheable. */ //???? subPtr->Cacheable = (short)((thisPtr->IntType == csNTv2TypeCanada) && (thisPtr->SubOverlap == 0)); subPtr->Cacheable = FALSE; /* Skip over the data records in the file. */ skipAmount = subPtr->GridRecCnt * thisPtr->RecSize; seekStat = CS_fseek (stream,skipAmount,SEEK_CUR); if (seekStat != 0) { CS_erpt (cs_INV_FILE); goto error; } } /* Now we figure out who the mammas and the pappas are. Note, all we have to work with are parent names. Therefore, we have to work bassackwards. End result of all of this, is that each child needs to have the index of its parent; and each sub-grid that has a child needs to be so marked. */ for (idx = 0;idx < thisPtr->SubCount;idx += 1) { kidPtr = &thisPtr->SubGridDir [idx]; if (CS_stricmp (kidPtr->Parent,"NONE ")) { /* Its a child, find the parent. */ for (parIdx = 0;parIdx < thisPtr->SubCount;parIdx += 1) { parPtr = &thisPtr->SubGridDir [parIdx]; if (!CS_stricmp (kidPtr->Parent,parPtr->Name)) { /* Save the index of the parent. */ kidPtr->ParentIndex = parIdx; /* Mark the parent as having a child, if not already so marked. */ if (parPtr->ChildIndex == -1 || parPtr->ChildIndex > idx) { parPtr->ChildIndex = idx; } } } } } /* To accomodate the Spanish (and perhaps others in the future, we check the parent grids in the list of sub-grids for overlap. If overlap exists, we turn on the SubOverlap flag. Of course, if this flag is already on, we have nothing to do. If we did indeed turn on the SubOverlap flag, we need to cruise through all the sub-grids and set the Cacheable flag to false to assure that no data from this file makes it to the grid cell cache. */ if (thisPtr->SubOverlap == 0) { for (parIdx = 0;parIdx < thisPtr->SubCount && thisPtr->SubOverlap == 0;parIdx += 1) { parPtr = &thisPtr->SubGridDir [parIdx]; /* Top level grids only, we know the children overlap. */ if (parPtr->ParentIndex >= 0) continue; overlap = FALSE; for (idx = 0;idx < thisPtr->SubCount;idx += 1) { if (idx == parIdx) continue; subPtr = &thisPtr->SubGridDir [idx]; if (subPtr->ParentIndex >= 0) continue; /* See if subPtr overlaps with parPtr. */ overlap = subPtr->SeReference [LNG] > parPtr->SeReference [LNG] && subPtr->SeReference [LAT] > parPtr->SeReference [LAT] && subPtr->SeReference [LNG] < parPtr->NwReference [LNG] && subPtr->SeReference [LAT] < parPtr->NwReference [LAT]; overlap |= subPtr->NwReference [LNG] > parPtr->SeReference [LNG] && subPtr->NwReference [LAT] > parPtr->SeReference [LAT] && subPtr->NwReference [LNG] < parPtr->NwReference [LNG] && subPtr->NwReference [LAT] < parPtr->NwReference [LAT]; if (overlap) { thisPtr->SubOverlap = TRUE; /* for testing ease */ } } } if (thisPtr->SubOverlap != 0) { for (idx = 0;idx < thisPtr->SubCount;idx += 1) { subPtr = &thisPtr->SubGridDir [idx]; subPtr->Cacheable = FALSE; } } } /* OK, we should be ready to rock and roll. We close the Stream until we actually need it. Often, we get constructed just so there is a record of the coverage afforded by the file. */ if (stream != NULL) { CS_fclose (stream); stream = NULL; } csErrnam [0] = '\0'; return 0; error: if (stream != NULL) { CS_fclose (stream); stream = NULL; } if (thisPtr->SubGridDir != NULL) { CS_free (thisPtr->SubGridDir); thisPtr->SubGridDir = NULL; } thisPtr->HdrRecCnt = 0; thisPtr->SubCount = 0; thisPtr->RecSize = 16; thisPtr->CellIsValid = FALSE; thisPtr->SubOverlap = (short)((flags & 0x01) != 0); thisPtr->IntType = csNTv2TypeNone; thisPtr->BufferSize = bufferSize; thisPtr->sourceId [0] = '\0'; if (thisPtr->BufferSize <= 0) thisPtr->BufferSize = csNTv2BufrSz; if (thisPtr->BufferSize <= 4096) thisPtr->BufferSize = 4096; CSinitNTv2GridCell (&thisPtr->longitudeCell); CSinitNTv2GridCell (&thisPtr->latitudeCell); return -1; }
int EXP_LVL3 CS_dtupd (struct cs_Dtdef_ *dtdef,int crypt) { extern char csErrnam []; extern short cs_Protect; extern char cs_Unique; short cs_time; int st; int flag; int dummy; csFILE *strm; long fpos; char *cp; __ALIGNMENT__1 /* For some versions of Sun compiler. */ struct cs_Dtdef_ my_dtdef; /* Capture the current time. For our purposes here, time is the number of days since (approx) January 1, 1990. If this record does get written, the protect field will indicate that it has changed. */ cs_time = (short)((CS_time ((cs_Time_ *)0) - 630720000L) / 86400L); if (dtdef->protect >= 0) { dtdef->protect = cs_time; } /* Prepare for a possible error. */ strm = NULL; /* Adjust the name and make sure it is all upper case. By convention, datum names are case insensitive. */ st = CS_nampp (dtdef->key_nm); if (st != 0) goto error; /* Open up the Datum Dictionary and verify its magic number. */ strm = CS_dtopn (_STRM_BINUP); if (strm == NULL) { goto error; } /* See if we have a datum with this name already. */ flag = CS_bins (strm,(long)sizeof (cs_magic_t),0L,sizeof (*dtdef), (char *)dtdef,(CMPFUNC_CAST)CS_dtcmp); if (flag < 0) goto error; if (flag) { /* Here when the datum already exists. See if we are allowed to change this definition. */ if (cs_Protect >= 0) { /* Distribution protection is enabled. */ fpos = CS_ftell (strm); if (fpos < 0L) { CS_erpt (cs_IOERR); goto error; } st = CS_dtrd (strm,&my_dtdef,&dummy); if (st == 0) CS_erpt (cs_INV_FILE); if (st <= 0) { goto error; } if (my_dtdef.protect == 1) { CS_stncp (csErrnam,dtdef->key_nm,MAXPATH); CS_erpt (cs_DT_PROT); goto error; } if (cs_Protect > 0 && my_dtdef.protect > 0) { if (my_dtdef.protect < (cs_time - cs_Protect)) /*lint !e644 */ { CS_stncp (csErrnam,dtdef->key_nm,MAXPATH); CS_erpt (cs_DT_UPROT); goto error; } } st = CS_fseek (strm,fpos,SEEK_SET); if (st < 0L) { CS_erpt (cs_IOERR); goto error; } } /* If we're still here, it's OK to update this definition. */ if (CS_dtwr (strm,dtdef,crypt)) { goto error; } } else { /* Here if the datum definition doesn't exist. We have to add it. If cs_Unique is not zero, we require that a cs_Unique character be present in the key name before we'll allow it to be written. */ if (cs_Unique != '\0') { cp = strchr (dtdef->key_nm,cs_Unique); if (cp == NULL) { csErrnam [0] = cs_Unique; csErrnam [1] = '\0'; CS_erpt (cs_UNIQUE); goto error; } } /* Now we can add it. Write to the end of the file, and then sort the file. */ st = CS_fseek (strm,0L,SEEK_END); if (st != 0) { CS_erpt (cs_IOERR); goto error; } if (CS_dtwr (strm,dtdef,crypt)) { goto error; } /* Sort the file into proper order, thereby moving the new datum to its proper place in the dictionary. */ st = CS_fseek (strm,(long)sizeof (cs_magic_t),SEEK_SET); if (st != 0) { CS_erpt (cs_IOERR); goto error; } st = CS_ips (strm,sizeof (*dtdef),0L,(CMPFUNC_CAST)CS_dtcmp); if (st < 0) goto error; } /* The Datum Dictionary has been updated. */ CS_dtDictCls (strm); return (flag); error: if (strm != NULL) CS_dtDictCls (strm); return (-1); }