controller_load.c 19 KB
Newer Older
Jeroen Vreeken's avatar
Jeroen Vreeken committed
1
/*
Daan Vreeken's avatar
Daan Vreeken committed
2
3
	Copyright (c) 2015,
		Daan Vreeken <Daan @ Vitsch . nl> - Vitsch Electronics
Jeroen Vreeken's avatar
Jeroen Vreeken committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2009
	Copyright Stichting C.A. Muller Radioastronomiestation, 2009

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

 */

#include <stdio.h>
#include <string.h>
24
#include <stdlib.h>
Jeroen Vreeken's avatar
Jeroen Vreeken committed
25
#include <stdbool.h>
26
27
28
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
29

30
#include <controller/controller_load_int.h>
Jeroen Vreeken's avatar
Jeroen Vreeken committed
31
32
33
34
35
#include <controller/controller_load_parser.tab.h>
#include <controller/controller_load_parser.yy.h>
#include <controller/controller_load.h>
#include <controller/controller_sample.h>
#include <controller/controller_block.h>
36
#include <controller/controller_module.h>
Jeroen Vreeken's avatar
Jeroen Vreeken committed
37
#include <dynarg.h>
38
#include <limits.h>
39

Jeroen Vreeken's avatar
Jeroen Vreeken committed
40
#include <shell/shell.h>
Jeroen Vreeken's avatar
Jeroen Vreeken committed
41
#include <log/log.h>
Jeroen Vreeken's avatar
Jeroen Vreeken committed
42

43
44
45
46
struct controller_load_array {
	void *array;
	size_t size;
};
Jeroen Vreeken's avatar
Jeroen Vreeken committed
47

Jeroen Vreeken's avatar
Jeroen Vreeken committed
48

49
50
51
52
struct controller_load_extra {
	char *filename;
	int ret;
	bool va_list_start;
53
	int va_list_argc;
54
	va_list va_list;
55
	char **va_type_list;
56
57
58
59
60
61
62
	struct controller_load_va_entry *va_entries;
	struct controller_load_array *array;
	bool use_string;
	char *string;
	size_t stringlen;
	FILE *input_file;
};
Jeroen Vreeken's avatar
Jeroen Vreeken committed
63

64

65
66
67
static int controller_load_one_file(char *filename);


Jeroen Vreeken's avatar
Jeroen Vreeken committed
68
void yyerror(yyscan_t scanner, char const *s)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
69
{
70
71
	struct controller_load_extra *extra = yyget_extra(scanner);
	
72
	log_send(LOG_T_ERROR, "%s:%d: %s", 
73
	    extra->filename,
Jeroen Vreeken's avatar
Jeroen Vreeken committed
74
75
	    yyget_lineno(scanner),
	    s);
76
	extra->ret = -1;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
77
78
}

79
80
81
82
83
84
void controller_load_fatal_error (char* msg , yyscan_t yyscanner)
{
    	log_send(LOG_T_ERROR, "Fatal: %s", msg );
	log_server_flush();
	exit(1);
}
Jeroen Vreeken's avatar
Jeroen Vreeken committed
85

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
static int controller_load_add_va_type(yyscan_t scanner, char *type)
{
	struct controller_load_extra *extra = yyget_extra(scanner);
	char **tmp;
	
	tmp = realloc(extra->va_type_list, sizeof(char *)*(extra->va_list_argc+1));
	if (!tmp)
		return -1;
	extra->va_type_list = tmp;
	tmp[extra->va_list_argc] = type;
	extra->va_list_argc++;
	
	return 0;
}

Jeroen Vreeken's avatar
Jeroen Vreeken committed
101
102
103
104
105
106
struct controller_load_va_entry {
	struct controller_load_va_entry *next;
	void *ptr;
};


