]> granicus.if.org Git - postgis/commitdiff
Commit initial version of reworked shp2pgsql architecture that defines a common API...
authorMark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
Sun, 13 Dec 2009 20:31:54 +0000 (20:31 +0000)
committerMark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
Sun, 13 Dec 2009 20:31:54 +0000 (20:31 +0000)
There are still some minor TODOs left, however the basic concept appears to work well and I've also spent time running tests under valgrind to ensure that we don't leak memory on larger shapefiles.

git-svn-id: http://svn.osgeo.org/postgis/trunk@4996 b70326c6-7e19-0410-871a-916f4a2858ee

loader/Makefile.in
loader/shp2pgsql-cli.c
loader/shp2pgsql-core.c
loader/shp2pgsql-core.h
loader/shp2pgsql-gui.c
loader/shp2pgsql.c

index 794a0de0fc27e8dc32b742d3ef882ac4174f5148..6a932ffdb20c4ac08cbe1c87fb0b4c4bfee25b6b 100644 (file)
@@ -27,9 +27,8 @@ CFLAGS=@CFLAGS@ @PICFLAGS@ @WARNFLAGS@
 
 # Filenames with extension as determined by the OS
 PGSQL2SHP=pgsql2shp@EXESUFFIX@
-SHP2PGSQL=shp2pgsql@EXESUFFIX@
+SHP2PGSQL-CLI=shp2pgsql@EXESUFFIX@
 SHP2PGSQL-GUI=shp2pgsql-gui@EXESUFFIX@
-SHP2PGSQL-CLI=shp2pgsql-cli@EXESUFFIX@
 
 # PostgreSQL frontend CPPFLAGS and LDFLAGS (for compiling and linking with libpq)
 PGSQL_FE_CPPFLAGS=@PGSQL_FE_CPPFLAGS@
@@ -56,7 +55,7 @@ endif
 
 
 # The real parts of the Makefile
-all: $(SHP2PGSQL) $(PGSQL2SHP) @GTK_BUILD@
+all: $(SHP2PGSQL-CLI) $(PGSQL2SHP) @GTK_BUILD@
 
 gui: $(SHP2PGSQL-GUI) $(SHP2PGSQL-CLI)
 
@@ -71,30 +70,24 @@ pgsql2shp.o: pgsql2shp.c
 $(PGSQL2SHP): shpopen.o dbfopen.o getopt.o pgsql2shp.o $(LIBLWGEOM) 
        $(CC) $(CFLAGS) $^ $(ICONV_LDFLAGS) $(PGSQL_FE_LDFLAGS) -lm -o $@ 
 
-$(SHP2PGSQL): shpopen.o dbfopen.o getopt.o  shp2pgsql.o $(LIBLWGEOM) 
-       $(CC) $(CFLAGS) $^ $(ICONV_LDFLAGS) -lm -o $@ 
-
-shp2pgsql-core-gui.o: shp2pgsql-core.c
-       $(CC) $(CFLAGS) -DPGUI -c -o $@ $^
+$(SHP2PGSQL-CLI): stringbuffer.o shpopen.o dbfopen.o getopt.o shp2pgsql-core.o shp2pgsql-cli.o $(LIBLWGEOM) 
+       $(CC) $(CFLAGS) $(ICONV_LDFLAGS) -lm $^ -o $@
 
 shp2pgsql-gui.o: shp2pgsql-gui.c
        $(CC) $(PGSQL_FE_CPPFLAGS) $(CFLAGS) $(GTK_CFLAGS) -o $@ -c shp2pgsql-gui.c
 
-$(SHP2PGSQL-GUI): stringbuffer.o shpopen.o dbfopen.o shp2pgsql-core-gui.o shp2pgsql-gui.o $(LIBLWGEOM) 
+$(SHP2PGSQL-GUI): stringbuffer.o shpopen.o dbfopen.o shp2pgsql-core.o shp2pgsql-gui.o $(LIBLWGEOM) 
        $(CC) $(CFLAGS) $(GTK_LIBS) $(ICONV_LDFLAGS) $(PGSQL_FE_LDFLAGS) -lm $^ -o $@
 
-$(SHP2PGSQL-CLI): stringbuffer.o shpopen.o dbfopen.o getopt.o shp2pgsql-core.o shp2pgsql-cli.o $(LIBLWGEOM) 
-       $(CC) $(CFLAGS) $(ICONV_LDFLAGS) -lm $^ -o $@
-
 install: all
        @mkdir -p $(DESTDIR)$(bindir)
        $(INSTALL) $(PGSQL2SHP) $(DESTDIR)$(bindir)
-       $(INSTALL) $(SHP2PGSQL) $(DESTDIR)$(bindir)
+       $(INSTALL) $(SHP2PGSQL-CLI) $(DESTDIR)$(bindir)
 
 uninstall:
        @rm -f $(DESTDIR)$(bindir)/$(PGSQL2SHP)
-       @rm -f $(DESTDIR)$(bindir)/$(SHP2PGSQL)
+       @rm -f $(DESTDIR)$(bindir)/$(SHP2PGSQL-CLI)
 
 clean:
-       @rm -f *.o $(SHP2PGSQL) $(PGSQL2SHP) 
+       @rm -f *.o $(SHP2PGSQL-CLI) $(PGSQL2SHP) 
 
index 7d985777ad56dacb14bceb2802d92cc960b5ccd3..f47373b251357f50c263028e8116f024fae9d548 100644 (file)
@@ -4,6 +4,7 @@
  * PostGIS - Spatial Types for PostgreSQL
  * http://postgis.refractions.net
  * Copyright 2008 OpenGeo.org
+ * Copyright 2009 Mark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
  *
  * This is free software; you can redistribute and/or modify it under
  * the terms of the GNU General Public Licence. See the COPYING file.
 #include <unistd.h>
 #include "shp2pgsql-core.h"
 
+
 static void
-pcli_usage(char *me, int exitcode, FILE* out)
+usage()
 {
-    fprintf(out, "RCSID: %s RELEASE: %s\n", RCSID, POSTGIS_VERSION);
-       fprintf(out, "USAGE: %s [<options>] <shapefile> [<schema>.]<table>\n", me);
-       fprintf(out, "OPTIONS:\n");
-       fprintf(out, "  -s <srid>  Set the SRID field. If not specified it defaults to -1.\n");
-       fprintf(out, "  (-d|a|c|p) These are mutually exclusive options:\n");
-       fprintf(out, "      -d  Drops the table, then recreates it and populates\n");
-       fprintf(out, "          it with current shape file data.\n");
-       fprintf(out, "      -a  Appends shape file into current table, must be\n");
-       fprintf(out, "          exactly the same table schema.\n");
-       fprintf(out, "      -c  Creates a new table and populates it, this is the\n");
-       fprintf(out, "          default if you do not specify any options.\n");
-       fprintf(out, "      -p  Prepare mode, only creates the table.\n");
-       fprintf(out, "  -g <geometry_column> Specify the name of the geometry column\n");
-       fprintf(out, "     (mostly useful in append mode).\n");
-       fprintf(out, "  -D  Use postgresql dump format (defaults to sql insert statments.\n");
-       fprintf(out, "  -k  Keep postgresql identifiers case.\n");
-       fprintf(out, "  -i  Use int4 type for all integer dbf fields.\n");
-       fprintf(out, "  -I  Create a GiST index on the geometry column.\n");
-       fprintf(out, "  -S  Generate simple geometries instead of MULTI geometries.\n");
+       printf("RCSID: %s RELEASE: %s\n", RCSID, POSTGIS_VERSION);
+       printf("USAGE: shp2pgsql [<options>] <shapefile> [<schema>.]<table>\n");
+       printf("OPTIONS:\n");
+       printf("  -s <srid>  Set the SRID field. If not specified it defaults to -1.\n");
+       printf("  (-d|a|c|p) These are mutually exclusive options:\n");
+       printf("      -d  Drops the table, then recreates it and populates\n");
+       printf("          it with current shape file data.\n");
+       printf("      -a  Appends shape file into current table, must be\n");
+       printf("          exactly the same table schema.\n");
+       printf("      -c  Creates a new table and populates it, this is the\n");
+       printf("          default if you do not specify any options.\n");
+       printf("      -p  Prepare mode, only creates the table.\n");
+       printf("  -g <geometry_column> Specify the name of the geometry column\n");
+       printf("     (mostly useful in append mode).\n");
+       printf("  -D  Use postgresql dump format (defaults to sql insert statments.\n");
+       printf("  -k  Keep postgresql identifiers case.\n");
+       printf("  -i  Use int4 type for all integer dbf fields.\n");
+       printf("  -I  Create a GiST index on the geometry column.\n");
+       printf("  -S  Generate simple geometries instead of MULTI geometries.\n");
 #ifdef HAVE_ICONV
-       fprintf(out, "  -W <encoding> Specify the character encoding of Shape's\n");
-       fprintf(out, "     attribute column. (default : \"ASCII\")\n");
+       printf("  -W <encoding> Specify the character encoding of Shape's\n");
+       printf("     attribute column. (default : \"ASCII\")\n");
 #endif
-       fprintf(out, "  -N <policy> Specify NULL geometries handling policy (insert,skip,abort)\n");
-       fprintf(out, "  -n  Only import DBF file.\n");
-    fprintf(out, "  -? Display this help screen\n");
-       exit (exitcode);
+       printf("  -N <policy> Specify NULL geometries handling policy (insert,skip,abort)\n");
+       printf("  -n  Only import DBF file.\n");
+       printf("  -? Display this help screen\n");
 }
 
 
