~vcs-imports/gawk/master

356 by Arnold D. Robbins
Move old extension stuff into a separate directory.
1
/*
2
 * spec_array.c - Support for specialized associative arrays.
3
 */
4
5
/*
408.5.25 by Arnold D. Robbins
Update some copyright years.
6
 * Copyright (C) 2012, 2014 the Free Software Foundation, Inc.
356 by Arnold D. Robbins
Move old extension stuff into a separate directory.
7
 * 
8
 * This file is part of GAWK, the GNU implementation of the
9
 * AWK Programming Language.
10
 * 
11
 * GAWK is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation; either version 3 of the License, or
14
 * (at your option) any later version.
15
 * 
16
 * GAWK is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 * 
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program; if not, write to the Free Software
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
24
 */
25
26
#include "awk.h"
27
#include "spec_array.h"
28
29
typedef struct spec_array {
30
	Fetch_func_t fetch_func;
31
	Store_func_t store_func;
32
	Load_func_t load_func;
33
	void *data;
34
} array_t;
35
36
/*
37
 * The array_t structure is attached to the array itself without
38
 * the necessity to maintain a list of symbols; this works only
39
 * because there is just enough free space in the NODE strcture when
40
 * the base array is str_array.
41
 */
42
408.4.107 by Arnold D. Robbins
Make old extensions compile and work under Linux.
43
#define	SUPER(F)	(*str_array_func[F ## _ind])
356 by Arnold D. Robbins
Move old extension stuff into a separate directory.
44
45
46
/*
47
 * deferred_array --- Deferred loading of array at run-time.
48
 *
49
 * The load routine takes two arguments, the array and
50
 * a void * data:
51
 *
52
 *	void load_func(NODE *array, void *data)
53
 *
54
 * Use register_deferred_array(array, load_func, void *data) to
55
 * bind an array to the load routine. 
56
 */