107
void controller_load_var_add_str(char *string, yyscan_t scanner)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
108
{
109
110
	struct controller_load_extra *extra = yyget_extra(scanner);

Jeroen Vreeken's avatar
Jeroen Vreeken committed
111
112
	struct controller_load_va_entry **entryp;
	
113
114
115
	if (!extra->va_list_start) {
		va_new(extra->va_list);
		extra->va_list_start = true;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
116
117
	}
	
118
	va_add(extra->va_list, char *, string);
119
	controller_load_add_va_type(scanner, "char*");
Jeroen Vreeken's avatar
Jeroen Vreeken committed
120
	
121
	for (entryp = &extra->va_entries; *entryp; entryp = &(*entryp)->next);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
122
123
124
125
126
127
	
	*entryp = malloc(sizeof(struct controller_load_va_entry));
	(*entryp)->next = NULL;
	(*entryp)->ptr = string;
}

128
void controller_load_var_add_dbl(double dbl, yyscan_t scanner)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
129
{
130
131
132
133
134
	struct controller_load_extra *extra = yyget_extra(scanner);

	if (!extra->va_list_start) {
		va_new(extra->va_list);
		extra->va_list_start = true;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
135
136
	}
	
137
	va_add(extra->va_list, double, dbl);
138
	controller_load_add_va_type(scanner, "double");
Jeroen Vreeken's avatar
Jeroen Vreeken committed
139
140
}

141
void controller_load_var_add_flt(float flt, yyscan_t scanner)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
142
{
143
144
145
146
147
148
149
150
	struct controller_load_extra *extra = yyget_extra(scanner);

	if (extra->array) {
		extra->array->array = realloc(
		    extra->array->array,
		    extra->array->size + sizeof(float));
		*(float*)(extra->array->array + extra->array->size) = flt;
		extra->array->size += sizeof(float);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
151
152
	} else {
		/* float is promoted to double in a variadic function */
153
		controller_load_var_add_dbl(flt, scanner);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
154
155
156
	}
}

157
void controller_load_var_add_float_array_start(yyscan_t scanner)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
158
{
159
160
161
162
163
	struct controller_load_extra *extra = yyget_extra(scanner);

	extra->array = malloc(sizeof(struct controller_load_array));
	extra->array->array = NULL;
	extra->array->size = 0;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
164
165
}

166
void controller_load_var_add_float_array_end(yyscan_t scanner)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
167
{
168
	struct controller_load_extra *extra = yyget_extra(scanner);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
169
170
	struct controller_load_va_entry **entryp;
	
171
172
173
	if (!extra->va_list_start) {
		va_new(extra->va_list);
		extra->va_list_start = true;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
174
175
	}
	
176
	va_add(extra->va_list, float *, extra->array->array);
177
	controller_load_add_va_type(scanner, "float*");
Jeroen Vreeken's avatar
Jeroen Vreeken committed
178
	
179
	for (entryp = &extra->va_entries; *entryp; entryp = &(*entryp)->next);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
180
181
182
	
	*entryp = malloc(sizeof(struct controller_load_va_entry));
	(*entryp)->next = NULL;
183
	(*entryp)->ptr = extra->array->array;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
184
	
185
186
	free(extra->array);
	extra->array = NULL;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
187
188
}

Jeroen Vreeken's avatar
Jeroen Vreeken committed
189
190
191
192
193
194
195
196
197
198
void controller_load_var_add_int_array_start(yyscan_t scanner)
{
	struct controller_load_extra *extra = yyget_extra(scanner);

	extra->array = malloc(sizeof(struct controller_load_array));
	extra->array->array = NULL;
	extra->array->size = 0;
}

void controller_load_var_add_int_array_end(yyscan_t scanner)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
199
{
200
	struct controller_load_extra *extra = yyget_extra(scanner);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
201
202
	struct controller_load_va_entry **entryp;
	
203
204
205
	if (!extra->va_list_start) {
		va_new(extra->va_list);
		extra->va_list_start = true;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
206
207
	}
	
Jeroen Vreeken's avatar
Jeroen Vreeken committed
208
	va_add(extra->va_list, int *, extra->array->array);
209
	controller_load_add_va_type(scanner, "int*");
Jeroen Vreeken's avatar
Jeroen Vreeken committed
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
	
	for (entryp = &extra->va_entries; *entryp; entryp = &(*entryp)->next);
	
	*entryp = malloc(sizeof(struct controller_load_va_entry));
	(*entryp)->next = NULL;
	(*entryp)->ptr = extra->array->array;
	
	free(extra->array);
	extra->array = NULL;
}