-static int
-pcli_cmdline(int ARGC, char **ARGV)
+int
+main (int argc, char **argv)
 {
-       int c;
-       int curindex=0;
-       char  *ptr;
-       extern char *optarg;
-       extern int optind;
-
-       if ( ARGC == 1 ) {
-               pcli_usage(ARGV[0], 0, stdout);
+       SHPLOADERCONFIG *config;
+       SHPLOADERSTATE *state;
+       char *header, *footer, *record;
+       char c;
+       int ret, i;
+
+
+       /* If no options are specified, display usage */
+       if (argc == 1) {
+               usage();
+               exit(0);
        }
 
-       while ((c = pgis_getopt(ARGC, ARGV, "kcdapDs:Sg:iW:wIN:n")) != EOF){
-               switch (c) {
+       /* Parse command line options and set configuration */
+       config = malloc(sizeof(SHPLOADERCONFIG));
+       set_config_defaults(config);
+
+       while ((c = pgis_getopt(argc, argv, "kcdapDs:Sg:iW:wIN:n")) != EOF)
+       {
+               switch (c)
+               {
                        case 'c':
-                               if (opt == ' ')
-                                        opt ='c';
-                               else
-                                        return 0;
-                               break;
                        case 'd':
-                               if (opt == ' ')
-                                        opt ='d';
-                               else
-                                        return 0;
-                               break;
                        case 'a':
-                               if (opt == ' ')
-                                        opt ='a';
-                               else
-                                        return 0;
-                               break;
                        case 'p':
-                               if (opt == ' ')
-                                        opt ='p';
+                               if (config->opt == ' ')
+                                       config->opt = c;
                                else
-                                        return 0;
+                                       /* Only one of these options can be chosen */
+                                       usage();
+                                       exit(0);
                                break;
+
                        case 'D':
-                               dump_format =1;
+                               config->dump_format = 1;
                                break;
+
                        case 'S':
-                               simple_geometries =1;
+                               config->simple_geometries = 1;
                                break;
+
                        case 's':
-                               if( optarg 
-                                       (void)sscanf(optarg, "%d", &sr_id);
+                               if (optarg
+                                       sscanf(optarg, "%d", &(config->sr_id));
                                else 
-                                       pcli_usage(ARGV[0], 0, stdout); 
+                                       /* With -s, user must specify SRID */
+                                       usage();
+                                       exit(0);
                                break;
+
                        case 'g':
-                               geom = optarg;
+                               config->geom = optarg;
                                break;
+
                        case 'k':
-                               quoteidentifiers = 1;
+                               config->quoteidentifiers = 1;
                                break;
+
                        case 'i':
-                               forceint4 = 1;
+                               config->forceint4 = 1;
                                break;
+
                        case 'I':
-                               createindex = 1;
+                               config->createindex = 1;
+                               break;
+
+                       case 'w':
+                               config->hwgeom = 1;
                                break;
+
                        case 'n':
-                               readshape = 0;
+                               config->readshape = 0;
                                break;
+
                        case 'W':
 #ifdef HAVE_ICONV
-                               encoding = optarg;
+                               config->encoding = optarg;
 #else
                                fprintf(stderr, "WARNING: the -W switch will have no effect. UTF8 disabled at compile time\n");
 #endif
                                break;
+
                        case 'N':
                                switch (optarg[0])
                                {       
                                        case 'a':
-                                               null_policy = abort_on_null;
+                                               config->null_policy = POLICY_NULL_ABORT;
                                                break;
                                        case 'i':
-                                               null_policy = insert_null;
+                                               config->null_policy = POLICY_NULL_INSERT;
                                                break;
                                        case 's':
-                                               null_policy = skip_null;
+                                               config->null_policy = POLICY_NULL_SKIP;
                                                break;
                                        default:
                                                fprintf(stderr, "Unsupported NULL geometry handling policy.\nValid policies: insert, skip, abort\n");
                                                exit(1);
                                }
                                break;
+
                        case '?':
-                               pcli_usage(ARGV[0], 0, stdout); 
-                       default:              
-                               return 0;
+                               usage();
+                               exit(0);
+
+                       default:
+                               usage();
+                               exit(0);
                }
        }
 
-       if ( !sr_id ) sr_id = -1;
+       /* Determine the shapefile name from the next argument */
+       if (optind < argc)
+       {
+               config->shp_file = argv[optind];
+               optind++;
+       }       
 
-       if ( !geom ) geom = "the_geom";
+       /* Determine the table and schema names from the next argument */
+       if (optind < argc)
+       {
+               char *ptr;
+
+               ptr = strchr(argv[optind], '.');
 
-       if ( opt==' ' ) opt = 'c';
+               /* Schema qualified table name */
+               if (ptr)
+               {
+                       config->schema = malloc(strlen(argv[optind]) + 1);
+                       snprintf(config->schema, ptr - argv[optind] + 1, "%s", argv[optind]);
 
-       for (; optind < ARGC; optind++){
-               if(curindex ==0){
-                       shp_file = ARGV[optind];
-               }else if(curindex == 1){
-                       table = ARGV[optind];
-                       if ( (ptr=strchr(table, '.')) )
-                       {
-                               *ptr = '\0';
-                               schema = table;
-                               table = ptr+1;
-                       }
+                       config->table = malloc(strlen(argv[optind]));
+                       snprintf(config->table, strlen(argv[optind]) - strlen(config->schema), "%s", ptr + 1);
+               }
+               else
+               {
+                       config->table = malloc(strlen(argv[optind]) + 1);
+                       strcpy(config->table, argv[optind]);
                }
-               curindex++;
        }
        
-       /*
-        * Third argument (if present) is supported for compatibility
-        * with old shp2pgsql versions taking also database name.
-        */
-       if(curindex < 2 || curindex > 3){
-               return 0;
+       /* Transform table name to lower case if no quoting specified */
+       if (!config->quoteidentifiers)
+       {
+               strtolower(config->table);
+               if (config->schema)
+                       strtolower(config->schema);
        }
 
-       /* 
-        * Transform table name to lower case unless asked
-        * to keep original case (we'll quote it later on)
-        */
-       if ( ! quoteidentifiers )
+       /* Create the shapefile state object */
+       state = ShpLoaderCreate(config);
+
+       /* Open the shapefile */
+       ret = ShpLoaderOpenShape(state);
+       if (ret != SHPLOADEROK)
        {
-               LowerCase(table);
-               if ( schema ) LowerCase(schema);
-       }
+               fprintf(stderr, "%s\n", state->message);
 
-       return 1;
-}
+               if (ret == SHPLOADERERR)
+                       exit(1);
+       }
 
-int
-main (int ARGC, char **ARGV)
-{
+       /* If reading the whole shapefile, display its type */
+       if (state->config->readshape)
+       {
+               fprintf(stderr, "Shapefile type: %s\n", SHPTypeName(state->shpfiletype));
+               fprintf(stderr, "Postgis type: %s[%d]\n", state->pgtype, state->pgdims);
+       }
 
-       int rv = 0;
+       /* Print the header to stdout */
+       ret = ShpLoaderGetSQLHeader(state, &header);
+       if (ret != SHPLOADEROK)
+       {
+               fprintf(stderr, "%s\n", state->message);
 
-       /* Emit output to stdout/stderr, not a GUI */
-       gui_mode = 0;
+               if (ret == SHPLOADERERR)
+                       exit(1);
+       }
 
-       /* Parse command line options and set globals */
-       if ( ! pcli_cmdline(ARGC, ARGV) ) pcli_usage(ARGV[0], 2, stderr);
+       printf("%s", header);
+       free(header);
 
-       /* Set record number to beginning of file, and translation stage to first one */
-       cur_entity = -1;
-       translation_stage = 1;
+       /* If in COPY mode, output the COPY statement */
+       if (state->config->dump_format)
+       {
+               ret = ShpLoaderGetSQLCopyStatement(state, &header);
+               if (ret != SHPLOADEROK)
+               {
+                       fprintf(stderr, "%s\n", state->message);
        
-       rv = translation_start();
-       if( ! rv ) return 1;
-       while( translation_stage == 2 ) 
+                       if (ret == SHPLOADERERR)
+                               exit(1);
+               }
+
+               printf("%s", header);
+               free(header);
+       }
+
+       /* Main loop: iterate through all of the records and send them to stdout */
+       for (i = 0; i < ShpLoaderGetRecordCount(state); i++)
+       {
+               ret = ShpLoaderGenerateSQLRowStatement(state, i, &record);
+
+               switch(ret)
+               {
+                       case SHPLOADEROK:
+                               /* Simply display the geometry */
+                               printf("%s\n", record);
+                               free(record);
+                               break;
+
+                       case SHPLOADERERR:
+                               /* Display the error message then stop */
+                               fprintf(stderr, "%s\n", state->message);
+                               exit(1);
+                               break;
+
+                       case SHPLOADERWARN:
+                               /* Display the warning, but continue */
+                               fprintf(stderr, "%s\n", state->message);
+                               printf("%s\n", record);
+                               free(record);
+                               break;
+
+                       case SHPLOADERRECDELETED:
+                               /* Record is marked as deleted - ignore */
+                               break;
+
+                       case SHPLOADERRECISNULL:
+                               /* Record is NULL and should be ignored according to NULL policy */
+                               break;
+               }
+       }
+
+       /* Print the footer to stdout */
+       ret = ShpLoaderGetSQLFooter(state, &footer);
+       if (ret != SHPLOADEROK)
        {
-               rv = translation_middle();
-               if( ! rv ) return 1;
+               fprintf(stderr, "%s\n", state->message);
+
+               if (ret == SHPLOADERERR)
+                       exit(1);
        }
-       rv = translation_end();
-       if( ! rv ) return 1;
 
-       return 0;
+       printf("%s", footer);
+       free(footer);
 
-}
 
+       /* Free the state object */
+       ShpLoaderDestroy(state);
 
-/**********************************************************************
- * $Log$
- *
- **********************************************************************/
+       /* Free configuration variables */
+       if (config->schema)
+               free(config->schema);
+       free(config->table);
+       free(config);
+
+       return 0;
+}
index be590d055aab28466a2694942261e1858f9784d8..c030c22f1a0b1a86dce0449acfd01852e848f95c 100644 (file)
  *
  * PostGIS - Spatial Types for PostgreSQL
  * http://postgis.refractions.net
- * Copyright 2001-2003 Refractions Research Inc.
  * Copyright 2008 OpenGeo.org
- *
+ * Copyright 2009 Mark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
  * This is free software; you can redistribute and/or modify it under
  * the terms of the GNU General Public Licence. See the COPYING file.
  *
- **********************************************************************
- * Using shapelib 1.2.8, this program reads in shape files and
- * processes it's contents into a Insert statements which can be
- * easily piped into a database frontend.
- * Specifically designed to insert type 'geometry' (a custom
- * written PostgreSQL type) for the shape files and PostgreSQL
- * standard types for all attributes of the entity.
- *
  * Maintainer: Paul Ramsey <pramsey@opengeo.org>
- * Original Author: Jeff Lounsbury <jeffloun@refractions.net>
  *
  **********************************************************************/
 
-#include "../postgis_config.h"
 #include "shp2pgsql-core.h"
 
-#define        POINTTYPE       1
-#define        LINETYPE        2
-#define        POLYGONTYPE     3
-#define        MULTIPOINTTYPE  4
-#define        MULTILINETYPE   5
-#define        MULTIPOLYGONTYPE        6
-#define        COLLECTIONTYPE  7
-
-#define WKBZOFFSET 0x80000000
-#define WKBMOFFSET 0x40000000
-
 
-typedef struct
+/* Internal ring/point structures */
+typedef struct struct_point
 {
        double x, y, z, m;
-}
-Point;
+} Point;
 
-typedef struct Ring
+typedef struct struct_ring
 {
        Point *list;            /* list of points */
-       struct Ring  *next;
+       struct struct_ring *next;
        int n;                  /* number of points in list */
        unsigned int linked;    /* number of "next" rings */
+} Ring;
+
+
+/* liblwgeom allocator callback - install the defaults (malloc/free/stdout/stderr) */
+void lwgeom_init_allocators()
+{
+       lwgeom_install_default_allocators();
 }
-Ring;
-
-
-/* Public globals */
-int dump_format = 0; /* 0 = SQL inserts, 1 = dump */
-int simple_geometries = 0; /* 0 = MULTIPOLYGON/MULTILINESTRING, 1 = force to POLYGON/LINESTRING */
-int quoteidentifiers = 0; /* 0 = columnname, 1 = "columnName" */
-int forceint4 = 0; /* 0 = allow int8 fields, 1 = no int8 fields */
-int createindex = 0; /* 0 = no index, 1 = create index after load */
-int readshape = 1; /* 0 = load DBF file only, 1 = load everything */
-char opt = ' '; /* load mode: c = create, d = delete, a = append, p = prepare */
-char *table = NULL; /* table to load into */
-char *schema = NULL; /* schema to load into */
-char *geom = NULL; /* geometry column name to use */
+
+
+/*
+ * Internal functions
+ */
+
 #ifdef HAVE_ICONV
-char *encoding = NULL; /* iconv encoding name */
+char *utf8(const char *fromcode, char *inputbuf);
 #endif
-int null_policy = insert_null; /* how to handle nulls */
-int sr_id = 0; /* SRID specified */
-char *shp_file = NULL; /* the shape file (without the .shp extension) */
-int gui_mode = 0; /* 1 = GUI, 0 = commandline */
-int translation_stage = TRANSLATION_IDLE;
-
-/* Private globals */
-stringbuffer_t *sb_row; /* stringbuffer to append results to */
-char    *col_names = NULL;
-char   *pgtype;
-int    istypeM = 0;
-int    pgdims;
-unsigned int wkbtype;
-DBFFieldType *types;   /* Fields type, width and precision */
-SHPHandle  hSHPHandle;
-DBFHandle  hDBFHandle;
-int shpfiletype;
-SHPObject  *obj=NULL;
-int    *widths;
-int    *precisions;
-int     num_fields,num_records,num_entities;
-int cur_entity = -1;
-char    **field_names;
-
-
-/* Prototypes */
-int Insert_attributes(DBFHandle hDBFHandle, int row);
+void vasbappend(stringbuffer_t *sb, char *fmt, ... );
 char *escape_copy_string(char *str);
 char *escape_insert_string(char *str);
-int PIP( Point P, Point* V, int n );
-int CreateTable(void);
-int CreateIndex(void);
-void usage(char *me, int exitcode, FILE* out);
-int InsertPoint(void);
-int InsertPolygon(void);
-int InsertLineString(void);
-int OutputGeometry(char *geometry);
-void SetPgType(void);
-#ifdef HAVE_ICONV
-char *utf8(const char *fromcode, char *inputbuf);
-#endif
+
+int GeneratePointGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry);
+int GenerateLineStringGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry);
+int PIP(Point P, Point *V, int n);
 int FindPolygons(SHPObject *obj, Ring ***Out);
 void ReleasePolygons(Ring **polys, int npolys);
-int DropTable(char *schema, char *table, char *geom);
-void GetFieldsSpec(void);
-int LoadData(void);
-int OpenShape(void);
-void LowerCase(char *s);
-void Cleanup(void);
-
-static int
-pgis_exec(const char *sql)
-{
-#ifdef PGUI
-       return pgui_exec(sql);
-#else
-       printf("%s;\n", sql);
-       return 1;
-#endif
-}
-
-static int
-pgis_copy_write(const char *line)
-{
-#ifdef PGUI
-       return pgui_copy_write(line);
-#else
-       printf("%s", line);
-       return 1;
-#endif
-}
-
-static int
-pgis_copy_start(const char *sql)
-{
-#ifdef PGUI
-       return pgui_copy_start(sql);
-#else
-       printf("%s;\n", sql);
-       return 1;
-#endif
-}
+int GeneratePolygonGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry);
 
-static int
-pgis_copy_end(const int rollback)
-{
-#ifdef PGUI
-       return pgui_copy_end(rollback);
-#else
-       printf("\\.\n");
-       return 1;
-#endif
-}
 
-static void
-pgis_logf(const char *fmt, ... )
+/* Append variadic formatted string to a stringbuffer */
+void
+vasbappend(stringbuffer_t *sb, char *fmt, ... )
 {
-#ifndef PGUI
-       char *msg;
-#endif
        va_list ap;
+       char *msg;
 
        va_start(ap, fmt);
 
-#ifdef PGUI
-       pgui_log_va(fmt, ap);
-#else
        if (!lw_vasprintf (&msg, fmt, ap))
        {
                va_end (ap);
                return;
        }
-       fprintf(stderr, "%s\n", msg);
+
+       /* Append to the stringbuffer */
+       stringbuffer_append(sb, msg);
        free(msg);
-#endif
+
        va_end(ap);
 }
 
-/** liblwgeom allocator callback - install the defaults (malloc/free/stdout/stderr) */
-/**
-       TODO: hook lwnotice/lwerr up to the GUI
-       */
-void lwgeom_init_allocators()
+
+#ifdef HAVE_ICONV
+/* Return allocated string containing UTF8 string converted from encoding fromcode */
+char *
+utf8(const char *fromcode, char *inputbuf)
 {
-       lwgeom_install_default_allocators();
+       iconv_t cd;
+       char *outputptr;
+       char *outputbuf;
+       size_t outbytesleft;
+       size_t inbytesleft;
+
+       inbytesleft = strlen(inputbuf);
+
+       cd = iconv_open("UTF-8", fromcode);
+       if (cd == (iconv_t) - 1)
+               return NULL;
+
+       outbytesleft = inbytesleft * 3 + 1; /* UTF8 string can be 3 times larger */
+                                       /* then local string */
+       outputbuf = (char *)malloc(outbytesleft);
+       if (!outputbuf)
+               return NULL;
+       
+       memset(outputbuf, 0, outbytesleft);
+       outputptr = outputbuf;
+
+       if (-1 == iconv(cd, &inputbuf, &inbytesleft, &outputptr, &outbytesleft))
+               return NULL;
+
+       iconv_close (cd);
+
+       return outputbuf;
 }
+#endif
 
 
 /**
- * Escape input string suitable for COPY
+ * Escape input string suitable for COPY. If no characters require escaping, simply return
+ * the input pointer. Otherwise return a new allocated string.
  */
-
 char *
 escape_copy_string(char *str)
 {
-       /**
+       /*
         * Escape the following characters by adding a preceding backslash
         *      tab, backslash, cr, lf
         *
@@ -215,57 +135,45 @@ escape_copy_string(char *str)
        char *ptr, *optr;
        int toescape = 0;
        size_t size;
-#ifdef HAVE_ICONV
-       char *utf8str=NULL;
-
-       if ( encoding )
-       {
-               utf8str=utf8(encoding, str);
-               if ( ! utf8str ) exit(1);
-               str = utf8str;
-       }
-#endif
 
        ptr = str;
 
+       /* Count how many characters we need to escape so we know the size of the string we need to return */
        while (*ptr)
        {
-               if ( *ptr == '\t' || *ptr == '\\' ||
-                       *ptr == '\n' || *ptr == '\r' )
-                               toescape++;
+               if (*ptr == '\t' || *ptr == '\\' || *ptr == '\n' || *ptr == '\r')
+                       toescape++;
+
                ptr++;
        }
 
-       if (toescape == 0) return str;
-
-       size = ptr-str+toescape+1;
+       /* If we don't have to escape anything, simply return the input pointer */
+       if (toescape == 0)
+               return str;
 
+       size = ptr - str + toescape + 1;
        result = calloc(1, size);
+       optr = result;
+       ptr = str;
 
-       optr=result;
-       ptr=str;
        while (*ptr)
        {
-               if ( *ptr == '\t' || *ptr == '\\' ||
-                       *ptr == '\n' || *ptr == '\r' )
-                               *optr++='\\';
-                               *optr++=*ptr++;
+               if ( *ptr == '\t' || *ptr == '\\' || *ptr == '\n' || *ptr == '\r' )
+                       *optr++ = '\\';
+
+               *optr++ = *ptr++;
        }
-       *optr='\0';
 
-#ifdef HAVE_ICONV
-       if ( encoding ) free(str);
-#endif
+       *optr = '\0';
 
        return result;
-
 }
 
 
 /**
- * Escape input string suitable for INSERT
+ * Escape input string suitable for INSERT. If no characters require escaping, simply return
+ * the input pointer. Otherwise return a new allocated string.
  */
-
 char *
 escape_insert_string(char *str)
 {
@@ -276,666 +184,192 @@ escape_insert_string(char *str)
         * 2. make new string
         */
 
-       char    *result;
-       char    *ptr, *optr;
-       int     toescape = 0;
+       char *result;
+       char *ptr, *optr;
+       int toescape = 0;
        size_t size;
-#ifdef HAVE_ICONV
-       char *utf8str=NULL;
-
-       if ( encoding )
-       {
-               utf8str=utf8(encoding, str);
-               if ( ! utf8str ) exit(1);
-               str = utf8str;
-       }
-#endif
 
        ptr = str;
 
+       /* Count how many characters we need to escape so we know the size of the string we need to return */
        while (*ptr)
        {
-               if ( *ptr == '\'' ) toescape++;
+               if (*ptr == '\'')
+                       toescape++;
+
                ptr++;
        }
 
-       if (toescape == 0) return str;
-
-       size = ptr-str+toescape+1;
+       /* If we don't have to escape anything, simply return the input pointer */
+       if (toescape == 0)
+               return str;
 
+       size = ptr - str + toescape + 1;
        result = calloc(1, size);
+       optr = result;
+       ptr = str;
 
-       optr=result;
-       ptr=str;
        while (*ptr)
        {
-               if ( *ptr == '\'') *optr++='\'';
-               *optr++=*ptr++;
+               if (*ptr == '\'')
+                       *optr++='\'';
+
+               *optr++ = *ptr++;
        }
-       *optr='\0';
 
-#ifdef HAVE_ICONV
-       if ( encoding ) free(str);
-#endif
+       *optr='\0';
 
        return result;
 }
 
 
-
 /**
- * PIP(): crossing number test for a point in a polygon
- *      input:   P = a point,
- *               V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
- *      returns: 0 = outside, 1 = inside
+ * @brief Generate an allocated geometry string for shapefile object obj using the state parameters
  */
 int
-PIP( Point P, Point* V, int n )
+GeneratePointGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry)
 {
-       int cn = 0;    /* the crossing number counter */
-       int i;
+       LWCOLLECTION *lwcollection;
 
-       /* loop through all edges of the polygon */
-       for (i=0; i<n-1; i++)
-       {    /* edge from V[i] to V[i+1] */
-               if (((V[i].y <= P.y) && (V[i+1].y > P.y))    /* an upward crossing */
-                               || ((V[i].y > P.y) && (V[i+1].y <= P.y)))
-               { /* a downward crossing */
-                       double vt = (float)(P.y - V[i].y) / (V[i+1].y - V[i].y);
-                       if (P.x < V[i].x + vt * (V[i+1].x - V[i].x)) /* P.x < intersect */
-                               ++cn;   /* a valid crossing of y=P.y right of P.x */
-               }
-       }
-       return (cn&1);    /* 0 if even (out), and 1 if odd (in) */
+       LWGEOM **lwmultipoints;
+       uchar *serialized_lwgeom;
+       LWGEOM_UNPARSER_RESULT lwg_unparser_result;
 
-}
+       DYNPTARRAY **dpas;
+       POINT4D point4d;
 
+       int dims = 0, hasz = 0, hasm = 0;
+       int result;
+       int u;
 
+       char *mem;
 
 
+       /* Determine the correct dimensions: note that in hwgeom-compatible mode we cannot use
+          the M coordinate */
+       if (state->wkbtype & WKBZOFFSET)
+               hasz = 1;
 
-int
-Insert_attributes(DBFHandle hDBFHandle, int row)
-{
-       int i,num_fields;
-       char val[1024];
-       char *escval;
+       if (!state->config->hwgeom)
+               if (state->wkbtype & WKBMOFFSET)
+                       hasm = 1;
+
+       TYPE_SETZM(dims, hasz, hasm);
+
+       /* Allocate memory for our array of LWPOINTs and our dynptarrays */
+       lwmultipoints = malloc(sizeof(LWPOINT *) * obj->nVertices);
+       dpas = malloc(sizeof(DYNPTARRAY *) * obj->nVertices);
 
-       num_fields = DBFGetFieldCount( hDBFHandle );
-       for ( i = 0; i < num_fields; i++ )
+       /* We need an array of pointers to each of our sub-geometries */
+       for (u = 0; u < obj->nVertices; u++)
        {
-               if (DBFIsAttributeNULL( hDBFHandle, row, i))
-               {
-                       if (dump_format)
-                       {
-                               stringbuffer_append(sb_row, "\\N");
-                       }
-                       else
-                       {
-                               stringbuffer_append(sb_row, "NULL");
-                       }
-               }
-               else /* Attribute NOT NULL */
-               {
-                       switch (types[i])
-                       {
-                       case FTInteger:
-                       case FTDouble:
-                               if ( -1 == snprintf(val, 1024, "%s", DBFReadStringAttribute(hDBFHandle, row, i)) )
-                               {
-                                       pgis_logf("Warning: field %d name truncated", i);
-                                       val[1023] = '\0';
-                               }
-                               /* pg_atoi() does not do this */
-                               if ( val[0] == '\0' )
-                               {
-                                       val[0] = '0';
-                                       val[1] = '\0';
-                               }
-                               if ( val[strlen(val)-1] == '.' ) val[strlen(val)-1] = '\0';
-                               break;
+               /* Generate the point */
+               point4d.x = obj->padfX[u];
+               point4d.y = obj->padfY[u];
 
-                       case FTString:
-                       case FTLogical:
-                       case FTDate:
-                               if ( -1 == snprintf(val, 1024, "%s", DBFReadStringAttribute(hDBFHandle, row, i)) )
-                               {
-                                       pgis_logf("Warning: field %d name truncated", i);
-                                       val[1023] = '\0';
-                               }
-                               break;
+               if (state->wkbtype & WKBZOFFSET)
+                       point4d.z = obj->padfZ[u];
+               if (state->wkbtype & WKBMOFFSET)
+                       point4d.m = obj->padfM[u];
 
-                       default:
-                               pgis_logf(
-                                       "Error: field %d has invalid or unknown field type (%d)",
-                                       i, types[i]);
-                               return 0;
-                       }
+               /* Create a dynptarray containing a single point */
+               dpas[u] = dynptarray_create(1, dims);
+               dynptarray_addPoint4d(dpas[u], &point4d, 0);
 
-                       if (dump_format)
-                       {
-                               escval = escape_copy_string(val);
-                               stringbuffer_aprintf(sb_row, "%s", escval);
-                               //printf("\t");
-                       }
-                       else
-                       {
-                               escval = escape_insert_string(val);
-                               stringbuffer_aprintf(sb_row, "'%s'", escval);
-                               //printf(",");
-                       }
-                       if ( val != escval ) free(escval);
-               }
-               //only put in delimeter if not last field or a shape will follow
-               if (readshape == 1 || i < (num_fields - 1))
-               {
-                       if (dump_format)
-                       {
-                               stringbuffer_append_c(sb_row, '\t');
-                       }
-                       else
-                       {
-                               stringbuffer_append_c(sb_row, ',');
-                       }
-               }
+               /* Generate the LWPOINT */
+               lwmultipoints[u] = lwpoint_as_lwgeom(lwpoint_construct(state->config->sr_id, NULL, dpas[u]->pa));
        }
-       return 1;
-}
 
+       /* If we have more than 1 vertex then we are working on a MULTIPOINT and so generate a MULTIPOINT
+       rather than a POINT */
+       if (obj->nVertices > 1)
+       {
+               lwcollection = lwcollection_construct(MULTIPOINTTYPE, state->config->sr_id, NULL, obj->nVertices, lwmultipoints);
+               serialized_lwgeom = lwgeom_serialize(lwcollection_as_lwgeom(lwcollection));
+       }
+       else
+       {
+               serialized_lwgeom = lwgeom_serialize(lwmultipoints[0]);
+       }
 
+       if (!state->config->hwgeom)
+               result = serialized_lwgeom_to_hexwkb(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_NONE, -1);
+       else
+               result = serialized_lwgeom_to_ewkt(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_NONE);
 
+       if (result)
+       {
+               snprintf(state->message, SHPLOADERMSGLEN, "%s", lwg_unparser_result.message);
 
-/**
- * formerly main()
- */
-int
-translation_start ()
-{
+               return SHPLOADERERR;
+       }
 
-       sb_row = stringbuffer_create();
+       /* Allocate a string containing the resulting geometry */
+       mem = malloc(strlen(lwg_unparser_result.wkoutput) + 1);
+       strcpy(mem, lwg_unparser_result.wkoutput);
 
-       /*
-        * Open shapefile and initialize globals
-        */
-       if ( ! OpenShape() )
-               return 0;
+       /* Free all of the allocated items */
+       lwfree(lwg_unparser_result.wkoutput);
+       lwfree(serialized_lwgeom);
 
-       if (readshape == 1)
+       for (u = 0; u < obj->nVertices; u++)
        {
-               /*
-                * Compute output geometry type
-                */
+               if (dpas[u]->pa->serialized_pointlist)
+                       lwfree(dpas[u]->pa->serialized_pointlist);
 
-               SetPgType();
-
-               pgis_logf("Shapefile type: %s", SHPTypeName(shpfiletype));
-               pgis_logf("PostGIS type: %s [%dD]", pgtype, pgdims);
-       }
+               lwpoint_free(lwgeom_as_lwpoint(lwmultipoints[u]));
 
-#ifdef HAVE_ICONV
-       if ( encoding )
-       {
-               if ( ! pgis_exec("SET CLIENT_ENCODING TO UTF8") ) return 0;
+               lwfree(dpas[u]);
        }
-#endif /* defined HAVE_ICONV */
-
-       /*
-        * Use SQL-standard string escaping rather than PostgreSQL standard
-        */
-       if ( ! pgis_exec("SET STANDARD_CONFORMING_STRINGS TO ON") ) return 0;
-
-       /*
-        * Drop table if requested
-        */
-       if (opt == 'd')
-               if ( ! DropTable(schema, table, geom) )
-                       return 0;
 
-       /*
-        * Get col names and types for table creation
-        * and data load.
-        */
-       GetFieldsSpec();
-
-       if ( ! pgis_exec("BEGIN") ) return 0;
+       lwfree(dpas);
+       lwfree(lwmultipoints);
 
-       /*
-        * If not in 'append' mode create the spatial table
-        */
-       if (opt != 'a')
-               if ( ! CreateTable() )
-                       return 0;
+       /* Return the string - everything ok */
+       *geometry = mem;
 
-       translation_stage = TRANSLATION_LOAD; /* done start */
-       return 1;
+       return SHPLOADEROK;
 }
 
-int
-translation_middle()
-{
-       /*
-        * Generate INSERT or COPY lines
-        */
-       if (opt != 'p')
-       {
-               if ( ! LoadData() )
-                       return 0;
-       }
-       else
-       {
-               translation_stage = TRANSLATION_CLEANUP; /* done middle */
-       }
-       return 1;
-}
 
+/**
+ * @brief Generate an allocated geometry string for shapefile object obj using the state parameters
+ */
 int
-translation_end()
+GenerateLineStringGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry)
 {
-       /*
-        * Create GiST index if requested
-        */
-       if (createindex)
-               if ( ! CreateIndex() )
-                       return 0;
+       LWCOLLECTION *lwcollection = NULL;
 
-       if ( ! pgis_exec("END") ) return 0; /* End the last transaction */
+       LWGEOM **lwmultilinestrings;
+       uchar *serialized_lwgeom;
+       LWGEOM_UNPARSER_RESULT lwg_unparser_result;
 
-       translation_stage = TRANSLATION_DONE;
+       DYNPTARRAY **dpas;
+       POINT4D point4d;
 
-       return 1;
-}
+       int dims = 0, hasz = 0, hasm = 0;
+       int result;
+       int u, v, start_vertex, end_vertex;
 