57
58
static NODE **deferred_array_init(NODE *, NODE *);
59
static NODE **deferred_array_lookup(NODE *, NODE *);
60
static NODE **deferred_array_exists(NODE *, NODE *);
61
static NODE **deferred_array_remove(NODE *, NODE *);
62
static NODE **deferred_array_clear(NODE *, NODE *);
63
static NODE **deferred_array_list(NODE *, NODE *);
64
static NODE **deferred_array_copy(NODE *, NODE *);
65
static NODE **deferred_array_length(NODE *, NODE *);
66
67
static afunc_t deferred_array_func[] = {
68
	deferred_array_init,
69
	(afunc_t) 0,	/* typeof */
70
	deferred_array_length,
71
	deferred_array_lookup,
72
	deferred_array_exists,
73
	deferred_array_clear,
74
	deferred_array_remove,
75
	deferred_array_list, 
76
	deferred_array_copy,
77
	null_afunc,	/* dump */
78
	(afunc_t) 0,	/* store */
79
};
80
81
82
/* deferred_array_init --- called when array becomes empty, e.g: delete BOUND_ARRAY */
83
84
static NODE **
85
deferred_array_init(NODE *symbol, NODE *subs)
86
{
87
	if (symbol != NULL) {
88
		array_t *av = (array_t *) symbol->xarray;
89
		symbol->xarray = NULL;	/* this is to avoid an assertion failure in null_array */ 
90
		null_array(symbol);	/* typeless empty array */
91
		if (symbol->parent_array == NULL) {
92
			/* main array */
93
			symbol->array_funcs = deferred_array_func;	/* restore type */
94
			symbol->xarray = (NODE *) av;
95
		} else if (av)	/* sub-array */
96
			efree(av);
97
	}
98
	return NULL;
99
}
100
101
/* deferred_array_length --- get the length of the array */
102
103
static NODE **
104
deferred_array_length(NODE *symbol, NODE *subs)
105
{
106
	static NODE *length_node;
107
	array_t *av = (array_t *) symbol->xarray;
108
	if (av) {
109
		symbol->xarray = NULL;
110
		(*av->load_func)(symbol, av->data);
111
		symbol->xarray = (NODE *) av;
112
	}
113
	length_node = symbol;
114
	return & length_node;
115
}
116
117
#define DEF_ARR(F) static NODE **			\
118
deferred_array_##F(NODE *symbol, NODE *subs)		\
119
{							\
120
	array_t *av = (array_t *) symbol->xarray;	\
121
	if (av) {					\
122
		symbol->xarray = NULL;			\
123
		(*av->load_func)(symbol, av->data);	\
124
		symbol->xarray = (NODE *) av;		\
125
	}						\
126
	return SUPER(a##F)(symbol, subs);		\
127
}
128
129
/* the rest of the routines */
130
131
DEF_ARR(exists)
132
DEF_ARR(lookup)
133
DEF_ARR(list)
134
DEF_ARR(copy)
135
136
#undef DEF_ARR
137
138
/* deferred_array_remove --- remove the index from the array */
139
140
static NODE **
141
deferred_array_remove(NODE *symbol, NODE *subs)
142
{
143
	array_t *av = (array_t *) symbol->xarray;
144
	
145
	(void) SUPER(aremove)(symbol, subs);
146
	if (av) {
147
		symbol->xarray = NULL;
148
		(*av->load_func)(symbol, av->data);
149
		symbol->xarray = (NODE *) av;
150
	}
151
	return NULL;
152
}
153
154
/* deferred_array_clear --- flush all the values in symbol[] */
155
156
static NODE **
157
deferred_array_clear(NODE *symbol, NODE *subs)
158
{
159
	array_t *av = (array_t *) symbol->xarray;
160
	
161
	(void) SUPER(aclear)(symbol, subs);
162
	if (av) {
163
		symbol->xarray = NULL;
164
		(*av->load_func)(symbol, av->data);
165
		symbol->xarray = (NODE *) av;
166
	}
167
	return NULL;
168
}
169
170
171
/*
172
 * dyn_array --- array with triggers for reading and writing
173
 * 	an element.
174
 *
175
 * The fetch routine should expect three arguments, the array,
176
 * the subscript and optional void * data. It should return the value
177
 * if it exists or NULL otherwise.
178
 *
179
 * 	NODE *fetch_func(NODE *array, NODE *subs, void *data)
180
 *
181
 * The store routine must take an additional argument for the
182
 * value. The value can be NULL if the specific element is
183
 * removed from the array. The subscript (and the value) is NULL
184
 * when the entire array is deleted. 
185
 *
186
 * 	void store_func(NODE *array, NODE *subs, NODE *value, void *data)
187
 *
188
 * Use register_dyn_array(array, fetch_func, store_func, void *data) to
189
 * bind an array to the fetch/store routine.
190
 */
191
192
193
static NODE **dyn_array_init(NODE *, NODE *);
194
static NODE **dyn_array_lookup(NODE *, NODE *);
195
static NODE **dyn_array_exists(NODE *, NODE *);
196
static NODE **dyn_array_remove(NODE *, NODE *);
197
static NODE **dyn_array_clear(NODE *, NODE *);
198
static NODE **dyn_array_list(NODE *, NODE *);
199
static NODE **dyn_array_copy(NODE *, NODE *);
200
static NODE **dyn_array_store(NODE *, NODE *);
201
202
static afunc_t dyn_array_func[] = {
203
	dyn_array_init,
204
	(afunc_t) 0,	/* typeof */
205
	null_length,	/* length */
206
	dyn_array_lookup,
207
	dyn_array_exists,
208
	dyn_array_clear,
209
	dyn_array_remove,
210
	dyn_array_list, 
211
	dyn_array_copy,
212
	null_afunc,	/* dump */
213
	dyn_array_store,
214
};
215
216
/* dyn_array_init --- called when array becomes empty */
217
218
static NODE **
219
dyn_array_init(NODE *symbol, NODE *subs)
220
{
221
	if (symbol != NULL) {
222
		array_t *av = (array_t *) symbol->xarray;
223
		symbol->xarray = NULL;
224
		null_array(symbol);	/* typeless empty array */
225
		if (symbol->parent_array == NULL) {
226
			/* main array */
227
			symbol->array_funcs = dyn_array_func;	/* restore type */
228
			symbol->xarray = (NODE *) av;
229
		} else if (av)	/* sub-array */
230
			efree(av);
231
	}
232
	return NULL;
233
}
234
235
/* dyn_array_exists --- check if the SUBS exists */
236
237
static NODE **
238
dyn_array_exists(NODE *symbol, NODE *subs)
239
{
240
	NODE *r;
241
	array_t *av = (array_t *) symbol->xarray;
242
243
	if (av && av->fetch_func) {
244
		symbol->xarray = NULL;
245
		r = (*av->fetch_func)(symbol, subs, av->data);
246
		symbol->xarray = (NODE *) av;
247
		if (r != NULL) {
248
			NODE **lhs;
249
			lhs = SUPER(alookup)(symbol, subs);
250
			unref(*lhs);
251
			*lhs = r;
252
			return lhs;
253
		}
254
	}
255
256
	return SUPER(aexists)(symbol, subs);
257
}
258
259
/* dyn_array_lookup --- lookup SUBS and return a pointer to store its value */
260
261
static NODE **
262
dyn_array_lookup(NODE *symbol, NODE *subs)
263
{
264
	NODE **lhs;
265
	NODE *r;
266
	array_t *av = (array_t *) symbol->xarray;
267
268
	lhs = SUPER(alookup)(symbol, subs);
269
	if (av && av->fetch_func) {
270
		symbol->xarray = NULL;
271
		r = (*av->fetch_func)(symbol, subs, av->data);
272
		symbol->xarray = (NODE *) av;
273
		if (r != NULL) {
274
			unref(*lhs);
275
			*lhs = r;
276
		}
277
	}
278
	return lhs;
279
}
280
281
/* dyn_array_store --- call the store routine after an assignment */
282
283
static NODE **
284
dyn_array_store(NODE *symbol, NODE *subs)
285
{
286
	array_t *av = (array_t *) symbol->xarray;
287
288
	if (av && av->store_func) {
289
		NODE **lhs;
290
		lhs = SUPER(aexists)(symbol, subs);
291
		symbol->xarray = NULL;
292
		(*av->store_func)(symbol, subs, *lhs, av->data);
293
		symbol->xarray = (NODE *) av;
294
	}
295
	return NULL;
296
}
297
298
/* dyn_array_remove --- remove the index from the array */
299
300
static NODE **
301
dyn_array_remove(NODE *symbol, NODE *subs)
302
{
303
	array_t *av = (array_t *) symbol->xarray;
304
305
	(void) SUPER(aremove)(symbol, subs);
306
	if (av && av->store_func) {
307
		symbol->xarray = NULL;
308
		(*av->store_func)(symbol, subs, NULL, av->data);
309
		symbol->xarray = (NODE *) av;
310
	}
311
	return NULL;
312
}
313
314
/* dyn_array_clear --- flush all the values in symbol[] */
315
316
static NODE **
317
dyn_array_clear(NODE *symbol, NODE *subs)
318
{
319
	array_t *av = (array_t *) symbol->xarray;
320
321
	(void) SUPER(aclear)(symbol, subs);
322
	if (av && av->store_func) {
323
		symbol->xarray = NULL;
324
		(*av->store_func)(symbol, NULL, NULL, av->data);
325
		symbol->xarray = (NODE *) av;
326
	}
327
	return NULL;
328
}
329
330
/* dyn_array_list --- return a list of items in symbol[] */
331
332
static NODE **
333
dyn_array_list(NODE *symbol, NODE *subs)
334
{
335
	return SUPER(alist)(symbol, subs);
336
}
337
338
/* dyn_array_copy --- duplicate the array */
339
340
static NODE **
341
dyn_array_copy(NODE *symbol, NODE *subs)
342
{
343
	return SUPER(acopy)(symbol, subs);
344
}
345
346
/* register_array_s --- attach the specified routine(s) to an array */
347
348
static void
349
register_array_s(NODE *symbol, Fetch_func_t fetch_func,
350
			Store_func_t store_func, Load_func_t load_func, void *data)
351
{
352
	array_t *av;
353
354
	if (symbol->type != Node_var_array)
355
		fatal(_("register_array_s: argument is not an array"));
356
357
	if (symbol->array_funcs == deferred_array_func
358
			|| symbol->array_funcs == dyn_array_func)
359
		fatal(_("register_array_s: `%s' already is a deferred/dyn array"),
360
			array_vname(symbol));
361
362
	assoc_clear(symbol);
363
	assert(symbol->xarray == NULL);
364
	emalloc(av, array_t *, sizeof (array_t), "register_spec_array");
365
	av->fetch_func = fetch_func;
366
	av->store_func = store_func;
367
	av->load_func = load_func;
368
	av->data = data;
369
	symbol->xarray = (NODE *) av;
370
}
371
372
/* register_deferred_array --- make the array to be loaded at run-time */
373
374
void
375
register_deferred_array(NODE *symbol, Load_func_t load_func, void *dq)
376
{
377
	if (! load_func)
378
		fatal(_("register_deferred_array: null load function"));
379
	register_array_s(symbol, 0, 0, load_func, dq);
380
	symbol->array_funcs = deferred_array_func;
381
}
382
383
/* register_dyn_array --- attach read and write triggers to an array */
384
385
void
386
register_dyn_array(NODE *symbol, Fetch_func_t fetch_func,
387
		Store_func_t store_func, void *dq)
388
{
389
	register_array_s(symbol, fetch_func, store_func, 0, dq);
390
	symbol->array_funcs = dyn_array_func;
391
}
392
393
/* unregister_array_s --- un-special the array */
394
395
void *
396
unregister_array_s(NODE *symbol)
397
{
398
	void *data = NULL;
399
	if (symbol->type != Node_var_array)
400
		fatal(_("unregister_array_s: argument is not an array"));
401
402
	if (symbol->array_funcs == dyn_array_func
403
		|| symbol->array_funcs == deferred_array_func
404
	) {
405
		array_t *av;
406
407
		av = (array_t *) symbol->xarray;
408
		assert(av != NULL);
409
		data = av->data;
410
		efree(av);
411
		symbol->array_funcs = str_array_func;
412
		symbol->xarray = NULL;
413
		/* FIXME: do we assoc_clear the array ? */
414
	}
415
	return data;
416
}