Merge remote-tracking branch 'origin/pycurl' into planetlab-4_0-branch
[plcapi.git] / psycopg2 / psycopg / typecast_array.c
1 /* typecast_array.c - array typecasters
2  *
3  * Copyright (C) 2005 Federico Di Gregorio <fog@debian.org>
4  *
5  * This file is part of the psycopg module.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2,
10  * or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 #define MAX_DIMENSIONS 16
23
24 /** typecast_array_cleanup - remove the horrible [...]= stuff **/
25
26 static int
27 typecast_array_cleanup(char **str, int *len)
28 {
29     int i, depth = 1;
30     
31     if ((*str)[0] != '[') return -1;
32     
33     for (i=1 ; depth > 0 && i < *len ; i++) {
34         if ((*str)[i] == '[')
35             depth += 1;
36         else if ((*str)[i] == ']')
37             depth -= 1;
38     }
39     if ((*str)[i] != '=') return -1;
40     
41     *str = &((*str)[i+1]);
42     *len = *len - i - 1;
43     return 0;
44 }
45
46 /** typecast_array_scan - scan a string looking for array items **/
47
48 #define ASCAN_ERROR -1
49 #define ASCAN_EOF    0
50 #define ASCAN_BEGIN  1
51 #define ASCAN_END    2
52 #define ASCAN_TOKEN  3
53 #define ASCAN_QUOTED 4
54
55 static int
56 typecast_array_tokenize(char *str, int strlength,
57                         int *pos, char** token, int *length)
58 {
59     /* FORTRAN glory */
60     int i, j, q, b, l, res;
61
62     Dprintf("typecast_array_tokenize: '%s', %d/%d",
63             &str[*pos], *pos, strlength);
64
65     /* we always get called with pos pointing at the start of a token, so a
66        fast check is enough for ASCAN_EOF, ASCAN_BEGIN and ASCAN_END */
67     if (*pos == strlength) {
68         return ASCAN_EOF;
69     }
70     else if (str[*pos] == '{') {
71         *pos += 1;
72         return ASCAN_BEGIN;
73     }
74     else if (str[*pos] == '}') {
75         *pos += 1;
76         if (str[*pos] == ',')
77             *pos += 1;
78         return ASCAN_END;
79     }
80
81     /* now we start looking for the first unquoted ',' or '}', the only two
82        tokens that can limit an array element */
83     q = 0; /* if q is odd we're inside quotes */
84     b = 0; /* if b is 1 we just encountered a backslash */
85     res = ASCAN_TOKEN;
86     
87     for (i = *pos ; i < strlength ; i++) {
88         switch (str[i]) {
89         case '"':
90             if (b == 0)
91                 q += 1;
92             else
93                 b = 0;
94             break;
95
96         case '\\':
97             res = ASCAN_QUOTED;
98             if (b == 0)
99                 b = 1;
100             else
101                 /* we're backslashing a backslash */
102                 b = 0;
103             break;
104
105         case '}':
106         case ',':
107             if (b == 0 && ((q&1) == 0))
108                 goto tokenize;
109             break;
110
111         default:
112             /* reset the backslash counter */
113             b = 0;
114             break;
115         }
116     }
117
118  tokenize:
119     /* remove initial quoting character and calculate raw length */
120     l = i - *pos;
121     if (str[*pos] == '"') {
122         *pos += 1;
123         l -= 2;
124     }
125
126     if (res == ASCAN_QUOTED) { 
127         char *buffer = PyMem_Malloc(l+1);
128         if (buffer == NULL) return ASCAN_ERROR;
129
130         *token = buffer;
131         
132         for (j = *pos; j < *pos+l; j++) {
133             if (str[j] != '\\'
134                 || (j > *pos && str[j-1] == '\\'))
135                 *(buffer++) = str[j];
136         }
137         
138         *buffer = '\0';
139         *length = buffer - *token;
140     }
141     else {
142         *token = &str[*pos];
143         *length = l;
144     }
145
146     *pos = i;
147     
148     /* skip the comma and set position to the start of next token */
149     if (str[i] == ',') *pos += 1;
150
151     return res;
152 }
153
154 static int
155 typecast_array_scan(char *str, int strlength,
156                     PyObject *curs, PyObject *base, PyObject *array)
157 {
158     int state, length = 0, pos = 0;
159     char *token;
160
161     PyObject *stack[MAX_DIMENSIONS];
162     int stack_index = 0;
163     
164     while (1) {
165         token = NULL;
166         state = typecast_array_tokenize(str, strlength, &pos, &token, &length);
167         Dprintf("typecast_array_scan: state = %d, length = %d, token = '%s'",
168                 state, length, token);
169         if (state == ASCAN_TOKEN || state == ASCAN_QUOTED) {
170             PyObject *obj = typecast_cast(base, token, length, curs);
171
172             /* before anything else we free the memory */
173             if (state == ASCAN_QUOTED) PyMem_Free(token);
174             if (obj == NULL) return 0;
175
176             PyList_Append(array, obj);
177             Py_DECREF(obj);
178         }
179
180         else if (state == ASCAN_BEGIN) {
181             PyObject *sub = PyList_New(0);            
182             if (sub == NULL) return 0;
183
184             PyList_Append(array, sub);
185             Py_DECREF(sub);
186
187             if (stack_index == MAX_DIMENSIONS)
188                 return 0;
189
190             stack[stack_index++] = array;
191             array = sub;
192         }
193         
194         else if (state == ASCAN_ERROR) {
195             return 0;
196         }
197
198         else if (state == ASCAN_END) {
199             if (--stack_index < 0)
200                 return 0;
201             array = stack[stack_index];
202         }
203
204         else if (state ==  ASCAN_EOF)
205             break;
206     }
207
208     return 1;
209 }
210
211
212 /** GENERIC - a generic typecaster that can be used when no special actions
213     have to be taken on the single items **/
214    
215 static PyObject *
216 typecast_GENERIC_ARRAY_cast(char *str, int len, PyObject *curs)
217 {
218     PyObject *obj = NULL;
219     PyObject *base = ((typecastObject*)((cursorObject*)curs)->caster)->bcast;
220    
221     Dprintf("typecast_GENERIC_ARRAY_cast: str = '%s', len = %d", str, len);
222
223     if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
224     if (str[0] == '[')
225         typecast_array_cleanup(&str, &len);
226     if (str[0] != '{') {
227         PyErr_SetString(Error, "array does not start with '{'");
228         return NULL;
229     }
230
231     Dprintf("typecast_GENERIC_ARRAY_cast: str = '%s', len = %d", str, len);
232     
233     obj = PyList_New(0);
234
235     /* scan the array skipping the first level of {} */
236     if (typecast_array_scan(&str[1], len-2, curs, base, obj) == 0) {
237         Py_DECREF(obj);
238         obj = NULL;
239     }
240     
241     return obj;
242 }
243
244 /** almost all the basic array typecasters are derived from GENERIC **/
245
246 #define typecast_LONGINTEGERARRAY_cast typecast_GENERIC_ARRAY_cast
247 #define typecast_INTEGERARRAY_cast typecast_GENERIC_ARRAY_cast
248 #define typecast_FLOATARRAY_cast typecast_GENERIC_ARRAY_cast
249 #define typecast_DECIMALARRAY_cast typecast_GENERIC_ARRAY_cast
250 #define typecast_STRINGARRAY_cast typecast_GENERIC_ARRAY_cast
251 #define typecast_UNICODEARRAY_cast typecast_GENERIC_ARRAY_cast
252 #define typecast_BOOLEANARRAY_cast typecast_GENERIC_ARRAY_cast
253 #define typecast_DATETIMEARRAY_cast typecast_GENERIC_ARRAY_cast
254 #define typecast_DATEARRAY_cast typecast_GENERIC_ARRAY_cast
255 #define typecast_TIMEARRAY_cast typecast_GENERIC_ARRAY_cast
256 #define typecast_INTERVALARRAY_cast typecast_GENERIC_ARRAY_cast
257 #define typecast_BINARYARRAY_cast typecast_GENERIC_ARRAY_cast
258 #define typecast_ROWIDARRAY_cast typecast_GENERIC_ARRAY_cast