-void
-LowerCase(char *s)
-{
-       int j;
-       for (j=0; j<strlen(s); j++) s[j] = tolower(s[j]);
-}
+       char *mem;
 
-void
-Cleanup(void)
-{
-       if ( col_names ) free(col_names);
-}
 
-int
-OpenShape(void)
-{
-       int j;
-       SHPObject *obj=NULL;
+       /* Determine the correct dimensions: note that in hwgeom-compatible mode we cannot use
+          the M coordinate */
+       if (state->wkbtype & WKBZOFFSET)
+               hasz = 1;
+
+       if (!state->config->hwgeom)
+               if (state->wkbtype & WKBMOFFSET)
+                       hasm = 1;
+
+       TYPE_SETZM(dims, hasz, hasm);
 
-       if (readshape == 1)
+       if (state->config->simple_geometries == 1 && obj->nParts > 1)
        {
-               hSHPHandle = SHPOpen( shp_file, "rb" );
-               if (hSHPHandle == NULL)
-               {
-                       pgis_logf("%s: shape (.shp) or index files (.shx) can not be opened, will just import attribute data.", shp_file);
-                       readshape = 0;
-               }
-       }
-
-       hDBFHandle = DBFOpen( shp_file, "rb" );
-       if ((hSHPHandle == NULL && readshape == 1) || hDBFHandle == NULL)
-       {
-               pgis_logf("%s: dbf file (.dbf) can not be opened.",shp_file);
-               return 0;
-       }
-
-       if (readshape == 1)
-       {
-               SHPGetInfo(hSHPHandle, &num_entities, &shpfiletype, NULL, NULL);
-
-               if ( null_policy == abort_on_null )
-               {
-                       for (j=0; j<num_entities; j++)
-                       {
-                               obj = SHPReadObject(hSHPHandle,j);
-                               if ( ! obj )
-                               {
-                                       pgis_logf("Error reading shape object %d", j);
-                                       return 0;
-                               }
-                               if ( obj->nVertices == 0 )
-                               {
-                                       pgis_logf("Empty geometries found, aborted.");
-                                       return 0;
-                               }
-                               SHPDestroyObject(obj);
-                       }
-               }
-       }
-       else
-       {
-               num_entities = DBFGetRecordCount(hDBFHandle);
-       }
-
-       return 1;
-}
-
-int
-CreateTable(void)
-{
-       int j;
-       int field_precision, field_width;
-       DBFFieldType type = -1;
-
-       /*
-        * Create a table for inserting the shapes into with appropriate
-        * columns and types
-        */
-
-       stringbuffer_clear(sb_row);
-
-       if ( schema )
-       {
-               stringbuffer_aprintf(sb_row, "CREATE TABLE \"%s\".\"%s\" (gid serial PRIMARY KEY", schema, table);
-       }
-       else
-       {
-               stringbuffer_aprintf(sb_row, "CREATE TABLE \"%s\" (gid serial PRIMARY KEY", table);
-       }
-
-       for (j=0;j<num_fields;j++)
-       {
-               type = types[j];
-               field_width = widths[j];
-               field_precision = precisions[j];
-
-               stringbuffer_aprintf(sb_row, ",\n\"%s\" ", field_names[j]);
-
-               if (type == FTString)
-               {
-                       /* use DBF attribute size as maximum width */
-                       stringbuffer_aprintf (sb_row, "varchar(%d)", field_width);
-               }
-               else if (type == FTDate)
-               {
-                       stringbuffer_append (sb_row, "date");
-               }
-               else if (type == FTInteger)
-               {
-                       if ( forceint4 )
-                       {
-                               stringbuffer_append (sb_row, "int4");
-                       }
-                       else if  ( field_width < 5 )
-                       {
-                               stringbuffer_append (sb_row, "int2");
-                       }
-                       else if  ( field_width < 10 )
-                       {
-                               stringbuffer_append (sb_row, "int4");
-                       }
-                       else
-                       {
-                               stringbuffer_aprintf (sb_row, "numeric(%d,0)", field_width);
-                       }
-               }
-               else if (type == FTDouble)
-               {
-                       if ( field_width > 18 )
-                       {
-                               stringbuffer_append (sb_row, "numeric");
-                       }
-                       else
-                       {
-                               stringbuffer_append (sb_row, "float8");
-                       }
-               }
-               else if (type == FTLogical)
-               {
-                       stringbuffer_append (sb_row, "boolean");
-               }
-               else
-               {
-                       pgis_logf ("Invalid type in DBF file");
-               }
-       }
-       /* Run the CREATE TABLE statement in the buffer and clear. */
-       stringbuffer_append_c(sb_row, ')');
-       if ( ! pgis_exec(stringbuffer_getstring(sb_row)) ) return 0;
-       stringbuffer_clear(sb_row);
-
-       /* Create the geometry column with an addgeometry call */
-       if ( schema && readshape == 1 )
-       {
-               stringbuffer_aprintf(sb_row, "SELECT AddGeometryColumn('%s','%s','%s','%d',",
-                                                        schema, table, geom, sr_id);
-       }
-       else if (readshape == 1)
-       {
-               stringbuffer_aprintf(sb_row, "SELECT AddGeometryColumn('','%s','%s','%d',",
-                                                        table, geom, sr_id);
-       }
-       if (pgtype)
-       { //pgtype will only be set if we are loading geometries
-               stringbuffer_aprintf(sb_row, "'%s',%d)", pgtype, pgdims);
-       }
-
-       /* Run the AddGeometryColumn() statement in the buffer and clear. */
-       if ( ! pgis_exec(stringbuffer_getstring(sb_row)) ) return 0;
-       stringbuffer_clear(sb_row);
-
-       return 1;
-}
-
-int
-CreateIndex(void)
-{
-
-       stringbuffer_clear(sb_row);
-
-       if ( schema )
-       {
-               stringbuffer_aprintf(sb_row, "CREATE INDEX \"%s_%s_gist\" ON \"%s\".\"%s\" using gist (\"%s\" gist_geometry_ops)", table, geom, schema, table, geom);
-       }
-       else
-       {
-               stringbuffer_aprintf(sb_row, "CREATE INDEX \"%s_%s_gist\" ON \"%s\" using gist (\"%s\" gist_geometry_ops)", table, geom, table, geom);
-       }
-
-       /* Run the CREATE INDEX statement in the buffer and clear. */
-       if ( ! pgis_exec(stringbuffer_getstring(sb_row)) ) return 0;
-       stringbuffer_clear(sb_row);
-
-       return 1;
-}
-
-int
-LoadData(void)
-{
-       int trans=0;
-
-       if (cur_entity == -1 && dump_format)
-       {
-               char *copysql;
-               if ( schema )
-               {
-                       lw_asprintf(&copysql, "COPY \"%s\".\"%s\" %s FROM stdin",
-                                        schema, table, col_names);
-               }
-               else
-               {
-                       lw_asprintf(&copysql, "COPY \"%s\" %s FROM stdin",
-                                        table, col_names);
-               }
-               pgis_copy_start(copysql);
-               free(copysql);
-       }
-
-       /**************************************************************
-        *
-        *   MAIN SHAPE OBJECTS SCAN
-        *
-        **************************************************************/
-       while (cur_entity <  num_entities - 1)
-       {
-
-               cur_entity++;
-
-               /*wrap a transaction block around each 250 inserts... */
-               if ( ! dump_format )
-               {
-                       if (trans == 250)
-                       {
-                               trans=0;
-                               if ( ! pgis_exec("END") ) return 0;
-                               if ( ! pgis_exec("BEGIN") ) return 0;
-                       }
-               }
-               trans++;
-               /* transaction stuff done */
-
-               if ( gui_mode && ( cur_entity % 200 == 0 ) )
-               {
-                       pgis_logf("Feature #%d", cur_entity);
-               }
-
-               /* skip the record if it has been deleted */
-               if (readshape != 1 && DBFReadDeleted(hDBFHandle, cur_entity))
-               {
-                       continue;
-               }
-
-               /* open the next object */
-               if (readshape == 1)
-               {
-                       obj = SHPReadObject(hSHPHandle,cur_entity);
-                       if ( ! obj )
-                       {
-                               pgis_logf("Error reading shape object %d", cur_entity);
-                               return 0;
-                       }
-
-                       if ( null_policy == skip_null && obj->nVertices == 0 )
-                       {
-                               SHPDestroyObject(obj);
-                               continue;
-                       }
-               }
-
-               /* New row, clear the stringbuffer. */
-               stringbuffer_clear(sb_row);
-
-               if (!dump_format)
-               {
-                       if ( schema )
-                       {
-                               stringbuffer_aprintf(sb_row, "INSERT INTO \"%s\".\"%s\" %s VALUES (",
-                                                                        schema, table, col_names);
-                       }
-                       else
-                       {
-                               stringbuffer_aprintf(sb_row, "INSERT INTO \"%s\" %s VALUES (",
-                                                                        table, col_names);
-                       }
-               }
-               if ( ! Insert_attributes(hDBFHandle,cur_entity) ) return 0;
-
-               if (readshape == 1)
-               {
-                       /* ---------- NULL SHAPE ----------------- */
-                       if (obj->nVertices == 0)
-                       {
-                               if (dump_format)
-                               {
-                                       stringbuffer_append(sb_row, "\\N\n");
-                                       pgis_copy_write(stringbuffer_getstring(sb_row));
-                               }
-                               else
-                               {
-                                       stringbuffer_append(sb_row, "NULL)");
-                                       if ( ! pgis_exec(stringbuffer_getstring(sb_row)) ) return 0;
-                               }
-                               SHPDestroyObject(obj);
-                               continue;
-                       }
-
-                       switch (obj->nSHPType)
-                       {
-                       case SHPT_POLYGON:
-                       case SHPT_POLYGONM:
-                       case SHPT_POLYGONZ:
-                               if ( ! InsertPolygon() ) return 0;
-                               break;
-
-                       case SHPT_POINT:
-                       case SHPT_POINTM:
-                       case SHPT_POINTZ:
-                       case SHPT_MULTIPOINT:
-                       case SHPT_MULTIPOINTM:
-                       case SHPT_MULTIPOINTZ:
-                               if ( ! InsertPoint() ) return 0;
-                               break;
-
-                       case SHPT_ARC:
-                       case SHPT_ARCM:
-                       case SHPT_ARCZ:
-                               if ( ! InsertLineString() ) return 0;
-                               break;
-
-                       default:
-                               pgis_logf ("**** Type is NOT SUPPORTED, type id = %d ****",
-                                                  obj->nSHPType);
-                               break;
-
-                       }
-
-                       SHPDestroyObject(obj);
-               }
-               else
-               {
-                       if ( dump_format )
-                       {   /* close for dbf only dump format */
-                               stringbuffer_append_c(sb_row, '\n');
-                               pgis_copy_write(stringbuffer_getstring(sb_row));
-                       }
-                       else
-                       {   /* close for dbf only sql insert format */
-                               if ( ! pgis_exec(stringbuffer_getstring(sb_row)) ) return 0;
-                       }
-               }
-
-               /* Just do 100 entries at a time, then return to the idle loop. */
-               if ( cur_entity % 100 == 0 ) return 1;
-
-       } /* END of MAIN SHAPE OBJECT LOOP */
-
-       if ( dump_format )
-       {
-               pgis_copy_end(0);
-       }
-
-       translation_stage = TRANSLATION_CLEANUP; /* done middle */
-
-       return 1;
-}
-
-
-int
-InsertLineString()
-{
-       LWCOLLECTION *lwcollection;
-
-       LWGEOM **lwmultilinestrings;
-       uchar *serialized_lwgeom;
-       LWGEOM_UNPARSER_RESULT lwg_unparser_result;
-
-       DYNPTARRAY **dpas;
-       POINT4D point4d;
-
-       int dims = 0, hasz = 0, hasm = 0;
-       int result;
-       int u, v, start_vertex, end_vertex;
-
-       if (wkbtype & WKBZOFFSET) hasz = 1;
-       if (wkbtype & WKBMOFFSET) hasm = 1;
-       TYPE_SETZM(dims, hasz, hasm);
+               snprintf(state->message, SHPLOADERMSGLEN, "We have a Multilinestring with %d parts, can't use -S switch!", obj->nParts);
 
-       if (simple_geometries == 1 && obj->nParts > 1)
-       {
-               pgis_logf("We have a Multilinestring with %d parts, can't use -S switch!", obj->nParts);
-               return 0;
+               return SHPLOADERERR;
        }
 
        /* Allocate memory for our array of LWLINEs and our dynptarrays */
@@ -963,22 +397,22 @@ InsertLineString()
                        point4d.x = obj->padfX[v];
                        point4d.y = obj->padfY[v];
 
-                       if (wkbtype & WKBZOFFSET)
+                       if (state->wkbtype & WKBZOFFSET)
                                point4d.z = obj->padfZ[v];
-                       if (wkbtype & WKBMOFFSET)
+                       if (state->wkbtype & WKBMOFFSET)
                                point4d.m = obj->padfM[v];
 
                        dynptarray_addPoint4d(dpas[u], &point4d, 0);
                }
 
                /* Generate the LWLINE */
-               lwmultilinestrings[u] = lwline_as_lwgeom(lwline_construct(sr_id, NULL, dpas[u]->pa));
+               lwmultilinestrings[u] = lwline_as_lwgeom(lwline_construct(state->config->sr_id, NULL, dpas[u]->pa));
        }
 
        /* If using MULTILINESTRINGs then generate the serialized collection, otherwise just a single LINESTRING */
-       if (simple_geometries == 0)
+       if (state->config->simple_geometries == 0)
        {
-               lwcollection = lwcollection_construct(MULTILINETYPE, sr_id, NULL, obj->nParts, lwmultilinestrings);
+               lwcollection = lwcollection_construct(MULTILINETYPE, state->config->sr_id, NULL, obj->nParts, lwmultilinestrings);
                serialized_lwgeom = lwgeom_serialize(lwcollection_as_lwgeom(lwcollection));
        }
        else
@@ -986,15 +420,22 @@ InsertLineString()
                serialized_lwgeom = lwgeom_serialize(lwmultilinestrings[0]);
        }
 
-       result = serialized_lwgeom_to_hexwkb(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_NONE, -1);
+       if (!state->config->hwgeom)
+               result = serialized_lwgeom_to_hexwkb(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_NONE, -1);
+       else
+               result = serialized_lwgeom_to_ewkt(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_NONE);
 
+       /* Return the error message if we failed */
        if (result)
        {
-               pgis_logf("ERROR: %s", lwg_unparser_result.message);
-               return 0;
+               snprintf(state->message, SHPLOADERMSGLEN, "%s", lwg_unparser_result.message);
+
+               return SHPLOADERERR;
        }
 
-       if ( ! OutputGeometry(lwg_unparser_result.wkoutput) ) return 0;
+       /* Allocate a string containing the resulting geometry */
+       mem = malloc(strlen(lwg_unparser_result.wkoutput) + 1);
+       strcpy(mem, lwg_unparser_result.wkoutput);
 
        /* Free all of the allocated items */
        lwfree(lwg_unparser_result.wkoutput);
@@ -1002,16 +443,49 @@ InsertLineString()
 
        for (u = 0; u < obj->nParts; u++)
        {
+               lwfree(dpas[u]->pa->serialized_pointlist);
                lwline_free(lwgeom_as_lwline(lwmultilinestrings[u]));
                lwfree(dpas[u]);
        }
 
        lwfree(dpas);
        lwfree(lwmultilinestrings);
+       if (lwcollection)
+               lwfree(lwcollection);
+
+       /* Return the string - everything ok */
+       *geometry = mem;
+
+       return SHPLOADEROK;
+}
+
+
+/**
+ * @brief PIP(): crossing number test for a point in a polygon
+ *      input:   P = a point,
+ *               V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
+ * @return   0 = outside, 1 = inside
+ */
+int
+PIP(Point P, Point *V, int n)
+{
+       int cn = 0;    /* the crossing number counter */
+       int i;
+
+       /* loop through all edges of the polygon */
+       for (i = 0; i < n-1; i++) {    /* edge from V[i] to V[i+1] */
+          if (((V[i].y <= P.y) && (V[i + 1].y > P.y))    /* an upward crossing */
+               || ((V[i].y > P.y) && (V[i + 1].y <= P.y))) { /* a downward crossing */
+                       double vt = (float)(P.y - V[i].y) / (V[i + 1].y - V[i].y);
+                       if (P.x < V[i].x + vt * (V[i + 1].x - V[i].x)) /* P.x < intersect */
+                               ++cn;   /* a valid crossing of y=P.y right of P.x */
+               }
+       }
 
-       return 1;
+       return (cn&1);    /* 0 if even (out), and 1 if odd (in) */
 }
 