void controller_load_var_add_int(int i, yyscan_t scanner)
{
	struct controller_load_extra *extra = yyget_extra(scanner);

	if (extra->array) {
		extra->array->array = realloc(
		    extra->array->array,
		    extra->array->size + sizeof(int));
		*(int*)(extra->array->array + extra->array->size) = i;
		extra->array->size += sizeof(int);
	} else {
		if (!extra->va_list_start) {
			va_new(extra->va_list);
			extra->va_list_start = true;
		}
	
		va_add(extra->va_list, int, i);
238
		controller_load_add_va_type(scanner, "int");
Jeroen Vreeken's avatar
Jeroen Vreeken committed
239
	}
Jeroen Vreeken's avatar
Jeroen Vreeken committed
240
241
}

242
void controller_load_var_add_ul(unsigned long ul, yyscan_t scanner)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
243
{
244
245
246
247
	struct controller_load_extra *extra = yyget_extra(scanner);
	if (!extra->va_list_start) {
		va_new(extra->va_list);
		extra->va_list_start = true;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
248
249
	}
	
250
	va_add(extra->va_list, unsigned long, ul);
251
	controller_load_add_va_type(scanner, "unsigned long");
Jeroen Vreeken's avatar
Jeroen Vreeken committed
252
253
}

254
void controller_load_var_clear(yyscan_t scanner)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
255
{
256
	struct controller_load_extra *extra = yyget_extra(scanner);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
257
258
	struct controller_load_va_entry *entry, *next;
	
259
	for (entry = extra->va_entries; entry; entry = next) {
Jeroen Vreeken's avatar
Jeroen Vreeken committed
260
261
262
263
264
		if (entry->ptr)
			free(entry->ptr);
		next = entry->next;
		free(entry);
	}
265
	extra->va_entries = NULL;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
266
	
267
268
	if (extra->va_list_start) {
		va_free(extra->va_list);
269
270
		free(extra->va_type_list);
		extra->va_type_list = NULL;
271
		extra->va_list_start = false;
272
		extra->va_list_argc = 0;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
273
274
275
	}
}

276
277
278
279
int controller_load_trigger(char *name, yyscan_t scanner)
{
	struct controller_load_extra *extra = yyget_extra(scanner);

280
	return controller_sample_trigger_create(name, extra->va_list_argc,
281
	    extra->va_list, extra->va_type_list);
282
283
}

284
285
286
287
288
289
290
291
static char *rel2file(yyscan_t scanner, char *rel_name)
{
	struct controller_load_extra *extra = yyget_extra(scanner);
	struct stat stattmp;
	char *ret;
	char *tmp;
	char *slash;

292
	if ((stat(rel_name, &stattmp) == 0) && (!(stattmp.st_mode & S_IFDIR)))
293
294
295
296
297
298
		return strdup(rel_name);
	
	if (!extra->filename)
		return NULL;

	tmp = strdup(extra->filename);
299
300
301
302
303
304
	slash = strrchr(tmp, '/');
	if (slash == NULL) {
		free(tmp);
		return NULL;
	}
	*slash = '\0';
305
306
307
	asprintf(&ret, "%s/%s", tmp, rel_name);
	free(tmp);

308
	if ((stat(ret, &stattmp) == 0) && (!(stattmp.st_mode & S_IFDIR)))
309
310
311
312
313
314
315
		return ret;

	free(ret);
	return NULL;
}


316
317
318
int controller_load_block_create(char *type, char *name, yyscan_t scanner)
{
	struct controller_load_extra *extra = yyget_extra(scanner);
319
320
321
	int ret;
	char *prefname = controller_module_prefix_block(name);

322
	ret = controller_block_create(type, prefname, extra->va_list_argc, 
323
	    extra->va_list, extra->va_type_list);
324
325
326
327
328
329
330
331
332

	if (!ret)
		return 0;
	
	
	char *file_name = rel2file(scanner, type);

	if (!file_name) {
		log_send(LOG_T_ERROR, 
333
		    "Could not load or create block '%s'", type);
334
335
336
337
338
339
		return -1;
	}
	
	log_send(LOG_T_DEBUG, "Load module '%s' with prefix '%s'", 
	    file_name, prefname);
	controller_module_push(prefname);
340
	ret = controller_load_one_file(file_name);
341
342
343
344
	controller_module_pop();
	log_send(LOG_T_DEBUG, "End of module '%s': %d", file_name, ret);

	free(prefname);
345

346
	return ret;
347
}
Jeroen Vreeken's avatar
Jeroen Vreeken committed
348

