2 * Master Configuration routines.
4 * This file contains the common parts of the Bacula
5 * configuration routines.
7 * Note, the configuration file parser consists of three parts
9 * 1. The generic lexical scanner in lib/lex.c and lib/lex.h
11 * 2. The generic config scanner in lib/parse_conf.c and
13 * These files contain the parser code, some utility
14 * routines, and the common store routines (name, int,
15 * string, time, int64, size, ...).
17 * 3. The daemon specific file, which contains the Resource
18 * definitions as well as any specific store routines
19 * for the resource records.
21 * N.B. This is a two pass parser, so if you malloc() a string
22 * in a "store" routine, you must ensure to do it during
23 * only one of the two passes, or to free it between.
24 * Also, note that the resource record is malloced and
25 * saved in save_resource() during pass 1. Anything that
26 * you want saved after pass two (e.g. resource pointers)
27 * must explicitly be done in save_resource. Take a look
28 * at the Job resource in src/dird/dird_conf.c to see how
31 * Kern Sibbald, January MM
36 Copyright (C) 2000-2006 Kern Sibbald
38 This program is free software; you can redistribute it and/or
39 modify it under the terms of the GNU General Public License
40 version 2 as amended with additional clauses defined in the
41 file LICENSE in the main source directory.
43 This program is distributed in the hope that it will be useful,
44 but WITHOUT ANY WARRANTY; without even the implied warranty of
45 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46 the file LICENSE for additional details.
53 /* Each daemon has a slightly different set of
54 * resources, so it will define the following
59 extern RES_TABLE resources[];
60 extern RES **res_head;
63 // work around visual studio name manling preventing external linkage since res_all
64 // is declared as a different type when instantiated.
65 extern "C" CURES res_all;
69 extern int res_all_size;
71 extern brwlock_t res_lock; /* resource lock */
74 /* Forward referenced subroutines */
75 static void scan_types(LEX *lc, MSGS *msg, int dest, char *where, char *cmd);
78 /* Common Resource definitions */
80 /* Message resource directives
81 * name handler value code flags default_value
83 RES_ITEM msgs_items[] = {
84 {"name", store_name, ITEM(res_msgs.hdr.name), 0, 0, 0},
85 {"description", store_str, ITEM(res_msgs.hdr.desc), 0, 0, 0},
86 {"mailcommand", store_str, ITEM(res_msgs.mail_cmd), 0, 0, 0},
87 {"operatorcommand", store_str, ITEM(res_msgs.operator_cmd), 0, 0, 0},
88 {"syslog", store_msgs, ITEM(res_msgs), MD_SYSLOG, 0, 0},
89 {"mail", store_msgs, ITEM(res_msgs), MD_MAIL, 0, 0},
90 {"mailonerror", store_msgs, ITEM(res_msgs), MD_MAIL_ON_ERROR, 0, 0},
91 {"file", store_msgs, ITEM(res_msgs), MD_FILE, 0, 0},
92 {"append", store_msgs, ITEM(res_msgs), MD_APPEND, 0, 0},
93 {"stdout", store_msgs, ITEM(res_msgs), MD_STDOUT, 0, 0},
94 {"stderr", store_msgs, ITEM(res_msgs), MD_STDERR, 0, 0},
95 {"director", store_msgs, ITEM(res_msgs), MD_DIRECTOR, 0, 0},
96 {"console", store_msgs, ITEM(res_msgs), MD_CONSOLE, 0, 0},
97 {"operator", store_msgs, ITEM(res_msgs), MD_OPERATOR, 0, 0},
98 {NULL, NULL, {0}, 0, 0, 0}
105 /* Various message types */
106 static struct s_mtypes msg_types[] = {
111 {"warning", M_WARNING},
114 {"notsaved", M_NOTSAVED},
115 {"skipped", M_SKIPPED},
117 {"terminate", M_TERM},
118 {"restored", M_RESTORED},
119 {"security", M_SECURITY},
125 /* Used for certain KeyWord tables */
132 * Tape Label types permitted in Pool records
134 * tape label label code = token
136 static s_kw tapelabels[] = {
137 {"bacula", B_BACULA_LABEL},
138 {"ansi", B_ANSI_LABEL},
139 {"ibm", B_IBM_LABEL},
144 /* Simply print a message */
145 static void prtmsg(void *sock, const char *fmt, ...)
149 va_start(arg_ptr, fmt);
150 vfprintf(stdout, fmt, arg_ptr);
154 const char *res_to_str(int rcode)
156 if (rcode < r_first || rcode > r_last) {
157 return _("***UNKNOWN***");
159 return resources[rcode-r_first].name;
165 * Initialize the static structure to zeros, then
166 * apply all the default values.
168 void init_resource(int type, RES_ITEM *items, int pass)
171 int rindex = type - r_first;
172 static bool first = true;
175 if (first && (errstat=rwl_init(&res_lock)) != 0) {
176 Emsg1(M_ABORT, 0, _("Unable to initialize resource lock. ERR=%s\n"),
181 memset(&res_all, 0, res_all_size);
182 res_all.hdr.rcode = type;
183 res_all.hdr.refcnt = 1;
185 /* Set defaults in each item */
186 for (i=0; items[i].name; i++) {
187 Dmsg3(900, "Item=%s def=%s defval=%d\n", items[i].name,
188 (items[i].flags & ITEM_DEFAULT) ? "yes" : "no",
189 items[i].default_value);
190 if (items[i].flags & ITEM_DEFAULT && items[i].default_value != 0) {
191 if (items[i].handler == store_bit) {
192 *(int *)(items[i].value) |= items[i].code;
193 } else if (items[i].handler == store_bool) {
194 *(bool *)(items[i].value) = items[i].default_value;
195 } else if (items[i].handler == store_pint ||
196 items[i].handler == store_int) {
197 *(int *)(items[i].value) = items[i].default_value;
198 } else if (items[i].handler == store_int64) {
199 *(int64_t *)(items[i].value) = items[i].default_value;
200 } else if (items[i].handler == store_size) {
201 *(uint64_t *)(items[i].value) = (uint64_t)items[i].default_value;
202 } else if (items[i].handler == store_time) {
203 *(utime_t *)(items[i].value) = (utime_t)items[i].default_value;
204 } else if (pass == 1 && items[i].handler == store_addresses) {
205 init_default_addresses((dlist**)items[i].value, items[i].default_value);
208 /* If this triggers, take a look at lib/parse_conf.h */
209 if (i >= MAX_RES_ITEMS) {
210 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
216 /* Store Messages Destination information */
217 void store_msgs(LEX *lc, RES_ITEM *item, int index, int pass)
224 Dmsg2(900, "store_msgs pass=%d code=%d\n", pass, item->code);
226 switch (item->code) {
229 case MD_SYSLOG: /* syslog */
231 scan_types(lc, (MSGS *)(item->value), item->code, NULL, NULL);
233 case MD_OPERATOR: /* send to operator */
234 case MD_DIRECTOR: /* send to Director */
235 case MD_MAIL: /* mail */
236 case MD_MAIL_ON_ERROR: /* mail if Job errors */
237 if (item->code == MD_OPERATOR) {
238 cmd = res_all.res_msgs.operator_cmd;
240 cmd = res_all.res_msgs.mail_cmd;
242 dest = get_pool_memory(PM_MESSAGE);
245 /* Pick up comma separated list of destinations */
247 token = lex_get_token(lc, T_NAME); /* scan destination */
248 dest = check_pool_memory_size(dest, dest_len + lc->str_len + 2);
250 pm_strcat(dest, " "); /* separate multiple destinations with space */
253 pm_strcat(dest, lc->str);
254 dest_len += lc->str_len;
255 Dmsg2(900, "store_msgs newdest=%s: dest=%s:\n", lc->str, NPRT(dest));
256 token = lex_get_token(lc, T_SKIP_EOL);
257 if (token == T_COMMA) {
258 continue; /* get another destination */
260 if (token != T_EQUALS) {
261 scan_err1(lc, _("expected an =, got: %s"), lc->str);
265 Dmsg1(900, "mail_cmd=%s\n", NPRT(cmd));
266 scan_types(lc, (MSGS *)(item->value), item->code, dest, cmd);
267 free_pool_memory(dest);
268 Dmsg0(900, "done with dest codes\n");
270 case MD_FILE: /* file */
271 case MD_APPEND: /* append */
272 dest = get_pool_memory(PM_MESSAGE);
273 /* Pick up a single destination */
274 token = lex_get_token(lc, T_NAME); /* scan destination */
275 pm_strcpy(dest, lc->str);
276 dest_len = lc->str_len;
277 token = lex_get_token(lc, T_SKIP_EOL);
278 Dmsg1(900, "store_msgs dest=%s:\n", NPRT(dest));
279 if (token != T_EQUALS) {
280 scan_err1(lc, _("expected an =, got: %s"), lc->str);
282 scan_types(lc, (MSGS *)(item->value), item->code, dest, NULL);
283 free_pool_memory(dest);
284 Dmsg0(900, "done with dest codes\n");
288 scan_err1(lc, _("Unknown item code: %d\n"), item->code);
293 set_bit(index, res_all.hdr.item_present);
294 Dmsg0(900, "Done store_msgs\n");
298 * Scan for message types and add them to the message
299 * destination. The basic job here is to connect message types
300 * (WARNING, ERROR, FATAL, INFO, ...) with an appropriate
301 * destination (MAIL, FILE, OPERATOR, ...)
303 static void scan_types(LEX *lc, MSGS *msg, int dest_code, char *where, char *cmd)
311 lex_get_token(lc, T_NAME); /* expect at least one type */
313 if (lc->str[0] == '!') {
320 for (i=0; msg_types[i].name; i++) {
321 if (strcasecmp(str, msg_types[i].name) == 0) {
322 msg_type = msg_types[i].token;
328 scan_err1(lc, _("message type: %s not found"), str);
332 if (msg_type == M_MAX+1) { /* all? */
333 for (i=1; i<=M_MAX; i++) { /* yes set all types */
334 add_msg_dest(msg, dest_code, i, where, cmd);
337 rem_msg_dest(msg, dest_code, msg_type, where);
339 add_msg_dest(msg, dest_code, msg_type, where, cmd);
344 Dmsg0(900, "call lex_get_token() to eat comma\n");
345 lex_get_token(lc, T_ALL); /* eat comma */
347 Dmsg0(900, "Done scan_types()\n");
352 * This routine is ONLY for resource names
353 * Store a name at specified address.
355 void store_name(LEX *lc, RES_ITEM *item, int index, int pass)
357 POOLMEM *msg = get_pool_memory(PM_EMSG);
358 lex_get_token(lc, T_NAME);
359 if (!is_name_valid(lc->str, &msg)) {
360 scan_err1(lc, "%s\n", msg);
362 free_pool_memory(msg);
363 /* Store the name both pass 1 and pass 2 */
364 if (*(item->value)) {
365 scan_err2(lc, _("Attempt to redefine name \"%s\" to \"%s\"."),
366 *(item->value), lc->str);
368 *(item->value) = bstrdup(lc->str);
370 set_bit(index, res_all.hdr.item_present);
375 * Store a name string at specified address
376 * A name string is limited to MAX_RES_NAME_LENGTH
378 void store_strname(LEX *lc, RES_ITEM *item, int index, int pass)
380 lex_get_token(lc, T_NAME);
383 *(item->value) = bstrdup(lc->str);
386 set_bit(index, res_all.hdr.item_present);
389 /* Store a string at specified address */
390 void store_str(LEX *lc, RES_ITEM *item, int index, int pass)
392 lex_get_token(lc, T_STRING);
394 *(item->value) = bstrdup(lc->str);
397 set_bit(index, res_all.hdr.item_present);
401 * Store a directory name at specified address. Note, we do
402 * shell expansion except if the string begins with a vertical
403 * bar (i.e. it will likely be passed to the shell later).
405 void store_dir(LEX *lc, RES_ITEM *item, int index, int pass)
407 lex_get_token(lc, T_STRING);
409 if (lc->str[0] != '|') {
410 do_shell_expansion(lc->str, sizeof(lc->str));
412 *(item->value) = bstrdup(lc->str);
415 set_bit(index, res_all.hdr.item_present);
419 /* Store a password specified address in MD5 coding */
420 void store_password(LEX *lc, RES_ITEM *item, int index, int pass)
423 struct MD5Context md5c;
424 unsigned char digest[CRYPTO_DIGEST_MD5_SIZE];
428 lex_get_token(lc, T_STRING);
431 MD5Update(&md5c, (unsigned char *) (lc->str), lc->str_len);
432 MD5Final(digest, &md5c);
433 for (i = j = 0; i < sizeof(digest); i++) {
434 sprintf(&sig[j], "%02x", digest[i]);
437 *(item->value) = bstrdup(sig);
440 set_bit(index, res_all.hdr.item_present);
444 /* Store a resource at specified address.
445 * If we are in pass 2, do a lookup of the
448 void store_res(LEX *lc, RES_ITEM *item, int index, int pass)
452 lex_get_token(lc, T_NAME);
454 res = GetResWithName(item->code, lc->str);
456 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
457 lc->str, lc->line_no, lc->line);
459 if (*(item->value)) {
460 scan_err3(lc, _("Attempt to redefine resource \"%s\" referenced on line %d : %s\n"),
461 item->name, lc->line_no, lc->line);
463 *(item->value) = (char *)res;
466 set_bit(index, res_all.hdr.item_present);
470 * Store a resource pointer in an alist. default_value indicates how many
471 * times this routine can be called -- i.e. how many alists
473 * If we are in pass 2, do a lookup of the
476 void store_alist_res(LEX *lc, RES_ITEM *item, int index, int pass)
479 int count = item->default_value;
484 if (count == 0) { /* always store in item->value */
486 if ((item->value)[i] == NULL) {
487 list = New(alist(10, not_owned_by_alist));
489 list = (alist *)(item->value)[i];
492 /* Find empty place to store this directive */
493 while ((item->value)[i] != NULL && i++ < count) { }
495 scan_err4(lc, _("Too many %s directives. Max. is %d. line %d: %s\n"),
496 lc->str, count, lc->line_no, lc->line);
498 list = New(alist(10, not_owned_by_alist));
502 lex_get_token(lc, T_NAME); /* scan next item */
503 res = GetResWithName(item->code, lc->str);
505 scan_err3(lc, _("Could not find config Resource \"%s\" referenced on line %d : %s\n"),
506 item->name, lc->line_no, lc->line);
508 Dmsg5(900, "Append %p to alist %p size=%d i=%d %s\n",
509 res, list, list->size(), i, item->name);
511 (item->value)[i] = (char *)list;
512 if (lc->ch != ',') { /* if no other item follows */
515 lex_get_token(lc, T_ALL); /* eat comma */
519 set_bit(index, res_all.hdr.item_present);
524 * Store a string in an alist.
526 void store_alist_str(LEX *lc, RES_ITEM *item, int index, int pass)
531 if (*(item->value) == NULL) {
532 list = New(alist(10, owned_by_alist));
534 list = (alist *)(*(item->value));
537 lex_get_token(lc, T_STRING); /* scan next item */
538 Dmsg4(900, "Append %s to alist %p size=%d %s\n",
539 lc->str, list, list->size(), item->name);
540 list->append(bstrdup(lc->str));
541 *(item->value) = (char *)list;
544 set_bit(index, res_all.hdr.item_present);
550 * Store default values for Resource from xxxDefs
551 * If we are in pass 2, do a lookup of the
552 * resource and store everything not explicitly set
555 * Note, here item points to the main resource (e.g. Job, not
556 * the jobdefs, which we look up).
558 void store_defs(LEX *lc, RES_ITEM *item, int index, int pass)
562 lex_get_token(lc, T_NAME);
564 Dmsg2(900, "Code=%d name=%s\n", item->code, lc->str);
565 res = GetResWithName(item->code, lc->str);
567 scan_err3(lc, _("Missing config Resource \"%s\" referenced on line %d : %s\n"),
568 lc->str, lc->line_no, lc->line);
576 /* Store an integer at specified address */
577 void store_int(LEX *lc, RES_ITEM *item, int index, int pass)
579 lex_get_token(lc, T_INT32);
580 *(int *)(item->value) = lc->int32_val;
582 set_bit(index, res_all.hdr.item_present);
585 /* Store a positive integer at specified address */
586 void store_pint(LEX *lc, RES_ITEM *item, int index, int pass)
588 lex_get_token(lc, T_PINT32);
589 *(int *)(item->value) = lc->pint32_val;
591 set_bit(index, res_all.hdr.item_present);
595 /* Store an 64 bit integer at specified address */
596 void store_int64(LEX *lc, RES_ITEM *item, int index, int pass)
598 lex_get_token(lc, T_INT64);
599 *(int64_t *)(item->value) = lc->int64_val;
601 set_bit(index, res_all.hdr.item_present);
604 /* Store a size in bytes */
605 void store_size(LEX *lc, RES_ITEM *item, int index, int pass)
611 Dmsg0(900, "Enter store_size\n");
612 token = lex_get_token(lc, T_SKIP_EOL);
617 case T_UNQUOTED_STRING:
618 bstrncpy(bsize, lc->str, sizeof(bsize)); /* save first part */
619 /* if terminated by space, scan and get modifier */
620 while (lc->ch == ' ') {
621 token = lex_get_token(lc, T_ALL);
625 case T_UNQUOTED_STRING:
626 bstrncat(bsize, lc->str, sizeof(bsize));
630 if (!size_to_uint64(bsize, strlen(bsize), &uvalue)) {
631 scan_err1(lc, _("expected a size number, got: %s"), lc->str);
633 *(uint64_t *)(item->value) = uvalue;
636 scan_err1(lc, _("expected a size, got: %s"), lc->str);
639 if (token != T_EOL) {
642 set_bit(index, res_all.hdr.item_present);
643 Dmsg0(900, "Leave store_size\n");
647 /* Store a time period in seconds */
648 void store_time(LEX *lc, RES_ITEM *item, int index, int pass)
654 token = lex_get_token(lc, T_SKIP_EOL);
659 case T_UNQUOTED_STRING:
660 bstrncpy(period, lc->str, sizeof(period)); /* get first part */
661 /* if terminated by space, scan and get modifier */
662 while (lc->ch == ' ') {
663 token = lex_get_token(lc, T_ALL);
667 case T_UNQUOTED_STRING:
668 bstrncat(period, lc->str, sizeof(period));
672 if (!duration_to_utime(period, &utime)) {
673 scan_err1(lc, _("expected a time period, got: %s"), period);
675 *(utime_t *)(item->value) = utime;
678 scan_err1(lc, _("expected a time period, got: %s"), lc->str);
681 if (token != T_EOL) {
684 set_bit(index, res_all.hdr.item_present);
688 /* Store a yes/no in a bit field */
689 void store_bit(LEX *lc, RES_ITEM *item, int index, int pass)
691 lex_get_token(lc, T_NAME);
692 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
693 *(int *)(item->value) |= item->code;
694 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
695 *(int *)(item->value) &= ~(item->code);
697 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
700 set_bit(index, res_all.hdr.item_present);
703 /* Store a bool in a bit field */
704 void store_bool(LEX *lc, RES_ITEM *item, int index, int pass)
706 lex_get_token(lc, T_NAME);
707 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
708 *(bool *)(item->value) = true;
709 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
710 *(bool *)(item->value) = false;
712 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
715 set_bit(index, res_all.hdr.item_present);
720 * Store Tape Label Type (Bacula, ANSI, IBM)
723 void store_label(LEX *lc, RES_ITEM *item, int index, int pass)
727 token = lex_get_token(lc, T_NAME);
728 /* Store the label pass 2 so that type is defined */
729 for (i=0; tapelabels[i].name; i++) {
730 if (strcasecmp(lc->str, tapelabels[i].name) == 0) {
731 *(int *)(item->value) = tapelabels[i].token;
737 scan_err1(lc, _("Expected a Tape Label keyword, got: %s"), lc->str);
740 set_bit(index, res_all.hdr.item_present);
750 /*********************************************************************
752 * Parse configuration file
754 * Return 0 if reading failed, 1 otherwise
755 * Note, the default behavior unless you have set an alternate
756 * scan_error handler is to die on an error.
759 parse_config(const char *cf, LEX_ERROR_HANDLER *scan_error, int err_type)
764 enum parse_state state = p_none;
765 RES_ITEM *items = NULL;
768 /* Make two passes. The first builds the name symbol table,
769 * and the second picks up the items.
771 Dmsg0(900, "Enter parse_config()\n");
772 for (pass=1; pass <= 2; pass++) {
773 Dmsg1(900, "parse_config pass %d\n", pass);
774 if ((lc = lex_open_file(lc, cf, scan_error)) == NULL) {
776 /* We must create a lex packet to print the error */
777 lc = (LEX *)malloc(sizeof(LEX));
778 memset(lc, 0, sizeof(LEX));
780 lc->scan_error = scan_error;
782 lex_set_default_error_handler(lc);
784 lex_set_error_handler_error_type(lc, err_type) ;
785 bstrncpy(lc->str, cf, sizeof(lc->str));
787 scan_err2(lc, _("Cannot open config file \"%s\": %s\n"),
788 lc->str, be.strerror());
792 lex_set_error_handler_error_type(lc, err_type) ;
793 while ((token=lex_get_token(lc, T_ALL)) != T_EOF) {
794 Dmsg1(900, "parse got token=%s\n", lex_tok_to_str(token));
797 if (token == T_EOL) {
800 if (token != T_IDENTIFIER) {
801 scan_err1(lc, _("Expected a Resource name identifier, got: %s"), lc->str);
804 for (i=0; resources[i].name; i++)
805 if (strcasecmp(resources[i].name, lc->str) == 0) {
807 items = resources[i].items;
808 res_type = resources[i].rcode;
809 init_resource(res_type, items, pass);
812 if (state == p_none) {
813 scan_err1(lc, _("expected resource name, got: %s"), lc->str);
824 scan_err1(lc, _("not in resource definition: %s"), lc->str);
827 for (i=0; items[i].name; i++) {
828 if (strcasecmp(items[i].name, lc->str) == 0) {
829 /* If the ITEM_NO_EQUALS flag is set we do NOT
830 * scan for = after the keyword */
831 if (!(items[i].flags & ITEM_NO_EQUALS)) {
832 token = lex_get_token(lc, T_SKIP_EOL);
833 Dmsg1 (900, "in T_IDENT got token=%s\n", lex_tok_to_str(token));
834 if (token != T_EQUALS) {
835 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
839 Dmsg1(800, "calling handler for %s\n", items[i].name);
840 /* Call item handler */
841 items[i].handler(lc, &items[i], i, pass);
847 Dmsg2(900, "level=%d id=%s\n", level, lc->str);
848 Dmsg1(900, "Keyword = %s\n", lc->str);
849 scan_err1(lc, _("Keyword \"%s\" not permitted in this resource.\n"
850 "Perhaps you left the trailing brace off of the previous resource."), lc->str);
858 Dmsg0(900, "T_EOB => define new resource\n");
859 if (res_all.hdr.name == NULL) {
860 scan_err0(lc, _("Name not specified for resource"));
862 save_resource(res_type, items, pass); /* save resource */
869 scan_err2(lc, _("unexpected token %d %s in resource definition"),
870 token, lex_tok_to_str(token));
875 scan_err1(lc, _("Unknown parser state %d\n"), state);
879 if (state != p_none) {
880 scan_err0(lc, _("End of conf file reached with unclosed resource."));
883 if (debug_level >= 900 && pass == 2) {
885 for (i=r_first; i<=r_last; i++) {
886 dump_resource(i, res_head[i-r_first], prtmsg, NULL);
889 lc = lex_close_file(lc);
891 Dmsg0(900, "Leave parse_config()\n");
895 /*********************************************************************
897 * Free configuration resources
900 void free_config_resources()
902 for (int i=r_first; i<=r_last; i++) {
903 free_resource(res_head[i-r_first], i);
904 res_head[i-r_first] = NULL;
908 RES **save_config_resources()
910 int num = r_last - r_first + 1;
911 RES **res = (RES **)malloc(num*sizeof(RES *));
912 for (int i=0; i<num; i++) {
913 res[i] = res_head[i];
921 int size = (r_last - r_first + 1) * sizeof(RES *);
922 RES **res = (RES **)malloc(size);
923 memset(res, 0, size);