+
 int
 FindPolygons(SHPObject *obj, Ring ***Out)
 {
@@ -1029,11 +503,11 @@ FindPolygons(SHPObject *obj, Ring ***Out)
        LWDEBUGF(4, "FindPolygons[%d]: allocated space for %d rings\n", call, obj->nParts);
 
        /* Allocate initial memory */
-       Outer = (Ring**)malloc(sizeof(Ring*)*obj->nParts);
-       Inner = (Ring**)malloc(sizeof(Ring*)*obj->nParts);
+       Outer = (Ring **)malloc(sizeof(Ring *) * obj->nParts);
+       Inner = (Ring **)malloc(sizeof(Ring *) * obj->nParts);
 
        /* Iterate over rings dividing in Outers and Inners */
-       for (pi=0; pi<obj->nParts; pi++)
+       for (pi=0; pi < obj->nParts; pi++)
        {
                int vi; /* vertex index */
                int vs; /* start index */
@@ -1043,33 +517,37 @@ FindPolygons(SHPObject *obj, Ring ***Out)
                Ring *ring;
 
                /* Set start and end vertexes */
-               if ( pi==obj->nParts-1 ) ve = obj->nVertices;
-               else ve = obj->panPartStart[pi+1];
+               if (pi == obj->nParts - 1)
+                       ve = obj->nVertices;
+               else
+                       ve = obj->panPartStart[pi + 1];
+
                vs = obj->panPartStart[pi];
 
                /* Compute number of vertexes */
-               nv = ve-vs;
+               nv = ve - vs;
 
                /* Allocate memory for a ring */
-               ring = (Ring*)malloc(sizeof(Ring));
-               ring->list = (Point*)malloc(sizeof(Point)*nv);
+               ring = (Ring *)malloc(sizeof(Ring));
+               ring->list = (Point *)malloc(sizeof(Point) * nv);
                ring->n = nv;
                ring->next = NULL;
                ring->linked = 0;
 
                /* Iterate over ring vertexes */
-               for ( vi=vs; vi<ve; vi++)
+               for (vi = vs; vi < ve; vi++)
                {
                        int vn = vi+1; /* next vertex for area */
-                       if ( vn==ve ) vn = vs;
+                       if (vn == ve)
+                               vn = vs;
 
-                       ring->list[vi-vs].x = obj->padfX[vi];
-                       ring->list[vi-vs].y = obj->padfY[vi];
-                       ring->list[vi-vs].z = obj->padfZ[vi];
-                       ring->list[vi-vs].m = obj->padfM[vi];
+                       ring->list[vi - vs].x = obj->padfX[vi];
+                       ring->list[vi - vs].y = obj->padfY[vi];
+                       ring->list[vi - vs].z = obj->padfZ[vi];
+                       ring->list[vi - vs].m = obj->padfM[vi];
 
                        area += (obj->padfX[vi] * obj->padfY[vn]) -
-                                       (obj->padfY[vi] * obj->padfX[vn]);
+                               (obj->padfY[vi] * obj->padfX[vn]);
                }
 
                /* Close the ring with first vertex  */
@@ -1079,15 +557,14 @@ FindPolygons(SHPObject *obj, Ring ***Out)
                /*ring->list[vi].m = obj->padfM[vs]; */
 
                /* Clockwise (or single-part). It's an Outer Ring ! */
-               if (area < 0.0 || obj->nParts ==1)
+               if (area < 0.0 || obj->nParts == 1)
                {
                        Outer[out_index] = ring;
                        out_index++;
                }
-
-               /* Counterclockwise. It's an Inner Ring ! */
                else
                {
+                       /* Counterclockwise. It's an Inner Ring ! */
                        Inner[in_index] = ring;
                        in_index++;
                }
@@ -1097,11 +574,11 @@ FindPolygons(SHPObject *obj, Ring ***Out)
 
        /* Put the inner rings into the list of the outer rings */
        /* of which they are within */
-       for (pi=0; pi<in_index; pi++)
+       for (pi = 0; pi < in_index; pi++)
        {
-               Point pt,pt2;
+               Point pt, pt2;
                int i;
-               Ring *inner=Inner[pi], *outer=NULL;
+               Ring *inner = Inner[pi], *outer = NULL;
 
                pt.x = inner->list[0].x;
                pt.y = inner->list[0].y;
@@ -1109,22 +586,25 @@ FindPolygons(SHPObject *obj, Ring ***Out)
                pt2.x = inner->list[1].x;
                pt2.y = inner->list[1].y;
 
-               for (i=0; i<out_index; i++)
+               for(i = 0; i < out_index; i++)
                {
                        int in;
 
                        in = PIP(pt, Outer[i]->list, Outer[i]->n);
-                       if ( in || PIP(pt2, Outer[i]->list, Outer[i]->n) )
+                       if( in || PIP(pt2, Outer[i]->list, Outer[i]->n) )
                        {
                                outer = Outer[i];
                                break;
                        }
+                       /*fprintf(stderr, "!PIP %s\nOUTE %s\n", dump_ring(inner), dump_ring(Outer[i])); */
                }
 
-               if ( outer )
+               if (outer)
                {
                        outer->linked++;
-                       while (outer->next) outer = outer->next;
+                       while (outer->next)
+                               outer = outer->next;
+
                        outer->next = inner;
                }
                else
@@ -1144,31 +624,38 @@ FindPolygons(SHPObject *obj, Ring ***Out)
        return out_index;
 }
 
+
 void
 ReleasePolygons(Ring **polys, int npolys)
 {
        int pi;
+
        /* Release all memory */
-       for (pi=0; pi<npolys; pi++)
+       for (pi = 0; pi < npolys; pi++)
        {
                Ring *Poly, *temp;
                Poly = polys[pi];
-               while (Poly != NULL)
-               {
+               while (Poly != NULL){
                        temp = Poly;
                        Poly = Poly->next;
                        free(temp->list);
                        free(temp);
                }
        }
+
        free(polys);
 }
 
-/*This function basically deals with the polygon case. */
-/*it sorts the polys in order of outer,inner,inner, so that inners */
-/*always come after outers they are within  */
+
+/**
+ * @brief Generate an allocated geometry string for shapefile object obj using the state parameters
+ *
+ * This function basically deals with the polygon case. It sorts the polys in order of outer,
+ * inner,inner, so that inners always come after outers they are within.
+ * 
+ */
 int
-InsertPolygon(void)
+GeneratePolygonGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry)
 {
        Ring **Outer;
        int polygon_total, ring_total;
@@ -1189,17 +676,27 @@ InsertPolygon(void)
        int dims = 0, hasz = 0, hasm = 0;
        int result;
 
-       /* Determine the correct dimensions: */
-       if (wkbtype & WKBZOFFSET) hasz = 1;
-       if (wkbtype & WKBMOFFSET) hasm = 1;
+       char *mem;
+
+
+       /* Determine the correct dimensions: note that in hwgeom-compatible mode we cannot use
+          the M coordinate */
+       if (state->wkbtype & WKBZOFFSET)
+               hasz = 1;
+
+       if (!state->config->hwgeom)
+               if (state->wkbtype & WKBMOFFSET)
+                       hasm = 1;
+
        TYPE_SETZM(dims, hasz, hasm);
 
        polygon_total = FindPolygons(obj, &Outer);
 
-       if (simple_geometries == 1 && polygon_total != 1) /* We write Non-MULTI geometries, but have several parts: */
+       if (state->config->simple_geometries == 1 && polygon_total != 1) /* We write Non-MULTI geometries, but have several parts: */
        {
-               pgis_logf("We have a Multipolygon with %d parts, can't use -S switch!", polygon_total);
-               return 0;
+               snprintf(state->message, SHPLOADERMSGLEN, "We have a Multipolygon with %d parts, can't use -S switch!", polygon_total);
+
+               return SHPLOADERERR;
        }
 
        /* Allocate memory for our array of LWPOLYs */
@@ -1234,15 +731,15 @@ InsertPolygon(void)
                        /* Create a DYNPTARRAY containing the points making up the ring */
                        dpas = dynptarray_create(polyring->n, dims);
 
-                       for (vi = 0; vi < polyring->n; vi++)
+                       for(vi = 0; vi < polyring->n; vi++)
                        {
                                /* Build up a point array of all the points in this ring */
                                point4d.x = polyring->list[vi].x;
                                point4d.y = polyring->list[vi].y;
 
-                               if (wkbtype & WKBZOFFSET)
+                               if (state->wkbtype & WKBZOFFSET)
                                        point4d.z = polyring->list[vi].z;
-                               if (wkbtype & WKBMOFFSET)
+                               if (state->wkbtype & WKBMOFFSET)
                                        point4d.m = polyring->list[vi].m;
 
                                dynptarray_addPoint4d(dpas, &point4d, 0);
@@ -1261,16 +758,14 @@ InsertPolygon(void)
                }
 
                /* Generate the LWGEOM */
-               lwpoly = lwpoly_construct(sr_id, NULL, ring_total, pas[pi]);
+               lwpoly = lwpoly_construct(state->config->sr_id, NULL, ring_total, pas[pi]);
                lwpolygons[pi] = lwpoly_as_lwgeom(lwpoly);
        }
 
-       ReleasePolygons(Outer, polygon_total);
-
        /* If using MULTIPOLYGONS then generate the serialized collection, otherwise just a single POLYGON */
-       if (simple_geometries == 0)
+       if (state->config->simple_geometries == 0)
        {
-               lwcollection = lwcollection_construct(MULTIPOLYGONTYPE, sr_id, NULL, polygon_total, lwpolygons);
+               lwcollection = lwcollection_construct(MULTIPOLYGONTYPE, state->config->sr_id, NULL, polygon_total, lwpolygons);
                serialized_lwgeom = lwgeom_serialize(lwcollection_as_lwgeom(lwcollection));
        }
        else
@@ -1278,15 +773,38 @@ InsertPolygon(void)
                serialized_lwgeom = lwgeom_serialize(lwpolygons[0]);
        }
 
-       result = serialized_lwgeom_to_hexwkb(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_NONE, -1);
-
-       if (result)
+       /* Note: lwpoly_free() currently doesn't free its serialized pointlist, so do it manually */
+       for (pi = 0; pi < polygon_total; pi++)
        {
-               pgis_logf( "ERROR: %s", lwg_unparser_result.message);
-               return 0;
-       }
-
-       if ( ! OutputGeometry(lwg_unparser_result.wkoutput) ) return 0;
+               Ring *polyring = Outer[pi];
+               int ring_index = 0;
+               while (polyring)
+               {
+                       if (pas[pi][ring_index]->serialized_pointlist)
+                               lwfree(pas[pi][ring_index]->serialized_pointlist);
+
+                       polyring = polyring->next;
+                       ring_index++;
+               }
+       }
+
+       ReleasePolygons(Outer, polygon_total);
+
+       if (!state->config->hwgeom)
+               result = serialized_lwgeom_to_hexwkb(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_NONE, -1);
+       else
+               result = serialized_lwgeom_to_ewkt(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_NONE);
+
+       if (result)
+       {
+               snprintf(state->message, SHPLOADERMSGLEN, "%s", lwg_unparser_result.message);
+
+               return SHPLOADERERR;
+       }
+
+       /* Allocate a string containing the resulting geometry */
+       mem = malloc(strlen(lwg_unparser_result.wkoutput) + 1);
+       strcpy(mem, lwg_unparser_result.wkoutput);
 
        /* Free all of the allocated items */
        lwfree(lwg_unparser_result.wkoutput);
@@ -1299,580 +817,1010 @@ InsertPolygon(void)
        /* Free the pointer arrays */
        lwfree(pas);
        lwfree(lwpolygons);
-       if (simple_geometries == 0)
+       if (lwcollection)
                lwfree(lwcollection);
 
-       return 1;
+       /* Return the string - everything ok */
+       *geometry = mem;
+
+       return SHPLOADEROK;
 }
 
+
 /*
- * Insert either a POINT or MULTIPOINT into the output stream
+ * External functions (defined in shp2pgsql-core.h)
  */
-int
-InsertPoint(void)
+
+
+/* Convert the string to lower case */
+void
+strtolower(char *s)
 {
-       LWCOLLECTION *lwcollection;
+       int j;
 
-       LWGEOM **lwmultipoints;
-       uchar *serialized_lwgeom;
-       LWGEOM_UNPARSER_RESULT lwg_unparser_result;
+       for (j = 0; j < strlen(s); j++)
+               s[j] = tolower(s[j]);
+}
 
-       DYNPTARRAY **dpas;
-       POINT4D point4d;
 
-       int dims = 0, hasz = 0, hasm = 0;
-       int result;
-       int u;
+/* Default configuration settings */
+void
+set_config_defaults(SHPLOADERCONFIG *config)
+{
+       config->opt = 'c';
+       config->schema = NULL;
+       config->table = NULL;
+       config->geom = strdup("the_geom");
+       config->readshape = 1;
+       config->sr_id = -1;
+       config->hwgeom = 0;
+       config->dump_format = 0;
+}
 
-       /* Determine the correct dimensions:  */
-       if (wkbtype & WKBZOFFSET) hasz = 1;
-       if (wkbtype & WKBMOFFSET) hasm = 1;
-       TYPE_SETZM(dims, hasz, hasm);
 