349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
static char *get_next_type(char **str)
{
	char *comma, *tmp;
	int comma_idx;

	if (**str == '\0')
		// No more arguments left.
		return NULL;

	// This function returns a copy of the first part of a comma separated
	// string, upto the comma. The user is expected to free() the returned
	// string. When a comma is found, the '*str' pointer is advanced to
	// the first character following the comma.
	comma = index(*str, ',');
	if (comma == NULL) {
		// This is the last argument. Advance '*str' to \0 character.
		tmp = *str;
		*str += strlen(tmp);
		return strdup(tmp);
	}
	
	// More arguments to come. Return this argument and advance '*str'.
	comma_idx = comma - *str;
	tmp = *str;
	*str += comma_idx + 1;
	return strndup(tmp, comma_idx);
}

377
static int check_one_argument_type_list(char *expected_types,
Daan Vreeken's avatar
Daan Vreeken committed
378
    int supplied_argc, char **supplied_types, bool show_errs)
379
380
381
{
	char *supplied_type, *expected_type, *iter;
	int idx;
382

383
	//log_send(LOG_T_DEBUG, "check '%s'", expected_types);
384
385
386
387
388
	if (strcmp(expected_types, "*") == 0) {
		// It's the "we'll accept anything" wildcard. We're
		// done.
		return 0;
	}
389
390
391
	
	// Walk through the supplied va_list, filled with strings that
	// describe the argument types and compare them to the expected types.
392
	iter = expected_types;
393
	for (idx = 0; idx < supplied_argc; idx++) {
394
		supplied_type = supplied_types[idx];
395
		expected_type = get_next_type(&iter);
396
		//log_send(LOG_T_DEBUG, "sup: '%s', idx:%d, exp: '%s'", supplied_type, idx, expected_type);
397

398
399
		if (expected_type == NULL) {
			// Too many arguments supplied.
Daan Vreeken's avatar
Daan Vreeken committed
400
401
402
403
404
			if (show_errs)
				log_send(LOG_T_ERROR, "Invalid number of "
				    "arguments! Expecting %d argument%s "
				    "instead of %d.", idx,
				    (idx > 1) ? "s" : "", supplied_argc);
405
406
407
			return -1;
		}
		
408
409
410
411
412
413
414
		if (strcmp(expected_type, "*") == 0) {
			// It's the "we'll accept anything" wildcard. We're
			// done.
			free(expected_type);
			return 0;
		}
		
415
		if (strcmp(supplied_type, expected_type) != 0) {
Daan Vreeken's avatar
Daan Vreeken committed
416
417
418
419
420
			if (show_errs)
				log_send(LOG_T_ERROR, "Argument mismatch in "
				    "argument %d. Expected argument of type "
				    "'%s' instead of '%s'", idx + 1,
				    expected_type, supplied_type);
421
422
423
424
425
426
			free(expected_type);
			return -1;
		}
		free(expected_type);
	}
	if (*iter != '\0') {
Daan Vreeken's avatar
Daan Vreeken committed
427
428
429
430
431
432
433
434
435
		// Not enough arguments.
		if (show_errs) {
			// See how many are missing..
			while (expected_type != NULL) {
				expected_type = get_next_type(&iter);
				if (expected_type != NULL)
					free(expected_type);
				idx++;
			}
Jeroen Vreeken's avatar
Jeroen Vreeken committed
436
			idx--;
Daan Vreeken's avatar
Daan Vreeken committed
437
438
439
440
			log_send(LOG_T_ERROR, "Invalid number of arguments! "
			    "Expecting %d argument%s instead of %d.", idx,
			    (idx > 1) ? "s" : "", supplied_argc);
		}
441
442
443
444
		return -1;
	}
	
	return 0;
445
446
}

