[postgis-commits] svn - r3538 - in trunk: . liblwgeom loader
postgis-commits at postgis.refractions.net
postgis-commits at postgis.refractions.net
Mon Jan 19 13:33:15 PST 2009
Author: pramsey
Date: 2009-01-19 13:33:14 -0800 (Mon, 19 Jan 2009)
New Revision: 3538
Added:
trunk/loader/shp2pgsql-cli.c
trunk/loader/shp2pgsql-core.c
trunk/loader/shp2pgsql-core.h
trunk/loader/shp2pgsql-gui.c
trunk/loader/stringbuffer.c
trunk/loader/stringbuffer.h
Modified:
trunk/configure.ac
trunk/liblwgeom/liblwgeom.h
trunk/loader/
trunk/loader/Makefile.in
trunk/loader/dbfopen.c
Log:
First revision of the GUI. Configure using --with-gui to enable full GUI build. New core/cli will build by default. Old utilities remain in place for now.
Modified: trunk/configure.ac
===================================================================
--- trunk/configure.ac 2009-01-16 17:58:39 UTC (rev 3537)
+++ trunk/configure.ac 2009-01-19 21:33:14 UTC (rev 3538)
@@ -12,6 +12,7 @@
AC_INIT()
AC_CONFIG_HEADERS([postgis_config.h])
+AC_CONFIG_MACRO_DIR([macros])
dnl Invoke libtool: we do this as it is the easiest way to find the PIC
dnl flags required to build liblwgeom
@@ -69,6 +70,9 @@
AC_SUBST([YACC])
+dnl ===========================================================================
+dnl Find components needed to build documentation
+dnl ===========================================================================
dnl
dnl Search for xsltproc which is required for building documentation
dnl
@@ -78,7 +82,6 @@
AC_MSG_WARN([xsltproc is not installed so documentation cannot be built])
fi
-
dnl
dnl Search for db2pdf which is required for building PDF documentation
dnl
@@ -88,7 +91,6 @@
AC_MSG_WARN([db2pdf is not installed so PDF documentation cannot be built])
fi
-
dnl
dnl Search for dblatex which is required for building PDF documentation
dnl
@@ -98,8 +100,6 @@
AC_MSG_WARN([dblatex is not installed so PDF documentation cannot be built])
fi
-
-
dnl
dnl Allow the user to specify the location of the html/docbook.xsl stylesheet
dnl
@@ -110,7 +110,8 @@
XSLBASE_AUTO=""
if test "x$XSLBASE" = "x"; then
- dnl If the user did not specify a directory for the docbook stylesheet, choose the first directory
+ dnl If the user did not specify a directory for the docbook
+ dnl stylesheet, choose the first directory
dnl that matches from the following list
SEARCHPATH="
/usr/share/sgml/docbook/xsl-stylesheets
@@ -124,12 +125,14 @@
fi
done
- dnl Check to see if the automatically searched paths above located a valid Docbook stylesheet
+ dnl Check to see if the automatically searched paths above located a
+ dnl valid Docbook stylesheet
if test "x$XSLBASE_AUTO" = "x"; then
AC_MSG_WARN([could not locate Docbook stylesheets required to build the documentation])
fi
else
- dnl The user specified an alternate directory so make sure everything looks sensible
+ dnl The user specified an alternate directory so make sure everything
+ dnl looks sensible
if test ! -d "$XSLBASE"; then
AC_MSG_ERROR([the docbook stylesheet directory specified using --with-xsldir does not exist])
fi
@@ -140,8 +143,10 @@
fi
dnl
-dnl If XSLBASE has been set then at this point we know it must be valid and so we can just use it. If XSLBASE_AUTO has been set, and XSLBASE
-dnl is empty then a valid stylesheet was found in XSLBASE_AUTO so we should use that. Otherwise just continue silently with a blank XSLBASE
+dnl If XSLBASE has been set then at this point we know it must be
+dnl valid and so we can just use it. If XSLBASE_AUTO has been set, and XSLBASE
+dnl is empty then a valid stylesheet was found in XSLBASE_AUTO so we
+dnl should use that. Otherwise just continue silently with a blank XSLBASE
dnl variable which will trigger the error message in the documentation Makefile
dnl
@@ -154,14 +159,14 @@
AC_SUBST([XSLBASE])
-dnl
+dnl ===========================================================================
dnl Detect CUnit if it is installed (used for unit testing)
dnl
-dnl Note that we pass any specified CPPFLAGS and LDFLAGS into the Makefile as CUnit is
-dnl the only compile-time dependency that cannot obtain any specialised flags using
-dnl a --with-X parameter, and so we allow this information to be passed in if
-dnl required.
-dnl
+dnl Note that we pass any specified CPPFLAGS and LDFLAGS into the Makefile
+dnl as CUnit is the only compile-time dependency that cannot obtain any
+dnl specialised flags using a --with-X parameter, and so we allow this
+dnl information to be passed in if required.
+dnl ===========================================================================
CUNIT_LDFLAGS=""
AC_CHECK_HEADER([CUnit/CUnit.h], [
@@ -176,9 +181,10 @@
AC_SUBST([CUNIT_LDFLAGS])
-dnl
-dnl Detect iconv if it is installed (used for shp2pgsql encoding conversion if available)
-dnl
+dnl ===========================================================================
+dnl Detect iconv if it is installed (used for shp2pgsql encoding conversion
+dnl if available)
+dnl ===========================================================================
HAVE_ICONV_H=0
AC_CHECK_HEADER([iconv.h], [HAVE_ICONV_H=1], [])
@@ -213,9 +219,9 @@
AC_SUBST([ICONV_LDFLAGS])
-dnl
+dnl ===========================================================================
dnl Detect the version of PostgreSQL installed on the system
-dnl
+dnl ===========================================================================
AC_ARG_WITH([pgconfig],
[AS_HELP_STRING([--with-pgconfig=FILE], [specify an alternative pg_config file])],
@@ -243,11 +249,12 @@
fi
-dnl
-dnl Ensure that $PG_CONFIG --pgxs points to a valid file. This is because some distributions such as Debian
-dnl also include pg_config as part of libpq-dev packages, but don't install the Makefile it points to unless
+dnl ===========================================================================
+dnl Ensure that $PG_CONFIG --pgxs points to a valid file. This is because some
+dnl distributions such as Debian also include pg_config as part of libpq-dev
+dnl packages, but don't install the Makefile it points to unless
dnl the postgresql-server-dev packages are installed :)
-dnl
+dnl ===========================================================================
PGXS=`$PGCONFIG --pgxs`
if test ! -f $PGXS; then
@@ -316,9 +323,10 @@
AC_SUBST([POSTGIS_PGSQL_VERSION])
-dnl
+
+dnl ===========================================================================
dnl Detect the version of GEOS installed on the system
-dnl
+dnl ===========================================================================
AC_ARG_WITH([geosconfig],
[AS_HELP_STRING([--with-geosconfig=FILE], [specify an alternative geos-config file])],
@@ -345,9 +353,10 @@
fi
fi
-dnl Extract the version information from pg_config
-dnl Note: we extract the major & minor separately, ensure they are numeric, and then combine to give
-dnl the final version. This is to guard against user error...
+dnl Extract the version information from geos_config
+dnl Note: we extract the major & minor separately, ensure they are numeric,
+dnl and then combine to give the final version.
+dnl This is to guard against user error...
GEOS_MAJOR_VERSION=`$GEOSCONFIG --version | cut -d. -f1 | sed 's/[[^0-9]]//g'`
GEOS_MINOR_VERSION=`$GEOSCONFIG --version | cut -d. -f2 | sed 's/[[^0-9]]//g'`
POSTGIS_GEOS_VERSION="$GEOS_MAJOR_VERSION$GEOS_MINOR_VERSION"
@@ -386,9 +395,9 @@
AC_SUBST([POSTGIS_GEOS_VERSION])
-dnl
+dnl ===========================================================================
dnl Detect the version of PROJ.4 installed
-dnl
+dnl ===========================================================================
AC_ARG_WITH([projdir],
[AS_HELP_STRING([--with-projdir=PATH], [specify the PROJ.4 installation directory])],
@@ -411,6 +420,7 @@
fi
fi
+
dnl Check that we can find the proj_api.h header file
CPPFLAGS_SAVE="$CPPFLAGS"
CPPFLAGS="$PROJ_CPPFLAGS"
@@ -436,8 +446,51 @@
[])
LIBS="$LIBS_SAVE"
+dnl ===========================================================================
+dnl Detect GLib GTK for GUI
+dnl ===========================================================================
-dnl
+AC_ARG_WITH([gui],
+ [AS_HELP_STRING([--with-gui], [compile the data import GUI (requires GTK+2.0)])],
+ [GUI="yes"], [GUI="no"])
+
+if test "x$GUI" = "xyes"; then
+ AC_MSG_RESULT([GUI: Build requested, checking for dependencies (GKT+2.0)])
+ case $host in
+ *apple*)
+ for frmwrk [ in Cairo GLib Gtk ]; do
+ if test -d /Library/Frameworks/${frmwrk}.framework; then
+ GTK_INCLUDES="$GTK_INCLUDES -I/Library/Frameworks/${frmwrk}.framework/Headers"
+ GTK_LIBS="$GTK_LIBS -framework $frmwrk"
+ AC_MSG_RESULT([GUI: Found /Library/Frameworks/${frmwrk}.framework])
+ else
+ AC_MSG_ERROR([GUI: Mac OS/X build requires the GTK+2.0 frameworks available from http://www.gtk-osx.org])
+ fi
+ done
+ GTK_BUILD="gui"
+ ;;
+ *)
+ dnl Try to find the GTK libs with pkgconfig
+ if ! test -x `which pkg-config`; then
+ AC_MSG_ERROR([GUI: Building GTK applications requires 'pkg-config', please install it.])
+ else
+ AC_MSG_RESULT([GUI: Running 'pkg-config gtk+-2.0 --cflags'])
+ GTK_INCLUDES=`pkg-config gtk+-2.0 --cflags`
+ AC_MSG_RESULT([GUI: Running 'pkg-config gtk+-2.0 --libs'])
+ GTK_LIBS=`pkg-config gtk+-2.0 --libs`
+ if test ! "x$GTK_LIBS" = "x"; then
+ GTK_BUILD="gui"
+ fi
+ fi
+ esac
+fi
+AC_SUBST([GTK_INCLUDES])
+AC_SUBST([GTK_LIBS])
+AC_SUBST([GTK_BUILD])
+
+
+
+dnl ===========================================================================
dnl Allow the user to enable debugging with --enable-debug
dnl
dnl Currently we default to debug level 4. See DEBUG for more information.
@@ -448,7 +501,7 @@
AC_DEFINE_UNQUOTED([POSTGIS_DEBUG_LEVEL], [$POSTGIS_DEBUG_LEVEL], [PostGIS library debug level (0=disabled)])
-dnl
+dnl ===========================================================================
dnl Allow the user to enable GEOS profiling with --enable-profile
dnl
@@ -457,7 +510,7 @@
AC_DEFINE_UNQUOTED([POSTGIS_PROFILE], [$POSTGIS_PROFILE], [Enable GEOS profiling (0=disabled)])
-dnl
+dnl ===========================================================================
dnl Define version macros
dnl
@@ -477,7 +530,7 @@
AC_SUBST([POSTGIS_SCRIPTS_VERSION])
-dnl
+dnl ===========================================================================
dnl Other parameters
dnl
Modified: trunk/liblwgeom/liblwgeom.h
===================================================================
--- trunk/liblwgeom/liblwgeom.h 2009-01-16 17:58:39 UTC (rev 3537)
+++ trunk/liblwgeom/liblwgeom.h 2009-01-19 21:33:14 UTC (rev 3538)
@@ -1274,8 +1274,7 @@
extern const char *lwgeom_typeflags(uchar type);
/* Construct an empty pointarray */
-extern POINTARRAY *ptarray_construct(char hasz, char hasm,
- unsigned int npoints);
+extern POINTARRAY *ptarray_construct(char hasz, char hasm, unsigned int npoints);
/*
* extern POINTARRAY *ptarray_construct2d(uint32 npoints, const POINT2D *pts);
Property changes on: trunk/loader
___________________________________________________________________
Name: svn:ignore
- pgsql2shp
shp2pgsql
Makefile.shp2pgsql
Makefile.pgsql2shp
Makefile
+ pgsql2shp
shp2pgsql
shp2pgsql-gui
shp2pgsql-cli
Makefile.shp2pgsql
Makefile.pgsql2shp
Makefile
Modified: trunk/loader/Makefile.in
===================================================================
--- trunk/loader/Makefile.in 2009-01-16 17:58:39 UTC (rev 3537)
+++ trunk/loader/Makefile.in 2009-01-19 21:33:14 UTC (rev 3538)
@@ -16,12 +16,9 @@
# Filenames with extension as determined by the OS
PGSQL2SHP=pgsql2shp at EXESUFFIX@
SHP2PGSQL=shp2pgsql at EXESUFFIX@
+SHP2PGSQL-GUI=shp2pgsql-gui at EXESUFFIX@
+SHP2PGSQL-CLI=shp2pgsql-cli at EXESUFFIX@
-# Common shapefile library files
-OBJS= shpopen.o \
- dbfopen.o \
- getopt.o
-
# PostgreSQL frontend CPPFLAGS and LDFLAGS (for compiling and linking with libpq)
PGSQL_FE_CPPFLAGS=@PGSQL_FE_CPPFLAGS@
PGSQL_FE_LDFLAGS=@PGSQL_FE_LDFLAGS@
@@ -32,30 +29,51 @@
# iconv flags
ICONV_LDFLAGS=@ICONV_LDFLAGS@
+# liblwgeom
+LIBLWGEOM=../liblwgeom/liblwgeom.a
-all: $(SHP2PGSQL) $(PGSQL2SHP)
+# GTK includes and libraries
+GTK_INCLUDES = @GTK_INCLUDES@
+GTK_LIBS = @GTK_LIBS@
-# liblwgeom.a dependency to allow "make install" in the loader/ subdirectory to work
-../liblwgeom/liblwgeom.a:
+all: $(SHP2PGSQL) $(PGSQL2SHP) $(SHP2PGSQL-CLI) @GTK_BUILD@
+
+gui: $(SHP2PGSQL-GUI)
+
+# liblwgeom.a dependency to allow "make install" in
+# the loader/ subdirectory to work
+$(LIBLWGEOM):
make -C ../liblwgeom
pgsql2shp.o: pgsql2shp.c
$(CC) $(CFLAGS) $(PGSQL_FE_CPPFLAGS) -c $<
-$(PGSQL2SHP): ../liblwgeom/liblwgeom.a $(OBJS) pgsql2shp.o
- $(CC) $(CFLAGS) $(OBJS) pgsql2shp.o $(ICONV_LDFLAGS) $(PGSQL_FE_LDFLAGS) ../liblwgeom/liblwgeom.a -lm -o $@
+$(PGSQL2SHP): shpopen.o dbfopen.o getopt.o pgsql2shp.o $(LIBLWGEOM)
+ $(CC) $(CFLAGS) $^ $(ICONV_LDFLAGS) $(PGSQL_FE_LDFLAGS) -lm -o $@
-$(SHP2PGSQL): ../liblwgeom/liblwgeom.a $(OBJS) shp2pgsql.o
- $(CC) $(CFLAGS) $(OBJS) shp2pgsql.o $(ICONV_LDFLAGS) ../liblwgeom/liblwgeom.a -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-gui.o: shp2pgsql-gui.c
+ $(CC) $(PGSQL_FE_CPPFLAGS) $(CFLAGS) $(GTK_INCLUDES) -o $@ -c shp2pgsql-gui.c
+
+$(SHP2PGSQL-GUI): stringbuffer.o shpopen.o dbfopen.o shp2pgsql-core-gui.o shp2pgsql-gui.o $(LIBLWGEOM)
+ $(CC) $(CFLAGS) $(GTK_LIBS) $(ICONV_LDFLAGS) $(PGSQL_FE_LDFLAGS) -lm $^ -o $@
+
+$(SHP2PGSQL-CLI): stringbuffer.o shpopen.o dbfopen.o shp2pgsql-core.o shp2pgsql-cli.o $(LIBLWGEOM)
+ $(CC) $(CFLAGS) $(ICONV_LDFLAGS) -lm $^ -o $@
+
install: all
- cp $(PGSQL2SHP) $(PGSQL_BINDIR)/$(PGSQL2SHP)
- cp $(SHP2PGSQL) $(PGSQL_BINDIR)/$(SHP2PGSQL)
+ @cp $(PGSQL2SHP) $(PGSQL_BINDIR)/$(PGSQL2SHP)
+ @cp $(SHP2PGSQL) $(PGSQL_BINDIR)/$(SHP2PGSQL)
uninstall:
- rm -f $(PGSQL_BINDIR)/$(PGSQL2SHP)
- rm -f $(PGSQL_BINDIR)/$(SHP2PGSQL)
+ @rm -f $(PGSQL_BINDIR)/$(PGSQL2SHP)
+ @rm -f $(PGSQL_BINDIR)/$(SHP2PGSQL)
clean:
- rm -f $(OBJS) shp2pgsql.o pgsql2shp.o $(SHP2PGSQL) $(PGSQL2SHP)
+ @rm -f *.o $(SHP2PGSQL) $(PGSQL2SHP)
Modified: trunk/loader/dbfopen.c
===================================================================
--- trunk/loader/dbfopen.c 2009-01-16 17:58:39 UTC (rev 3537)
+++ trunk/loader/dbfopen.c 2009-01-19 21:33:14 UTC (rev 3538)
@@ -203,9 +203,6 @@
* Added header.
*/
-static char rcsid[] =
- "$Id$";
-
#include "shapefil.h"
#include <math.h>
Added: trunk/loader/shp2pgsql-cli.c
===================================================================
--- trunk/loader/shp2pgsql-cli.c 2009-01-16 17:58:39 UTC (rev 3537)
+++ trunk/loader/shp2pgsql-cli.c 2009-01-19 21:33:14 UTC (rev 3538)
@@ -0,0 +1,224 @@
+/**********************************************************************
+ * $Id: shp2pgsql-gui.c 3450 2008-12-18 20:42:09Z pramsey $
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2008 OpenGeo.org
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ * Maintainer: Paul Ramsey <pramsey at opengeo.org>
+ *
+ **********************************************************************/
+
+#include <unistd.h>
+#include "shp2pgsql-core.h"
+
+static void
+pcli_usage(char *me, int exitcode, FILE* out)
+{
+ 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");
+#ifdef HAVE_ICONV
+ fprintf(out, " -W <encoding> Specify the character encoding of Shape's\n");
+ fprintf(out, " 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);
+}
+
+
+static int
+pcli_cmdline(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);
+ }
+
+ while ((c = 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';
+ else
+ return 0;
+ break;
+ case 'D':
+ dump_format =1;
+ break;
+ case 'S':
+ simple_geometries =1;
+ break;
+ case 's':
+ if( optarg )
+ (void)sscanf(optarg, "%d", &sr_id);
+ else
+ pcli_usage(ARGV[0], 0, stdout);
+ break;
+ case 'g':
+ geom = optarg;
+ break;
+ case 'k':
+ quoteidentifiers = 1;
+ break;
+ case 'i':
+ forceint4 = 1;
+ break;
+ case 'I':
+ createindex = 1;
+ break;
+ case 'n':
+ readshape = 0;
+ break;
+ case 'W':
+#ifdef HAVE_ICONV
+ 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;
+ break;
+ case 'i':
+ null_policy = insert_null;
+ break;
+ case 's':
+ null_policy = skip_null;
+ 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;
+ }
+ }
+
+ if ( !sr_id ) sr_id = -1;
+
+ if ( !geom ) geom = "the_geom";
+
+ if ( opt==' ' ) opt = 'c';
+
+ 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;
+ }
+ }
+ 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 unless asked
+ * to keep original case (we'll quote it later on)
+ */
+ if ( ! quoteidentifiers )
+ {
+ LowerCase(table);
+ if ( schema ) LowerCase(schema);
+ }
+
+ return 1;
+}
+
+int
+main (int ARGC, char **ARGV)
+{
+
+ int rv = 0;
+
+ /* Emit output to stdout/stderr, not a GUI */
+ gui_mode = 0;
+
+ /* Parse command line options and set globals */
+ if ( ! pcli_cmdline(ARGC, ARGV) ) pcli_usage(ARGV[0], 2, stderr);
+
+ /* Set record number to beginning of file, and translation stage to first one */
+ cur_entity = -1;
+ translation_stage = 1;
+
+ rv = translation_start();
+ if( ! rv ) return 1;
+ while( translation_stage == 2 )
+ {
+ rv = translation_middle();
+ if( ! rv ) return 1;
+ }
+ rv = translation_end();
+ if( ! rv ) return 1;
+
+ return 0;
+
+}
+
+
+/**********************************************************************
+ * $Log$
+ *
+ **********************************************************************/
Added: trunk/loader/shp2pgsql-core.c
===================================================================
--- trunk/loader/shp2pgsql-core.c 2009-01-16 17:58:39 UTC (rev 3537)
+++ trunk/loader/shp2pgsql-core.c 2009-01-19 21:33:14 UTC (rev 3538)
@@ -0,0 +1,1862 @@
+/**********************************************************************
+ * $Id: shp2pgsql.c 3450 2008-12-18 20:42:09Z pramsey $
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ * Copyright 2008 OpenGeo.org
+ *
+ * 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 at opengeo.org>
+ * Original Author: Jeff Lounsbury <jeffloun at 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
+{
+ double x, y, z, m;
+}
+Point;
+
+typedef struct Ring
+{
+ Point *list; /* list of points */
+ struct Ring *next;
+ int n; /* number of points in list */
+ unsigned int linked; /* number of "next" rings */
+}
+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 */
+#ifdef HAVE_ICONV
+char *encoding = NULL; /* iconv encoding name */
+#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 = 0;
+
+/* 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);
+char *make_good_string(char *str);
+char *protect_quotes_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 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
+}
+
+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, ... )
+{
+#ifndef PGUI
+ char *msg;
+#endif
+ va_list ap;
+
+ va_start(ap, fmt);
+
+#ifdef PGUI
+ pgui_log_va(fmt, ap);
+#else
+ if (!vasprintf (&msg, fmt, ap))
+ {
+ va_end (ap);
+ return;
+ }
+ fprintf(stderr, "%s\n", 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()
+{
+ lwgeom_install_default_allocators();
+}
+
+char *
+make_good_string(char *str)
+{
+ /*
+ * find all the tabs and make them \<tab>s
+ *
+ * 1. find # of tabs
+ * 2. make new string
+ *
+ * we dont escape already escaped tabs
+ */
+
+ 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;
+
+ while (*ptr)
+ {
+ if ( *ptr == '\t' || *ptr == '\\' ) toescape++;
+ ptr++;
+ }
+
+ if (toescape == 0) return str;
+
+ size = ptr-str+toescape+1;
+
+ result = calloc(1, size);
+
+ optr=result;
+ ptr=str;
+ while (*ptr)
+ {
+ if ( *ptr == '\t' || *ptr == '\\' ) *optr++='\\';
+ *optr++=*ptr++;
+ }
+ *optr='\0';
+
+#ifdef HAVE_ICONV
+ if ( encoding ) free(str);
+#endif
+
+ return result;
+
+}
+
+char *
+protect_quotes_string(char *str)
+{
+ /*
+ * find all quotes and make them \quotes
+ * find all '\' and make them '\\'
+ *
+ * 1. find # of characters
+ * 2. make new string
+ */
+
+ 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;
+
+ while (*ptr)
+ {
+ if ( *ptr == '\'' || *ptr == '\\' ) toescape++;
+ ptr++;
+ }
+
+ if (toescape == 0) return str;
+
+ size = ptr-str+toescape+1;
+
+ result = calloc(1, size);
+
+ optr=result;
+ ptr=str;
+ while (*ptr)
+ {
+ if ( *ptr == '\\' ) *optr++='\\';
+ if ( *ptr == '\'') *optr++='\'';
+ *optr++=*ptr++;
+ }
+ *optr='\0';
+
+#ifdef HAVE_ICONV
+ if ( encoding ) free(str);
+#endif
+
+ 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
+ */
+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 (cn&1); /* 0 if even (out), and 1 if odd (in) */
+
+}
+
+
+
+
+
+int
+Insert_attributes(DBFHandle hDBFHandle, int row)
+{
+ int i,num_fields;
+ char val[1024];
+ char *escval;
+
+ num_fields = DBFGetFieldCount( hDBFHandle );
+ for ( i = 0; i < num_fields; i++ )
+ {
+ 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;
+
+ 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;
+
+ default:
+ pgis_logf(
+ "Error: field %d has invalid or unknown field type (%d)",
+ i, types[i]);
+ return 0;
+ }
+
+ if (dump_format)
+ {
+ escval = make_good_string(val);
+ stringbuffer_aprintf(sb_row, "%s", escval);
+ //printf("\t");
+ }
+ else
+ {
+ escval = protect_quotes_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, ',');
+ }
+ }
+ }
+ return 1;
+}
+
+
+
+
+/*
+ * formerly main()
+ */
+int
+translation_start ()
+{
+
+ sb_row = stringbuffer_create();
+
+ /*
+ * Open shapefile and initialize globals
+ */
+ if ( ! OpenShape() )
+ return 0;
+
+ if (readshape == 1)
+ {
+ /*
+ * Compute output geometry type
+ */
+
+ SetPgType();
+
+ pgis_logf("Shapefile type: %s", SHPTypeName(shpfiletype));
+ pgis_logf("PostGIS type: %s [%dD]", pgtype, pgdims);
+ }
+
+#ifdef HAVE_ICONV
+ if ( encoding )
+ {
+ if ( ! pgis_exec("SET CLIENT_ENCODING TO UTF8") ) return 0;
+ }
+#endif /* defined HAVE_ICONV */
+
+ /*
+ * 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;
+
+ /*
+ * If not in 'append' mode create the spatial table
+ */
+ if (opt != 'a')
+ if ( ! CreateTable() )
+ return 0;
+
+ translation_stage = 2; /* done start */
+ return 1;
+}
+
+int
+translation_middle()
+{
+ /*
+ * Generate INSERT or COPY lines
+ */
+ if (opt != 'p')
+ {
+ if ( ! LoadData() )
+ return 0;
+ }
+ else
+ {
+ translation_stage = 3; /* done middle */
+ }
+ return 1;
+}
+
+int
+translation_end()
+{
+ /*
+ * Create GiST index if requested
+ */
+ if (createindex)
+ if ( ! CreateIndex() )
+ return 0;
+
+ if ( ! pgis_exec("END") ) return 0; /* End the last transaction */
+
+ translation_stage = 4;
+
+ return 1;
+}
+
+void
+LowerCase(char *s)
+{
+ int j;
+ for (j=0; j<strlen(s); j++) s[j] = tolower(s[j]);
+}
+
+void
+Cleanup(void)
+{
+ if ( col_names ) free(col_names);
+}
+
+int
+OpenShape(void)
+{
+ int j;
+ SHPObject *obj=NULL;
+
+ if (readshape == 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 if ( field_width < 19 )
+ {
+ stringbuffer_append (sb_row, "int8");
+ }
+ 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 )
+ {
+ asprintf(©sql, "COPY \"%s\".\"%s\" %s FROM stdin",
+ schema, table, col_names);
+ }
+ else
+ {
+ asprintf(©sql, "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 = 3; /* 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);
+
+ 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;
+ }
+
+ /* Allocate memory for our array of LWLINEs and our dynptarrays */
+ lwmultilinestrings = malloc(sizeof(LWPOINT *) * obj->nParts);
+ dpas = malloc(sizeof(DYNPTARRAY *) * obj->nParts);
+
+ /* We need an array of pointers to each of our sub-geometries */
+ for (u = 0; u < obj->nParts; u++)
+ {
+ /* Create a dynptarray containing the line points */
+ dpas[u] = dynptarray_create(obj->nParts, dims);
+
+ /* Set the start/end vertices depending upon whether this is
+ a MULTILINESTRING or not */
+ if ( u == obj->nParts-1 )
+ end_vertex = obj->nVertices;
+ else
+ end_vertex = obj->panPartStart[u + 1];
+
+ start_vertex = obj->panPartStart[u];
+
+ for (v = start_vertex; v < end_vertex; v++)
+ {
+ /* Generate the point */
+ point4d.x = obj->padfX[v];
+ point4d.y = obj->padfY[v];
+
+ if (wkbtype & WKBZOFFSET)
+ point4d.z = obj->padfZ[v];
+ if (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));
+ }
+
+ /* If using MULTILINESTRINGs then generate the serialized collection, otherwise just a single LINESTRING */
+ if (simple_geometries == 0)
+ {
+ lwcollection = lwcollection_construct(MULTILINETYPE, sr_id, NULL, obj->nParts, lwmultilinestrings);
+ serialized_lwgeom = lwgeom_serialize(lwcollection_as_lwgeom(lwcollection));
+ }
+ else
+ {
+ serialized_lwgeom = lwgeom_serialize(lwmultilinestrings[0]);
+ }
+
+ result = serialized_lwgeom_to_hexwkb(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_ALL, -1);
+
+ if (result)
+ {
+ pgis_logf("ERROR: %s", lwg_unparser_result.message);
+ return 0;
+ }
+
+ if ( ! OutputGeometry(lwg_unparser_result.wkoutput) ) return 0;
+
+ /* Free all of the allocated items */
+ lwfree(lwg_unparser_result.wkoutput);
+ lwfree(serialized_lwgeom);
+
+ for (u = 0; u < obj->nParts; u++)
+ {
+ lwline_free(lwgeom_as_lwline(lwmultilinestrings[u]));
+ lwfree(dpas[u]);
+ }
+
+ lwfree(dpas);
+ lwfree(lwmultilinestrings);
+
+ return 1;
+}
+
+int
+FindPolygons(SHPObject *obj, Ring ***Out)
+{
+ Ring **Outer; /* Pointers to Outer rings */
+ int out_index=0; /* Count of Outer rings */
+ Ring **Inner; /* Pointers to Inner rings */
+ int in_index=0; /* Count of Inner rings */
+ int pi; /* part index */
+
+#if POSTGIS_DEBUG_LEVEL > 0
+ static int call = -1;
+ call++;
+#endif
+
+ 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);
+
+ /* Iterate over rings dividing in Outers and Inners */
+ for (pi=0; pi<obj->nParts; pi++)
+ {
+ int vi; /* vertex index */
+ int vs; /* start index */
+ int ve; /* end index */
+ int nv; /* number of vertex */
+ double area = 0.0;
+ Ring *ring;
+
+ /* Set start and end vertexes */
+ 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;
+
+ /* Allocate memory for a ring */
+ 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++)
+ {
+ int vn = vi+1; /* next vertex for area */
+ 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];
+
+ area += (obj->padfX[vi] * obj->padfY[vn]) -
+ (obj->padfY[vi] * obj->padfX[vn]);
+ }
+
+ /* Close the ring with first vertex */
+ /*ring->list[vi].x = obj->padfX[vs]; */
+ /*ring->list[vi].y = obj->padfY[vs]; */
+ /*ring->list[vi].z = obj->padfZ[vs]; */
+ /*ring->list[vi].m = obj->padfM[vs]; */
+
+ /* Clockwise (or single-part). It's an Outer Ring ! */
+ if (area < 0.0 || obj->nParts ==1)
+ {
+ Outer[out_index] = ring;
+ out_index++;
+ }
+
+ /* Counterclockwise. It's an Inner Ring ! */
+ else
+ {
+ Inner[in_index] = ring;
+ in_index++;
+ }
+ }
+
+ LWDEBUGF(4, "FindPolygons[%d]: found %d Outer, %d Inners\n", call, out_index, in_index);
+
+ /* Put the inner rings into the list of the outer rings */
+ /* of which they are within */
+ for (pi=0; pi<in_index; pi++)
+ {
+ Point pt,pt2;
+ int i;
+ Ring *inner=Inner[pi], *outer=NULL;
+
+ pt.x = inner->list[0].x;
+ pt.y = inner->list[0].y;
+
+ pt2.x = inner->list[1].x;
+ pt2.y = inner->list[1].y;
+
+ 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) )
+ {
+ outer = Outer[i];
+ break;
+ }
+ }
+
+ if ( outer )
+ {
+ outer->linked++;
+ while (outer->next) outer = outer->next;
+ outer->next = inner;
+ }
+ else
+ {
+ /* The ring wasn't within any outer rings, */
+ /* assume it is a new outer ring. */
+ LWDEBUGF(4, "FindPolygons[%d]: hole %d is orphan\n", call, pi);
+
+ Outer[out_index] = inner;
+ out_index++;
+ }
+ }
+
+ *Out = Outer;
+ free(Inner);
+
+ return out_index;
+}
+
+void
+ReleasePolygons(Ring **polys, int npolys)
+{
+ int pi;
+ /* Release all memory */
+ for (pi=0; pi<npolys; pi++)
+ {
+ Ring *Poly, *temp;
+ Poly = polys[pi];
+ 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 */
+int
+InsertPolygon(void)
+{
+ Ring **Outer;
+ int polygon_total, ring_total;
+ int pi, vi; // part index and vertex index
+ int u;
+
+ LWCOLLECTION *lwcollection = NULL;
+
+ LWGEOM **lwpolygons;
+ uchar *serialized_lwgeom;
+ LWGEOM_UNPARSER_RESULT lwg_unparser_result;
+
+ LWPOLY *lwpoly;
+ DYNPTARRAY *dpas;
+ POINTARRAY ***pas;
+ POINT4D point4d;
+
+ int dims = 0, hasz = 0, hasm = 0;
+ int result;
+
+ /* Determine the correct dimensions: */
+ if (wkbtype & WKBZOFFSET) hasz = 1;
+ if (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: */
+ {
+ pgis_logf("We have a Multipolygon with %d parts, can't use -S switch!", polygon_total);
+ return 0;
+ }
+
+ /* Allocate memory for our array of LWPOLYs */
+ lwpolygons = malloc(sizeof(LWPOLY *) * polygon_total);
+
+ /* Allocate memory for our POINTARRAY pointers for each polygon */
+ pas = malloc(sizeof(POINTARRAY **) * polygon_total);
+
+ /* Cycle through each individual polygon */
+ for (pi = 0; pi < polygon_total; pi++)
+ {
+ Ring *polyring;
+ int ring_index = 0;
+
+ /* Firstly count through the total number of rings in this polygon */
+ ring_total = 0;
+ polyring = Outer[pi];
+ while (polyring)
+ {
+ ring_total++;
+ polyring = polyring->next;
+ }
+
+ /* Reserve memory for the POINTARRAYs representing each ring */
+ pas[pi] = malloc(sizeof(POINTARRAY *) * ring_total);
+
+ /* Cycle through each ring within the polygon, starting with the outer */
+ polyring = Outer[pi];
+
+ while (polyring)
+ {
+ /* Create a DYNPTARRAY containing the points making up the ring */
+ dpas = dynptarray_create(polyring->n, dims);
+
+ 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)
+ point4d.z = polyring->list[vi].z;
+ if (wkbtype & WKBMOFFSET)
+ point4d.m = polyring->list[vi].m;
+
+ dynptarray_addPoint4d(dpas, &point4d, 0);
+ }
+
+ /* Copy the POINTARRAY pointer from the DYNPTARRAY structure so we can
+ use the LWPOLY constructor */
+ pas[pi][ring_index] = dpas->pa;
+
+ /* Free the DYNPTARRAY structure (we don't need this part anymore as we
+ have the reference to the internal POINTARRAY) */
+ lwfree(dpas);
+
+ polyring = polyring->next;
+ ring_index++;
+ }
+
+ /* Generate the LWGEOM */
+ lwpoly = lwpoly_construct(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)
+ {
+ lwcollection = lwcollection_construct(MULTIPOLYGONTYPE, sr_id, NULL, polygon_total, lwpolygons);
+ serialized_lwgeom = lwgeom_serialize(lwcollection_as_lwgeom(lwcollection));
+ }
+ else
+ {
+ serialized_lwgeom = lwgeom_serialize(lwpolygons[0]);
+ }
+
+ result = serialized_lwgeom_to_hexwkb(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_ALL, -1);
+
+ if (result)
+ {
+ pgis_logf( "ERROR: %s", lwg_unparser_result.message);
+ return 0;
+ }
+
+ if ( ! OutputGeometry(lwg_unparser_result.wkoutput) ) return 0;
+
+ /* Free all of the allocated items */
+ lwfree(lwg_unparser_result.wkoutput);
+ lwfree(serialized_lwgeom);
+
+ /* Cycle through each polygon, freeing everything we need... */
+ for (u = 0; u < polygon_total; u++)
+ lwpoly_free(lwgeom_as_lwpoly(lwpolygons[u]));
+
+ /* Free the pointer arrays */
+ lwfree(pas);
+ lwfree(lwpolygons);
+ if (simple_geometries == 0)
+ lwfree(lwcollection);
+
+ return 1;
+}
+
+/*
+ * Insert either a POINT or MULTIPOINT into the output stream
+ */
+int
+InsertPoint(void)
+{
+ LWCOLLECTION *lwcollection;
+
+ 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;
+
+ /* 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);
+
+ /* We need an array of pointers to each of our sub-geometries */
+ for (u = 0; u < obj->nVertices; u++)
+ {
+ /* Generate the point */
+ point4d.x = obj->padfX[u];
+ point4d.y = obj->padfY[u];
+
+ if (wkbtype & WKBZOFFSET)
+ point4d.z = obj->padfZ[u];
+ if (wkbtype & WKBMOFFSET)
+ point4d.m = obj->padfM[u];
+
+ /* Create a dynptarray containing a single point */
+ dpas[u] = dynptarray_create(1, dims);
+ dynptarray_addPoint4d(dpas[u], &point4d, 0);
+
+ /* Generate the LWPOINT */
+ lwmultipoints[u] = lwpoint_as_lwgeom(lwpoint_construct(sr_id, NULL, dpas[u]->pa));
+ }
+
+ /* 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, sr_id, NULL, obj->nVertices, lwmultipoints);
+ serialized_lwgeom = lwgeom_serialize(lwcollection_as_lwgeom(lwcollection));
+ }
+ else
+ {
+ serialized_lwgeom = lwgeom_serialize(lwmultipoints[0]);
+ }
+
+ result = serialized_lwgeom_to_hexwkb(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_ALL, -1);
+
+ if (result)
+ {
+ pgis_logf("ERROR: %s", lwg_unparser_result.message);
+ return 0;
+ }
+
+ if ( ! OutputGeometry(lwg_unparser_result.wkoutput) ) return 0;
+
+ /* Free all of the allocated items */
+ lwfree(lwg_unparser_result.wkoutput);
+ lwfree(serialized_lwgeom);
+
+ for (u = 0; u < obj->nVertices; u++)
+ {
+ lwpoint_free(lwgeom_as_lwpoint(lwmultipoints[u]));
+ lwfree(dpas[u]);
+ }
+
+ lwfree(dpas);
+ lwfree(lwmultipoints);
+
+ return 1;
+}
+
+int
+OutputGeometry(char *geometry)
+{
+ if (!dump_format)
+ stringbuffer_append(sb_row, "'");
+
+ stringbuffer_aprintf(sb_row, "%s", geometry);
+
+ if (!dump_format)
+ {
+ stringbuffer_append(sb_row, "')");
+ if ( ! pgis_exec(stringbuffer_getstring(sb_row)) ) return 0;
+ }
+ else
+ {
+ stringbuffer_append_c(sb_row, '\n');
+ pgis_copy_write(stringbuffer_getstring(sb_row));
+ }
+ stringbuffer_clear(sb_row);
+ return 1;
+
+}
+
+
+
+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;
+ }
+
+ if (simple_geometries)
+ {
+ // adjust geometry name for CREATE TABLE by skipping MULTI
+ if ((wkbtype & 0x7) == MULTIPOLYGONTYPE) pgtype += 5;
+ if ((wkbtype & 0x7) == MULTILINETYPE) pgtype += 5;
+ }
+}
+
+int
+DropTable(char *schema, char *table, char *geom)
+{
+ /*---------------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;
+
+ if ( schema )
+ {
+ if (readshape == 1)
+ {
+ asprintf(&sql, "SELECT DropGeometryColumn('%s','%s','%s')", schema, table, geom);
+ if ( ! pgis_exec(sql) )
+ {
+ free(sql);
+ return 0;
+ }
+ free(sql);
+ }
+ asprintf(&sql, "DROP TABLE \"%s\".\"%s\"", schema, table);
+ if ( ! pgis_exec(sql) )
+ {
+ free(sql);
+ return 0;
+ }
+ free(sql);
+ }
+ else
+ {
+ if (readshape == 1)
+ {
+ asprintf(&sql, "SELECT DropGeometryColumn('','%s','%s')", table, geom);
+ if ( ! pgis_exec(sql) )
+ {
+ free(sql);
+ return 0;
+ }
+ free(sql);
+ }
+ asprintf(&sql, "DROP TABLE \"%s\"", table);
+ if ( ! pgis_exec(sql) )
+ {
+ free(sql);
+ return 0;
+ }
+ free(sql);
+ }
+ return 1;
+}
+
+void
+GetFieldsSpec(void)
+{
+ /*
+ * 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;
+#ifdef HAVE_ICONV
+ char *utf8str;
+#endif
+
+ 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)
+ {
+ 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);
+ }
+ strcpy(col_names, "(" );
+
+ /*fprintf(stderr, "Number of fields from DBF: %d\n", num_fields); */
+ for (j=0;j<num_fields;j++)
+ {
+ type = DBFGetFieldInfo(hDBFHandle, j, name, &field_width, &field_precision);
+
+ /*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;
+
+#ifdef HAVE_ICONV
+ if ( encoding )
+ {
+ utf8str = utf8(encoding, name);
+ if ( ! utf8str ) exit(1);
+ strcpy(name, utf8str);
+ free(utf8str);
+ }
+#endif
+
+
+ /*
+ * Make field names lowercase unless asked to
+ * keep identifiers case.
+ */
+ if ( ! quoteidentifiers ) LowerCase(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") )
+ {
+ strcpy(name2+2, name);
+ name2[0] = '_';
+ name2[1] = '_';
+ strcpy(name, name2);
+ }
+
+ /* Avoid duplicating field names */
+ for (z=0; z < j ; z++)
+ {
+ if (strcmp(field_names[z],name)==0)
+ {
+ strcat(name,"__");
+ sprintf(name+strlen(name),"%i",j);
+ break;
+ }
+ }
+
+ 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))
+ {
+ //don't include last comma if its the last field and no geometry field will follow
+ strcat(col_names, "\",");
+ }
+ else
+ {
+ strcat(col_names, "\"");
+ }
+ }
+ /*sprintf(col_names, "%s\"%s\")", col_names, geom);*/
+ if (readshape == 1)
+ {
+ strcat(col_names, geom);
+ }
+ 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;
+
+ inbytesleft = strlen (inputbuf);
+
+ cd = iconv_open ("UTF-8", fromcode);
+ if (cd == (iconv_t) - 1)
+ {
+ pgis_logf("utf8: iconv_open: %s", strerror (errno));
+ return NULL;
+ }
+
+ outbytesleft = inbytesleft*3+1; /* UTF8 string can be 3 times larger */
+ /* then local string */
+ outputbuf = (char *) malloc (outbytesleft);
+ if (!outputbuf)
+ {
+ pgis_logf("utf8: malloc: %s", strerror (errno));
+ return NULL;
+ }
+ memset (outputbuf, 0, outbytesleft);
+ outputptr = outputbuf;
+
+ if (-1==iconv(cd, &inputbuf, &inbytesleft, &outputptr, &outbytesleft))
+ {
+ pgis_logf("utf8: %s", strerror (errno));
+ return NULL;
+ }
+
+ iconv_close (cd);
+
+ return outputbuf;
+}
+
+#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.
+ *
+ **********************************************************************/
Added: trunk/loader/shp2pgsql-core.h
===================================================================
--- trunk/loader/shp2pgsql-core.h 2009-01-16 17:58:39 UTC (rev 3537)
+++ trunk/loader/shp2pgsql-core.h 2009-01-19 21:33:14 UTC (rev 3538)
@@ -0,0 +1,70 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "shapefil.h"
+#include "getopt.h"
+
+#ifdef HAVE_ICONV
+#include <iconv.h>
+#endif
+
+#include "../liblwgeom/liblwgeom.h"
+#include "stringbuffer.h"
+
+#define RCSID "$Id: shp2pgsql.c 3450 2008-12-18 20:42:09Z pramsey $"
+
+enum {
+ insert_null,
+ skip_null,
+ abort_on_null
+};
+
+/*
+** 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? */
+
+
+/*
+** Global variables used only by GUI
+*/
+
+/*
+** 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);
+
Added: trunk/loader/shp2pgsql-gui.c
===================================================================
--- trunk/loader/shp2pgsql-gui.c 2009-01-16 17:58:39 UTC (rev 3537)
+++ trunk/loader/shp2pgsql-gui.c 2009-01-19 21:33:14 UTC (rev 3538)
@@ -0,0 +1,905 @@
+/**********************************************************************
+ * $Id: shp2pgsql-gui.c 3450 2008-12-18 20:42:09Z pramsey $
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2008 OpenGeo.org
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ * Maintainer: Paul Ramsey <pramsey at opengeo.org>
+ *
+ **********************************************************************/
+
+#define _GNU_SOURCE
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include "libpq-fe.h"
+#include "shp2pgsql-core.h"
+
+/*
+** Global variables for GUI only
+*/
+
+/* Main window */
+static GtkWidget *window_main;
+static GtkWidget *entry_pg_user;
+static GtkWidget *entry_pg_pass;
+static GtkWidget *entry_pg_host;
+static GtkWidget *entry_pg_port;
+static GtkWidget *entry_pg_db;
+static GtkWidget *entry_config_table;
+static GtkWidget *entry_config_schema;
+static GtkWidget *entry_config_srid;
+static GtkWidget *entry_config_geocolumn;
+static GtkWidget *label_pg_connection_test;
+static GtkWidget *textview_log;
+static GtkWidget *file_chooser_button_shape;
+static GtkTextBuffer *textbuffer_log;
+
+/* Options window */
+static GtkWidget *window_options;
+static GtkWidget *entry_options_encoding;
+static GtkWidget *entry_options_nullpolicy;
+static GtkWidget *checkbutton_options_preservecase;
+static GtkWidget *checkbutton_options_forceint;
+static GtkWidget *checkbutton_options_autoindex;
+static GtkWidget *checkbutton_options_dbfonly;
+
+/* Other */
+static char *pgui_errmsg = NULL;
+static PGconn *pg_connection;
+
+
+/*
+** Write a message to the Import Log text area.
+*/
+static void
+pgui_logf(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+
+ pgui_log_va(fmt, ap);
+
+ va_end(ap);
+ return;
+}
+
+/*
+** Write a message to the Import Log text area.
+*/
+void
+pgui_log_va(const char *fmt, va_list ap)
+{
+ char *msg;
+
+ if (!vasprintf (&msg, fmt, ap)) return;
+
+ 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) );
+
+ free(msg);
+ return;
+}
+
+static void
+pgui_seterr(const char *errmsg)
+{
+ if ( pgui_errmsg )
+ {
+ free(pgui_errmsg);
+ }
+ pgui_errmsg = strdup(errmsg);
+ return;
+}
+
+
+
+/*
+** Run a SQL command against the current connection.
+*/
+int
+pgui_exec(const char *sql)
+{
+ PGresult *res = NULL;
+ ExecStatusType status;
+
+ /* We need a connection to do anything. */
+ if ( ! pg_connection ) return 0;
+ if ( ! sql ) return 0;
+
+ res = PQexec(pg_connection, sql);
+ status = PQresultStatus(res);
+ PQclear(res);
+
+ /* Did something unexpected happen? */
+ if ( ! ( status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK ) )
+ {
+ /* Log notices and return success. */
+ if ( status == PGRES_NONFATAL_ERROR )
+ {
+ pgui_logf("%s", PQerrorMessage(pg_connection));
+ return 1;
+ }
+
+ /* 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;
+ }
+
+ return 1;
+}
+
+/*
+** Start the COPY process.
+*/
+int
+pgui_copy_start(const char *sql)
+{
+ PGresult *res = NULL;
+ ExecStatusType status;
+
+ /* We need a connection to do anything. */
+ if ( ! pg_connection ) return 0;
+ if ( ! sql ) return 0;
+
+ res = PQexec(pg_connection, sql);
+ status = PQresultStatus(res);
+ PQclear(res);
+
+ /* Did something unexpected happen? */
+ if ( status != PGRES_COPY_IN )
+ {
+ /* Log errors and return failure. */
+ pgui_logf("Failed SQL was: %s", sql);
+ pgui_logf("Failed in pgui_copy_start(): %s", PQerrorMessage(pg_connection));
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+** Send a line (row) of data into the COPY procedure.
+*/
+int
+pgui_copy_write(const char *line)
+{
+
+ /* We need a connection to do anything. */
+ if ( ! pg_connection ) return 0;
+ if ( ! line ) return 0;
+
+ /* Did something unexpected happen? */
+ 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;
+ }
+
+ return 1;
+
+}
+
+/*
+** Finish the COPY process.
+*/
+int
+pgui_copy_end(const int rollback)
+{
+ char *errmsg = NULL;
+
+ /* We need a connection to do anything. */
+ if ( ! pg_connection ) return 0;
+
+ if ( rollback ) errmsg = "Roll back the copy.";
+
+ /* Did something unexpected happen? */
+ if ( PQputCopyEnd(pg_connection, errmsg) < 0 )
+ {
+ /* Log errors and return failure. */
+ pgui_logf("Failed in pgui_copy_end(): %s", PQerrorMessage(pg_connection));
+ return 0;
+ }
+
+ return 1;
+}
+
+static gboolean
+check_translation_stage (gpointer data)
+{
+ int rv = 0;
+ if ( translation_stage == 0 ) return FALSE;
+ if ( translation_stage == 4 )
+ {
+ pgui_logf("Import complete.");
+ return FALSE;
+ }
+ if ( translation_stage == 1 )
+ {
+ rv = translation_start();
+ if ( ! rv )
+ {
+ pgui_logf("Import failed.");
+ translation_stage = 0;
+ }
+ return TRUE;
+ }
+ if ( translation_stage == 2 )
+ {
+ rv = translation_middle();
+ if ( ! rv )
+ {
+ pgui_logf("Import failed.");
+ translation_stage = 0;
+ }
+ return TRUE;
+ }
+ if ( translation_stage == 3 )
+ {
+ rv = translation_end();
+ if ( ! rv )
+ {
+ pgui_logf("Import failed.");
+ translation_stage = 0;
+ }
+ 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)
+{
+ const char *pg_host = gtk_entry_get_text(GTK_ENTRY(entry_pg_host));
+ const char *pg_port = gtk_entry_get_text(GTK_ENTRY(entry_pg_port));
+ const char *pg_user = gtk_entry_get_text(GTK_ENTRY(entry_pg_user));
+ const char *pg_pass = gtk_entry_get_text(GTK_ENTRY(entry_pg_pass));
+ const char *pg_db = gtk_entry_get_text(GTK_ENTRY(entry_pg_db));
+ char *connection_string = NULL;
+
+ if ( ! pg_host || strlen(pg_host) == 0 )
+ {
+ pgui_seterr("Fill in the server host.");
+ return NULL;
+ }
+ if ( ! pg_port || strlen(pg_port) == 0 )
+ {
+ pgui_seterr("Fill in the server port.");
+ return NULL;
+ }
+ if ( ! pg_user || strlen(pg_user) == 0 )
+ {
+ pgui_seterr("Fill in the user name.");
+ return NULL;
+ }
+ if ( ! pg_db || strlen(pg_db) == 0 )
+ {
+ pgui_seterr("Fill in the database name.");
+ return NULL;
+ }
+ if ( ! atoi(pg_port) )
+ {
+ pgui_seterr("Server port must be a number.");
+ return NULL;
+ }
+ if ( ! asprintf(&connection_string, "user=%s password=%s port=%s host=%s dbname=%s", pg_user, pg_pass, pg_port, pg_host, pg_db) )
+ {
+ return NULL;
+ }
+ if ( connection_string )
+ {
+ return connection_string;
+ }
+ 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 ( ! 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 > 0 && translation_stage < 4 )
+ {
+ 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 = 1;
+ 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 > 0 && translation_stage < 4 )
+ {
+ 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 > 0 && translation_stage < 4 )
+ {
+ pgui_logf("Import stopped.");
+
+ translation_stage = 0; /* return to idle if we are running */
+ }
+ else
+ {
+ pgui_quit(widget, data); /* quit if we're not running */
+ }
+ return;
+}
+
+static void
+pgui_action_connection_test(GtkWidget *widget, gpointer data)
+{
+ char *connection_string = NULL;
+
+ /* Do nothing if we're busy */
+ if ( translation_stage > 0 && translation_stage < 4 )
+ {
+ return;
+ }
+
+
+ if ( ! (connection_string = pgui_read_connection()) )
+ {
+ pgui_raise_error_dialogue();
+ return;
+ }
+ pgui_logf("Connecting: %s", connection_string);
+
+ 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);
+ PQfinish(pg_connection);
+ pg_connection = NULL;
+ return;
+ }
+ gtk_label_set_text(GTK_LABEL(label_pg_connection_test), "Connection succeeded.");
+ pgui_logf( "Connection succeeded." );
+ gtk_widget_show(label_pg_connection_test);
+ PQfinish(pg_connection);
+ pg_connection = NULL;
+ free(connection_string);
+
+ return;
+}
+
+static void
+pgui_action_close_options(GtkWidget *widget, gpointer data)
+{
+ gtk_widget_hide_all (window_options);
+ return;
+}
+
+static void
+pgui_action_shape_file_set(GtkWidget *widget, gpointer data)
+{
+ char *shp_file;
+ int shp_file_len;
+ char *table_start;
+ char *table_end;
+ char *table;
+
+ shp_file = strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)));
+ shp_file_len = strlen(shp_file);
+
+ /* Roll back from end to first slash character. */
+ table_start = shp_file + shp_file_len;
+ 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 ) {
+ table_end--;
+ }
+
+ /* Copy the table name into a fresh memory slot. */
+ table = lwalloc(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. */
+ gtk_entry_set_text(GTK_ENTRY(entry_config_table), table);
+
+ lwfree(shp_file);
+}
+
+static void
+pgui_create_options_dialogue_add_label(GtkWidget *table, const char *str, gfloat alignment, int row)
+{
+ GtkWidget *align = gtk_alignment_new( alignment, 0.5, 0.0, 1.0 );
+ GtkWidget *label = gtk_label_new( str );
+ gtk_table_attach_defaults(GTK_TABLE(table), align, 1, 3, row, row+1 );
+ gtk_container_add (GTK_CONTAINER (align), label);
+}
+
+static void
+pgui_create_options_dialogue()
+{
+ GtkWidget *table_options;
+ GtkWidget *button_options_ok;
+ GtkWidget *vbox_options;
+ GtkWidget *align_options_center;
+ static int text_width = 12;
+
+ window_options = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_modal (GTK_WINDOW(window_options), TRUE);
+ gtk_window_set_keep_above (GTK_WINDOW(window_options), TRUE);
+ gtk_window_set_title (GTK_WINDOW(window_options), "Import Options");
+ gtk_window_set_default_size (GTK_WINDOW(window_options), 180, 200);
+
+ table_options = gtk_table_new(7, 3, TRUE);
+ gtk_container_set_border_width (GTK_CONTAINER (table_options), 12);
+ gtk_table_set_row_spacings(GTK_TABLE(table_options), 5);
+ gtk_table_set_col_spacings(GTK_TABLE(table_options), 10);
+
+ pgui_create_options_dialogue_add_label(table_options, "DBF file character encoding", 0.0, 0);
+ entry_options_encoding = gtk_entry_new();
+ gtk_entry_set_width_chars(GTK_ENTRY(entry_options_encoding), text_width);
+ gtk_entry_set_text(GTK_ENTRY(entry_options_encoding), "LATIN1");
+ gtk_table_attach_defaults(GTK_TABLE(table_options), entry_options_encoding, 0, 1, 0, 1 );
+
+ pgui_create_options_dialogue_add_label(table_options, "Preserve case of column names", 0.0, 1);
+ checkbutton_options_preservecase = gtk_check_button_new();
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_options_preservecase), FALSE);
+ align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
+ gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 1, 2 );
+ gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_options_preservecase);
+
+ pgui_create_options_dialogue_add_label(table_options, "Do not create 'bigint' columns", 0.0, 2);
+ checkbutton_options_forceint = gtk_check_button_new();
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_options_forceint), TRUE);
+ align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
+ gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 2, 3 );
+ gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_options_forceint);
+
+ pgui_create_options_dialogue_add_label(table_options, "Create spatial index automatically after load", 0.0, 3);
+ checkbutton_options_autoindex = gtk_check_button_new();
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_options_autoindex), TRUE);
+ align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
+ gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 3, 4 );
+ gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_options_autoindex);
+
+ pgui_create_options_dialogue_add_label(table_options, "Load only attribute (dbf) data", 0.0, 4);
+ checkbutton_options_dbfonly = gtk_check_button_new();
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (checkbutton_options_dbfonly), FALSE);
+ align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 0.0 );
+ gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 4, 5 );
+ gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_options_dbfonly);
+
+ pgui_create_options_dialogue_add_label(table_options, "Policy for records with empty (null) shapes", 0.0, 5);
+ entry_options_nullpolicy = gtk_entry_new();
+ gtk_entry_set_width_chars(GTK_ENTRY(entry_options_nullpolicy), text_width);
+ gtk_entry_set_text(GTK_ENTRY(entry_options_nullpolicy), "0");
+ gtk_table_attach_defaults(GTK_TABLE(table_options), entry_options_nullpolicy, 0, 1, 5, 6 );
+
+ button_options_ok = gtk_button_new_with_label("OK");
+ g_signal_connect (G_OBJECT (button_options_ok), "clicked", G_CALLBACK (pgui_action_close_options), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(table_options), button_options_ok, 1, 2, 6, 7 );
+
+ vbox_options = gtk_vbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(vbox_options), table_options, FALSE, FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (window_options), vbox_options);
+
+
+}
+
+static void
+pgui_create_main_window(void)
+{
+ static int text_width = 12;
+ /* Reusable label handle */
+ GtkWidget *label;
+ /* Main widgets */
+ GtkWidget *vbox_main;
+ /* Shape file section */
+ GtkWidget *file_chooser_dialog_shape;
+ GtkFileFilter *file_filter_shape;
+ /* PgSQL section */
+ GtkWidget *frame_pg, *frame_shape, *frame_log, *frame_config;
+ GtkWidget *table_pg, *table_config;
+ GtkWidget *button_pg_test;
+ /* Button section */
+ GtkWidget *hbox_buttons, *button_options, *button_import, *button_cancel;
+ /* Log section */
+ GtkWidget *scrolledwindow_log;
+
+ /* create the main, top level, window */
+ window_main = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ /* give the window a 10px wide border */
+ gtk_container_set_border_width (GTK_CONTAINER (window_main), 10);
+
+ /* give it the title */
+ gtk_window_set_title (GTK_WINDOW (window_main), "Shape File to PostGIS Importer");
+
+ /* open it a bit wider so that both the label and title show up */
+ gtk_window_set_default_size (GTK_WINDOW (window_main), 180, 500);
+
+ /* Connect the destroy event of the window with our pgui_quit function
+ * When the window is about to be destroyed we get a notificaiton and
+ * stop the main GTK loop
+ */
+ g_signal_connect (G_OBJECT (window_main), "destroy", G_CALLBACK (pgui_quit), NULL);
+
+ /*
+ ** Shape file selector
+ */
+ frame_shape = gtk_frame_new("Shape File");
+ gtk_container_set_border_width (GTK_CONTAINER (frame_shape), 0);
+ file_chooser_dialog_shape = gtk_file_chooser_dialog_new( "Select a Shape File", GTK_WINDOW (window_main), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
+ file_chooser_button_shape = gtk_file_chooser_button_new_with_dialog( file_chooser_dialog_shape );
+ gtk_container_set_border_width (GTK_CONTAINER (file_chooser_button_shape), 8);
+ file_filter_shape = gtk_file_filter_new();
+ gtk_file_filter_add_pattern(GTK_FILE_FILTER(file_filter_shape), "*.shp");
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_chooser_button_shape), file_filter_shape);
+ gtk_container_add (GTK_CONTAINER (frame_shape), file_chooser_button_shape);
+ g_signal_connect (G_OBJECT (file_chooser_button_shape), "file-set", G_CALLBACK (pgui_action_shape_file_set), NULL);
+
+
+ /*
+ ** PostGIS info in a table
+ */
+ frame_pg = gtk_frame_new("PostGIS Connection");
+ table_pg = gtk_table_new(5, 3, TRUE);
+ gtk_container_set_border_width (GTK_CONTAINER (table_pg), 8);
+ gtk_table_set_col_spacings(GTK_TABLE(table_pg), 7);
+ gtk_table_set_row_spacings(GTK_TABLE(table_pg), 3);
+ /* User name row */
+ label = gtk_label_new("Username:");
+ entry_pg_user = gtk_entry_new();
+ gtk_table_attach_defaults(GTK_TABLE(table_pg), label, 0, 1, 0, 1 );
+ gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_user, 1, 3, 0, 1 );
+ /* Password row */
+ label = gtk_label_new("Password:");
+ entry_pg_pass = gtk_entry_new();
+ gtk_entry_set_visibility( GTK_ENTRY(entry_pg_pass), FALSE);
+ gtk_table_attach_defaults(GTK_TABLE(table_pg), label, 0, 1, 1, 2 );
+ gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_pass, 1, 3, 1, 2 );
+ /* Host and port row */
+ label = gtk_label_new("Server Host:");
+ entry_pg_host = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry_pg_host), "localhost");
+ gtk_entry_set_width_chars(GTK_ENTRY(entry_pg_host), text_width);
+ gtk_table_attach_defaults(GTK_TABLE(table_pg), label, 0, 1, 2, 3 );
+ gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_host, 1, 2, 2, 3 );
+ entry_pg_port = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry_pg_port), "5432");
+ gtk_entry_set_width_chars(GTK_ENTRY(entry_pg_port), 8);
+ gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_port, 2, 3, 2, 3 );
+ /* Database row */
+ label = gtk_label_new("Database:");
+ entry_pg_db = gtk_entry_new();
+ gtk_table_attach_defaults(GTK_TABLE(table_pg), label, 0, 1, 3, 4 );
+ gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_db, 1, 3, 3, 4 );
+ /* Test button row */
+ button_pg_test = gtk_button_new_with_label("Test Connection...");
+ gtk_table_attach_defaults(GTK_TABLE(table_pg), button_pg_test, 1, 2, 4, 5 );
+ g_signal_connect (G_OBJECT (button_pg_test), "clicked", G_CALLBACK (pgui_action_connection_test), NULL);
+ label_pg_connection_test = gtk_label_new("");
+ gtk_table_attach_defaults(GTK_TABLE(table_pg), label_pg_connection_test, 2, 3, 4, 5 );
+ /* Add table into containing frame */
+ gtk_container_add (GTK_CONTAINER (frame_pg), table_pg);
+
+ /*
+ ** Configuration in a table
+ */
+ frame_config = gtk_frame_new("Configuration");
+ table_config = gtk_table_new(2, 4, TRUE);
+ gtk_table_set_col_spacings(GTK_TABLE(table_config), 7);
+ gtk_table_set_row_spacings(GTK_TABLE(table_config), 3);
+ gtk_container_set_border_width (GTK_CONTAINER (table_config), 8);
+ /* Destination schemea row */
+ label = gtk_label_new("Destination Schema:");
+ entry_config_schema = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry_config_schema), "public");
+ gtk_entry_set_width_chars(GTK_ENTRY(entry_config_schema), text_width);
+ gtk_table_attach_defaults(GTK_TABLE(table_config), label, 0, 1, 0, 1 );
+ gtk_table_attach_defaults(GTK_TABLE(table_config), entry_config_schema, 1, 2, 0, 1 );
+ /* Destination table row */
+ label = gtk_label_new("Destination Table:");
+ entry_config_table = gtk_entry_new();
+ gtk_entry_set_width_chars(GTK_ENTRY(entry_config_table), text_width);
+ gtk_table_attach_defaults(GTK_TABLE(table_config), label, 0, 1, 1, 2 );
+ gtk_table_attach_defaults(GTK_TABLE(table_config), entry_config_table, 1, 2, 1, 2 );
+ /* SRID row */
+ label = gtk_label_new("SRID:");
+ entry_config_srid = gtk_entry_new();
+ gtk_entry_set_width_chars(GTK_ENTRY(entry_config_srid), text_width);
+ gtk_entry_set_text(GTK_ENTRY(entry_config_srid), "-1");
+ gtk_table_attach_defaults(GTK_TABLE(table_config), label, 2, 3, 0, 1 );
+ gtk_table_attach_defaults(GTK_TABLE(table_config), entry_config_srid, 3, 4, 0, 1 );
+ /* Geom column row */
+ label = gtk_label_new("Geometry Column:");
+ entry_config_geocolumn = gtk_entry_new();
+ gtk_entry_set_width_chars(GTK_ENTRY(entry_config_geocolumn), text_width);
+ gtk_entry_set_text(GTK_ENTRY(entry_config_geocolumn), "the_geom");
+ gtk_table_attach_defaults(GTK_TABLE(table_config), label, 2, 3, 1, 2 );
+ gtk_table_attach_defaults(GTK_TABLE(table_config), entry_config_geocolumn, 3, 4, 1, 2 );
+
+ /* Add table into containing frame */
+ gtk_container_add (GTK_CONTAINER (frame_config), table_config);
+
+
+ /*
+ ** Row of action buttons
+ */
+ hbox_buttons = gtk_hbox_new(TRUE, 15);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox_buttons), 0);
+ /* Create the buttons themselves */
+ button_options = gtk_button_new_with_label("Options...");
+ button_import = gtk_button_new_with_label("Import");
+ button_cancel = gtk_button_new_with_label("Cancel");
+ /* Add actions to the buttons */
+ g_signal_connect (G_OBJECT (button_import), "clicked", G_CALLBACK (pgui_action_import), NULL);
+ g_signal_connect (G_OBJECT (button_options), "clicked", G_CALLBACK (pgui_action_options), NULL);
+ g_signal_connect (G_OBJECT (button_cancel), "clicked", G_CALLBACK (pgui_action_cancel), NULL);
+ /* And insert the buttons into the hbox */
+ gtk_box_pack_start(GTK_BOX(hbox_buttons), button_options, TRUE, TRUE, 0);
+ gtk_box_pack_end(GTK_BOX(hbox_buttons), button_cancel, TRUE, TRUE, 0);
+ gtk_box_pack_end(GTK_BOX(hbox_buttons), button_import, TRUE, TRUE, 0);
+
+ /*
+ ** Log window
+ */
+ frame_log = gtk_frame_new("Import Log");
+ gtk_container_set_border_width (GTK_CONTAINER (frame_log), 0);
+ textview_log = gtk_text_view_new();
+ textbuffer_log = gtk_text_buffer_new(NULL);
+ scrolledwindow_log = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolledwindow_log), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
+ gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview_log), textbuffer_log);
+ gtk_container_set_border_width (GTK_CONTAINER (textview_log), 5);
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(textview_log), FALSE);
+ gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textview_log), FALSE);
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview_log), GTK_WRAP_WORD);
+ gtk_container_add (GTK_CONTAINER (scrolledwindow_log), textview_log);
+ gtk_container_add (GTK_CONTAINER (frame_log), scrolledwindow_log);
+
+ /*
+ ** Main window
+ */
+ vbox_main = gtk_vbox_new(FALSE, 10);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox_main), 0);
+ /* Add the frames into the main vbox */
+ gtk_box_pack_start(GTK_BOX(vbox_main), frame_shape, FALSE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox_main), frame_pg, FALSE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox_main), frame_config, FALSE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox_main), hbox_buttons, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox_main), frame_log, TRUE, TRUE, 0);
+ /* and insert the vbox into the main window */
+ gtk_container_add (GTK_CONTAINER (window_main), vbox_main);
+ /* make sure that everything, window and label, are visible */
+ gtk_widget_show_all (window_main);
+
+ return;
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ /* initialize the GTK stack */
+ gtk_init(&argc, &argv);
+
+ /* set up the user interface */
+ 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();
+
+ return 0;
+}
+
+/**********************************************************************
+ * $Log$
+ *
+ **********************************************************************/
Added: trunk/loader/stringbuffer.c
===================================================================
--- trunk/loader/stringbuffer.c 2009-01-16 17:58:39 UTC (rev 3537)
+++ trunk/loader/stringbuffer.c 2009-01-19 21:33:14 UTC (rev 3538)
@@ -0,0 +1,568 @@
+/*
+ *
+ * Copyright 2002 Thamer Alharbash
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Sringbuffer object:
+ *
+ * (*) allows printfing into a string,
+ * (*) fast string manipulation by keeping track of string length.
+ * (*) alignment of string against columns.
+ *
+ * Internally stringbuffer does not count the terminating null as the length.
+ * Therefore the raw string routines will always assume +1 when given length.
+ */
+
+#include "stringbuffer.h"
+
+/* * * * * * * * * * * * *
+ * raw string routines. *
+ * * * * * * * * * * * * */
+
+/* just malloc out a string. */
+static char *allocate_string(int len)
+{
+ char *s;
+ s = malloc(sizeof(char) * len + 1); /* add for null termination. */
+ s[len] = 0;
+ return s;
+}
+
+/* extend a string. */
+static char *extend_string(char *str, int cur_len, int ex_len)
+{
+
+ str = realloc(str, (cur_len * sizeof(char)) + (ex_len * sizeof(char)) + (1 * sizeof(char)));
+ str[cur_len] = 0; /* make sure it's null terminated. */
+
+ return str;
+}
+
+/* get a substring. */
+static char *substring(char *begin, int len)
+{
+ char *new_string;
+
+ new_string = allocate_string(len);
+ memcpy(new_string, begin, len);
+ new_string[len] = 0;
+
+ return new_string;
+}
+
+/* FIXME: get rid of pesky strlen() */
+/* used in aligning -- we try to get words up to end. */
+static char *get_string_align(char *s, int end, int *len)
+{
+ char *cur_ptr;
+
+ if (s == 0 || *s == 0) /* end of string or no string. */
+ return NULL;
+
+ /* if strlen is smaller than len go ahead and just return it. */
+ if (strlen(s) < end)
+ {
+ *len = strlen(s);
+ return strdup(s);
+ }
+
+ /* otherwise we need to hop to len */
+ cur_ptr = &s[end - 1];
+
+ /* now check to see if we have a whitespace behind cur_ptr
+ * if we do return at that point. */
+
+ for (; cur_ptr != s; cur_ptr--)
+ {
+ if (*cur_ptr == ' ' || *cur_ptr == '\t')
+ {
+ /* copy here and return. */
+ *len = (cur_ptr - s) + 1;
+ return (substring(s, *len));
+ }
+ }
+
+ /* keep walking till we find whitspace or end. */
+ for (cur_ptr = &s[end - 1]; *cur_ptr != 0 && *cur_ptr != ' ' && *cur_ptr != '\t'; cur_ptr++);
+
+ *len = (cur_ptr - s) + 1;
+ return (substring(s, *len));
+}
+
+/* zap newlines by placing spaces in their place.
+ * we use this before aligning. */
+static void stringbuffer_zap_newline(stringbuffer_t *sb)
+{
+
+ stringbuffer_replace_c(sb, '\n', ' ');
+ stringbuffer_replace_c(sb, '\r', ' ');
+}
+
+/* * * * * * * * * * * * * * *
+ * stringbuffer routines. *
+ * * * * * * * * * * * * * * */
+
+/* create a new stringbuffer */
+stringbuffer_t *stringbuffer_create(void)
+{
+ stringbuffer_t *sb;
+
+ sb = malloc(sizeof(stringbuffer_t));
+ sb->len = 0;
+ sb->capacity = 0;
+ sb->buf = allocate_string(0);
+
+ return sb;
+}
+
+/* destroy the stringbuffer */
+void stringbuffer_destroy(stringbuffer_t *sb)
+{
+ free(sb->buf);
+ free(sb);
+}
+
+/* clear a string. */
+void stringbuffer_clear(stringbuffer_t *sb)
+{
+ sb->len = 0;
+ sb->buf[0] = 0;
+}
+
+/* append character to stringbuffer */
+void stringbuffer_append_c(stringbuffer_t *sb, char c)
+{
+ if (sb->capacity <= (sb->len))
+ {
+ sb->buf = extend_string(sb->buf, sb->len, STRINGBUFFER_CHUNKSIZE);
+ sb->capacity += STRINGBUFFER_CHUNKSIZE;
+ }
+
+ sb->buf[sb->len] = c;
+ sb->len++;
+ sb->buf[sb->len] = 0;
+}
+
+/* append string to stringbuffer */
+void stringbuffer_append(stringbuffer_t *sb, const char *s)
+{
+ int len = strlen(s);
+
+ /* increase capacity if needed. */
+ if (sb->capacity <= (len + sb->len))
+ {
+
+ /* if we're bigger than the chunksize then allocate len. */
+ if (len > STRINGBUFFER_CHUNKSIZE)
+ {
+
+ sb->buf = extend_string(sb->buf, sb->capacity, len);
+ sb->capacity += len;
+
+ }
+ else
+ {
+
+ /* otherwise allocate chunksize. */
+ sb->buf = extend_string(sb->buf, sb->capacity, STRINGBUFFER_CHUNKSIZE);
+ sb->capacity += STRINGBUFFER_CHUNKSIZE;
+ }
+ }
+
+ /* copy new string into place: keep in mind we know all
+ * lengths so strcat() would be less effecient. */
+
+ memcpy(&sb->buf[sb->len], s, len);
+
+ sb->len += len;
+ sb->buf[sb->len] = 0;
+
+ return;
+}
+
+/* remove whitespace (including tabs) */
+stringbuffer_t *stringbuffer_trim_whitespace(stringbuffer_t *sb)
+{
+ char *newbuf;
+ int new_len;
+ int i, j;
+
+ if (sb->len == 0) /* empty string. */
+ return sb;
+
+ /* find beginning of string after tabs and whitespaces. */
+ for (i = 0; i < sb->len && (sb->buf[i] == ' ' || sb->buf[i] == '\t'); i++);
+
+ if (sb->buf[i] != '\0')
+ {
+
+ /* we do have whitespace in the beginning so find the end. */
+ for (j = (sb->len -1);(sb->buf[j] == ' ' || sb->buf[j] == '\t'); j--);
+
+ /* increment j since it's on the non whitespace character. */
+ j++;
+
+ /* create a new string. */
+ new_len = j - i;
+ newbuf = allocate_string(new_len);
+
+ /* copy in. */
+ memcpy(newbuf, &sb->buf[i], (j - i) * sizeof(char));
+ newbuf[new_len] = 0;
+
+ /* free up old. */
+ free(sb->buf);
+
+ /* set new. */
+ sb->buf = newbuf;
+ sb->len = new_len;
+ sb->capacity = new_len;
+
+
+ }
+ else
+ {
+
+ /* zap beginning of string. since its all whitespace. */
+ sb->buf[0] = 0;
+ sb->len = 0;
+ }
+
+ return sb;
+}
+
+/* get the last occurance of a specific character. useful for slicing. */
+char *stringbuffer_get_last_occurance(stringbuffer_t *sb, char c)
+{
+ char *ptr, *ptrend = NULL;
+ int i;
+
+ ptr = sb->buf;
+ for (i = 0;i < sb->len;i++)
+ {
+
+ if (ptr[i] == c)
+ ptrend = &ptr[i];
+ }
+
+ return ptrend;
+}
+
+/* remove the last newline character. */
+void stringbuffer_trim_newline(stringbuffer_t *sb)
+{
+ char *ptr;
+
+ ptr = stringbuffer_get_last_occurance(sb, '\n');
+ if (ptr != NULL)
+ *ptr = 0;
+
+
+ ptr = stringbuffer_get_last_occurance(sb, '\r');
+ if (ptr != NULL)
+ *ptr = 0;
+
+
+ sb->len = strlen(sb->buf);
+
+ return;
+}
+
+/* return the C string from the buffer. */
+const char *stringbuffer_getstring(stringbuffer_t *sb)
+{
+ return sb->buf;
+}
+
+/* set a string into a stringbuffer */
+void stringbuffer_set(stringbuffer_t *dest, const char *s)
+{
+ stringbuffer_clear(dest); /* zap */
+ stringbuffer_append(dest, s);
+}
+
+/* copy a stringbuffer into another stringbuffer */
+void stringbuffer_copy(stringbuffer_t *dest, stringbuffer_t *src)
+{
+ stringbuffer_set(dest, stringbuffer_getstring(src));
+}
+
+/* replace in stringbuffer occurances of c with replace */
+void stringbuffer_replace_c(stringbuffer_t *sb, char c, char replace)
+{
+ int i;
+
+ for (i = 0;i < sb->len;i++)
+ {
+ if (sb->buf[i] == c)
+ sb->buf[i] = replace;
+ }
+
+ return;
+}
+
+/* replace in stringbuffer occurances of c with replace */
+void stringbuffer_replace(stringbuffer_t *sb, const char *string, const char *replace)
+{
+ char *ptr;
+ int i;
+ int str_len = strlen(string);
+ stringbuffer_t *sb_replace;
+
+ if (string[0] == 0)
+ return; /* nothing to replace. */
+
+ sb_replace = stringbuffer_create();
+ ptr = sb->buf;
+
+ for (i = 0; i < sb->len; i++)
+ {
+
+ if ((sb->len - i) < str_len)
+ {
+
+ /* copy in. */
+ stringbuffer_copy(sb, sb_replace);
+
+ /* append what's left. */
+ stringbuffer_append(sb, &ptr[i]);
+
+ /* free up. */
+ stringbuffer_destroy(sb_replace);
+
+ /* we're done. */
+ return;
+ }
+
+ if (ptr[i] == string[0])
+ {
+
+ /* we know that we have at least enough to complete string. */
+ if (!memcmp(&ptr[i], string, str_len))
+ {
+
+ /* we have a match, replace. */
+ stringbuffer_append(sb_replace, replace);
+ i += (str_len - 1);
+ continue;
+ }
+ }
+
+ stringbuffer_append_c(sb_replace, ptr[i]);
+ }
+
+ /* we're done: we should only get here if the last string to
+ be replaced ended the string itself. */
+
+ /* copy in. */
+ stringbuffer_copy(sb, sb_replace);
+
+ /* free up. */
+ stringbuffer_destroy(sb_replace);
+
+ /* we're done. */
+ return;
+
+}
+
+/* align a stringbuffer on begin and end columns. */
+void stringbuffer_align(stringbuffer_t *sb, int begin, int end)
+{
+ char *ptr, *word_string;
+ stringbuffer_t *aligned_string;
+ int len, i;
+
+ stringbuffer_zap_newline(sb);
+
+ aligned_string = stringbuffer_create();
+ ptr = sb->buf;
+
+ while (1)
+ {
+
+ word_string = get_string_align(ptr, end, &len);
+
+ if (word_string == NULL)
+ break;
+
+ ptr += len;
+
+ for (i = 0; i < begin; i++)
+ stringbuffer_append(aligned_string, " ");
+
+ stringbuffer_append(aligned_string, word_string);
+ stringbuffer_append(aligned_string, "\n");
+ free(word_string);
+
+ }
+
+ stringbuffer_copy(sb, aligned_string);
+ stringbuffer_destroy(aligned_string);
+
+ return;
+}
+
+/* stringbuffer_*printf* these all use snprintf/snvprintf internally */
+
+/* append vprintf with alignment. */
+void stringbuffer_avprintf_align(stringbuffer_t *sb, int start, int end, const char *fmt, va_list ap)
+{
+ stringbuffer_t *tmp_sb;
+ char *str;
+ int total, len;
+
+ /* our first malloc is bogus. */
+ len = 1;
+ str = malloc(sizeof(char) * len);
+ total = vsnprintf(str, len, fmt, ap);
+
+ /* total is the real length needed. */
+ free(str);
+ len = total + 1;
+ str = malloc(sizeof(char) * len);
+ vsnprintf(str, len, fmt, ap);
+
+ /* now align if we want to align. */
+ if (start != 0 && end != 0)
+ {
+
+ tmp_sb = stringbuffer_create();
+
+ stringbuffer_append(tmp_sb, str);
+ stringbuffer_align(tmp_sb, start, end);
+ stringbuffer_append(sb, stringbuffer_getstring(tmp_sb));
+
+ stringbuffer_destroy(tmp_sb);
+
+ }
+ else
+ {
+ stringbuffer_append(sb, str);
+ }
+
+ free(str);
+
+ return;
+}
+
+/* append printf. */
+void stringbuffer_aprintf(stringbuffer_t *sb, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ stringbuffer_avprintf(sb, fmt, ap);
+
+ va_end(ap);
+}
+
+/* append printf with alignment. */
+void stringbuffer_aprintf_align(stringbuffer_t *sb, int start, int end, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ stringbuffer_avprintf_align(sb, start, end, fmt, ap);
+
+ va_end(ap);
+}
+
+/* append vprintf. */
+void stringbuffer_avprintf(stringbuffer_t *sb, const char *fmt, va_list ap)
+{
+ stringbuffer_avprintf_align(sb, 0, 0, fmt, ap);
+}
+
+/* newline marking and sweeping. this wrecks the string inside
+ * the stringbuffer. */
+
+/* mark newlines to walk through.
+ * our sentinel is the null terminator.
+ * two null terminations marks the end of the string.
+ * we're guaranteed it is a unique sentinel since
+ * we never accept null terminators from outside sources
+ * and never build our own strings (obviously!) with
+ * null terminators inside of them.
+ */
+int stringbuffer_marknewlines(stringbuffer_t *sb)
+{
+ char *c;
+ int newline_count = 0;
+
+ /* first append one null termination to the end
+ * to act as a proper terminator. */
+ stringbuffer_append_c(sb, 0);
+
+
+ c = sb->buf;
+ while (1)
+ {
+
+ if (*c == '\n')
+ {
+ newline_count++;
+ *c = 0;
+ }
+
+ c++;
+ if (*c == 0)
+ break;
+ }
+
+ return newline_count; /* return our line count. */
+}
+
+/* called _after_ newlines are marked. */
+const char *stringbuffer_getnextline(stringbuffer_t *sb, const char *cptr)
+{
+ const char *ptr;
+
+ if (cptr == NULL)
+ {
+
+ /* get first line. */
+ cptr = sb->buf;
+
+ }
+ else
+ {
+
+ for (ptr = cptr; *ptr != 0; ptr++);
+ if (*ptr == 0 && *(ptr + 1) == 0)
+ {
+ return NULL;
+
+ }
+ else
+ {
+ cptr = ptr + 1;
+
+ }
+ }
+
+ return cptr;
+}
+
+int stringbuffer_getlen(stringbuffer_t *sb)
+{
+ return sb->len;
+}
Added: trunk/loader/stringbuffer.h
===================================================================
--- trunk/loader/stringbuffer.h 2009-01-16 17:58:39 UTC (rev 3537)
+++ trunk/loader/stringbuffer.h 2009-01-19 21:33:14 UTC (rev 3538)
@@ -0,0 +1,66 @@
+/*
+ *
+ * Copyright 2002 Thamer Alharbash
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Sringbuffer object:
+ *
+ * (*) allows printfing into a string,
+ * (*) fast string manipulation by keeping track of string length.
+ * (*) alignment of string against columns.
+ *
+ * Internally stringbuffer does not count the terminating null as the length.
+ * Therefore the raw string routines will always assume +1 when given length.
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+
+#define STRINGBUFFER_CHUNKSIZE 16
+
+typedef struct {
+ size_t len;
+ size_t capacity;
+ char *buf;
+} stringbuffer_t;
+
+extern stringbuffer_t *stringbuffer_create(void);
+extern void stringbuffer_destroy(stringbuffer_t *sb);
+extern void stringbuffer_clear(stringbuffer_t *sb);
+extern void stringbuffer_append_c(stringbuffer_t *sb, char c);
+extern void stringbuffer_append(stringbuffer_t *sb, const char *s);
+extern stringbuffer_t *stringbuffer_trim_whitespace(stringbuffer_t *sb);
+extern char *stringbuffer_get_last_occurance(stringbuffer_t *sb, char c);
+extern void stringbuffer_trim_newline(stringbuffer_t *sb);
+extern const char *stringbuffer_getstring(stringbuffer_t *sb);
+extern void stringbuffer_set(stringbuffer_t *dest, const char *s);
+extern void stringbuffer_copy(stringbuffer_t *dest, stringbuffer_t *src);
+extern void stringbuffer_replace_c(stringbuffer_t *sb, char c, char replace);
+extern void stringbuffer_replace(stringbuffer_t *sb, const char *string, const char *replace);
+extern void stringbuffer_align(stringbuffer_t *sb, int begin, int end);
+extern void stringbuffer_avprintf_align(stringbuffer_t *sb, int start, int end, const char *fmt, va_list ap);
+extern void stringbuffer_aprintf(stringbuffer_t *sb, const char *fmt, ...);
+extern void stringbuffer_aprintf_align(stringbuffer_t *sb, int start, int end, const char *fmt, ...);
+extern void stringbuffer_avprintf(stringbuffer_t *sb, const char *fmt, va_list ap);
+extern int stringbuffer_marknewlines(stringbuffer_t *sb);
+extern const char *stringbuffer_getnextline(stringbuffer_t *sb, const char *cptr);
+extern int stringbuffer_getlen(stringbuffer_t *sb);
More information about the postgis-commits
mailing list