-       /* Allocate memory for our array of LWPOINTs and our dynptarrays */
-       lwmultipoints = malloc(sizeof(LWPOINT *) * obj->nVertices);
-       dpas = malloc(sizeof(DYNPTARRAY *) * obj->nVertices);
+/* Create a new shapefile state object */
+SHPLOADERSTATE *
+ShpLoaderCreate(SHPLOADERCONFIG *config)
+{
+       SHPLOADERSTATE *state;
 
-       /* We need an array of pointers to each of our sub-geometries */
-       for (u = 0; u < obj->nVertices; u++)
+       /* Create a new state object and assign the config to it */
+       state = malloc(sizeof(SHPLOADERSTATE));
+       state->config = config;
+
+       /* Set any state defaults */
+       state->hSHPHandle = NULL;
+       state->hDBFHandle = NULL;
+       state->wkbtype = 0;
+
+       return state;
+}
+
+
+/* Open the shapefile and extract the relevant field information */
+int
+ShpLoaderOpenShape(SHPLOADERSTATE *state)
+{
+       SHPObject *obj = NULL;
+       int j, z;
+       int ret = SHPLOADEROK;
+
+       int field_precision, field_width;
+       char name[MAXFIELDNAMELEN];
+       char name2[MAXFIELDNAMELEN];
+       DBFFieldType type = -1;
+#ifdef HAVE_ICONV
+       char *utf8str;
+#endif
+
+       /* If we are reading the entire shapefile, open it */
+       if (state->config->readshape == 1)
        {
-               /* Generate the point */
-               point4d.x = obj->padfX[u];
-               point4d.y = obj->padfY[u];
+               state->hSHPHandle = SHPOpen(state->config->shp_file, "rb");
 
-               if (wkbtype & WKBZOFFSET)
-                       point4d.z = obj->padfZ[u];
-               if (wkbtype & WKBMOFFSET)
-                       point4d.m = obj->padfM[u];
+               if (state->hSHPHandle == NULL)
+               {
+                       snprintf(state->message, SHPLOADERMSGLEN, "%s: shape (.shp) or index files (.shx) can not be opened, will just import attribute data.", state->config->shp_file);
+                       state->config->readshape = 0;
 
-               /* Create a dynptarray containing a single point */
-               dpas[u] = dynptarray_create(1, dims);
-               dynptarray_addPoint4d(dpas[u], &point4d, 0);
+                       ret = SHPLOADERWARN;
+               }
+       }
 
-               /* Generate the LWPOINT */
-               lwmultipoints[u] = lwpoint_as_lwgeom(lwpoint_construct(sr_id, NULL, dpas[u]->pa));
+       /* Open the DBF (attributes) file */
+       state->hDBFHandle = DBFOpen(state->config->shp_file, "rb");
+       if ((state->hSHPHandle == NULL && state->config->readshape == 1) || state->hDBFHandle == NULL)
+       {
+               snprintf(state->message, SHPLOADERMSGLEN, "%s: dbf file (.dbf) can not be opened.", state->config->shp_file);
+
+               return SHPLOADERERR;
        }
 
-       /* If we have more than 1 vertex then we are working on a MULTIPOINT and so generate a MULTIPOINT
-       rather than a POINT */
-       if (obj->nVertices > 1)
+       /* If reading the whole shapefile (not just attributes)... */
+       if (state->config->readshape == 1)
        {
-               lwcollection = lwcollection_construct(MULTIPOINTTYPE, sr_id, NULL, obj->nVertices, lwmultipoints);
-               serialized_lwgeom = lwgeom_serialize(lwcollection_as_lwgeom(lwcollection));
+               SHPGetInfo(state->hSHPHandle, &state->num_entities, &state->shpfiletype, NULL, NULL);
+
+               /* If null_policy is set to abort, check for NULLs */
+               if (state->config->null_policy == POLICY_NULL_ABORT)
+               {
+                       /* If we abort on null items, scan the entire file for NULLs */
+                       for (j = 0; j < state->num_entities; j++)
+                       {
+                               obj = SHPReadObject(state->hSHPHandle, j);
+
+                               if (!obj)
+                               {
+                                       snprintf(state->message, SHPLOADERMSGLEN, "Error reading shape object %d", j);
+                                       return SHPLOADERERR;
+                               }
+
+                               if (obj->nVertices == 0)
+                               {
+                                       snprintf(state->message, SHPLOADERMSGLEN, "Empty geometries found, aborted.");
+                                       return SHPLOADERERR;
+                               }
+
+                               SHPDestroyObject(obj);
+                       }
+               }
+
+               /* Check the shapefile type */
+               switch (state->shpfiletype)
+               {
+                       case SHPT_POINT:
+                               /* Point */
+                               state->pgtype = "POINT";
+                               state->wkbtype = POINTTYPE;
+                               state->pgdims = 2;
+                               break;
+       
+                       case SHPT_ARC:
+                               /* PolyLine */
+                               state->pgtype = "MULTILINESTRING";
+                               state->wkbtype = MULTILINETYPE ;
+                               state->pgdims = 2;
+                               break;
+       
+                       case SHPT_POLYGON:
+                               /* Polygon */
+                               state->pgtype = "MULTIPOLYGON";
+                               state->wkbtype = MULTIPOLYGONTYPE;
+                               state->pgdims = 2;
+                               break;
+       
+                       case SHPT_MULTIPOINT:
+                               /* MultiPoint */
+                               state->pgtype = "MULTIPOINT";
+                               state->wkbtype = MULTIPOINTTYPE;
+                               state->pgdims = 2;
+                               break;
+       
+                       case SHPT_POINTM:
+                               /* PointM */
+                               state->wkbtype = POINTTYPE | WKBMOFFSET;
+       
+                               if (!state->config->hwgeom)
+                               {
+                                       state->pgtype = "POINTM";
+                                       state->pgdims = 3;
+                                       state->istypeM = 1;
+                               } 
+                               else 
+                               {
+                                       state->pgtype = "POINT";
+                                       state->pgdims = 2;
+                               }
+                               break;
+       
+                       case SHPT_ARCM:
+                               /* PolyLineM */
+                               state->wkbtype = MULTILINETYPE | WKBMOFFSET;
+       
+                               if (!state->config->hwgeom)
+                               {
+                                       state->pgtype = "MULTILINESTRINGM";
+                                       state->pgdims = 3;
+                                       state->istypeM = 1;
+                               } 
+                               else 
+                               {
+                                       state->pgtype = "MULTILINESTRING";
+                                       state->pgdims = 2;
+                               }
+                               break;
+       
+                       case SHPT_POLYGONM:
+                               /* PolygonM */
+                               state->wkbtype = MULTIPOLYGONTYPE | WKBMOFFSET;
+       
+                               if (!state->config->hwgeom)
+                               {
+                                       state->pgtype = "MULTIPOLYGONM";
+                                       state->pgdims = 3;
+                                       state->istypeM = 1;
+                               }
+                               else 
+                               {
+                                       state->pgtype = "MULTIPOLYGON";
+                                       state->pgdims = 2;
+                               }
+                               break;
+       
+                       case SHPT_MULTIPOINTM:
+                               /* MultiPointM */
+                               state->wkbtype = MULTIPOINTTYPE | WKBMOFFSET;
+       
+                               if (!state->config->hwgeom)
+                               {
+                                       state->pgtype = "MULTIPOINTM";
+                                       state->pgdims = 3;
+                                       state->istypeM = 1;
+                               } 
+                               else
+                               {
+                                       state->pgtype = "MULTIPOINT";
+                                       state->pgdims = 2;
+                               }
+                               break;
+       
+                       case SHPT_POINTZ: 
+                               /* PointZ */
+                               state->wkbtype = POINTTYPE | WKBMOFFSET | WKBZOFFSET;
+                               state->pgtype = "POINT";
+       
+                               if (!state->config->hwgeom)
+                                       state->pgdims = 4;
+                               else 
+                                       state->pgdims = 3;
+       
+                               break;
+       
+                       case SHPT_ARCZ:
+                               /* PolyLineZ */
+                               state->pgtype = "MULTILINESTRING";
+                               state->wkbtype = MULTILINETYPE | WKBZOFFSET | WKBMOFFSET;
+       
+                               if (!state->config->hwgeom)
+                                       state->pgdims = 4;
+                               else
+                                       state->pgdims = 3;
+       
+                               break;
+       
+                       case SHPT_POLYGONZ:
+                               /* MultiPolygonZ */
+                               state->pgtype = "MULTIPOLYGON";
+                               state->wkbtype = MULTIPOLYGONTYPE | WKBZOFFSET | WKBMOFFSET;
+       
+                               if (!state->config->hwgeom)
+                                       state->pgdims = 4;
+                               else
+                                       state->pgdims = 3;
+       
+                               break;
+       
+                       case SHPT_MULTIPOINTZ:
+                               /* MultiPointZ */
+                               state->pgtype = "MULTIPOINT";
+                               state->wkbtype = MULTIPOINTTYPE | WKBZOFFSET | WKBMOFFSET;
+       
+                               if (!state->config->hwgeom)
+                                       state->pgdims = 4;
+                               else
+                                       state->pgdims = 3;
+       
+                               break;
+       
+                       default:
+                               state->pgtype = "GEOMETRY";
+                               state->wkbtype = COLLECTIONTYPE | WKBZOFFSET | WKBMOFFSET;
+                               state->pgdims = 4;
+       
+                               snprintf(state->message, SHPLOADERMSGLEN, "Unknown geometry type: %d\n", state->shpfiletype);
+                               return SHPLOADERERR;
+       
+                               break;
+               }
+       
+               /* If in simple geometry mode, alter names for CREATE TABLE by skipping MULTI */
+               if (state->config->simple_geometries)
+               {
+                       if ((state->wkbtype & 0x7) == MULTIPOLYGONTYPE)
+                               state->pgtype += 5;
+       
+                       if ((state->wkbtype & 0x7) == MULTILINETYPE)
+                               state->pgtype += 5;
+               }
+
        }
        else
        {
-               serialized_lwgeom = lwgeom_serialize(lwmultipoints[0]);
+               /* Otherwise just count the number of records in the DBF */
+               state->num_entities = DBFGetRecordCount(state->hDBFHandle);
        }
 
-       result = serialized_lwgeom_to_hexwkb(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_NONE, -1);
 
-       if (result)
-       {
-               pgis_logf("ERROR: %s", lwg_unparser_result.message);
-               return 0;
-       }
+       /* Get the field information from the DBF */
+       state->num_fields = DBFGetFieldCount(state->hDBFHandle);
+       state->num_records = DBFGetRecordCount(state->hDBFHandle);
 
-       if ( ! OutputGeometry(lwg_unparser_result.wkoutput) ) return 0;
+       /* Allocate storage for field information */
+       state->field_names = malloc(state->num_fields * sizeof(char*));
+       state->types = (DBFFieldType *)malloc(state->num_fields * sizeof(int));
+       state->widths = malloc(state->num_fields * sizeof(int));
+       state->precisions = malloc(state->num_fields * sizeof(int));
+       state->col_names = malloc((state->num_fields + 2) * sizeof(char) * MAXFIELDNAMELEN);
 
-       /* Free all of the allocated items */
-       lwfree(lwg_unparser_result.wkoutput);
-       lwfree(serialized_lwgeom);
+       /* Generate a string of comma separated column names of the form "(col1, col2 ... colN)" for the SQL
+          insertion string */
+       strcpy(state->col_names, "(" );
 
-       for (u = 0; u < obj->nVertices; u++)
+       for(j = 0; j < state->num_fields; j++)
        {
-               lwpoint_free(lwgeom_as_lwpoint(lwmultipoints[u]));
-               lwfree(dpas[u]);
+               type = DBFGetFieldInfo(state->hDBFHandle, j, name, &field_width, &field_precision);
+
+               state->types[j] = type;
+               state->widths[j] = field_width;
+               state->precisions[j] = field_precision;
+
+#ifdef HAVE_ICONV
+               if (state->config->encoding)
+               {
+                       /* If we are converting from another encoding to UTF8, convert the field name to UTF8 */
+                       utf8str = utf8(state->config->encoding, name);
+                       if (!utf8str)
+                       {
+                               snprintf(state->message, SHPLOADERMSGLEN, "Unable to convert field name \"%s\" to UTF8: iconv reports \"%s\"", name, strerror(errno));
+                               return SHPLOADERERR;
+                       }
+
+                       strncpy(name, utf8str, MAXFIELDNAMELEN);
+                       free(utf8str);
+               }
+#endif
+
+               /*
+                * Make field names lowercase unless asked to
+                * keep identifiers case.
+                */
+               if (!state->config->quoteidentifiers)
+                       strtolower(name);
+
+               /*
+                * Escape names starting with the
+                * escape char (_), those named 'gid'
+                * or after pgsql reserved attribute names
+                */
+               if(name[0] == '_' ||
+                       ! strcmp(name, "gid") || ! strcmp(name, "tableoid") ||
+                       ! strcmp(name, "cmax") || ! strcmp(name, "xmax") ||
+                       ! strcmp(name, "cmin") || ! strcmp(name, "primary") ||
+                       ! strcmp(name, "oid") || ! strcmp(name, "ctid"))
+               {
+                       strncpy(name2 + 2, name, MAXFIELDNAMELEN);
+                       name2[0] = '_';
+                       name2[1] = '_';
+                       strcpy(name, name2);
+               }
+
+               /* Avoid duplicating field names */
+               for(z = 0; z < j ; z++)
+               {
+                       if (strcmp(state->field_names[z], name) == 0)
+                       {
+                               strncat(name, "__", MAXFIELDNAMELEN);
+                               snprintf(name + strlen(name), MAXFIELDNAMELEN, "%i", j);
+                               break;
+                       }
+               }
+
+               state->field_names[j] = malloc(strlen(name) + 1);
+               strcpy(state->field_names[j], name);
+
+               strcat(state->col_names, "\"");
+               strcat(state->col_names, name);
+
+               if (state->config->readshape == 1 || j < (state->num_fields - 1))
+               {
+                       /* Don't include last comma if its the last field and no geometry field will follow */
+                       strcat(state->col_names, "\",");
+               }
+               else
+               {
+                       strcat(state->col_names, "\"");
+               }
        }
 
-       lwfree(dpas);
-       lwfree(lwmultipoints);
+       /* Append the geometry column if required */
+       if (state->config->readshape == 1)
+               strcat(state->col_names, state->config->geom);
+
+       strcat(state->col_names, ")");
+
 
-       return 1;
+       /* Return status */
+       return ret;
 }
 
+
+/* Return a pointer to an allocated string containing the header for the specified loader state */
 int
-OutputGeometry(char *geometry)
+ShpLoaderGetSQLHeader(SHPLOADERSTATE *state, char **strheader)
 {
-       if (!dump_format)
-               stringbuffer_append(sb_row, "'");
+       stringbuffer_t *sb;
+       char *ret;
+       int j;
 
-       stringbuffer_aprintf(sb_row, "%s", geometry);
+       /* Create the stringbuffer containing the header; we use this API as it's easier
+          for handling string resizing during append */
+       sb = stringbuffer_create();
+       stringbuffer_clear(sb);
 
-       if (!dump_format)
-       {
-               stringbuffer_append(sb_row, "')");
-               if ( ! pgis_exec(stringbuffer_getstring(sb_row)) ) return 0;
-       }
-       else
+#ifdef HAVE_ICONV
+       /* Set the client encoding if required */
+       if (state->config->encoding)
        {
-               stringbuffer_append_c(sb_row, '\n');
-               pgis_copy_write(stringbuffer_getstring(sb_row));
+               vasbappend(sb, "SET CLIENT_ENCODING TO UTF8;\n");
        }
-       stringbuffer_clear(sb_row);
-       return 1;
+#endif
 
-}
+       /* Use SQL-standard string escaping rather than PostgreSQL standard */
+       vasbappend(sb, "SET STANDARD_CONFORMING_STRINGS TO ON;\n");
 