447
448
int controller_load_check_arg(char **expected_types, int supplied_argc,
    char **supplied_types)
449
450
{
	char **first;
451
	char *str;
Daan Vreeken's avatar
Daan Vreeken committed
452
	bool show_errs;
453
454
	int ret;

455
456
457
458
459
460
461
462
463
	if (expected_types[0] == NULL) {
		// This function doesn't expect arguments.
		if (supplied_argc > 0) {
			log_send(LOG_T_ERROR, "Too many arguments supplied! "
			    "This function doesn't accept arguments.");
			return -1;
		}
		return 0;
	}
464

Daan Vreeken's avatar
Daan Vreeken committed
465
466
	show_errs = (expected_types[1] == NULL);

467
468
469
	first = expected_types;
	while (*expected_types != NULL) {
		ret = check_one_argument_type_list(*expected_types,
Daan Vreeken's avatar
Daan Vreeken committed
470
		    supplied_argc, supplied_types, show_errs);
471
472
473
474
475
476
477
478
479
480
481
		if (ret == 0)
			// Found a match. We're done.
			return 0;
		
		expected_types++;
	}
	
	// If we make it to this point, none of the expected argument type
	// lists matched, so the supplied argument types are invalid (or
	// there are too many / not enough of them).
	// List the possible options to the user to help out the best we can..
482
	log_send(LOG_T_ERROR, 
483
484
	    "Invalid arguments! This function accepts %sthe following:",
	    (expected_types[1] != NULL) ? "any of " : "");
485
	expected_types = first;
486
487
488
489
490
	while (*expected_types != NULL) {
		str = *expected_types++;
		log_send(LOG_T_ERROR, "\t%s", (*str != '\0') ?
		    str : "(no arguments)");
	}
491
492
493
494
495
496
497
	if (supplied_argc) {
		log_send(LOG_T_ERROR, "The given arguments are:");
		int i;
		for (i = 0; i < supplied_argc; i++) {
			log_send(LOG_T_ERROR, "\t%s", supplied_types[i]);
		}
	}
498
499

	return -1;
500
501
}

502
int controller_load_block_param_set(char *block, char *param, yyscan_t scanner)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
503
{
504
	struct controller_load_extra *extra = yyget_extra(scanner);
505
506
507
508
	int ret;
	char *prefblock;
	
	prefblock = controller_module_prefix_block(block);
509

510
	ret = controller_block_param_set(prefblock, param, 
511
	    extra->va_list_argc, extra->va_list, extra->va_type_list);
512
513
514
	free(prefblock);
	
	return ret;
515
516
}

517
int controller_load_include(yyscan_t scanner, char *rel_name)
518
519
{
	int r;
520
521
	char *file_name = rel2file(scanner, rel_name);

Jeroen Vreeken's avatar
Typo    
Jeroen Vreeken committed
522
	log_send(LOG_T_DEBUG, "Include '%s' (%s)", file_name, rel_name);
523
524
525
526
527

	if (!file_name) {
		log_send(LOG_T_ERROR, "Could not find file");
		return -1;
	}
528

529
	log_send(LOG_T_DEBUG, "Include '%s'", file_name);
530
	r = controller_load_one_file(file_name);
531
	log_send(LOG_T_DEBUG, "End of include '%s': %d", file_name, r);
532

533
534
	free(rel_name);

535
	return r;
536
537
}

538
539
540
static int controller_load_import_nr;
static char **controller_load_imports;

