1 : /**********************************************************************
2 : * $Id: avc_e00write.c,v 1.21 2008/07/23 20:51:38 dmorissette Exp $
3 : *
4 : * Name: avc_e00write.c
5 : * Project: Arc/Info vector coverage (AVC) E00->BIN conversion library
6 : * Language: ANSI C
7 : * Purpose: Functions to create a binary coverage from a stream of
8 : * ASCII E00 lines.
9 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
10 : *
11 : **********************************************************************
12 : * Copyright (c) 1999-2001, Daniel Morissette
13 : *
14 : * Permission is hereby granted, free of charge, to any person obtaining a
15 : * copy of this software and associated documentation files (the "Software"),
16 : * to deal in the Software without restriction, including without limitation
17 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 : * and/or sell copies of the Software, and to permit persons to whom the
19 : * Software is furnished to do so, subject to the following conditions:
20 : *
21 : * The above copyright notice and this permission notice shall be included
22 : * in all copies or substantial portions of the Software.
23 : *
24 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 : * DEALINGS IN THE SOFTWARE.
31 : **********************************************************************
32 : *
33 : * $Log: avc_e00write.c,v $
34 : * Revision 1.21 2008/07/23 20:51:38 dmorissette
35 : * Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char
36 : * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495)
37 : *
38 : * Revision 1.20 2006/06/27 18:38:43 dmorissette
39 : * Cleaned up E00 reading (bug 1497, patch from James F.)
40 : *
41 : * Revision 1.19 2006/06/14 16:31:28 daniel
42 : * Added support for AVCCoverPC2 type (bug 1491)
43 : *
44 : * Revision 1.18 2006/03/02 22:46:26 daniel
45 : * Accept empty subclass names for TX6/TX7 sections (bug 1261)
46 : *
47 : * Revision 1.17 2005/06/03 03:49:59 daniel
48 : * Update email address, website url, and copyright dates
49 : *
50 : * Revision 1.16 2002/08/27 15:46:15 daniel
51 : * Applied fix made in GDAL/OGR by 'aubin' (moved include ctype.h after avc.h)
52 : *
53 : * Revision 1.15 2002/04/16 21:19:10 daniel
54 : * Use VSIRmdir()
55 : *
56 : * Revision 1.14 2002/03/18 19:00:44 daniel
57 : * Use VSIMkdir() and not VSIMkDir()
58 : *
59 : * Revision 1.13 2002/02/18 21:16:33 warmerda
60 : * modified to use VSIMkDir
61 : *
62 : * Revision 1.12 2001/05/23 15:23:17 daniel
63 : * Remove trailing '/' in info directory path when creating the info dir.
64 : *
65 : * Revision 1.11 2000/09/26 20:21:04 daniel
66 : * Added AVCCoverPC write
67 : *
68 : * Revision 1.10 2000/09/22 19:45:21 daniel
69 : * Switch to MIT-style license
70 : *
71 : * Revision 1.9 2000/05/29 22:47:39 daniel
72 : * Made validation on new coverage name more flexible.
73 : *
74 : * Revision 1.8 2000/05/29 15:31:31 daniel
75 : * Added Japanese DBCS support
76 : *
77 : * Revision 1.7 2000/02/14 17:19:53 daniel
78 : * Accept '-' cahracter in new coverage name
79 : *
80 : * Revision 1.6 2000/01/10 02:57:44 daniel
81 : * Little changes to accomodate read support for "weird" coverages
82 : *
83 : * Revision 1.5 1999/12/24 07:18:34 daniel
84 : * Added PC Arc/Info coverages support
85 : *
86 : * Revision 1.4 1999/08/26 17:36:36 daniel
87 : * Avoid overwriting arc.dir on Windows... happened only when several
88 : * coverages are created by the same process on Windows.
89 : *
90 : * Revision 1.3 1999/08/23 18:23:35 daniel
91 : * Added AVCE00DeleteCoverage()
92 : *
93 : * Revision 1.2 1999/05/17 16:23:36 daniel
94 : * Added AVC_DEFAULT_PREC + more cover name validation in AVCE00WriteOpen().
95 : *
96 : * Revision 1.1 1999/05/11 02:34:46 daniel
97 : * Initial revision
98 : *
99 : **********************************************************************/
100 :
101 : #include "cpl_vsi.h"
102 : #include "avc.h"
103 : #include <ctype.h> /* tolower() */
104 :
105 : static GBool _IsStringAlnum(const char *pszFname);
106 :
107 : /**********************************************************************
108 : * AVCE00WriteOpen()
109 : *
110 : * Open (create) an Arc/Info coverage, ready to be receive a stream
111 : * of ASCII E00 lines and convert that to the binary coverage format.
112 : *
113 : * For now, writing to or overwriting existing coverages is not supported
114 : * (and may quite well never be!)... you can only create new coverages.
115 : *
116 : * Important Note: The E00 source lines are assumed to be valid... the
117 : * library performs no validation on the consistency of what it is
118 : * given as input (i.e. topology, polygons consistency, etc.).
119 : * So the coverage that will be created will be only as good as the
120 : * E00 input that is used to generate it.
121 : *
122 : * pszCoverPath MUST be the name of the coverage directory, including
123 : * the path to it.
124 : * (contrary to AVCE00ReadOpen(), you cannot pass the name of one of
125 : * the files in the coverage directory).
126 : * The name of the coverage MUST be included in pszCoverPath... this
127 : * means that passing "." is invalid.
128 : *
129 : * eNewCoverType is the type of coverage to create.
130 : * Either AVCCoverV7 (Arc/Info V7 (Unix) coverage)
131 : * or AVCCoverPC (PC Arc/Info coverage)
132 : *
133 : * nPrecision should always be AVC_DEFAULT_PREC to automagically detect the
134 : * source coverage's precision and use that same precision
135 : * for the new coverage.
136 : *
137 : * This parameter has been included to allow adding the
138 : * possibility to eventually create coverages with a precision
139 : * different from the source E00.
140 : * Given the way the lib is built, it could be possible to
141 : * also pass AVC_SINGLE_PREC or AVC_DOUBLE_PREC to explicitly
142 : * request the creation of a coverage with that precision,
143 : * but the library does not (not yet!) properly convert the
144 : * TABLE attributes' precision, and the resulting coverage may
145 : * be invalid in some cases.
146 : * This improvement is on the ToDo list!
147 : *
148 : * Returns a new AVCE00WritePtr handle or NULL if the coverage could
149 : * not be created or if a coverage with that name already exists.
150 : *
151 : * The handle will eventually have to be released with AVCE00ReadClose().
152 : **********************************************************************/
153 0 : AVCE00WritePtr AVCE00WriteOpen(const char *pszCoverPath,
154 : AVCCoverType eNewCoverType, int nPrecision )
155 : {
156 : AVCE00WritePtr psInfo;
157 : int i, nLen;
158 : VSIStatBuf sStatBuf;
159 :
160 0 : CPLErrorReset();
161 :
162 : /*-----------------------------------------------------------------
163 : * Create pszCoverPath directory.
164 : *----------------------------------------------------------------*/
165 0 : if (pszCoverPath == NULL || strlen(pszCoverPath) == 0)
166 : {
167 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
168 : "Invalid (empty) coverage directory name.");
169 0 : return NULL;
170 : }
171 0 : else if ( VSIStat(pszCoverPath, &sStatBuf) == 0 &&
172 0 : VSI_ISDIR(sStatBuf.st_mode) )
173 : {
174 : /*-------------------------------------------------------------
175 : * Directory already exists... make sure it is empty
176 : * otherwise we can't use it as a coverage directory.
177 : *------------------------------------------------------------*/
178 : char **papszFiles;
179 0 : papszFiles = CPLReadDir(pszCoverPath);
180 0 : for(i=0; papszFiles && papszFiles[i]; i++)
181 : {
182 0 : if (!EQUAL(".", papszFiles[i]) &&
183 0 : !EQUAL("..", papszFiles[i]))
184 : {
185 0 : CPLError(CE_Failure, CPLE_OpenFailed,
186 : "Cannot create coverage %s: directory already exists "
187 : "and is not empty.", pszCoverPath);
188 0 : CSLDestroy(papszFiles);
189 0 : papszFiles = NULL;
190 0 : return NULL;
191 : }
192 : }
193 :
194 0 : CSLDestroy(papszFiles);
195 0 : papszFiles = NULL;
196 : }
197 : else
198 : {
199 : /*-------------------------------------------------------------
200 : * Create new pszCoverPath directory.
201 : * This will fail if a file with the same name already exists.
202 : *------------------------------------------------------------*/
203 0 : if( VSIMkdir(pszCoverPath, 0777) != 0 )
204 : {
205 0 : CPLError(CE_Failure, CPLE_OpenFailed,
206 : "Unable to create coverage directory: %s.", pszCoverPath);
207 0 : return NULL;
208 : }
209 : }
210 :
211 : /*-----------------------------------------------------------------
212 : * Alloc the AVCE00WritePtr handle
213 : *----------------------------------------------------------------*/
214 0 : psInfo = (AVCE00WritePtr)CPLCalloc(1, sizeof(struct AVCE00WriteInfo_t));
215 :
216 : /*-----------------------------------------------------------------
217 : * Validate and store coverage type
218 : *----------------------------------------------------------------*/
219 0 : if (eNewCoverType == AVCCoverV7 || eNewCoverType == AVCCoverPC)
220 0 : psInfo->eCoverType = eNewCoverType;
221 : else
222 : {
223 0 : CPLError(CE_Failure, CPLE_NotSupported,
224 : "Requested coverage type cannot be created. Please use "
225 : "the AVCCoverV7 or AVCCoverPC coverage type.");
226 0 : CPLFree(psInfo);
227 0 : return NULL;
228 : }
229 :
230 : /*-----------------------------------------------------------------
231 : * Requested precision for the new coverage... for now only
232 : * AVC_DEFAULT_PREC is supported. When the first section is
233 : * read, then this section's precision will be used for the whole
234 : * coverage. (This is done inside AVCE00WriteNextLine())
235 : *----------------------------------------------------------------*/
236 0 : if (psInfo->eCoverType == AVCCoverPC)
237 0 : psInfo->nPrecision = AVC_SINGLE_PREC; /* PC Cover always single prec.*/
238 0 : else if (nPrecision == AVC_DEFAULT_PREC)
239 0 : psInfo->nPrecision = nPrecision;
240 : else
241 : {
242 0 : CPLError(CE_Failure, CPLE_IllegalArg,
243 : "Coverages can only be created using AVC_DEFAULT_PREC. "
244 : "Please see the documentation for AVCE00WriteOpen().");
245 0 : CPLFree(psInfo);
246 0 : return NULL;
247 : }
248 :
249 : /*-----------------------------------------------------------------
250 : * Make sure coverage directory name is terminated with a '/' (or '\\')
251 : *----------------------------------------------------------------*/
252 0 : nLen = strlen(pszCoverPath);
253 :
254 0 : if (pszCoverPath[nLen-1] == '/' || pszCoverPath[nLen-1] == '\\')
255 0 : psInfo->pszCoverPath = CPLStrdup(pszCoverPath);
256 : else
257 : {
258 : #ifdef WIN32
259 : psInfo->pszCoverPath = CPLStrdup(CPLSPrintf("%s\\",pszCoverPath));
260 : #else
261 0 : psInfo->pszCoverPath = CPLStrdup(CPLSPrintf("%s/",pszCoverPath));
262 : #endif
263 : }
264 :
265 : /*-----------------------------------------------------------------
266 : * Extract the coverage name from the coverage path. Note that
267 : * for this the coverage path must be in the form:
268 : * "dir1/dir2/dir3/covername/" ... if it is not the case, then
269 : * we would have to use getcwd() to find the current directory name...
270 : * but for now we'll just produce an error if this happens.
271 : *----------------------------------------------------------------*/
272 0 : nLen = 0;
273 0 : for( i = strlen(psInfo->pszCoverPath)-1;
274 0 : i > 0 && psInfo->pszCoverPath[i-1] != '/' &&
275 0 : psInfo->pszCoverPath[i-1] != '\\'&&
276 0 : psInfo->pszCoverPath[i-1] != ':';
277 0 : i-- )
278 : {
279 0 : nLen++;
280 : }
281 :
282 0 : if (nLen > 0)
283 : {
284 0 : psInfo->pszCoverName = CPLStrdup(psInfo->pszCoverPath+i);
285 0 : psInfo->pszCoverName[nLen] = '\0';
286 : }
287 : else
288 : {
289 0 : CPLError(CE_Failure, CPLE_OpenFailed,
290 : "Invalid coverage path (%s): "
291 : "coverage name must be included in path.", pszCoverPath);
292 :
293 0 : CPLFree(psInfo->pszCoverPath);
294 0 : CPLFree(psInfo);
295 0 : return NULL;
296 : }
297 :
298 0 : if (strlen(psInfo->pszCoverName) > 13 ||
299 0 : !_IsStringAlnum(psInfo->pszCoverName) )
300 : {
301 0 : CPLError(CE_Failure, CPLE_OpenFailed,
302 : "Invalid coverage name (%s): "
303 : "coverage name must be 13 chars or less and contain only "
304 : "alphanumerical characters, '-' or '_'.",
305 : psInfo->pszCoverName);
306 :
307 0 : CPLFree(psInfo->pszCoverPath);
308 0 : CPLFree(psInfo->pszCoverName);
309 0 : CPLFree(psInfo);
310 0 : return NULL;
311 : }
312 :
313 0 : if (psInfo->eCoverType == AVCCoverPC || psInfo->eCoverType == AVCCoverPC2)
314 : {
315 : /*-------------------------------------------------------------
316 : * No 'info' directory is required for PC coverages
317 : *------------------------------------------------------------*/
318 0 : psInfo->pszInfoPath = NULL;
319 : }
320 : else
321 : {
322 : /*-------------------------------------------------------------
323 : * Lazy way to build the INFO path: simply add "../info/"...
324 : * this could probably be improved!
325 : *------------------------------------------------------------*/
326 0 : psInfo->pszInfoPath = (char*)CPLMalloc((strlen(psInfo->pszCoverPath)+9)
327 : *sizeof(char));
328 : #ifdef WIN32
329 : # define AVC_INFOPATH "..\\info\\"
330 : #else
331 : # define AVC_INFOPATH "../info/"
332 : #endif
333 0 : sprintf(psInfo->pszInfoPath, "%s%s", psInfo->pszCoverPath,
334 : AVC_INFOPATH);
335 :
336 : /*-------------------------------------------------------------
337 : * Check if the info directory exists and contains the "arc.dir"
338 : * if the info dir does not exist, then make sure we can create
339 : * the arc.dir file (i.e. try to create an empty one)
340 : *
341 : * Note: On Windows, this VSIStat() call seems to sometimes fail even
342 : * when the directory exists (buffering issue?), and the
343 : * following if() block is sometimes executed even if it
344 : * should not, but this should not cause problems since the
345 : * arc.dir is opened with "a+b" access.
346 : *------------------------------------------------------------*/
347 0 : if ( VSIStat(psInfo->pszInfoPath, &sStatBuf) == -1)
348 : {
349 : FILE *fp;
350 : char *pszArcDir;
351 : char *pszInfoDir;
352 :
353 0 : pszArcDir = CPLStrdup(CPLSPrintf("%s%s",
354 : psInfo->pszInfoPath, "arc.dir"));
355 :
356 : /* Remove the trailing "/" from pszInfoPath. Most OSes are
357 : * forgiving, and allow mkdir to include the trailing character,
358 : * but some UNIXes are not. [GEH 2001/05/17]
359 : */
360 0 : pszInfoDir = CPLStrdup(psInfo->pszInfoPath);
361 0 : pszInfoDir[strlen(pszInfoDir)-1] = '\0';
362 :
363 0 : VSIMkdir(pszInfoDir, 0777);
364 0 : fp = VSIFOpen(pszArcDir, "a+b");
365 :
366 0 : CPLFree(pszArcDir);
367 0 : CPLFree(pszInfoDir);
368 0 : if (fp)
369 : {
370 0 : VSIFClose(fp);
371 : }
372 : else
373 : {
374 0 : CPLError(CE_Failure, CPLE_OpenFailed,
375 : "Unable to create (or write to) 'info' directory %s",
376 : psInfo->pszInfoPath);
377 0 : CPLFree(psInfo->pszCoverPath);
378 0 : CPLFree(psInfo->pszInfoPath);
379 0 : CPLFree(psInfo);
380 0 : return NULL;
381 : }
382 : }
383 : }
384 :
385 : /*-----------------------------------------------------------------
386 : * Init the E00 parser.
387 : *----------------------------------------------------------------*/
388 0 : psInfo->hParseInfo = AVCE00ParseInfoAlloc();
389 0 : psInfo->eCurFileType = AVCFileUnknown;
390 :
391 : /*-----------------------------------------------------------------
392 : * Init multibyte encoding info
393 : *----------------------------------------------------------------*/
394 0 : psInfo->psDBCSInfo = AVCAllocDBCSInfo();
395 :
396 : /*-----------------------------------------------------------------
397 : * If an error happened during the open call, cleanup and return NULL.
398 : *----------------------------------------------------------------*/
399 0 : if (CPLGetLastErrorNo() != 0)
400 : {
401 0 : AVCE00WriteClose(psInfo);
402 0 : psInfo = NULL;
403 : }
404 :
405 0 : return psInfo;
406 : }
407 :
408 : /**********************************************************************
409 : * AVCE00WriteClose()
410 : *
411 : * Close a coverage and release all memory used by the AVCE00WritePtr
412 : * handle.
413 : **********************************************************************/
414 0 : void AVCE00WriteClose(AVCE00WritePtr psInfo)
415 : {
416 0 : CPLErrorReset();
417 :
418 0 : if (psInfo == NULL)
419 0 : return;
420 :
421 0 : CPLFree(psInfo->pszCoverPath);
422 0 : CPLFree(psInfo->pszCoverName);
423 0 : CPLFree(psInfo->pszInfoPath);
424 :
425 0 : if (psInfo->hFile)
426 0 : AVCBinWriteClose(psInfo->hFile);
427 :
428 0 : if (psInfo->hParseInfo)
429 0 : AVCE00ParseInfoFree(psInfo->hParseInfo);
430 :
431 0 : AVCFreeDBCSInfo(psInfo->psDBCSInfo);
432 :
433 0 : CPLFree(psInfo);
434 : }
435 :
436 :
437 : /**********************************************************************
438 : * _IsStringAlnum()
439 : *
440 : * Scan a string, and return TRUE if it contains only valid characters,
441 : * Return FALSE otherwise.
442 : *
443 : * We used to accept only isalnum() chars, but since extended chars with
444 : * accents seem to be accepted, we will only check for chars that
445 : * could confuse the lib.
446 : **********************************************************************/
447 0 : static GBool _IsStringAlnum(const char *pszFname)
448 : {
449 0 : GBool bOK = TRUE;
450 :
451 0 : while(bOK && *pszFname != '\0')
452 : {
453 0 : if (strchr(" \t.,/\\", (unsigned char)*pszFname) != NULL)
454 0 : bOK = FALSE;
455 0 : pszFname ++;
456 : }
457 :
458 0 : return bOK;
459 : }
460 :
461 : /**********************************************************************
462 : * _AVCE00WriteRenameTable()
463 : *
464 : * Rename the table and the system fields in a tabledef that will
465 : * be written to a new coverage.
466 : **********************************************************************/
467 0 : static void _AVCE00WriteRenameTable(AVCTableDef *psTableDef,
468 : const char *pszNewCoverName)
469 : {
470 : char szOldName[40], szOldExt[40], szNewName[40], *pszTmp;
471 : char szSysId[40], szUserId[40];
472 : int i;
473 :
474 0 : strcpy(szNewName, pszNewCoverName);
475 0 : for(i=0; szNewName[i] != '\0'; i++)
476 0 : szNewName[i] = toupper(szNewName[i]);
477 :
478 : /*-----------------------------------------------------------------
479 : * Extract components from the current table name.
480 : *----------------------------------------------------------------*/
481 0 : strcpy(szOldName, psTableDef->szTableName);
482 :
483 0 : if ( !EQUAL(psTableDef->szExternal, "XX") ||
484 : (pszTmp = strchr(szOldName, '.')) == NULL )
485 0 : return; /* We don't deal with that table */
486 :
487 0 : *pszTmp = '\0';
488 0 : pszTmp++;
489 :
490 0 : strcpy(szOldExt, pszTmp);
491 0 : if ( (pszTmp = strchr(szOldExt, ' ')) != NULL )
492 0 : *pszTmp = '\0';
493 :
494 0 : if (strlen(szOldExt) < 3)
495 0 : return; /* We don't deal with that table */
496 :
497 : /*-----------------------------------------------------------------
498 : * Look for system attributes with same name as table
499 : * If the table name extension is followed by a subclass name
500 : * (e.g. "TEST.PATCOUNTY") then this subclass is used to build
501 : * the system attributes (COUNTY# and COUNTY-ID) and thus we do
502 : * not need to rename them
503 : * Otherwise (e.g. COUNTY.PAT) the coverage name is used and then
504 : * we need to rename these attribs for the new coverage name.
505 : *----------------------------------------------------------------*/
506 0 : if (strlen(szOldExt) == 3)
507 : {
508 0 : sprintf(szSysId, "%s#", szOldName);
509 0 : sprintf(szUserId, "%s-ID", szOldName);
510 :
511 0 : for(i=0; i<psTableDef->numFields; i++)
512 : {
513 : /* Remove trailing spaces */
514 0 : if ((pszTmp=strchr(psTableDef->pasFieldDef[i].szName,' '))!=NULL)
515 0 : *pszTmp = '\0';
516 :
517 0 : if (EQUAL(psTableDef->pasFieldDef[i].szName, szSysId))
518 : {
519 0 : sprintf(psTableDef->pasFieldDef[i].szName, "%s#", szNewName);
520 : }
521 0 : else if (EQUAL(psTableDef->pasFieldDef[i].szName, szUserId))
522 : {
523 0 : sprintf(psTableDef->pasFieldDef[i].szName, "%s-ID", szNewName);
524 : }
525 : }
526 : }
527 :
528 : /*-----------------------------------------------------------------
529 : * Build new table name
530 : *----------------------------------------------------------------*/
531 0 : sprintf(psTableDef->szTableName, "%s.%s", szNewName, szOldExt);
532 :
533 : }
534 :
535 : /**********************************************************************
536 : * _AVCE00WriteCreateCoverFile()
537 : *
538 : * Create a coverage file for the specified file type.
539 : *
540 : * The main part of the work is to find the right filename to use based on
541 : * the file type, the coverage precision, etc... the rest of job is
542 : * done by AVCBinWriteCreate().
543 : *
544 : * Returns 0 on success, or -1 if an error happened.
545 : *
546 : * AVCWriteCloseCoverFile() will eventually have to be called to release the
547 : * resources used by the AVCBinFile structure.
548 : **********************************************************************/
549 0 : int _AVCE00WriteCreateCoverFile(AVCE00WritePtr psInfo, AVCFileType eType,
550 : const char *pszLine, AVCTableDef *psTableDef)
551 : {
552 0 : char *pszPath, szFname[50]="";
553 0 : int i, nStatus = 0;
554 :
555 : /* By now, new coverage precision should have been established */
556 : CPLAssert(psInfo->nPrecision != AVC_DEFAULT_PREC);
557 :
558 : /*-----------------------------------------------------------------
559 : * Establish filename based on file type, precision, and possibly the
560 : * contents of the header line.
561 : *----------------------------------------------------------------*/
562 0 : pszPath = psInfo->pszCoverPath;
563 0 : switch(eType)
564 : {
565 : case AVCFileARC:
566 0 : strcpy(szFname, "arc");
567 0 : break;
568 : case AVCFilePAL:
569 0 : strcpy(szFname, "pal");
570 0 : break;
571 : case AVCFileCNT:
572 0 : strcpy(szFname, "cnt");
573 0 : break;
574 : case AVCFileLAB:
575 0 : strcpy(szFname, "lab");
576 0 : break;
577 : case AVCFileTOL:
578 0 : if (psInfo->nPrecision == AVC_SINGLE_PREC)
579 0 : strcpy(szFname, "tol");
580 : else
581 0 : strcpy(szFname, "par");
582 0 : break;
583 : case AVCFilePRJ:
584 0 : strcpy(szFname, "prj");
585 0 : break;
586 : case AVCFileTXT:
587 0 : strcpy(szFname, "txt");
588 0 : break;
589 : case AVCFileTX6:
590 : /* For TX6/TX7: the filename is subclass_name.txt
591 : */
592 :
593 : /* See bug 1261: It seems that empty subclass names are valid
594 : * for TX7. In this case we'll default the filename to txt.txt
595 : */
596 0 : if (pszLine[0] == '\0')
597 : {
598 0 : strcpy(szFname, "txt.txt");
599 : }
600 0 : else if (strlen(pszLine) > 30 || strchr(pszLine, ' ') != NULL)
601 0 : CPLError(CE_Failure, CPLE_IllegalArg,
602 : "Invalid TX6/TX7 subclass name \"%s\"", pszLine);
603 : else
604 0 : sprintf(szFname, "%s.txt", pszLine);
605 0 : break;
606 : case AVCFileRPL:
607 : /* For RPL and RXP: the filename is region_name.pal or region_name.rxp
608 : */
609 0 : if (strlen(pszLine) > 30 || strchr(pszLine, ' ') != NULL)
610 0 : CPLError(CE_Failure, CPLE_IllegalArg,
611 : "Invalid RPL region name \"%s\"", pszLine);
612 : else
613 0 : sprintf(szFname, "%s.pal", pszLine);
614 0 : break;
615 : case AVCFileRXP:
616 0 : if (strlen(pszLine) > 30 || strchr(pszLine, ' ') != NULL)
617 0 : CPLError(CE_Failure, CPLE_IllegalArg,
618 : "Invalid RXP name \"%s\"", pszLine);
619 : else
620 0 : sprintf(szFname, "%s.rxp", pszLine);
621 0 : break;
622 : case AVCFileTABLE:
623 : /*-------------------------------------------------------------
624 : * For tables, Filename will be based on info in the psTableDef
625 : * but we need to rename the table and the system attributes
626 : * based on the new coverage name.
627 : *------------------------------------------------------------*/
628 0 : if (psInfo->eCoverType != AVCCoverPC &&
629 0 : psInfo->eCoverType != AVCCoverPC2)
630 0 : pszPath = psInfo->pszInfoPath;
631 0 : _AVCE00WriteRenameTable(psTableDef, psInfo->pszCoverName);
632 0 : break;
633 : default:
634 0 : CPLError(CE_Failure, CPLE_IllegalArg,
635 : "_AVCE00WriteCreateCoverFile(): Unsupported file type!");
636 0 : nStatus = -1;
637 : break;
638 : }
639 :
640 : /*-----------------------------------------------------------------
641 : * V7 coverage filenames default to have a .adf extension
642 : * but PC coverage filenames (except .dbf tables) have no extensions.
643 : *----------------------------------------------------------------*/
644 0 : if (psInfo->eCoverType == AVCCoverV7 && strchr(szFname, '.') == NULL)
645 0 : strcat(szFname, ".adf");
646 :
647 : /*-----------------------------------------------------------------
648 : * Make sure filename is all lowercase and attempt to create the file
649 : *----------------------------------------------------------------*/
650 0 : for(i=0; szFname[i] != '\0'; i++)
651 0 : szFname[i] = tolower(szFname[i]);
652 :
653 0 : if (nStatus == 0)
654 : {
655 0 : psInfo->eCurFileType = eType;
656 :
657 0 : if (eType == AVCFileTABLE)
658 0 : psInfo->hFile = AVCBinWriteCreateTable(pszPath,
659 0 : psInfo->pszCoverName,
660 : psTableDef,
661 : psInfo->eCoverType,
662 : psInfo->nPrecision,
663 : psInfo->psDBCSInfo);
664 : else
665 :
666 0 : psInfo->hFile = AVCBinWriteCreate(pszPath, szFname,
667 : psInfo->eCoverType,
668 : eType, psInfo->nPrecision,
669 : psInfo->psDBCSInfo);
670 :
671 0 : if (psInfo->hFile == NULL)
672 : {
673 0 : nStatus = -1;
674 0 : psInfo->eCurFileType = AVCFileUnknown;
675 : }
676 : }
677 :
678 0 : return nStatus;
679 : }
680 :
681 : /**********************************************************************
682 : * _AVCE00WriteCloseCoverFile()
683 : *
684 : * Close current coverage file and reset the contents of psInfo.
685 : *
686 : * File should have been previously opened by _AVCE00WriteCreateCoverFile().
687 : *
688 : **********************************************************************/
689 0 : void _AVCE00WriteCloseCoverFile(AVCE00WritePtr psInfo)
690 : {
691 : /*-----------------------------------------------------------------
692 : * PRJ sections behave differently... since there is only one "object"
693 : * per section, they accumulate lines while we read them, and we
694 : * write everything at once when we reach the end-of-section (EOP) line.
695 : *----------------------------------------------------------------*/
696 0 : if (psInfo->eCurFileType == AVCFilePRJ)
697 : {
698 0 : AVCBinWriteObject(psInfo->hFile, psInfo->hParseInfo->cur.papszPrj);
699 : }
700 :
701 0 : AVCBinWriteClose(psInfo->hFile);
702 0 : psInfo->hFile = NULL;
703 0 : psInfo->eCurFileType = AVCFileUnknown;
704 0 : }
705 :
706 : /**********************************************************************
707 : * AVCE00WriteNextLine()
708 : *
709 : * Take the next line of E00 input for this coverage, parse it and
710 : * write the result to the coverage.
711 : *
712 : * Important Note: The E00 source lines are assumed to be valid... the
713 : * library performs no validation on the consistency of what it is
714 : * given as input (i.e. topology, polygons consistency, etc.).
715 : * So the coverage that will be created will be only as good as the
716 : * E00 input that is used to generate it.
717 : *
718 : * Returns 0 on success or -1 on error.
719 : **********************************************************************/
720 0 : int AVCE00WriteNextLine(AVCE00WritePtr psInfo, const char *pszLine)
721 : {
722 : /*-----------------------------------------------------------------
723 : * TODO: Update this call to use _AVCE00ReadNextLineE00(), if
724 : * possible.
725 : *----------------------------------------------------------------*/
726 :
727 0 : int nStatus = 0;
728 :
729 0 : CPLErrorReset();
730 :
731 : /*-----------------------------------------------------------------
732 : * If we're at the top level inside a supersection... check if this
733 : * supersection ends here.
734 : *----------------------------------------------------------------*/
735 0 : if (AVCE00ParseSuperSectionEnd(psInfo->hParseInfo, pszLine) == TRUE)
736 : {
737 : /* Nothing to do... it's all been done by the call to
738 : * AVCE00ParseSuperSectionEnd()
739 : */
740 : }
741 0 : else if (psInfo->eCurFileType == AVCFileUnknown)
742 : {
743 : /*-------------------------------------------------------------
744 : * We're at the top level or inside a supersection... waiting
745 : * to encounter a valid section or supersection header
746 : * (i.e. "ARC 2", etc...)
747 : *------------------------------------------------------------*/
748 :
749 : /*-------------------------------------------------------------
750 : * First check for a supersection header (TX6, RXP, IFO, ...)
751 : *------------------------------------------------------------*/
752 0 : if ( AVCE00ParseSuperSectionHeader(psInfo->hParseInfo,
753 : pszLine) == AVCFileUnknown )
754 : {
755 : /*---------------------------------------------------------
756 : * This was not a supersection header... check if it's a simple
757 : * section header
758 : *--------------------------------------------------------*/
759 0 : psInfo->eCurFileType=AVCE00ParseSectionHeader(psInfo->hParseInfo,
760 : pszLine);
761 : }
762 :
763 : /*-------------------------------------------------------------
764 : * If the coverage was created using AVC_DEFAULT_PREC and we are
765 : * processing the first section header, then use this section's
766 : * precision for the new coverage.
767 : * (Note: this code segment will be executed only once per
768 : * coverage and only if AVC_DEFAULT_PREC was selected)
769 : *------------------------------------------------------------*/
770 0 : if (psInfo->nPrecision == AVC_DEFAULT_PREC &&
771 0 : psInfo->eCurFileType != AVCFileUnknown)
772 : {
773 0 : psInfo->nPrecision = psInfo->hParseInfo->nPrecision;
774 : }
775 :
776 0 : if (psInfo->eCurFileType == AVCFileTABLE)
777 : {
778 : /*---------------------------------------------------------
779 : * We can't create the file for a TABLE until the
780 : * whole header has been read... send the first header
781 : * line to the parser and wait until the whole header has
782 : * been read.
783 : *--------------------------------------------------------*/
784 0 : AVCE00ParseNextLine(psInfo->hParseInfo, pszLine);
785 : }
786 0 : else if (psInfo->eCurFileType != AVCFileUnknown)
787 : {
788 : /*---------------------------------------------------------
789 : * OK, we've found a valid section header... create the
790 : * corresponding file in the coverage.
791 : * Note: supersection headers don't trigger the creation
792 : * of any output file... they just alter the psInfo state.
793 : *--------------------------------------------------------*/
794 :
795 0 : nStatus = _AVCE00WriteCreateCoverFile(psInfo,
796 : psInfo->eCurFileType,
797 0 : psInfo->hParseInfo->pszSectionHdrLine,
798 : NULL);
799 : }
800 : }
801 0 : else if (psInfo->eCurFileType == AVCFileTABLE &&
802 0 : ! psInfo->hParseInfo->bTableHdrComplete )
803 : {
804 : /*-------------------------------------------------------------
805 : * We're reading a TABLE header... continue reading lines
806 : * from the header, and create the output file only once
807 : * the header will have been completely read.
808 : *
809 : * Note: When parsing a TABLE, the first object returned will
810 : * be the AVCTableDef, then data records will follow.
811 : *------------------------------------------------------------*/
812 : AVCTableDef *psTableDef;
813 0 : psTableDef = (AVCTableDef*)AVCE00ParseNextLine(psInfo->hParseInfo,
814 : pszLine);
815 0 : if (psTableDef)
816 : {
817 0 : nStatus = _AVCE00WriteCreateCoverFile(psInfo,
818 : psInfo->eCurFileType,
819 0 : psInfo->hParseInfo->pszSectionHdrLine,
820 : psTableDef);
821 : }
822 : }
823 : else
824 : {
825 : /*-------------------------------------------------------------
826 : * We're are in the middle of a section... first check if we
827 : * have reached the end.
828 : *
829 : * note: The first call to AVCE00ParseSectionEnd() with FALSE will
830 : * not reset the parser until we close the file... and then
831 : * we call the function again to reset the parser.
832 : *------------------------------------------------------------*/
833 0 : if (AVCE00ParseSectionEnd(psInfo->hParseInfo, pszLine, FALSE))
834 : {
835 0 : _AVCE00WriteCloseCoverFile(psInfo);
836 0 : AVCE00ParseSectionEnd(psInfo->hParseInfo, pszLine, TRUE);
837 : }
838 : else
839 : /*-------------------------------------------------------------
840 : * ... not at the end yet, so continue reading objects.
841 : *------------------------------------------------------------*/
842 : {
843 : void *psObj;
844 0 : psObj = AVCE00ParseNextLine(psInfo->hParseInfo, pszLine);
845 :
846 0 : if (psObj)
847 0 : AVCBinWriteObject(psInfo->hFile, psObj);
848 : }
849 : }
850 :
851 :
852 0 : if (psInfo->hParseInfo->bForceEndOfSection)
853 : {
854 : /*-------------------------------------------------------------
855 : * The last call encountered an implicit end of section, so
856 : * we close the section now without waiting for an end-of-section
857 : * line (there won't be any!)... and get ready to proceed with
858 : * the next section.
859 : * This is used for TABLEs.
860 : *------------------------------------------------------------*/
861 0 : _AVCE00WriteCloseCoverFile(psInfo);
862 0 : AVCE00ParseSectionEnd(psInfo->hParseInfo, pszLine, TRUE);
863 : /* psInfo->hParseInfo->bForceEndOfSection = FALSE; */
864 : }
865 :
866 0 : if (CPLGetLastErrorNo() != 0)
867 0 : nStatus = -1;
868 :
869 0 : return nStatus;
870 : }
871 :
872 :
873 : /**********************************************************************
874 : * AVCE00DeleteCoverage()
875 : *
876 : * Delete a coverage directory, its contents, and the associated info
877 : * tables.
878 : *
879 : * Note:
880 : * When deleting tables, only the ../info/arc????.nit and arc????.dat
881 : * need to be deleted; the arc.dir does not need to be updated. This
882 : * is exactly what Arc/Info's KILL command does.
883 : *
884 : * Returns 0 on success or -1 on error.
885 : **********************************************************************/
886 0 : int AVCE00DeleteCoverage(const char *pszCoverToDelete)
887 : {
888 0 : int i, j, nStatus = 0;
889 : char *pszInfoPath, *pszCoverPath, *pszCoverName;
890 : const char *pszFname;
891 0 : char **papszTables=NULL, **papszFiles=NULL;
892 : AVCE00ReadPtr psInfo;
893 : VSIStatBuf sStatBuf;
894 : AVCCoverType eCoverType;
895 :
896 0 : CPLErrorReset();
897 :
898 : /*-----------------------------------------------------------------
899 : * Since we don't want to duplicate all the logic to figure coverage
900 : * and info dir name, etc... we'll simply open the coverage and
901 : * grab the info we need from the coverage handle.
902 : * By the same way, this will verify that the coverage exists and is
903 : * valid.
904 : *----------------------------------------------------------------*/
905 0 : psInfo = AVCE00ReadOpen(pszCoverToDelete);
906 :
907 0 : if (psInfo == NULL)
908 : {
909 0 : CPLError(CE_Failure, CPLE_FileIO,
910 : "Cannot delete coverage %s: it does not appear to be valid\n",
911 : pszCoverToDelete);
912 0 : return -1;
913 : }
914 :
915 0 : pszCoverPath = CPLStrdup(psInfo->pszCoverPath);
916 0 : pszInfoPath = CPLStrdup(psInfo->pszInfoPath);
917 0 : pszCoverName = CPLStrdup(psInfo->pszCoverName);
918 0 : eCoverType = psInfo->eCoverType;
919 :
920 0 : AVCE00ReadClose(psInfo);
921 :
922 : /*-----------------------------------------------------------------
923 : * Delete files in cover directory.
924 : *----------------------------------------------------------------*/
925 0 : papszFiles = CPLReadDir(pszCoverPath);
926 0 : for(i=0; nStatus==0 && papszFiles && papszFiles[i]; i++)
927 : {
928 0 : if (!EQUAL(".", papszFiles[i]) &&
929 0 : !EQUAL("..", papszFiles[i]))
930 : {
931 0 : pszFname = CPLSPrintf("%s%s", pszCoverPath, papszFiles[i]);
932 0 : if (unlink(pszFname) != 0)
933 : {
934 0 : CPLError(CE_Failure, CPLE_FileIO,
935 : "Failed deleting %s%s",
936 0 : pszCoverPath, papszFiles[i]);
937 0 : nStatus = -1;
938 0 : break;
939 : }
940 : }
941 : }
942 :
943 0 : CSLDestroy(papszFiles);
944 0 : papszFiles = NULL;
945 :
946 : /*-----------------------------------------------------------------
947 : * Get the list of info files (ARC????) to delete and delete them
948 : * (No 'info' directory for PC coverages)
949 : *----------------------------------------------------------------*/
950 0 : if (nStatus == 0 && eCoverType != AVCCoverPC && eCoverType != AVCCoverPC2)
951 : {
952 0 : papszTables = AVCBinReadListTables(pszInfoPath, pszCoverName,
953 : &papszFiles, eCoverType,
954 : NULL /*DBCSInfo*/);
955 :
956 0 : for(i=0; nStatus==0 && papszFiles && papszFiles[i]; i++)
957 : {
958 : /* Convert table filename to lowercases */
959 0 : for(j=0; papszFiles[i] && papszFiles[i][j]!='\0'; j++)
960 0 : papszFiles[i][j] = tolower(papszFiles[i][j]);
961 :
962 : /* Delete the .DAT file */
963 0 : pszFname = CPLSPrintf("%s%s.dat", pszInfoPath, papszFiles[i]);
964 0 : if ( VSIStat(pszFname, &sStatBuf) != -1 &&
965 0 : unlink(pszFname) != 0)
966 : {
967 0 : CPLError(CE_Failure, CPLE_FileIO,
968 : "Failed deleting %s%s",
969 0 : pszInfoPath, papszFiles[i]);
970 0 : nStatus = -1;
971 0 : break;
972 : }
973 :
974 : /* Delete the .DAT file */
975 0 : pszFname = CPLSPrintf("%s%s.nit", pszInfoPath, papszFiles[i]);
976 0 : if ( VSIStat(pszFname, &sStatBuf) != -1 &&
977 0 : unlink(pszFname) != 0)
978 : {
979 0 : CPLError(CE_Failure, CPLE_FileIO,
980 : "Failed deleting %s%s",
981 0 : pszInfoPath, papszFiles[i]);
982 0 : nStatus = -1;
983 0 : break;
984 : }
985 : }
986 :
987 0 : CSLDestroy(papszTables);
988 0 : CSLDestroy(papszFiles);
989 : }
990 :
991 : /*-----------------------------------------------------------------
992 : * Delete the coverage directory itself
993 : * In some cases, the directory could be locked by another application
994 : * on the same system or somewhere on the network.
995 : * Define AVC_IGNORE_RMDIR_ERROR at compile time if you want this
996 : * error to be ignored.
997 : *----------------------------------------------------------------*/
998 0 : if (VSIRmdir(pszCoverPath) != 0)
999 : {
1000 : #ifndef AVC_IGNORE_RMDIR_ERROR
1001 0 : CPLError(CE_Failure, CPLE_FileIO,
1002 : "Failed deleting directory %s", pszCoverPath);
1003 0 : nStatus = -1;
1004 : #endif
1005 : }
1006 :
1007 0 : CPLFree(pszCoverPath);
1008 0 : CPLFree(pszInfoPath);
1009 0 : CPLFree(pszCoverName);
1010 :
1011 0 : return nStatus;
1012 : }
1013 :
|