+       /* Drop table if requested */
+       if (state->config->opt == 'd')
+       {
+               /**
+                * TODO: if the table has more then one geometry column
+                *      the DROP TABLE call will leave spurious records in
+                *      geometry_columns.
+                *
+                * If the geometry column in the table being dropped
+                * does not match 'the_geom' or the name specified with
+                * -g an error is returned by DropGeometryColumn.
+                *
+                * The table to be dropped might not exist.
+                */
+               if (state->config->schema)
+               {
+                       if (state->config->readshape == 1)
+                       {
+                               vasbappend(sb, "SELECT DropGeometryColumn('%s','%s','%s');\n",
+                                       state->config->schema, state->config->table, state->config->geom);
+                       }
 
+                       vasbappend(sb, "DROP TABLE \"%s\".\"%s\";\n", state->config->schema, 
+                               state->config->table);
+               }
+               else
+               {
+                       if (state->config->readshape == 1)
+                       {
+                               vasbappend(sb, "SELECT DropGeometryColumn('','%s','%s');\n",
+                                       state->config->table, state->config->geom);
+                       }
 
-void
-SetPgType(void)
-{
-       switch (shpfiletype)
-       {
-       case SHPT_POINT: /* Point */
-               pgtype = "POINT";
-               wkbtype = POINTTYPE;
-               pgdims = 2;
-               break;
-       case SHPT_ARC: /* PolyLine */
-               pgtype = "MULTILINESTRING";
-               wkbtype = MULTILINETYPE ;
-               pgdims = 2;
-               break;
-       case SHPT_POLYGON: /* Polygon */
-               pgtype = "MULTIPOLYGON";
-               wkbtype = MULTIPOLYGONTYPE;
-               pgdims = 2;
-               break;
-       case SHPT_MULTIPOINT: /* MultiPoint */
-               pgtype = "MULTIPOINT";
-               wkbtype = MULTIPOINTTYPE;
-               pgdims = 2;
-               break;
-       case SHPT_POINTM: /* PointM */
-               wkbtype = POINTTYPE | WKBMOFFSET;
-               pgtype = "POINTM";
-               pgdims = 3;
-               istypeM = 1;
-               break;
-       case SHPT_ARCM: /* PolyLineM */
-               wkbtype = MULTILINETYPE | WKBMOFFSET;
-               pgtype = "MULTILINESTRINGM";
-               pgdims = 3;
-               istypeM = 1;
-               break;
-       case SHPT_POLYGONM: /* PolygonM */
-               wkbtype = MULTIPOLYGONTYPE | WKBMOFFSET;
-               pgtype = "MULTIPOLYGONM";
-               pgdims = 3;
-               istypeM = 1;
-               break;
-       case SHPT_MULTIPOINTM: /* MultiPointM */
-               wkbtype = MULTIPOINTTYPE | WKBMOFFSET;
-               pgtype = "MULTIPOINTM";
-               pgdims = 3;
-               istypeM = 1;
-               break;
-       case SHPT_POINTZ: /* PointZ */
-               wkbtype = POINTTYPE | WKBMOFFSET | WKBZOFFSET;
-               pgtype = "POINT";
-               pgdims = 4;
-               break;
-       case SHPT_ARCZ: /* PolyLineZ */
-               pgtype = "MULTILINESTRING";
-               wkbtype = MULTILINETYPE | WKBZOFFSET | WKBMOFFSET;
-               pgdims = 4;
-               break;
-       case SHPT_POLYGONZ: /* MultiPolygonZ */
-               pgtype = "MULTIPOLYGON";
-               wkbtype = MULTIPOLYGONTYPE | WKBZOFFSET | WKBMOFFSET;
-               pgdims = 4;
-               break;
-       case SHPT_MULTIPOINTZ: /* MultiPointZ */
-               pgtype = "MULTIPOINT";
-               wkbtype = MULTIPOINTTYPE | WKBZOFFSET | WKBMOFFSET;
-               pgdims = 4;
-               break;
-       default:
-               pgtype = "GEOMETRY";
-               wkbtype = COLLECTIONTYPE | WKBZOFFSET | WKBMOFFSET;
-               pgdims = 4;
-               pgis_logf("Unknown geometry type: %d", shpfiletype);
-               break;
+                       vasbappend(sb, "DROP TABLE \"%s\";\n", state->config->table);
+               }
        }
 
-       if (simple_geometries)
+       /* Start of transaction */
+       vasbappend(sb, "BEGIN;\n");
+
+       /* If not in 'append' mode create the spatial table */
+       if (state->config->opt != 'a')
        {
-               // adjust geometry name for CREATE TABLE by skipping MULTI
-               if ((wkbtype & 0x7) == MULTIPOLYGONTYPE) pgtype += 5;
-               if ((wkbtype & 0x7) == MULTILINETYPE) pgtype += 5;
+               /*
+               * Create a table for inserting the shapes into with appropriate
+               * columns and types
+               */
+               if (state->config->schema)
+               {
+                       vasbappend(sb, "CREATE TABLE \"%s\".\"%s\" (gid serial PRIMARY KEY",
+                               state->config->schema, state->config->table);
+               }
+               else
+               {
+                       vasbappend(sb, "CREATE TABLE \"%s\" (gid serial PRIMARY KEY", state->config->table);
+               }
+       
+               /* Generate the field types based upon the shapefile information */ 
+               for(j = 0; j < state->num_fields; j++)
+               {
+                       vasbappend(sb, ",\n\"%s\" ", state->field_names[j]);
+       
+                       switch(state->types[j])
+                       {
+                               case FTString:
+                                       /* use DBF attribute size as maximum width */
+                                       vasbappend(sb, "varchar(%d)", state->widths[j]);
+                                       break;
+
+                               case FTDate:
+                                       vasbappend(sb, "date");
+                                       break;
+
+                               case FTInteger:
+                                       /* Determine exact type based upon field width */
+                                       if (state->config->forceint4)
+                                       {
+                                               vasbappend(sb, "int4");
+                                       }
+                                       else if (state->widths[j] < 5)
+                                       {
+                                               vasbappend(sb, "int2");
+                                       }
+                                       else if (state->widths[j] < 10)
+                                       {
+                                               vasbappend(sb, "int4");
+                                       }
+                                       else
+                                       {
+                                               vasbappend(sb, "numeric(%d,0)", state->widths[j]);
+                                       }
+                                       break;
+
+                               case FTDouble:
+                                       /* Determine exact type based upon field width */
+                                       if(state->widths[j] > 18)
+                                       {
+                                               vasbappend(sb, "numeric");
+                                       }
+                                       else
+                                       {
+                                               vasbappend(sb, "float8");
+                                       }
+                                       break;
+
+                               case FTLogical:
+                                       vasbappend(sb, "boolean");
+                                       break;
+
+                               default:
+                                       snprintf(state->message, SHPLOADERMSGLEN, "Invalid type %x in DBF file", state->types[j]);
+                                       stringbuffer_destroy(sb);
+                                       return SHPLOADERERR;
+                       }
+               }
+               vasbappend(sb, ");\n");
+       
+               /* Create the geometry column with an addgeometry call */
+               if (state->config->readshape == 1)
+               {
+                       if (state->config->schema) 
+                       {
+                               vasbappend(sb, "SELECT AddGeometryColumn('%s','%s','%s','%d',",
+                                       state->config->schema, state->config->table, state->config->geom, state->config->sr_id);
+                       }
+                       else
+                       {
+                               vasbappend(sb, "SELECT AddGeometryColumn('','%s','%s','%d',",
+                                       state->config->table, state->config->geom, state->config->sr_id);
+                       }
+
+                       vasbappend(sb, "'%s',%d);\n", state->pgtype, state->pgdims);
+               }
        }
+
+       /* Copy the string buffer into a new string, destroying the string buffer */
+       ret = (char *)malloc(strlen((char *)stringbuffer_getstring(sb)) + 1);
+       strcpy(ret, (char *)stringbuffer_getstring(sb));
+       stringbuffer_destroy(sb);
+
+       *strheader = ret;
+
+       return SHPLOADEROK;
 }
 
+
+/* Return an allocated string containing the copy statement for this state */
 int
-DropTable(char *schema, char *table, char *geom)
+ShpLoaderGetSQLCopyStatement(SHPLOADERSTATE *state, char **strheader)
 {
-       /*---------------Drop the table--------------------------
-        * TODO: if the table has more then one geometry column
-        * the DROP TABLE call will leave spurious records in
-        * geometry_columns.
-        *
-        * If the geometry column in the table being dropped
-        * does not match 'the_geom' or the name specified with
-        * -g an error is returned by DropGeometryColumn.
-        *
-        * The table to be dropped might not exist.
-        *
-        */
-       char *sql;
+       char *copystr;
 
-       if ( schema )
+       /* Allocate the string for the COPY statement */
+       if (state->config->dump_format)
        {
-               if (readshape == 1)
+               if (state->config->schema)
                {
-                       lw_asprintf(&sql, "SELECT DropGeometryColumn('%s','%s','%s')",  schema, table, geom);
-                       if ( ! pgis_exec(sql) )
-                       {
-                               free(sql);
-                               return 0;
-                       }
-                       free(sql);
+                       copystr = malloc(strlen(state->config->schema) + strlen(state->config->table) +
+                               strlen(state->col_names) + 40);
+
+                       sprintf(copystr, "COPY \"%s\".\"%s\" %s FROM stdin;\n",
+                               state->config->schema, state->config->table, state->col_names);
                }
-               lw_asprintf(&sql, "DROP TABLE \"%s\".\"%s\"", schema, table);
-               if ( ! pgis_exec(sql) )
+               else
                {
-                       free(sql);
-                       return 0;
+                       copystr = malloc(strlen(state->config->table) + strlen(state->col_names) + 40);
+
+                       sprintf(copystr, "COPY \"%s\" %s FROM stdin;\n", state->config->table, state->col_names);
                }
-               free(sql);
+
+               *strheader = copystr;
+               return SHPLOADEROK;
        }
        else
        {
-               if (readshape == 1)
-               {
-                       lw_asprintf(&sql, "SELECT DropGeometryColumn('','%s','%s')", table, geom);
-                       if  ( ! pgis_exec(sql) )
-                       {
-                               free(sql);
-                               return 0;
-                       }
-                       free(sql);
-               }
-               lw_asprintf(&sql, "DROP TABLE \"%s\"", table);
-               if ( ! pgis_exec(sql) )
-               {
-                       free(sql);
-                       return 0;
-               }
-               free(sql);
+               /* Flag an error as something has gone horribly wrong */
+               snprintf(state->message, SHPLOADERMSGLEN, "Internal error: attempt to generate a COPY statement for data that hasn't been requested in COPY format");
+
+               return SHPLOADERERR;
        }
-       return 1;
 }
 
-void
-GetFieldsSpec(void)
+
+/* Return a count of the number of entities in this shapefile */
+int
+ShpLoaderGetRecordCount(SHPLOADERSTATE *state)
 {
-       /*
-        * Shapefile (dbf) field name are at most 10chars + 1 NULL.
-        * Postgresql field names are at most 63 bytes + 1 NULL.
-        */
-#define MAXFIELDNAMELEN 64
-       int field_precision, field_width;
-       int j, z;
-       char  name[MAXFIELDNAMELEN];
-       char  name2[MAXFIELDNAMELEN];
-       DBFFieldType type = -1;
+       return state->num_entities;
+}
+
+
+/* Return an allocated string representation of a specified record item */
+int
+ShpLoaderGenerateSQLRowStatement(SHPLOADERSTATE *state, int item, char **strrecord)
+{
+       SHPObject *obj = NULL;
+       stringbuffer_t *sb;
+       stringbuffer_t *sbwarn;
+       char val[MAXVALUELEN];
+       char *escval;
+       char *geometry, *ret;
 #ifdef HAVE_ICONV
        char *utf8str;
 #endif
+       int res, i;
 
-       num_fields = DBFGetFieldCount( hDBFHandle );
-       num_records = DBFGetRecordCount(hDBFHandle);
-       field_names = malloc(num_fields*sizeof(char*));
-       types = (DBFFieldType *)malloc(num_fields*sizeof(int));
-       widths = malloc(num_fields*sizeof(int));
-       precisions = malloc(num_fields*sizeof(int));
-       if (readshape == 1)
+       /* Clear the stringbuffers */
+       sbwarn = stringbuffer_create();
+       stringbuffer_clear(sbwarn);
+       sb = stringbuffer_create();
+       stringbuffer_clear(sb);
+
+       /* If we are reading the DBF only and the record has been marked deleted, return deleted record status */
+       if (state->config->readshape == 0 && DBFReadDeleted(state->hDBFHandle, item))
        {
-               col_names = malloc((num_fields+2) * sizeof(char) * MAXFIELDNAMELEN);
-       }
-       {       //for dbf only, we do not need to allocate slot for the_geom
-               col_names = malloc((num_fields+1) * sizeof(char) * MAXFIELDNAMELEN);
+               *strrecord = NULL;
+               return SHPLOADERRECDELETED;
        }
-       strcpy(col_names, "(" );
 
-       /*fprintf(stderr, "Number of fields from DBF: %d\n", num_fields); */
-       for (j=0;j<num_fields;j++)
+       /* If we are reading the shapefile, open the specified record */
+       if (state->config->readshape == 1)
        {
-               type = DBFGetFieldInfo(hDBFHandle, j, name, &field_width, &field_precision);
+               obj = SHPReadObject(state->hSHPHandle, item);
+               if (!obj)
+               {
+                       snprintf(state->message, SHPLOADERMSGLEN, "Error reading shape object %d", item);
+                       return SHPLOADERERR;
+               }
 
-               /*fprintf(stderr, "Field %d (%s) width/decimals: %d/%d\n", j, name, field_width, field_precision); */
-               types[j] = type;
-               widths[j] = field_width;
-               precisions[j] = field_precision;
+               /* If we are set to skip NULLs, return a NULL record status */
+               if (state->config->null_policy == POLICY_NULL_SKIP && obj->nVertices == 0 )
+               {
+                       SHPDestroyObject(obj);
 
-#ifdef HAVE_ICONV
-               if ( encoding )
+                       *strrecord = NULL;
+                       return SHPLOADERRECISNULL;
+               }
+       }
+
+       /* If not in dump format, generate the INSERT string */
+       if (!state->config->dump_format)
+       {
+               if (state->config->schema)
                {
-                       utf8str = utf8(encoding, name);
-                       if ( ! utf8str ) exit(1);
-                       strcpy(name, utf8str);
-                       free(utf8str);
+                       vasbappend(sb, "INSERT INTO \"%s\".\"%s\" %s VALUES (", state->config->schema, 
+                               state->config->table, state->col_names);
                }
+               else
+               {
+                       vasbappend(sb, "INSERT INTO \"%s\" %s VALUES (", state->config->table, 
+                               state->col_names);
+               }
+       }
+
+
+       /* Read all of the attributes from the DBF file for this item */
+       for (i = 0; i < DBFGetFieldCount(state->hDBFHandle); i++)
+       {
+               /* Special case for NULL attributes */
+               if (DBFIsAttributeNULL(state->hDBFHandle, item, i))
+               {
+                       if (state->config->dump_format)
+                               vasbappend(sb, "\\N");
+                       else
+                               vasbappend(sb, "NULL");
+               }
+               else
+               {
+                       /* Attribute NOT NULL */
+                       switch (state->types[i])
+                       {
+                               case FTInteger:
+                               case FTDouble:
+                                       if (-1 == snprintf(val, MAXVALUELEN, "%s", DBFReadStringAttribute(state->hDBFHandle, item, i)))
+                                       {
+                                               vasbappend(sbwarn, "Warning: field %d name truncated\n", i);
+                                               val[MAXVALUELEN - 1] = '\0';
+                                       }
+
+                                       /* If the value is an empty string, change to 0 */
+                                       if (val[0] == '\0')
+                                       {
+                                               val[0] = '0';
+                                               val[1] = '\0';
+                                       }
+
+                                       /* If the value ends with just ".", remove the dot */
+                                       if (val[strlen(val) - 1] == '.')
+                                               val[strlen(val) - 1] = '\0';
+                                       break;
+
+                               case FTString:
+                               case FTLogical:
+                               case FTDate:
+                                       if (-1 == snprintf(val, MAXVALUELEN, "%s", DBFReadStringAttribute(state->hDBFHandle, item, i)))
+                                       {
+                                               vasbappend(sbwarn, "Warning: field %d name truncated\n", i);
+                                               val[MAXVALUELEN - 1] = '\0';
+                                       }
+                                       break;
+
+                               default:
+                                       snprintf(state->message, SHPLOADERMSGLEN, "Error: field %d has invalid or unknown field type (%d)", i, state->types[i]);
+
+                                       SHPDestroyObject(obj);
+                                       stringbuffer_destroy(sbwarn);
+                                       stringbuffer_destroy(sb);
+
+                                       return SHPLOADERERR;
+                       }
+
+#ifdef HAVE_ICONV
+                       if (state->config->encoding)
+                       {
+                               /* If we are converting from another encoding to UTF8, convert the field value to UTF8 */
+                               utf8str = utf8(state->config->encoding, val);
+                               if (!utf8str)
+                               {
+                                       snprintf(state->message, SHPLOADERMSGLEN, "Unable to convert field value \"%s\" to UTF8: iconv reports \"%s\"", val, strerror(errno));
+                                       return SHPLOADERERR;
+                               }
+       
+                               strncpy(val, utf8str, MAXVALUELEN);
+                               free(utf8str);
+                       }
 #endif
 
+                       /* Escape attribute correctly according to dump format */
+                       if (state->config->dump_format)
+                               escval = escape_copy_string(val);
+                       else
+                               escval = escape_insert_string(val);
+                       
+                       vasbappend(sb, "'%s'", escval);
 
-               /*
-                * Make field names lowercase unless asked to
-                * keep identifiers case.
-                */
-               if ( ! quoteidentifiers ) LowerCase(name);
+                       /* Free the escaped version if required */
+                       if (val != escval)
+                               free(escval);
+               }
 
-               /*
-                * Escape names starting with the
-                * escape char (_), those named 'gid'
-                * or after pgsql reserved attribute names
-                */
-               if ( name[0]=='_' ||
-                               ! strcmp(name,"gid") ||
-                               ! strcmp(name, "tableoid") ||
-                               ! strcmp(name, "cmax") ||
-                               ! strcmp(name, "xmax") ||
-                               ! strcmp(name, "cmin") ||
-                               ! strcmp(name, "primary") ||
-                               ! strcmp(name, "oid") ||
-                               ! strcmp(name, "ctid") )
+               /* Only put in delimeter if not last field or a shape will follow */
+               if (state->config->readshape == 1 || i < DBFGetFieldCount(state->hDBFHandle) - 1)
                {
-                       strcpy(name2+2, name);
-                       name2[0] = '_';
-                       name2[1] = '_';
-                       strcpy(name, name2);
+                       if (state->config->dump_format)
+                               vasbappend(sb, "\t");
+                       else
+                               vasbappend(sb, ",");
                }
 
-               /* Avoid duplicating field names */
-               for (z=0; z < j ; z++)
+               /* End of DBF attribute loop */
+       }
+
+
+       /* Add the shape attribute if we are reading it */
+       if (state->config->readshape == 1)
+       {
+               /* Handle the case of a NULL shape */
+               if (obj->nVertices == 0)
                {
-                       if (strcmp(field_names[z],name)==0)
+                       if (state->config->dump_format)
+                               vasbappend(sb, "\\N");
+                       else
+                               vasbappend(sb, "NULL");
+               }
+               else
+               {
+                       /* Handle all other shape attributes */
+                       switch (obj->nSHPType)
                        {
-                               strcat(name,"__");
-                               sprintf(name+strlen(name),"%i",j);
-                               break;
+                               case SHPT_POLYGON:
+                               case SHPT_POLYGONM:
+                               case SHPT_POLYGONZ:
+                                       res = GeneratePolygonGeometry(state, obj, &geometry);
+                                       if (res != SHPLOADEROK)
+                                       {
+                                               /* Error message has already been set */
+                                               SHPDestroyObject(obj);
+                                               stringbuffer_destroy(sbwarn);
+                                               stringbuffer_destroy(sb);
+       
+                                               return SHPLOADERERR;
+                                       }
+                                       break;
+       
+                               case SHPT_POINT:
+                               case SHPT_POINTM:
+                               case SHPT_POINTZ:
+                               case SHPT_MULTIPOINT:
+                               case SHPT_MULTIPOINTM:
+                               case SHPT_MULTIPOINTZ:
+                                       res = GeneratePointGeometry(state, obj, &geometry);
+                                       if (res != SHPLOADEROK)
+                                       {
+                                               /* Error message has already been set */
+                                               SHPDestroyObject(obj);
+                                               stringbuffer_destroy(sbwarn);
+                                               stringbuffer_destroy(sb);
+       
+                                               return SHPLOADERERR;
+                                       }
+                                       break;
+       
+                               case SHPT_ARC:
+                               case SHPT_ARCM:
+                               case SHPT_ARCZ:
+                                       res = GenerateLineStringGeometry(state, obj, &geometry);
+                                       if (res != SHPLOADEROK)
+                                       {
+                                               /* Error message has already been set */
+                                               SHPDestroyObject(obj);
+                                               stringbuffer_destroy(sbwarn);
+                                               stringbuffer_destroy(sb);
+       
+                                               return SHPLOADERERR;
+                                       }
+                                       break;
+       
+                               default:
+                                       snprintf(state->message, SHPLOADERMSGLEN, "Shape type is NOT SUPPORTED, type id = %d", obj->nSHPType);
+
+                                       SHPDestroyObject(obj);
+                                       stringbuffer_destroy(sbwarn);
+                                       stringbuffer_destroy(sb);
+
+                                       return SHPLOADERERR;
                        }
                }
 
-               field_names[j] = malloc (strlen(name)+1);
-               strcpy(field_names[j], name);
 
-               /*sprintf(col_names, "%s\"%s\",", col_names, name);*/
-               strcat(col_names, "\"");
-               strcat(col_names, name);
-               if (readshape == 1 || j < (num_fields - 1))
+               /* Now generate the geometry string according to the current configuration */
+               if (state->config->hwgeom)
                {
-                       //don't include last comma if its the last field and no geometry field will follow
-                       strcat(col_names, "\",");
+                       /* Old-style hwgeom (WKT) */
+                       if (!state->config->dump_format)
+                               vasbappend(sb, "GeomFromText('");
+                       else
+                       {
+                               /* Output SRID if relevant */
+                               if (state->config->sr_id != 0)
+                                       vasbappend(sb, "SRID=%d;", state->config->sr_id);
+                       }
+       
+                       vasbappend(sb, "%s", geometry);
+       
+                       if (!state->config->dump_format)
+                       {
+                               vasbappend(sb, "'");
+       
+                               /* Output SRID if relevant */
+                               if (state->config->sr_id != 0)
+                                       vasbappend(sb, ", %d)", state->config->sr_id);
+       
+                               vasbappend(sb, ");");
+                       }
                }
                else
                {
-                       strcat(col_names, "\"");
+                       /* New style lwgeom (HEXEWKB) */
+                       if (!state->config->dump_format)
+                               vasbappend(sb, "'");
+       
+                       vasbappend(sb, "%s", geometry);
+       
+                       if (!state->config->dump_format)
+                               vasbappend(sb, "');");
                }
+
+               /* Tidy up everything */
+               SHPDestroyObject(obj);
+               free(geometry);
        }
-       /*sprintf(col_names, "%s\"%s\")", col_names, geom);*/
-       if (readshape == 1)
+       else
        {
-               strcat(col_names, geom);
+               /* Close the line correctly for dump/insert format */
+               if (!state->config->dump_format)
+                       vasbappend(sb, ");");
        }
-       strcat(col_names, ")");
-}
 
-#ifdef HAVE_ICONV
 
-char *
-utf8 (const char *fromcode, char *inputbuf)
-{
-       iconv_t cd;
-       char *outputptr;
-       char *outputbuf;
-       size_t outbytesleft;
-       size_t inbytesleft;
+       /* Copy the string buffer into a new string, destroying the string buffer */
+       ret = (char *)malloc(strlen((char *)stringbuffer_getstring(sb)) + 1);
+       strcpy(ret, (char *)stringbuffer_getstring(sb));
+       stringbuffer_destroy(sb);
 
-       inbytesleft = strlen (inputbuf);
+       *strrecord = ret;
 
-       cd = iconv_open ("UTF-8", fromcode);
-       if (cd == (iconv_t) - 1)
+       /* If any warnings occurred, set the returned message string and warning status */
+       if (strlen((char *)stringbuffer_getstring(sbwarn)) > 0)
        {
-               pgis_logf("utf8: iconv_open: %s", strerror (errno));
-               return NULL;
+               snprintf(state->message, SHPLOADERMSGLEN, "%s", stringbuffer_getstring(sbwarn));
+               stringbuffer_destroy(sbwarn);
+               
+               return SHPLOADERWARN;
        }
-
-       outbytesleft = inbytesleft*3+1; /* UTF8 string can be 3 times larger */
-       /* then local string */
-       outputbuf = (char *) malloc (outbytesleft);
-       if (!outputbuf)
+       else
        {
-               pgis_logf("utf8: malloc: %s", strerror (errno));
-               return NULL;
+               /* Everything went okay */
+               stringbuffer_destroy(sbwarn);
+
+               return SHPLOADEROK;
        }
-       memset (outputbuf, 0, outbytesleft);
-       outputptr = outputbuf;
+}
+
 
-       if (-1==iconv(cd, &inputbuf, &inbytesleft, &outputptr, &outbytesleft))
+/* Return a pointer to an allocated string containing the header for the specified loader state */
+int
+ShpLoaderGetSQLFooter(SHPLOADERSTATE *state, char **strfooter)
+{
+       stringbuffer_t *sb;
+       char *ret;
+
+       /* Create the stringbuffer containing the header; we use this API as it's easier
+          for handling string resizing during append */
+       sb = stringbuffer_create();
+       stringbuffer_clear(sb);
+
+       /* Create gist index if specified */
+       if (state->config->createindex)
        {
-               pgis_logf("utf8: %s", strerror (errno));
-               return NULL;
+               if (state->config->schema)
+               {
+                       vasbappend(sb, "CREATE INDEX \"%s_%s_gist\" ON \"%s\".\"%s\" using gist (\"%s\" gist_geometry_ops);\n", state->config->table, state->config->geom, 
+                       state->config->schema, state->config->table, state->config->geom);
+               }
+               else
+               {
+                       vasbappend(sb, "CREATE INDEX \"%s_%s_gist\" ON \"%s\" using gist (\"%s\" gist_geometry_ops);\n", state->config->table, state->config->geom, state->config->table, state->config->geom);
+               }
        }
 
-       iconv_close (cd);
+       /* End the transaction */
+       vasbappend(sb, "COMMIT;\n"); 
 
-       return outputbuf;
+       /* Copy the string buffer into a new string, destroying the string buffer */
+       ret = (char *)malloc(strlen((char *)stringbuffer_getstring(sb)) + 1);
+       strcpy(ret, (char *)stringbuffer_getstring(sb));
+       stringbuffer_destroy(sb);
+
+       *strfooter = ret;
+
+       return SHPLOADEROK;
 }
 
-#endif /* defined HAVE_ICONV */
 
-/**********************************************************************
- * $Log$
- * Revision 1.109  2008/04/09 14:12:17  robe
- *         - Added support to load dbf-only files
- *
- * Revision 1.108  2006/06/16 14:12:17  strk
- *         - BUGFIX in pgsql2shp successful return code.
- *         - BUGFIX in shp2pgsql handling of MultiLine WKT.
- *
- * Revision 1.107  2006/04/18 09:16:26  strk
- * Substituted bzero() use with memset()
- *
- * Revision 1.106  2006/01/16 10:42:58  strk
- * Added support for Bool and Date DBF<=>PGIS mapping
- *
- * Revision 1.105  2006/01/09 16:40:16  strk
- * ISO C90 comments, signedness mismatch fixes
- *
- * Revision 1.104  2005/11/01 09:25:47  strk
- * Reworked NULL geometries handling code letting user specify policy (insert,skip,abort). Insert is the default.
- *
- * Revision 1.103  2005/10/24 15:54:22  strk
- * fixed wrong assumption about maximum size of integer attributes (width is maximum size of text representation)
- *
- * Revision 1.102  2005/10/24 11:30:59  strk
- *
- * Fixed a bug in string attributes handling truncating values of maximum
- * allowed length, curtesy of Lars Roessiger.
- * Reworked integer attributes handling to be stricter in dbf->sql mapping
- * and to allow for big int8 values in sql->dbf conversion
- *
- * Revision 1.101  2005/10/21 11:33:55  strk
- * Applied patch by Lars Roessiger handling numerical values with a trailing decima
- * l dot
- *
- * Revision 1.100  2005/10/13 13:40:20  strk
- * Fixed return code from shp2pgsql
- *
- * Revision 1.99  2005/10/03 18:08:55  strk
- * Stricter string attributes lenght handling. DBF header will be used
- * to set varchar maxlenght, (var)char typmod will be used to set DBF header
- * len.
- *
- * Revision 1.98  2005/10/03 07:45:58  strk
- * Issued a warning when -W is specified and no UTF8 support has been compiled in.
- *
- * Revision 1.97  2005/09/30 08:59:29  strk
- * Fixed release of stack memory occurring when shp2pgsql is compiled with USE_ICONV defined, an attribute value needs to be escaped and no -W is used
- *
- * Revision 1.96  2005/08/29 22:36:25  strk
- * Removed premature object destruction in InsertLineString{WKT,} causing segfault
- *
- * Revision 1.95  2005/08/29 11:48:33  strk
- * Fixed sprintf() calls to avoid overlapping memory,
- * reworked not-null objects existance check to reduce startup costs.
- *
- * Revision 1.94  2005/07/27 02:47:14  strk
- * Support for multibyte field names in loader
- *
- * Revision 1.93  2005/07/27 02:35:50  strk
- * Minor cleanups in loader
- *
- * Revision 1.92  2005/07/27 02:07:01  strk
- * Fixed handling of POINT types as WKT (-w) in loader
- *
- * Revision 1.91  2005/07/04 09:47:03  strk
- * Added conservative iconv detection code
- *
- * Revision 1.90  2005/06/16 17:55:58  strk
- * Added -I switch for GiST index creation in loader
- *
- * Revision 1.89  2005/04/21 09:08:34  strk
- * Applied patch from Ron Mayer fixing a segfault in string escaper funx
- *
- * Revision 1.88  2005/04/14 12:58:59  strk
- * Applied patch by Gino Lucrezi fixing bug in string escaping code.
- *
- * Revision 1.87  2005/04/06 14:16:43  strk
- * Removed manual update of gid field.
- *
- * Revision 1.86  2005/04/06 14:02:08  mschaber
- * added -p option (prepare mode) that spits out the table schema without
- * inserting any data.
- *
- * Revision 1.85  2005/04/06 10:46:10  strk
- * Bugfix in -w (hwgeom) handling of ZM shapefiles.
- * Big reorganizzation of code to easy maintainance.
- *
- * Revision 1.84  2005/04/04 20:51:26  strk
- * Added -w flag to output old (WKT/HWGEOM) sql.
- *
- * Revision 1.83  2005/03/15 12:24:40  strk
- * hole-in-ring detector made more readable
- *
- * Revision 1.82  2005/03/14 22:02:31  strk
- * Fixed holes handling.
- *
- * Revision 1.81  2005/03/08 11:06:33  strk
- * modernized old-style parameter declarations
- *
- * Revision 1.80  2005/03/04 14:48:22  strk
- * Applied patch from Jonne Savolainen fixing multilines handling
- *
- * Revision 1.79  2005/01/31 22:15:22  strk
- * Added maintainer notice, to reduce Jeff-strk mail bounces
- *
- * Revision 1.78  2005/01/17 09:21:13  strk
- * Added one more bytes for terminating NULL in utf8 encoder
- *
- * Revision 1.77  2005/01/16 16:50:01  strk
- * String escaping algorithm made simpler and more robust.
- * Removed escaped strings leaking.
- * Fixed UTF8 encoder to allocate enough space for 3bytes chars strings.
- *
- * Revision 1.76  2005/01/12 17:03:20  strk
- * Added optional UTF8 output support as suggested by IIDA Tetsushi
- *
- * Revision 1.75  2004/11/15 10:51:35  strk
- * Fixed a bug in PIP invocation, added some debugging lines.
- *
- * Revision 1.74  2004/10/17 13:25:44  strk
- * removed USE_WKB partially-used define
- *
- * Revision 1.73  2004/10/17 13:24:44  strk
- * HEXWKB polygon
- *
- * Revision 1.72  2004/10/17 12:59:12  strk
- * HEXWKB multiline output
- *
- * Revision 1.71  2004/10/17 12:26:02  strk
- * Point and MultiPoint loaded using HEXWKB.
- *
- * Revision 1.70  2004/10/15 22:01:35  strk
- * Initial WKB functionalities
- *
- * Revision 1.69  2004/10/07 21:52:28  strk
- * Lots of rewriting/cleanup. TypeM/TypeZ supports.
- *
- * Revision 1.68  2004/10/07 06:54:24  strk
- * cleanups
- *
- * Revision 1.67  2004/10/06 10:11:16  strk
- * Other separator fixes
- *
- * Revision 1.66  2004/10/06 09:40:27  strk
- * Handled 0-DBF-attributes corner case.
- *
- * Revision 1.65  2004/09/20 17:13:31  strk
- * changed comments to better show shape type handling
- *
- * Revision 1.64  2004/08/20 08:14:37  strk
- * Whole output wrapped in transaction blocks.
- * Drops are out of transaction, and multiple transactions are used
- * for INSERT mode.
- *
- **********************************************************************/
+void
+ShpLoaderDestroy(SHPLOADERSTATE *state)
+{
+       /* Destroy a state object created with ShpLoaderOpenShape */
+       int i;
+
+       if (state != NULL)
+       {
+               if (state->hSHPHandle)
+                       SHPClose(state->hSHPHandle);
+               if (state->hDBFHandle)
+                       DBFClose(state->hDBFHandle);
+               if (state->field_names)
+               {
+                       for (i = 0; i < state->num_fields; i++)
+                               free(state->field_names[i]);
+       
+                       free(state->field_names);
+               }
+               if (state->types)
+                       free(state->types);
+               if (state->widths)
+                       free(state->widths);
+               if (state->precisions)
+                       free(state->precisions);
+               if (state->col_names)
+                       free(state->col_names);
+
+               /* Free the state itself */     
+               free(state);
+       }
+}
index b16f2198cb987b065cbe1a44f24c231166ed1e20..5d45da1f08cc5b00f24dc6e5e21bd556bce52b97 100644 (file)
@@ -1,3 +1,15 @@
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ * Copyright 2009 Paul Ramsey
+ * Copyright 2009 Mark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ */
 
 #include <stdio.h>
 #include <string.h>
 #include "shapefil.h"
 #include "getopt.h"
 
+#include "../liblwgeom/liblwgeom.h"
+
 #ifdef HAVE_ICONV
 #include <iconv.h>
 #endif
 
-#include "../liblwgeom/liblwgeom.h"
 #include "stringbuffer.h"
 
 #define RCSID "$Id$"
 
-#define TRANSLATION_IDLE 0
-#define TRANSLATION_CREATE 1
-#define TRANSLATION_LOAD 2
-#define TRANSLATION_CLEANUP 3
-#define TRANSLATION_DONE 4
 
-enum {
-       insert_null,
-       skip_null,
-       abort_on_null
-};
+/*
+ * Loader policy constants
+ */
+
+#define POLICY_NULL_ABORT      0x0
+#define POLICY_NULL_INSERT     0x1
+#define POLICY_NULL_SKIP       0x2
+
 
 /*
-** Global variables for Core
-*/
-extern char opt; /* load mode: c = create, d = delete, a = append, p = prepare */
-extern char *table; /* table to load into */
-extern char *schema; /* schema to load into */
-extern char *geom; /* geometry column name to use */
-extern char *shp_file; /* the shape file (without the .shp extension) */
-extern int dump_format; /* 0 = SQL inserts, 1 = dump */
-extern int simple_geometries; /* 0 = MULTIPOLYGON/MULTILINESTRING, 1 = force to POLYGON/LINESTRING */
-extern int quoteidentifiers; /* 0 = columnname, 1 = "columnName" */
-extern int forceint4; /* 0 = allow int8 fields, 1 = no int8 fields */
-extern int createindex; /* 0 = no index, 1 = create index after load */
-extern int readshape; /* 0 = load DBF file only, 1 = load everything */
-#ifdef HAVE_ICONV
-extern char *encoding; /* iconv encoding name */
-#endif
-extern int null_policy; /* how to handle nulls */
-extern int sr_id; /* SRID specified */
-extern int gui_mode; /* 1 = GUI, 0 = commandline */
-extern int translation_stage; /* 1 = ready, 2 = done start, 3 = done middle, 4 = done end */
-extern int cur_entity; /* what record are we working on? */
+ * Error message handling
+ */
+
+#define SHPLOADERMSGLEN                1024
+
+#define SHPLOADEROK            -1
+#define SHPLOADERERR           0
+#define SHPLOADERWARN          1
+
+#define SHPLOADERRECDELETED    2
+#define SHPLOADERRECISNULL     3
+
+/*
+ * Shapefile (dbf) field name are at most 10chars + 1 NULL.
+ * Postgresql field names are at most 63 bytes + 1 NULL.
+ */
+#define MAXFIELDNAMELEN 64
+#define MAXVALUELEN 1024
 
 
 /*
-** Global variables used only by GUI
-*/
+ * Structure to hold the loader configuration options 
+ */
+
+typedef struct shp_loader_config
+{
+       /* load mode: c = create, d = delete, a = append, p = prepare */
+       char opt;
+
+       /* table to load into */
+       char *table;
+
+       /* schema to load into */
+       char *schema;
+
+       /* geometry column name to use */
+       char *geom; 
+
+       /* the shape file (without the .shp extension) */
+       char *shp_file;
+
+       /* 0 = SQL inserts, 1 = dump */
+       int dump_format;
+
+       /* 0 = MULTIPOLYGON/MULTILINESTRING, 1 = force to POLYGON/LINESTRING */
+       int simple_geometries;
+
+       /* 0 = columnname, 1 = "columnName" */
+       int quoteidentifiers;
+
+       /* 0 = allow int8 fields, 1 = no int8 fields */
+       int forceint4;
+
+       /* 0 = no index, 1 = create index after load */
+       int createindex;
+
+       /* 0 = load DBF file only, 1 = load everything */
+       int readshape;
+
+       /* iconv encoding name */
+       char *encoding;
+
+       /* how to handle nulls */
+       int null_policy;
+
+       /* SRID specified */
+       int sr_id;
+
+       /* 0 = new style (PostGIS 1.x) geometries, 1 = old style (PostGIS 0.9.x) geometries */
+       int hwgeom;
+
+} SHPLOADERCONFIG;
+
 
 /*
-** Prototypes across modules
-*/
-extern int translation_start(void); 
-extern int translation_middle(void); 
-extern int translation_end(void); 
-extern void pgui_log_va(const char *fmt, va_list ap);
-extern int pgui_exec(const char *sql);
-extern int pgui_copy_write(const char *line);
-extern int pgui_copy_start(const char *sql);
-extern int pgui_copy_end(const int rollback);
-extern void LowerCase(char *s);
+ * Structure to holder the current loader state 
+ */
+
+typedef struct shp_loader_state
+{
+       /* Configuration for this state */
+       SHPLOADERCONFIG *config;
+
+       /* Shapefile handle */
+       SHPHandle hSHPHandle;
+       
+       /* Shapefile type */
+       int shpfiletype;
+
+       /* Data file handle */
+       DBFHandle hDBFHandle;
+
+       /* Number of rows in the shapefile */
+       int num_entities;
+
+       /* Number of fields in the shapefile */
+       int num_fields;
+
+       /* Number of rows in the DBF file */
+       int num_records;
+
+       /* Pointer to an array of field names */
+       char **field_names;
+
+       /* Field type */
+       DBFFieldType *types;
+
+       /* Arrays of field widths and precisions */
+       int *widths;
+       int *precisions;
+
+       /* String containing colume name list in the form "(col1, col2, col3 ... , colN)" */
+       char *col_names;
+
+       /* String containing the PostGIS geometry type, e.g. POINT, POLYGON etc. */
+       char *pgtype;
+
+       /* PostGIS geometry type (numeric version) */
+       unsigned int wkbtype;
+
+       /* Number of dimensions to output */
+       int pgdims;
+
+       /* 0 = simple geometry, 1 = multi geometry */
+       int istypeM;
+
+       /* Last (error) message */
+       char message[SHPLOADERMSGLEN];
+
+} SHPLOADERSTATE;
+
+
+/* Externally accessible functions */
+void strtolower(char *s);
+void set_config_defaults(SHPLOADERCONFIG *config);
 
+SHPLOADERSTATE *ShpLoaderCreate(SHPLOADERCONFIG *config);
+int ShpLoaderOpenShape(SHPLOADERSTATE *state);
+int ShpLoaderGetSQLHeader(SHPLOADERSTATE *state, char **strheader);
+int ShpLoaderGetSQLCopyStatement(SHPLOADERSTATE *state, char **strheader);
+int ShpLoaderGetRecordCount(SHPLOADERSTATE *state);
+int ShpLoaderGenerateSQLRowStatement(SHPLOADERSTATE *state, int item, char **strrecord);
+int ShpLoaderGetSQLFooter(SHPLOADERSTATE *state, char **strfooter);
+void ShpLoaderDestroy(SHPLOADERSTATE *state);
index 07d106b776ad231947e4b40b9ad922699f6122f9..cba9a1313e6934ef9b6e9d57d0a033076d33d48e 100644 (file)
@@ -52,38 +52,44 @@ static GtkWidget *checkbutton_options_dbfonly;
 /* Other */
 static char *pgui_errmsg = NULL;
 static PGconn *pg_connection;
+static SHPLOADERCONFIG *config;
+static SHPLOADERSTATE *state;
 
 
 /*
 ** Write a message to the Import Log text area.
 */
 static void
-pgui_logf(const char *fmt, ...)
+pgui_log_va(const char *fmt, va_list ap)
 {
-       va_list ap;
-       va_start(ap, fmt);
+       char *msg;
 
-       pgui_log_va(fmt, ap);
+       if (!lw_vasprintf (&msg, fmt, ap)) return;
 
-       va_end(ap);
+       gtk_text_buffer_insert_at_cursor(textbuffer_log, msg, -1);
+       gtk_text_buffer_insert_at_cursor(textbuffer_log, "\n", -1);
+       gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(textview_log), gtk_text_buffer_get_insert(textbuffer_log) );
+
+       /* Allow GTK to process events */
+       while (gtk_events_pending())
+               gtk_main_iteration();
+
+       free(msg);
        return;
 }
 
 /*
 ** Write a message to the Import Log text area.
 */