541
int controller_load_import(yyscan_t scanner, char *rel_name)
542
543
{
	int r, i;
544
	char *file_name = rel2file(scanner, rel_name);
545
546
	char **imports;

547
548
549
550
551
552
553
554
	log_send(LOG_T_DEBUG, "Import '%s' (%s)", file_name, rel_name);

	if (!file_name) {
		log_send(LOG_T_ERROR, "Could not find file");
		return -1;
	}

	char *realname = realpath(file_name, NULL);
555
556
557

	if (!realname) {
		log_send(LOG_T_ERROR, "Could not find realpath");
558
		free(file_name);
559
560
561
562
563
564
565
566
567
		return -1;
	}

	for (i = 0; i < controller_load_import_nr; i++) {
		if (!strcmp(realname, controller_load_imports[i])) {
			log_send(LOG_T_DEBUG, 
			    "'%s' ('%s') has already been imported",
			    file_name, realname);
			free(realname);
568
			free(file_name);
569
570
571
572
573
574
575
576
577

			return 0;
		}
	}
	imports = realloc(controller_load_imports,
	    (controller_load_import_nr + 1) * sizeof(char *));
	if (!imports) {
		log_send(LOG_T_ERROR, "Could not allocate memory for import administration");
		free(realname);
578
		free(file_name);
579
580
581
582
583
584
585
		
		return -1;
	}
	imports[controller_load_import_nr] = realname;
	controller_load_imports = imports;
	controller_load_import_nr++;

586
	r = controller_load_one_file(file_name);
587
588
	log_send(LOG_T_DEBUG, "End of import '%s': %d", file_name, r);

589
590
	free(file_name);

591
592
	return r;
}
593

594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
int controller_load_block_connect(char *outblock, char *outterm,
    char *inblock, char *interm, int chain)
{
	int ret;
	char *prefinblock, *prefoutblock;
	
	prefinblock = controller_module_prefix_block(inblock);
	prefoutblock = controller_module_prefix_block(outblock);
	
	ret = controller_block_connect(
	    prefoutblock, outterm, prefinblock, interm, chain);
	
	free(prefinblock);
	free(prefoutblock);
	
	return ret;
}

612
int controller_load_yy_input(char *buf, size_t *readbytes, size_t sizebytes, yyscan_t scanner)
613
614
615
616
617
{
	struct controller_load_extra *extra = yyget_extra(scanner);

	if (!extra->use_string) {
		*readbytes = fread(buf, 1, sizebytes, extra->input_file);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
618
619
620
621
	} else {
		int copybytes;
		
		copybytes = sizebytes;
622
623
		if (copybytes > extra->stringlen)
			copybytes = extra->stringlen;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
624
		*readbytes = copybytes;
625
626
627
		memcpy(buf, extra->string, copybytes);
		extra->string += copybytes;
		extra->stringlen -= copybytes;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
628
629
630
631
632
633
634
635
636
	}
	
	return 0;
}


static int controller_load_shell(char *args, char *out, int *outlen)
{
	int ret;
637
	struct controller_load_extra extra;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
638
	char *safe_context;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
639
	yyscan_t scanner;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
640

Jeroen Vreeken's avatar
Jeroen Vreeken committed
641
642
643
644
645
646
	if (!args) {
		*outlen = sprintf(out,
		     "no arguments\n");
		return *outlen;
	}

647
	extra.filename = "shell";
648
649
650
	extra.ret = 0;
	extra.va_entries = NULL;
	extra.va_list_start = false;
651
	extra.va_list_argc = 0;
652
	extra.va_type_list = NULL;
653
654
655
656
	extra.array = NULL;
	extra.use_string = true;
	extra.stringlen = strlen(args);
	extra.string = args;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
657

658
	log_send(LOG_T_DEBUG, "parsing: %s", args);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
659

660
	yylex_init_extra(&extra, &scanner);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
661

Jeroen Vreeken's avatar
Jeroen Vreeken committed
662
663
664
	safe_context = controller_block_context_get();
	controller_block_context_set("shell");

Jeroen Vreeken's avatar
Jeroen Vreeken committed
665
	ret = yyparse(scanner);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
666

Jeroen Vreeken's avatar
Jeroen Vreeken committed
667
668
	controller_block_context_set(safe_context);

Jeroen Vreeken's avatar
Jeroen Vreeken committed
669
670
	yylex_destroy(scanner);
	
Jeroen Vreeken's avatar
Jeroen Vreeken committed
671
672
673
674
675
676
677
678
679
680
681
	if (ret) {
		*outlen = sprintf(out,
		     "failed to parse arguments\n");
	} else {
		*outlen = sprintf(out,
		     "parsed arguments\n");
	}
	
	return *outlen;
}

