1 /*!
2    \file lib/vector/Vlib/close.c
3 
4    \brief Vector library - Close vector map
5 
6    Higher level functions for reading/writing/manipulating vectors.
7 
8    (C) 2001-2015 by the GRASS Development Team
9 
10    This program is free software under the GNU General Public License
11    (>=v2). Read the file COPYING that comes with GRASS for details.
12 
13    \author Original author CERL, probably Dave Gerdes or Mike Higgins.
14    \author Update to GRASS 5.7 Radim Blazek and David D. Gray.
15    \author Update to GRASS 7 Martin Landa <landa.martin gmail.com>
16  */
17 
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 
25 #include <grass/vector.h>
26 #include <grass/glocale.h>
27 
28 #include "local_proto.h"
29 
clo_dummy()30 static int clo_dummy()
31 {
32     return -1;
33 }
34 
35 #if !defined HAVE_OGR || !defined HAVE_POSTGRES
format()36 static int format()
37 {
38     G_fatal_error(_("Requested format is not compiled in this version"));
39     return 0;
40 }
41 #endif
42 
43 static int (*Close_array[][2]) () = {
44     {
45     clo_dummy, V1_close_nat}
46 #ifdef HAVE_OGR
47     , {
48     clo_dummy, V1_close_ogr}
49     , {
50     clo_dummy, V1_close_ogr}
51 #else
52     , {
53     clo_dummy, format}
54     , {
55     clo_dummy, format}
56 #endif
57 #ifdef HAVE_POSTGRES
58     , {
59     clo_dummy, V1_close_pg}
60 #else
61     , {
62     clo_dummy, format}
63 #endif
64 };
65 
66 static void unlink_file(const struct Map_info *, const char *);
67 
68 /*!
69    \brief Close vector map
70 
71    \param Map pointer to Map_info
72 
73    \return 0 on success
74    \return non-zero on error
75  */
Vect_close(struct Map_info * Map)76 int Vect_close(struct Map_info *Map)
77 {
78     int create_link; /* used for external formats only */
79     struct Coor_info CInfo;
80 
81     G_debug(1, "Vect_close(): name = %s, mapset = %s, format = %d, level = %d, is_tmp = %d",
82 	    Map->name, Map->mapset, Map->format, Map->level, Map->temporary);
83 
84     if (Map->temporary &&
85         (Map->fInfo.ogr.dsn || Map->fInfo.pg.conninfo)) {
86         /* transfer features for external output format */
87         struct Map_info Out;
88 
89         putenv("GRASS_VECTOR_EXTERNAL_IMMEDIATE=1");
90         if (-1 == Vect_open_new(&Out, Vect_get_name(Map), Vect_is_3d(Map))) {
91             G_warning(_("Unable to create vector map <%s>"),
92                       Vect_get_name(Map));
93             return 1;
94         }
95 
96         /* copy metadata */
97         Vect_hist_copy(Map, &Out);
98         Vect_copy_head_data(Map, &Out);
99         /* copy dblinks (temporary map -> output map) to transfer
100            (input map -> output map) attributes */
101         Vect_copy_map_dblinks(Map, &Out, TRUE);
102         /* afterwords, dblinks must be removed from temporary map
103            otherwise when deleting temporary map also original
104            attribute tables would be deteled */
105         Vect_map_del_dblink(Map, -1); /* delete db links for all layers */
106 
107         if (0 != Vect_copy_map_lines_field(Map, 1, &Out)) { /* always layer = 1 for OGR/PG maps */
108             G_warning(_("Copying features failed"));
109             return -1;
110         }
111 
112         Vect_build(&Out);
113 
114         Vect_close(&Out);
115         putenv("GRASS_VECTOR_EXTERNAL_IMMEDIATE="); /* unset variable */
116     }
117 
118     /* check for external formats whether to create a link */
119     create_link = TRUE;
120     if (Map->format == GV_FORMAT_OGR ||
121         Map->format == GV_FORMAT_POSTGIS) {
122         char *def_file;
123 
124         if (Map->format == GV_FORMAT_POSTGIS) {
125             if (getenv("GRASS_VECTOR_PGFILE"))
126                 def_file = getenv("GRASS_VECTOR_PGFILE");
127             else
128                 def_file = "PG";
129         }
130         else {
131             def_file = "OGR";
132         }
133         if (G_find_file2("", def_file, G_mapset())) {
134             FILE       *fp;
135             const char *p;
136 
137             struct Key_Value *key_val;
138 
139             fp = G_fopen_old("", def_file, G_mapset());
140             if (!fp) {
141                 G_warning(_("Unable to open %s file"), def_file);
142             }
143             else {
144                 key_val = G_fread_key_value(fp);
145                 fclose(fp);
146 
147                 /* create a vector link in the current mapset ? */
148                 p = G_find_key_value("link", key_val);
149                 if (p && G_strcasecmp(p, "no") == 0) {
150                     create_link = FALSE;
151                 }
152                 else {
153                     p = G_find_key_value("link_name", key_val);
154                     if (p) {
155                         /* use different name for a link */
156                         G_free(Map->name);
157                         Map->name = G_store(p);
158                     }
159                 }
160             }
161         }
162     }
163 
164     /* store support files for vector maps in the current mapset if in
165        write mode on level 2 */
166     if (strcmp(Map->mapset, G_mapset()) == 0 &&
167         Map->support_updated &&
168         Map->plus.built == GV_BUILD_ALL &&
169         create_link) {
170 
171         unlink_file(Map, GV_TOPO_ELEMENT); /* topo */
172 
173 	unlink_file(Map, GV_SIDX_ELEMENT); /* sidx */
174 
175 	unlink_file(Map, GV_CIDX_ELEMENT); /* cidx */
176 
177 	if (Map->format == GV_FORMAT_OGR || Map->format == GV_FORMAT_POSTGIS) {
178 	    unlink_file(Map, GV_FIDX_ELEMENT); /* fidx */
179 	}
180 
181 	Vect_coor_info(Map, &CInfo);
182 	Map->plus.coor_size = CInfo.size;
183 	Map->plus.coor_mtime = CInfo.mtime;
184 
185 	/* write out topo file */
186         Vect_save_topo(Map);
187 
188 	/* write out sidx file */
189 	Map->plus.Spidx_new = TRUE; /* force writing */
190 	Vect_save_sidx(Map);
191 
192 	/* write out cidx file */
193 	Vect_cidx_save(Map);
194 
195 	/* write out fidx file */
196 	if (Map->format == GV_FORMAT_OGR)
197 	    V2_close_ogr(Map);
198 	else if (Map->format == GV_FORMAT_POSTGIS)
199             V2_close_pg(Map);
200     }
201 
202     /* spatial index must also be closed when opened with topo but not
203      * modified */
204     if (Map->plus.spidx_fp.file &&
205         Map->plus.Spidx_built == TRUE &&
206 	!Map->support_updated &&
207 	Map->plus.built == GV_BUILD_ALL) {
208 
209         G_debug(1, "spatial index file closed");
210 	fclose(Map->plus.spidx_fp.file);
211     }
212 
213     /* release memory if required */
214     if (Map->level > 1 && Map->plus.release_support) {
215 	G_debug(1, "free topology, spatial index, and category index");
216 	dig_free_plus(&(Map->plus));
217     }
218 
219     G_debug(1, "close history file");
220     if (Map->hist_fp)
221         fclose(Map->hist_fp);
222 
223     /* close level 1 files / data sources if not head_only */
224     if (!Map->head_only) {
225 	if (create_link && ((*Close_array[Map->format][1]) (Map)) != 0) {
226 	    G_warning(_("Unable to close vector <%s>"),
227 		      Vect_get_full_name(Map));
228 	    return 1;
229 	}
230     }
231 
232     G_free(Map->name);
233     G_free(Map->mapset);
234     G_free(Map->location);
235     G_free(Map->gisdbase);
236 
237     Map->open = VECT_CLOSED_CODE;
238 
239     return 0;
240 }
241 
242 /*!
243    \brief Save format definition file for vector map
244 
245    \param Map pointer to Map_info structure
246 
247    \return 1 on success
248    \return 0 on error
249  */
Vect_save_frmt(struct Map_info * Map)250 int Vect_save_frmt(struct Map_info *Map)
251 {
252     FILE *fd;
253     char buf[GPATH_MAX];
254 
255     if (Map->format != GV_FORMAT_OGR &&
256         Map->format != GV_FORMAT_POSTGIS) {
257         G_warning(_("Invalid request for writing frmt file - map format is %d"), Map->format);
258         return 0;
259     }
260 
261     /* create frmt file */
262     sprintf(buf, "%s/%s", GV_DIRECTORY, Map->name);
263     fd = G_fopen_new(buf, GV_FRMT_ELEMENT);
264     if (fd == NULL) {
265         G_fatal_error("Unable to create file '%s'", buf);
266     }
267 
268     if (Map->format == GV_FORMAT_POSTGIS) {
269 #ifdef HAVE_POSTGRES
270         fprintf(fd, "format: postgis\n");
271         fprintf(fd, "conninfo: %s\n", Map->fInfo.pg.conninfo);
272         fprintf(fd, "schema: %s\n",   Map->fInfo.pg.schema_name);
273         fprintf(fd, "table: %s\n",    Map->fInfo.pg.table_name);
274 #else
275         G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
276         return 0;
277 #endif
278     } else if (Map->format == GV_FORMAT_OGR) {
279 #ifdef HAVE_OGR
280         fprintf(fd, "format: ogr\n");
281         fprintf(fd, "dsn: %s\n",   Map->fInfo.ogr.dsn);
282         fprintf(fd, "layer: %s\n", Map->fInfo.ogr.layer_name);
283 #else
284         G_fatal_error(_("GRASS is not compiled with OGR support"));
285         return 0;
286 #endif
287     }
288 
289     G_verbose_message(_("Link to vector map <%s> created"), Map->name);
290 
291     /* close frmt file */
292     fclose(fd);
293 
294     return 1;
295 }
296 
297 /*! Free memory of line cache
298 
299   \param cache pointer to lines cache to be freed
300 */
Vect__free_cache(struct Format_info_cache * cache)301 void Vect__free_cache(struct Format_info_cache *cache) {
302     int i;
303     /* destroy lines in cache */
304     for (i = 0; i < cache->lines_alloc; i++) {
305 	Vect_destroy_line_struct(cache->lines[i]);
306     }
307     G_free(cache->lines);
308     G_free(cache->lines_types);
309     G_free(cache->lines_cats);
310 
311     G_zero(cache, sizeof(struct Format_info_cache));
312 }
313 
314 /*! Free memory of offset array
315 
316   \param cache pointer to offset array to be freed
317 */
Vect__free_offset(struct Format_info_offset * offset)318 void Vect__free_offset(struct Format_info_offset *offset)
319 {
320     G_free(offset->array);
321     G_zero(offset, sizeof(struct Format_info_offset));
322 }
323 
unlink_file(const struct Map_info * Map,const char * name)324 void unlink_file(const struct Map_info *Map, const char *name)
325 {
326     char path[GPATH_MAX];
327 
328     /* delete old support files if available */
329     Vect__get_element_path(path, Map, name);
330     if (access(path, F_OK) == 0) { /* file exists? */
331         G_debug(2, "\t%s: unlink", path);
332         unlink(path);
333     }
334 }
335