-void
-pgui_log_va(const char *fmt, va_list ap)
+static void
+pgui_logf(const char *fmt, ...)
 {
-       char *msg;
-
-       if (!lw_vasprintf (&msg, fmt, ap)) return;
+       va_list ap;
+       va_start(ap, fmt);
 
-       gtk_text_buffer_insert_at_cursor(textbuffer_log, msg, -1);
-       gtk_text_buffer_insert_at_cursor(textbuffer_log, "\n", -1);
-       gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(textview_log), gtk_text_buffer_get_insert(textbuffer_log) );
+       pgui_log_va(fmt, ap);
 
-       free(msg);
+       va_end(ap);
        return;
 }
 
@@ -98,12 +104,173 @@ pgui_seterr(const char *errmsg)
        return;
 }
 
+static void
+pgui_raise_error_dialogue(void)
+{
+       GtkWidget *dialog, *label;
+       gint result;
+
+       label = gtk_label_new(pgui_errmsg);
+       dialog = gtk_dialog_new_with_buttons("Error", GTK_WINDOW(window_main), 
+                       GTK_DIALOG_MODAL & GTK_DIALOG_NO_SEPARATOR & GTK_DIALOG_DESTROY_WITH_PARENT, 
+                       GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+       gtk_dialog_set_has_separator ( GTK_DIALOG(dialog), FALSE );
+       gtk_container_set_border_width (GTK_CONTAINER(dialog), 5);
+       gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), 15);
+       gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), label);
+       gtk_widget_show_all (dialog);
+       result = gtk_dialog_run(GTK_DIALOG(dialog));
+       gtk_widget_destroy(dialog);
+       return;
+}
+
+/* Terminate the main loop and exit the application. */
+static void
+pgui_quit (GtkWidget *widget, gpointer data)
+{
+       if ( pg_connection) PQfinish(pg_connection);
+       pg_connection = NULL;
+       gtk_main_quit ();
+}
+
+/* Set the global configuration based upon the current UI */
+static void
+pgui_set_config_from_ui()
+{
+       const char *pg_table = gtk_entry_get_text(GTK_ENTRY(entry_config_table));
+       const char *pg_schema = gtk_entry_get_text(GTK_ENTRY(entry_config_schema));
+       const char *pg_geom = gtk_entry_get_text(GTK_ENTRY(entry_config_geocolumn));
+
+       const char *source_file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser_button_shape));
+
+       const char *entry_srid = gtk_entry_get_text(GTK_ENTRY(entry_config_srid));
+       const char *entry_encoding = gtk_entry_get_text(GTK_ENTRY(entry_options_encoding));
+
+       gboolean preservecase = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_options_preservecase));
+       gboolean forceint = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_options_forceint));
+       gboolean createindex = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_options_autoindex));
+       gboolean dbfonly = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_options_dbfonly));
 
+       char *c;
+
+
+       /* Set the destination schema, table and column parameters */
+       if (config->table)
+               free(config->table);
+
+       config->table = strdup(pg_table);
+
+       if (config->schema)
+               free(config->schema);
+
+       if (strlen(pg_schema) == 0)
+               config->schema = strdup("public");
+       else
+               config->schema = strdup(pg_schema);
+
+       if (strlen(pg_geom) == 0)
+               config->geom = strdup("the_geom");
+       else
+               config->geom = strdup(pg_geom);
+
+       /* Set the destination filename: note the shp2pgsql core engine simply wants the file
+          without the .shp extension */
+       if (config->shp_file)
+               free(config->shp_file);
+
+       /* Handle empty selection */
+       if (source_file == NULL)
+               config->shp_file = strdup("");
+       else
+               config->shp_file = strdup(source_file);
+
+       for (c = config->shp_file + strlen(config->shp_file); c >= config->shp_file; c--)
+       {
+               if (*c == '.')
+               {
+                       *c = '\0';
+                       break;
+               }
+       }
+
+       /* Encoding */
+       if( entry_encoding && strlen(entry_encoding) > 0 ) 
+       {
+               if (config->encoding)
+                       free(config->encoding);
+
+               config->encoding = strdup(entry_encoding);
+       }
+       
+       /* SRID */
+       if ( ! ( config->sr_id = atoi(entry_srid) ) ) 
+       {
+               config->sr_id = -1;
+       }
+
+       /* Preserve case */
+       if ( preservecase )
+               config->quoteidentifiers = 1;
+       else
+               config->quoteidentifiers = 0;
+
+       /* No long integers in table */
+       if ( forceint )
+               config->forceint4 = 1;
+       else
+               config->forceint4 = 0;
+       
+       /* Create spatial index after load */
+       if ( createindex )
+               config->createindex = 1;
+       else
+               config->createindex = 0;
+       
+       /* Read the .shp file, don't ignore it */
+       if ( dbfonly )
+               config->readshape = 0;
+       else
+               config->readshape = 1;
+
+       return;
+}
+
+/* Validate the configuration, returning true or false */
+static int
+pgui_validate_config()
+{
+       /* Validate table parameters */
+       if ( ! config->table || strlen(config->table) == 0 )
+       {
+               pgui_seterr("Fill in the destination table.");
+               return 0;
+       }
+
+       if ( ! config->schema || strlen(config->schema) == 0 )
+       {
+               pgui_seterr("Fill in the destination schema.");
+               return 0;
+       }
+
+       if ( ! config->geom || strlen(config->geom) == 0 )
+       {
+               pgui_seterr("Fill in the destination column.");
+               return 0;
+       }
+
+       if ( ! config->shp_file || strlen(config->shp_file) == 0 )
+       {
+               pgui_seterr("Select a shape file to import.");
+               return 0;
+       }
+
+       return 1;
+}
 
 /*
 ** Run a SQL command against the current connection.
 */