682
683
684
685
686
687
688
689
690
691
static int controller_load_file_shell(char *args, char *out, int *outlen)
{
	int ret;

	if (!args) {
		*outlen = sprintf(out,
		     "no arguments\n");
		return *outlen;
	}

692
	log_send(LOG_T_DEBUG, "loading: %s", args);
693

694
	ret = controller_load_one_file(args);
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
	
	if (ret) {
		*outlen = sprintf(out,
		     "failed to load file\n");
	} else {
		*outlen = sprintf(out,
		     "parsed file\n");
	}
	
	return *outlen;
}

static struct shell_cmd controller_cmd[] = {
	{ "controller", 
	  "parse arguments as controller commands",
	  controller_load_shell
	},
	{ "controller_file", 
	  "load controller commands from file",
	  controller_load_file_shell
	}
Jeroen Vreeken's avatar
Jeroen Vreeken committed
716
717
718
719
};

int controller_load_shell_add(void)
{
720
721
722
723
724
	int ret = 0, i;
	
	for (i = 0; i < sizeof(controller_cmd)/sizeof(struct shell_cmd); i++) {
		ret |= shell_cmd_add(&controller_cmd[i]);
	}
Jeroen Vreeken's avatar
Jeroen Vreeken committed
725
726
727
728
	
	return ret;
}

729
730
731
732
733
// This routine is called (possibly recursively) to load a single file.
// Linking all inputs/outputs is allowed to fail here, since there might be
// links that go to/come from blocks that will be instantiated in other files
// later on.
static int controller_load_one_file(char *filename)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
734
{
735
	struct controller_load_extra extra;
736
	char *saved_context;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
737
	yyscan_t scanner;
738
	int ret;
739
740
741
742
743
	
	extra.filename = filename;
	extra.ret = 0;
	extra.va_entries = NULL;
	extra.va_list_start = false;
744
	extra.va_list_argc = 0;
745
	extra.va_type_list = NULL;
746
747
748
	extra.array = NULL;
	extra.use_string = false;
	extra.stringlen = 0;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
749

Jeroen Vreeken's avatar
Jeroen Vreeken committed
750

751
	extra.input_file = fopen(filename, "r");
Jeroen Vreeken's avatar
Jeroen Vreeken committed
752
753
754
755
	if (!extra.input_file) {
		extra.ret = -1;
		goto err_nofile;
	}
Jeroen Vreeken's avatar
Jeroen Vreeken committed
756
	
757
	yylex_init_extra(&extra, &scanner);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
758

759
	saved_context = controller_block_context_get();
Jeroen Vreeken's avatar
Jeroen Vreeken committed
760
761
	controller_block_context_set(filename);

Jeroen Vreeken's avatar
Jeroen Vreeken committed
762
	extra.ret = yyparse(scanner);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
763

764
	controller_block_context_set(saved_context);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
765
	
Jeroen Vreeken's avatar
Jeroen Vreeken committed
766
	yylex_destroy(scanner);
767
	fclose(extra.input_file);
768

769
	ret = controller_block_link();
770
771
772
773
	// We'll allow -1 (missing blocks), but we'll bail out on lower return
	// values, which indicate unrecoverable errors like type mismatches in
	// links, etc.
	if (ret < -1) {
774
775
776
		log_send(LOG_T_ERROR, "Failed to link controller!");
		return ret;
	}
Jeroen Vreeken's avatar
Jeroen Vreeken committed
777
	
Jeroen Vreeken's avatar
Jeroen Vreeken committed
778
err_nofile:
779
	return extra.ret;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
780
}
781

782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
// This routine will only be called to load the top-most file in a hierarchy.
// When it is loaded successfully, all earlier unresolved links must have been
// resolved.
int controller_load(char *filename)
{
	int ret;
	
	ret = controller_load_one_file(filename);
	if (ret != 0)
		return ret;
	
	// See if the linker can now resolve all links.
	ret = controller_block_link();
	if (ret != 0) {
		log_send(LOG_T_ERROR, "Final linking stage failed.");
	}
	return ret;
}