diff -urN postgis-cvs.vanilla/Makefile postgis-cvs/Makefile --- postgis-cvs.vanilla/Makefile Tue May 4 17:55:39 2004 +++ postgis-cvs/Makefile Wed May 26 23:19:25 2004 @@ -130,8 +130,13 @@ GIST_SUPPORT=71 GIST_ESTIMATE= else - GIST_SUPPORT=72 - GIST_ESTIMATE=postgis_estimate.o + ifeq ($(USE_VERSION),75) + GIST_SUPPORT=75 + GIST_ESTIMATE=postgis_estimate.o + else + GIST_SUPPORT=72 + GIST_ESTIMATE=postgis_estimate.o + endif endif ifeq ($(USE_GEOS),1) diff -urN postgis-cvs.vanilla/postgis.h postgis-cvs/postgis.h --- postgis-cvs.vanilla/postgis.h Fri Mar 26 00:54:09 2004 +++ postgis-cvs/postgis.h Wed May 26 23:12:51 2004 @@ -94,7 +94,7 @@ * it defines BYTE_ORDER and LITTLE_ENDIAN */ -#ifdef __CYGWIN__ +#if defined(__CYGWIN__) || defined(__MINGW32__) #include // FOR ENDIAN DEFINES #endif @@ -667,10 +667,10 @@ -// from contrib/cube/cube.c - -#define max(a,b) ((a) > (b) ? (a) : (b)) -#define min(a,b) ((a) <= (b) ? (a) : (b)) -#define abs(a) ((a) < (0) ? (-a) : (a)) - - + // from contrib/cube/cube.c + +#if !defined(__MINGW32__) + #define max(a,b) ((a) > (b) ? (a) : (b)) + #define min(a,b) ((a) <= (b) ? (a) : (b)) +#endif + #define abs(a) ((a) < (0) ? (-a) : (a)) diff -urN postgis-cvs.vanilla/postgis_gist_75.c postgis-cvs/postgis_gist_75.c --- postgis-cvs.vanilla/postgis_gist_75.c Thu Jan 1 00:00:00 1970 +++ postgis-cvs/postgis_gist_75.c Wed May 26 23:04:10 2004 @@ -0,0 +1,662 @@ + +/********************************************************************** + * $Id: postgis_gist_72.c,v 1.11 2004/04/28 22:26:02 pramsey Exp $ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + ********************************************************************** + * $Log: postgis_gist_72.c,v $ + * Revision 1.11 2004/04/28 22:26:02 pramsey + * Fixed spelling mistake in header text. + * + * Revision 1.10 2004/04/08 17:05:20 dblasby + * Somehow the memory leak changes I made got removed - I've re-added them. + * + * Revision 1.9 2004/04/08 17:00:27 dblasby + * Changed ggeometry_consistent to be aware of NULL queries. Ie. + * select * from where the_geom && null::geometry; + * + * This tends to happen when you're joining two tables using && and the table + * has NULLs in it. + * + * Revision 1.8 2004/03/05 18:16:47 strk + * Applied Mark Cave-Ayland patch + * + * Revision 1.7 2004/02/25 13:17:31 strk + * RTContainedBy and RTOverlap strategries implemented locally with a pgbox_overlap function + * + * Revision 1.6 2003/08/08 18:19:20 dblasby + * Conformance changes. + * Removed junk from postgis_debug.c and added the first run of the long + * transaction locking support. (this will change - dont use it) + * conformance tests were corrected + * some dos cr/lf removed + * empty geometries i.e. GEOMETRYCOLLECT(EMPTY) added (with indexing support) + * pointN(,1) now returns the first point (used to return 2nd) + * + * Revision 1.5 2003/07/01 18:30:55 pramsey + * Added CVS revision headers. + * + * + ********************************************************************** + * + * GiST indexing functions for pgsql >= 7.2 + * + **********************************************************************/ + +#include "postgres.h" + +#include +#include +#include +#include +#include + +#include "access/gist.h" +#include "access/itup.h" +#include "access/rtree.h" + +#include "fmgr.h" + +#include "postgis.h" +#include "utils/elog.h" + +//Norman Vine found this problem for compiling under cygwin +// it defines BYTE_ORDER and LITTLE_ENDIAN + +#ifdef __CYGWIN__ +#include // FOR ENDIAN DEFINES +#endif + +#define SHOW_DIGS_DOUBLE 15 +#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1) + +//#define DEBUG_GIST + +//for GIST index +typedef char* (*BINARY_UNION)(char*, char*, int*); +typedef float (*SIZE_BOX)(char*); +typedef Datum (*RDF)(PG_FUNCTION_ARGS); + + +BOX *convert_box3d_to_box(BOX3D *in); +Datum ggeometry_compress(PG_FUNCTION_ARGS); +Datum ggeometry_consistent(PG_FUNCTION_ARGS); +static bool rtree_internal_consistent(BOX *key, BOX *query,StrategyNumber strategy); +static bool rtree_leaf_consistent(BOX *key, BOX *query,StrategyNumber strategy); +Datum rtree_decompress(PG_FUNCTION_ARGS); +Datum gbox_union(PG_FUNCTION_ARGS); +Datum gbox_picksplit(PG_FUNCTION_ARGS); +Datum gbox_penalty(PG_FUNCTION_ARGS); +Datum gbox_same(PG_FUNCTION_ARGS); +static float size_box(Datum box); +extern void convert_box3d_to_box_p(BOX3D *in,BOX *out); + + +int debug = 0; + +//puts result in pre-allocated "out" +void convert_box3d_to_box_p(BOX3D *in,BOX *out) +{ + + out->high.x = in->URT.x; + out->high.y = in->URT.y; + + out->low.x = in->LLB.x; + out->low.y = in->LLB.y; +} + + +BOX *convert_box3d_to_box(BOX3D *in) +{ + BOX *out = palloc (sizeof (BOX) ); + + out->high.x = in->URT.x; + out->high.y = in->URT.y; + + out->low.x = in->LLB.x; + out->low.y = in->LLB.y; + + return out; +} + +PG_FUNCTION_INFO_V1(ggeometry_compress); +Datum ggeometry_compress(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry=(GISTENTRY*)PG_GETARG_POINTER(0); + GISTENTRY *retval; + + if ( entry->leafkey) + { + retval = palloc(sizeof(GISTENTRY)); + if ( DatumGetPointer(entry->key) != NULL ) + { + + GEOMETRY *in; + BOX *r; + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: ggeometry_compress called on geometry\n"); + fflush( stdout ); +#endif + + in = (GEOMETRY*)PG_DETOAST_DATUM( entry->key ); + + if (in->nobjs ==0) // this is the EMPTY geometry + { + //elog(NOTICE,"found an empty geometry"); + // dont bother adding this to the index + PG_RETURN_POINTER(entry); + } + else + { + r = convert_box3d_to_box(&in->bvol); + if ( in != (GEOMETRY*)DatumGetPointer(entry->key) ) + { + pfree( in ); + } + + gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page, + entry->offset, sizeof(BOX), FALSE); + } + + } + else + { + gistentryinit(*retval, (Datum) 0, entry->rel, entry->page, + entry->offset, 0,FALSE); + } + } + else + { + retval = entry; + } + PG_RETURN_POINTER(retval); +} + +PG_FUNCTION_INFO_V1(ggeometry_consistent); +Datum ggeometry_consistent(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0); + GEOMETRY *query ; + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + BOX thebox; + bool result; + + /* + ** if entry is not leaf, use rtree_internal_consistent, + ** else use rtree_leaf_consistent + */ + + if ( ( (Pointer *) PG_GETARG_DATUM(1) ) == NULL) + { + //elog(NOTICE,"ggeometry_consistent:: got null query!"); + PG_RETURN_BOOL(false); // null query - this is screwy! + } + + query = (GEOMETRY*) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); + + + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: ggeometry_consistent called\n"); + fflush( stdout ); +#endif + + if ( ! (DatumGetPointer(entry->key) != NULL && query) ) + PG_RETURN_BOOL(FALSE); + + convert_box3d_to_box_p( &(query->bvol) , &thebox); + + if (GIST_LEAF(entry)) + result = rtree_leaf_consistent((BOX *) DatumGetPointer(entry->key), &thebox, strategy ); + else + result = rtree_internal_consistent((BOX *) DatumGetPointer(entry->key), &thebox, strategy ); + + PG_FREE_IF_COPY(query, 1); + PG_RETURN_BOOL(result); +} + + +static bool +rtree_internal_consistent(BOX *key, + BOX *query, + StrategyNumber strategy) +{ + bool retval; + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: rtree_internal_consist called\n"); + fflush( stdout ); +#endif + + switch(strategy) { + case RTLeftStrategyNumber: + case RTOverLeftStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_overleft, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + case RTOverlapStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_overlap, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + case RTOverRightStrategyNumber: + case RTRightStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_overright, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + case RTSameStrategyNumber: + case RTContainsStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_contain, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + case RTContainedByStrategyNumber: + retval = DatumGetBool( DirectFunctionCall2( box_overlap, PointerGetDatum(key), PointerGetDatum(query) ) ); + break; + default: + retval = FALSE; + } + return(retval); +} + + +static bool +rtree_leaf_consistent(BOX *key, + BOX *query, + StrategyNumber strategy) +{ + bool retval; + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: rtree_leaf_consist called\n"); + fflush( stdout ); +#endif + + switch (strategy) + { + case RTLeftStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_left, PointerGetDatum(key), PointerGetDatum(query))); + break; + case RTOverLeftStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_overleft, PointerGetDatum(key), PointerGetDatum(query))); + break; + case RTOverlapStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_overlap, PointerGetDatum(key), PointerGetDatum(query))); + break; + case RTOverRightStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_overright, PointerGetDatum(key), PointerGetDatum(query))); + break; + case RTRightStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_right, PointerGetDatum(key), PointerGetDatum(query))); + break; + case RTSameStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_same, PointerGetDatum(key), PointerGetDatum(query))); + break; + case RTContainsStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_contain, PointerGetDatum(key), PointerGetDatum(query))); + break; + case RTContainedByStrategyNumber: + retval = DatumGetBool(DirectFunctionCall2(box_contained, PointerGetDatum(key), PointerGetDatum(query))); + break; + default: + retval = FALSE; + } + return (retval); +} + + +PG_FUNCTION_INFO_V1(rtree_decompress); +Datum rtree_decompress(PG_FUNCTION_ARGS) +{ + PG_RETURN_POINTER(PG_GETARG_POINTER(0)); +} + +/* +** The GiST Union method for boxes +** returns the minimal bounding box that encloses all the entries in entryvec +*/ +PG_FUNCTION_INFO_V1(gbox_union); +Datum gbox_union(PG_FUNCTION_ARGS) +{ + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + int *sizep = (int *) PG_GETARG_POINTER(1); + int numranges, + i; + BOX *cur, + *pageunion; + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: gbox_union called\n"); + fflush( stdout ); +#endif + + numranges = entryvec->n; + pageunion = (BOX *) palloc(sizeof(BOX)); + cur = DatumGetBoxP(entryvec->vector[0].key); + memcpy((void *) pageunion, (void *) cur, sizeof(BOX)); + + for (i = 1; i < numranges; i++) + { + cur = DatumGetBoxP(entryvec->vector[i].key); + if (pageunion->high.x < cur->high.x) + pageunion->high.x = cur->high.x; + if (pageunion->low.x > cur->low.x) + pageunion->low.x = cur->low.x; + if (pageunion->high.y < cur->high.y) + pageunion->high.y = cur->high.y; + if (pageunion->low.y > cur->low.y) + pageunion->low.y = cur->low.y; + } + *sizep = sizeof(BOX); + + PG_RETURN_POINTER(pageunion); +} + +/* +** The GiST Penalty method for boxes +** As in the R-tree paper, we use change in area as our penalty metric +*/ +PG_FUNCTION_INFO_V1(gbox_penalty); +Datum gbox_penalty(PG_FUNCTION_ARGS) +{ + GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); + GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1); + float *result = (float *) PG_GETARG_POINTER(2); + Datum ud; + float tmp1; + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: gbox_penalty called\n"); + fflush( stdout ); +#endif + + ud = DirectFunctionCall2(rt_box_union, origentry->key, newentry->key); + tmp1 = size_box(ud); + if (DatumGetPointer(ud) != NULL) + pfree(DatumGetPointer(ud)); + + *result = tmp1 - size_box(origentry->key); + PG_RETURN_POINTER(result); +} + +typedef struct { + BOX *key; + int pos; +} KBsort; + +static int +compare_KB(const void* a, const void* b) { + BOX *abox = ((KBsort*)a)->key; + BOX *bbox = ((KBsort*)b)->key; + float sa = (abox->high.x - abox->low.x) * (abox->high.y - abox->low.y); + float sb = (bbox->high.x - bbox->low.x) * (bbox->high.y - bbox->low.y); + + if ( sa==sb ) return 0; + return ( sa>sb ) ? 1 : -1; +} + +/* +** The GiST PickSplit method +** New linear algorithm, see 'New Linear Node Splitting Algorithm for R-tree', +** C.H.Ang and T.C.Tan +*/ +PG_FUNCTION_INFO_V1(gbox_picksplit); +Datum +gbox_picksplit(PG_FUNCTION_ARGS) +{ + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); + OffsetNumber i; + OffsetNumber *listL, + *listR, + *listB, + *listT; + BOX *unionL, + *unionR, + *unionB, + *unionT; + int posL, + posR, + posB, + posT; + BOX pageunion; + BOX *cur; + char direction = ' '; + bool allisequal = true; + OffsetNumber maxoff; + int nbytes; + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: gbox_picksplit called\n"); + fflush( stdout ); +#endif + + posL = posR = posB = posT = 0; + maxoff = entryvec->n - 1; + + cur = DatumGetBoxP(entryvec->vector[FirstOffsetNumber].key); + memcpy((void *) &pageunion, (void *) cur, sizeof(BOX)); + + /* find MBR */ + for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i)) + { + cur = DatumGetBoxP(entryvec->vector[i].key); + if ( allisequal == true && ( + pageunion.high.x != cur->high.x || + pageunion.high.y != cur->high.y || + pageunion.low.x != cur->low.x || + pageunion.low.y != cur->low.y + ) ) + allisequal = false; + + if (pageunion.high.x < cur->high.x) + pageunion.high.x = cur->high.x; + if (pageunion.low.x > cur->low.x) + pageunion.low.x = cur->low.x; + if (pageunion.high.y < cur->high.y) + pageunion.high.y = cur->high.y; + if (pageunion.low.y > cur->low.y) + pageunion.low.y = cur->low.y; + } + + nbytes = (maxoff + 2) * sizeof(OffsetNumber); + listL = (OffsetNumber *) palloc(nbytes); + listR = (OffsetNumber *) palloc(nbytes); + unionL = (BOX *) palloc(sizeof(BOX)); + unionR = (BOX *) palloc(sizeof(BOX)); + if (allisequal) + { + cur = DatumGetBoxP(entryvec->vector[OffsetNumberNext(FirstOffsetNumber)].key); + if (memcmp((void *) cur, (void *) &pageunion, sizeof(BOX)) == 0) + { + v->spl_left = listL; + v->spl_right = listR; + v->spl_nleft = v->spl_nright = 0; + memcpy((void *) unionL, (void *) &pageunion, sizeof(BOX)); + memcpy((void *) unionR, (void *) &pageunion, sizeof(BOX)); + + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + if (i <= (maxoff - FirstOffsetNumber + 1) / 2) + { + v->spl_left[v->spl_nleft] = i; + v->spl_nleft++; + } + else + { + v->spl_right[v->spl_nright] = i; + v->spl_nright++; + } + } + v->spl_ldatum = BoxPGetDatum(unionL); + v->spl_rdatum = BoxPGetDatum(unionR); + + PG_RETURN_POINTER(v); + } + } + + listB = (OffsetNumber *) palloc(nbytes); + listT = (OffsetNumber *) palloc(nbytes); + unionB = (BOX *) palloc(sizeof(BOX)); + unionT = (BOX *) palloc(sizeof(BOX)); + +#define ADDLIST( list, unionD, pos, num ) do { \ + if ( pos ) { \ + if ( unionD->high.x < cur->high.x ) unionD->high.x = cur->high.x; \ + if ( unionD->low.x > cur->low.x ) unionD->low.x = cur->low.x; \ + if ( unionD->high.y < cur->high.y ) unionD->high.y = cur->high.y; \ + if ( unionD->low.y > cur->low.y ) unionD->low.y = cur->low.y; \ + } else { \ + memcpy( (void*)unionD, (void*) cur, sizeof( BOX ) ); \ + } \ + list[pos] = num; \ + (pos)++; \ +} while(0) + + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + cur = DatumGetBoxP(entryvec->vector[i].key); + if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x) + ADDLIST(listL, unionL, posL,i); + else + ADDLIST(listR, unionR, posR,i); + if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y) + ADDLIST(listB, unionB, posB,i); + else + ADDLIST(listT, unionT, posT,i); + } + + /* bad disposition, sort by ascending and resplit */ + if ( (posR==0 || posL==0) && (posT==0 || posB==0) ) { + KBsort *arr = (KBsort*)palloc( sizeof(KBsort) * maxoff ); + posL = posR = posB = posT = 0; + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { + arr[i-1].key = DatumGetBoxP(entryvec->vector[i].key); + arr[i-1].pos = i; + } + qsort( arr, maxoff, sizeof(KBsort), compare_KB ); + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { + cur = arr[i-1].key; + if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x) + ADDLIST(listL, unionL, posL,arr[i-1].pos); + else if ( cur->low.x - pageunion.low.x == pageunion.high.x - cur->high.x ) { + if ( posL>posR ) + ADDLIST(listR, unionR, posR,arr[i-1].pos); + else + ADDLIST(listL, unionL, posL,arr[i-1].pos); + } else + ADDLIST(listR, unionR, posR,arr[i-1].pos); + + if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y) + ADDLIST(listB, unionB, posB,arr[i-1].pos); + else if ( cur->low.y - pageunion.low.y == pageunion.high.y - cur->high.y ) { + if ( posB>posT ) + ADDLIST(listT, unionT, posT,arr[i-1].pos); + else + ADDLIST(listB, unionB, posB,arr[i-1].pos); + } else + ADDLIST(listT, unionT, posT,arr[i-1].pos); + } + pfree(arr); + } + + /* which split more optimal? */ + if (Max(posL, posR) < Max(posB, posT)) + direction = 'x'; + else if (Max(posL, posR) > Max(posB, posT)) + direction = 'y'; + else + { + Datum interLR = DirectFunctionCall2(rt_box_inter, + BoxPGetDatum(unionL), + BoxPGetDatum(unionR)); + Datum interBT = DirectFunctionCall2(rt_box_inter, + BoxPGetDatum(unionB), + BoxPGetDatum(unionT)); + float sizeLR, + sizeBT; + + sizeLR = size_box(interLR); + sizeBT = size_box(interBT); + + if (sizeLR < sizeBT) + direction = 'x'; + else + direction = 'y'; + } + + if (direction == 'x') + { + pfree(unionB); + pfree(listB); + pfree(unionT); + pfree(listT); + + v->spl_left = listL; + v->spl_right = listR; + v->spl_nleft = posL; + v->spl_nright = posR; + v->spl_ldatum = BoxPGetDatum(unionL); + v->spl_rdatum = BoxPGetDatum(unionR); + } + else + { + pfree(unionR); + pfree(listR); + pfree(unionL); + pfree(listL); + + v->spl_left = listB; + v->spl_right = listT; + v->spl_nleft = posB; + v->spl_nright = posT; + v->spl_ldatum = BoxPGetDatum(unionB); + v->spl_rdatum = BoxPGetDatum(unionT); + } + + PG_RETURN_POINTER(v); +} + +/* +** Equality method +*/ +PG_FUNCTION_INFO_V1(gbox_same); +Datum gbox_same(PG_FUNCTION_ARGS) +{ + BOX *b1 = (BOX *) PG_GETARG_POINTER(0); + BOX *b2 = (BOX *) PG_GETARG_POINTER(1); + bool *result = (bool *) PG_GETARG_POINTER(2); + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: gbox_same called\n"); + fflush( stdout ); +#endif + + if (b1 && b2) + *result = DatumGetBool(DirectFunctionCall2(box_same, PointerGetDatum(b1), PointerGetDatum(b2))); + else + *result = (b1 == NULL && b2 == NULL) ? TRUE : FALSE; + PG_RETURN_POINTER(result); +} + +static float +size_box(Datum box) +{ + +#ifdef DEBUG_GIST + elog(NOTICE,"GIST: size_box called\n"); + fflush( stdout ); +#endif + + if (DatumGetPointer(box) != NULL) + { + float size; + + DirectFunctionCall2(rt_box_size, + box, PointerGetDatum(&size)); + return size; + } + else + return 0.0; +} diff -urN postgis-cvs.vanilla/postgis_inout.c postgis-cvs/postgis_inout.c --- postgis-cvs.vanilla/postgis_inout.c Wed Apr 28 23:26:02 2004 +++ postgis-cvs/postgis_inout.c Wed May 26 23:10:58 2004 @@ -1617,8 +1617,14 @@ char *last_paren; char *current_paren; - first_paren= index (str,'('); - last_paren = rindex(str,')'); + //from mappostgis.c + #if defined(__MINGW32__) + first_paren= strchr (str,'('); + last_paren = strrchr(str,')'); + #else + first_paren= index (str,'('); + last_paren = rindex(str,')'); + #endif if ( (first_paren == NULL) || (last_paren == NULL) || (first_paren >last_paren) ) {