-int
+static int
 pgui_exec(const char *sql)
 {
        PGresult *res = NULL;
@@ -128,7 +295,6 @@ pgui_exec(const char *sql)
                }
 
                /* Log errors and return failure. */
-               pgui_logf("Failed record number #%d", cur_entity);
                pgui_logf("Failed SQL was: %s", sql);
                pgui_logf("Failed in pgui_exec(): %s", PQerrorMessage(pg_connection));
                return 0;
@@ -140,7 +306,7 @@ pgui_exec(const char *sql)
 /*
 ** Start the COPY process.
 */
-int
+static int
 pgui_copy_start(const char *sql)
 {
        PGresult *res = NULL;
@@ -169,7 +335,7 @@ pgui_copy_start(const char *sql)
 /*
 ** Send a line (row) of data into the COPY procedure.
 */
-int
+static int
 pgui_copy_write(const char *line)
 {
 
@@ -181,7 +347,6 @@ pgui_copy_write(const char *line)
        if ( PQputCopyData(pg_connection, line, strlen(line)) < 0 )
        {
                /* Log errors and return failure. */
-               pgui_logf("Failed record number #%d", cur_entity);
                pgui_logf("Failed row was: %s", line);
                pgui_logf("Failed in pgui_copy_write(): %s", PQerrorMessage(pg_connection));
                return 0;
@@ -194,7 +359,7 @@ pgui_copy_write(const char *line)
 /*
 ** Finish the COPY process.
 */
-int
+static int
 pgui_copy_end(const int rollback)
 {
        char *errmsg = NULL;
@@ -215,58 +380,6 @@ pgui_copy_end(const int rollback)
        return 1;
 }
 
-static gboolean
-check_translation_stage (gpointer data)
-{
-       int rv = 0;
-       if ( translation_stage == TRANSLATION_IDLE ) return FALSE;
-       if ( translation_stage == TRANSLATION_DONE )
-       {
-               pgui_logf("Import complete.");
-               return FALSE;
-       }
-       if ( translation_stage == TRANSLATION_CREATE )
-       {
-               rv = translation_start();
-               if ( ! rv )
-               {
-                       pgui_logf("Import failed.");
-                       translation_stage = TRANSLATION_IDLE;
-               }
-               return TRUE;
-       }
-       if ( translation_stage == TRANSLATION_LOAD )
-       {
-               rv = translation_middle();
-               if ( ! rv )
-               {
-                       pgui_logf("Import failed.");
-                       translation_stage = TRANSLATION_IDLE;
-               }
-               return TRUE;
-       }
-       if ( translation_stage == TRANSLATION_CLEANUP )
-       {
-               rv = translation_end();
-               if ( ! rv )
-               {
-                       pgui_logf("Import failed.");
-                       translation_stage = TRANSLATION_IDLE;
-               }
-               return TRUE;
-       }
-       return FALSE;
-}
-
-/* Terminate the main loop and exit the application. */
-static void
-pgui_quit (GtkWidget *widget, gpointer data)
-{
-       if ( pg_connection) PQfinish(pg_connection);
-       pg_connection = NULL;
-       gtk_main_quit ();
-}
-
 static char *
 pgui_read_connection(void)
 {
@@ -313,212 +426,10 @@ pgui_read_connection(void)
        return NULL;
 }
 
-static char *
-pgui_read_destination(void)
-{
-       const char *pg_table = gtk_entry_get_text(GTK_ENTRY(entry_config_table));
-       const char *pg_schema = gtk_entry_get_text(GTK_ENTRY(entry_config_schema));
-       const char *pg_geom = gtk_entry_get_text(GTK_ENTRY(entry_config_geocolumn));
-       
-       char *dest_string = NULL;
-
-       if ( ! pg_table || strlen(pg_table) == 0 )
-       {
-               pgui_seterr("Fill in the destination table.");
-               return NULL;
-       }
-       if ( ! pg_schema || strlen(pg_schema) == 0 )
-       {
-               pg_schema = "public";
-       }
-       if ( ! pg_geom || strlen(pg_geom) == 0 )
-       {
-               pg_geom = "the_geom";
-       }
-
-       if ( ! lw_asprintf(&dest_string, "%s.%s", pg_schema, pg_table) )
-       {
-               return NULL;
-       }
-
-       if ( dest_string )
-       {
-               /* Set the schema and table into the globals. */
-               /* TODO change the core code to use dest_string instead */
-               /* and move the global set into the import function. */
-               table = strdup(pg_table);
-               schema = strdup(pg_schema);
-               geom = strdup(pg_geom);
-               return dest_string;
-       }
-       return NULL;
-}
-
-static void
-pgui_raise_error_dialogue(void)
-{
-       GtkWidget *dialog, *label;
-       gint result;
-
-       label = gtk_label_new(pgui_errmsg);
-       dialog = gtk_dialog_new_with_buttons("Error", GTK_WINDOW(window_main), 
-                       GTK_DIALOG_MODAL & GTK_DIALOG_NO_SEPARATOR & GTK_DIALOG_DESTROY_WITH_PARENT, 
-                       GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
-       gtk_dialog_set_has_separator ( GTK_DIALOG(dialog), FALSE );
-       gtk_container_set_border_width (GTK_CONTAINER(dialog), 5);
-       gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), 15);
-       gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), label);
-       gtk_widget_show_all (dialog);
-       result = gtk_dialog_run(GTK_DIALOG(dialog));
-       gtk_widget_destroy(dialog);
-       return;
-}
-
-static void
-pgui_action_import(GtkWidget *widget, gpointer data)
-{
-       char *connection_string = NULL;
-       char *dest_string = NULL;
-       char *source_file = NULL;
-       
-       const char *entry_srid = gtk_entry_get_text(GTK_ENTRY(entry_config_srid));
-       const char *entry_encoding = gtk_entry_get_text(GTK_ENTRY(entry_options_encoding));
-
-       /* Do nothing if we're busy */
-       if ( translation_stage > TRANSLATION_IDLE && translation_stage < TRANSLATION_DONE )
-       {
-               return;
-       }
-
-       if ( ! (connection_string = pgui_read_connection() ) )
-       {
-               pgui_raise_error_dialogue();
-               return;
-       }
-
-       if ( ! (dest_string = pgui_read_destination() ) )
-       {
-               pgui_raise_error_dialogue();
-               return;
-       }
-
-       if ( ! (source_file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser_button_shape))) )
-       {
-               pgui_seterr("Select a shape file to import.");
-               pgui_raise_error_dialogue();
-               return;
-       }
-
-       /* Log what we know so far */
-       pgui_logf("Connection: %s", connection_string);
-       pgui_logf("Destination: %s", dest_string);
-       pgui_logf("Source File: %s", source_file);
-
-       /* Set the shape file into the global. */
-       shp_file = strdup(source_file);
-       g_free(source_file);
-
-       /* Set the mode to "create" in the global. */
-       opt = 'c';
-
-       /* Set the output mode to inserts. */
-       dump_format = 0;
-
-       /* 
-       ** Read the options from the options dialogue... 
-       */
-       
-       /* Encoding */
-       if( entry_encoding && strlen(entry_encoding) > 0 ) 
-       {
-               encoding = strdup(entry_encoding);
-       }
-       
-       /* SRID */
-       if ( ! ( sr_id = atoi(entry_srid) ) ) 
-       {
-               sr_id = -1;
-       }
-
-       /* Preserve case */
-       if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_options_preservecase)) )
-               quoteidentifiers = 1;
-       else
-               quoteidentifiers = 0;
-
-       /* No long integers in table */
-       if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_options_forceint)) )
-               forceint4 = 1;
-       else
-               forceint4 = 0;
-       
-       /* Create spatial index after load */
-       if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_options_autoindex)) )
-               createindex = 1;
-       else
-               createindex = 0;
-       
-       /* Read the .shp file, don't ignore it */
-       if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_options_dbfonly)) )
-               readshape = 0;
-       else
-               readshape = 1;
-
-       /* Connect to the database. */
-       if ( pg_connection ) PQfinish(pg_connection);
-       pg_connection = PQconnectdb(connection_string);
-
-       if (PQstatus(pg_connection) == CONNECTION_BAD)
-       {
-               pgui_logf( "Connection failed: %s", PQerrorMessage(pg_connection));
-               gtk_label_set_text(GTK_LABEL(label_pg_connection_test), "Connection failed.");
-               free(connection_string);
-               free(dest_string);
-               PQfinish(pg_connection);
-               pg_connection = NULL;
-               return;
-       }
-
-       /* add the idle action */
-       cur_entity = -1;
-       translation_stage = TRANSLATION_CREATE;
-       g_idle_add(check_translation_stage, NULL);
-
-       free(connection_string);
-       free(dest_string);
-
-       return;
-
-}
-
-static void
-pgui_action_options(GtkWidget *widget, gpointer data)
-{
-       /* Do nothing if we're busy */
-       if ( translation_stage > TRANSLATION_IDLE && translation_stage < TRANSLATION_DONE )
-       {
-               return;
-       }
-       /* TODO Open the options dialog window here... */
-       pgui_logf("Open the options dialog...");
-       gtk_widget_show_all (window_options);
-       return;
-}
-
 static void
 pgui_action_cancel(GtkWidget *widget, gpointer data)
 {
-       if ( translation_stage > TRANSLATION_IDLE && translation_stage < TRANSLATION_DONE )
-       {
-               pgui_logf("Import stopped.");
-
-               translation_stage = TRANSLATION_IDLE; /* return to idle if we are running */
-       }
-       else
-       {
-               pgui_quit(widget, data); /* quit if we're not running */
-       }
-       return;
+       pgui_quit(widget, data); /* quit if we're not running */
 }
 
 static void
@@ -526,13 +437,6 @@ pgui_action_connection_test(GtkWidget *widget, gpointer data)
 {
        char *connection_string = NULL;
 
-       /* Do nothing if we're busy */
-       if ( translation_stage > TRANSLATION_IDLE && translation_stage < TRANSLATION_DONE )
-       {
-               return;
-       }
-
-
        if ( ! (connection_string = pgui_read_connection()) )
        {
                pgui_raise_error_dialogue();
@@ -563,6 +467,15 @@ pgui_action_connection_test(GtkWidget *widget, gpointer data)
        return;
 }
 
+static void
+pgui_action_options(GtkWidget *widget, gpointer data)
+{
+       /* TODO Open the options dialog window here... */
+       pgui_logf("Open the options dialog...");
+       gtk_widget_show_all (window_options);
+       return;
+}
+
 static void
 pgui_action_close_options(GtkWidget *widget, gpointer data)
 {
@@ -592,26 +505,233 @@ pgui_action_shape_file_set(GtkWidget *widget, gpointer data)
 
        /* Roll back from end to first slash character. */
        table_start = shp_file + shp_file_len;
-       while( *table_start != '/' && *table_start != '\\' && table_start > shp_file) {
+       while ( *table_start != '/' && *table_start != '\\' && table_start > shp_file) {
                table_start--;
        }
        table_start++; /* Forward one to start of actual characters. */
 
        /* Roll back from end to first . character. */
        table_end = shp_file + shp_file_len;
-       while( *table_end != '.' && table_end > shp_file && table_end > table_start ) {
+       while ( *table_end != '.' && table_end > shp_file && table_end > table_start ) {
                table_end--;
        }
        
        /* Copy the table name into a fresh memory slot. */
-       table = lwalloc(table_end - table_start + 1);
+       table = malloc(table_end - table_start + 1);
        memcpy(table, table_start, table_end - table_start);
        table[table_end - table_start + 1] = '\0';
 
-       /* Set the table name into the entry. */
+       /* Set the table name into the configuration */
+       config->table = table;
+
        gtk_entry_set_text(GTK_ENTRY(entry_config_table), table);
        
-       lwfree(shp_file);
+       free(shp_file);
+}
+
+static void
+pgui_action_import(GtkWidget *widget, gpointer data)
+{
+       char *connection_string = NULL;
+       char *dest_string = NULL;
+       int ret, i;
+       char *header, *footer, *record; 
+
+
+       if ( ! (connection_string = pgui_read_connection() ) )
+       {
+               pgui_raise_error_dialogue();
+               return;
+       }
+
+       /* 
+       ** Set the configuration from the UI and validate
+       */
+       pgui_set_config_from_ui();
+       if (! pgui_validate_config() )
+       {
+               pgui_raise_error_dialogue();
+               free(connection_string);
+
+               return;
+       }
+
+       /* Log what we know so far */
+       pgui_logf("Connection: %s", connection_string);
+       pgui_logf("Destination: %s.%s", config->schema, config->table);
+       pgui_logf("Source File: %s", config->shp_file);
+
+       /* Connect to the database. */
+       if ( pg_connection ) PQfinish(pg_connection);
+       pg_connection = PQconnectdb(connection_string);
+
+       if (PQstatus(pg_connection) == CONNECTION_BAD)
+       {
+               pgui_logf( "Connection failed: %s", PQerrorMessage(pg_connection));
+               gtk_label_set_text(GTK_LABEL(label_pg_connection_test), "Connection failed.");
+               free(connection_string);
+               free(dest_string);
+               PQfinish(pg_connection);
+               pg_connection = NULL;
+               return;
+       }
+
+       /*
+        * Loop through the items in the shapefile
+        */
+
+       /* Create the shapefile state object */
+       state = ShpLoaderCreate(config);
+
+       /* Open the shapefile */
+       ret = ShpLoaderOpenShape(state);
+       if (ret != SHPLOADEROK)
+       {
+               pgui_logf("%s", state->message);
+
+               if (ret == SHPLOADERERR)
+                       return;
+       }
+
+       /* If reading the whole shapefile, display its type */
+       if (state->config->readshape)
+       {
+               pgui_logf("Shapefile type: %s", SHPTypeName(state->shpfiletype));
+               pgui_logf("Postgis type: %s[%d]", state->pgtype, state->pgdims);
+       }
+
+       /* Get the header */
+       ret = ShpLoaderGetSQLHeader(state, &header);
+       if (ret != SHPLOADEROK)
+       {
+               pgui_logf("%s", state->message);
+
+               if (ret == SHPLOADERERR)
+                       return;
+       }
+
+       /* Send the header to the remote server: if we are in COPY mode then the last
+          statement will be a COPY and so will change connection mode */
+       ret = pgui_exec(header);
+       free(header);
+
+       if (!ret)
+               return;
+
+       /* If we are in COPY (dump format) mode, output the COPY statement and enter COPY mode */
+       if (state->config->dump_format)
+       {
+               ret = ShpLoaderGetSQLCopyStatement(state, &header);
+               if (ret != SHPLOADEROK)
+               {
+                       pgui_logf("%s", state->message);
+       
+                       if (ret == SHPLOADERERR)
+                               return;
+               }
+
+               /* Send the result to the remote server: this should put us in COPY mode */
+               ret = pgui_copy_start(header);
+               free(header);
+
+               if (!ret)
+                       return;
+       }
+
+       /* Main loop: iterate through all of the records and send them to stdout */
+       for (i = 0; i < ShpLoaderGetRecordCount(state); i++)
+       {
+               ret = ShpLoaderGenerateSQLRowStatement(state, i, &record);
+
+               switch(ret)
+               {
+                       case SHPLOADEROK:
+                               /* Simply send the statement */
+                               if (state->config->dump_format)
+                                       ret = pgui_copy_write(record);
+                               else
+                                       ret = pgui_exec(record);
+
+                               /* Display a record number if we failed */
+                               if (!ret)
+                                       pgui_logf("Failed record number #%d", i);
+
+                               free(record);
+                               break;
+
+                       case SHPLOADERERR:
+                               /* Display the error message then stop */
+                               pgui_logf("%s\n", state->message);
+                               return;
+                               break;
+
+                       case SHPLOADERWARN:
+                               /* Display the warning, but continue */
+                               pgui_logf("%s\n", state->message);
+
+                               if (state->config->dump_format)
+                                       ret = pgui_copy_write(record);
+                               else
+                                       ret = pgui_exec(record);
+
+                               /* Display a record number if we failed */
+                               if (!ret)
+                                       pgui_logf("Failed record number #%d", i);
+
+                               free(record);
+                               break;
+
+                       case SHPLOADERRECDELETED:
+                               /* Record is marked as deleted - ignore */
+                               break;
+
+                       case SHPLOADERRECISNULL:
+                               /* Record is NULL and should be ignored according to NULL policy */
+                               break;
+               }
+
+               /* Allow GTK events to get a look in */
+               while (gtk_events_pending())
+                       gtk_main_iteration();
+       }
+
+       /* If we are in COPY (dump format) mode, leave COPY mode */
+       if (state->config->dump_format)
+       {
+               if (! pgui_copy_end(0) )
+                       return;
+       }
+
+       /* Get the footer */
+       ret = ShpLoaderGetSQLFooter(state, &footer);
+       if (ret != SHPLOADEROK)
+       {
+               pgui_logf("%s\n", state->message);
+
+               if (ret == SHPLOADERERR)
+                       return;
+       }
+
+       /* Send the footer to the server */
+       ret = pgui_exec(footer);
+       free(footer);
+
+       if (!ret)
+               return;
+
+       /* Free the state object */
+       ShpLoaderDestroy(state);
+
+
+       /* Tidy up */
+       free(connection_string);
+       free(dest_string);
+
+       /* Disconnect from the database */
+       PQfinish(pg_connection);
+       pg_connection = NULL;
+
+       return;
 }
 
 static void
@@ -888,6 +1008,9 @@ pgui_create_main_window(void)
 int
 main(int argc, char *argv[])
 {
+       /* Setup the configuration */
+       config = malloc(sizeof(SHPLOADERCONFIG));
+       set_config_defaults(config);
 
        /* initialize the GTK stack */
        gtk_init(&argc, &argv);
@@ -896,16 +1019,11 @@ main(int argc, char *argv[])
        pgui_create_main_window();
        pgui_create_options_dialogue();
        
-       /* set up and global variables we want before running */
-       gui_mode = 1;
-
        /* start the main loop */
        gtk_main();
 
+       /* Free the configuration */
+       free(config);
+
        return 0;
 }
-
-/**********************************************************************
- * $Log$
- *
- **********************************************************************/
index 0dca2841251db4c567cc98d07e95789199927545..aa2082d482792044d2bc0534b11d8ae1b12092ad 100644 (file)
@@ -4,6 +4,8 @@
  * PostGIS - Spatial Types for PostgreSQL
  * http://postgis.refractions.net
  * Copyright 2001-2003 Refractions Research Inc.
+ * Copyright 2009 Paul Ramsey
+ * Copyright 2009 Mark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
  *
  * This is free software; you can redistribute and/or modify it under
  * the terms of the GNU General Public Licence. See the COPYING file.