* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2017 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for reading the configuration file, and for displaying
{ "local_from_prefix", opt_stringptr, &local_from_prefix },
{ "local_from_suffix", opt_stringptr, &local_from_suffix },
{ "local_interfaces", opt_stringptr, &local_interfaces },
+#ifdef HAVE_LOCAL_SCAN
{ "local_scan_timeout", opt_time, &local_scan_timeout },
+#endif
{ "local_sender_retain", opt_bool, &local_sender_retain },
{ "localhost_number", opt_stringptr, &host_number_string },
{ "log_file_path", opt_stringptr, &log_file_path },
+/*************************************************
+* Deal with an assignment to a macro *
+*************************************************/
+
+/* We have a new definition; append to the list.
+
+Args:
+ name Name of the macro; will be copied
+ val Expansion result for the macro; will be copied
+*/
+
+macro_item *
+macro_create(const uschar * name, const uschar * val, BOOL command_line)
+{
+macro_item * m = store_get(sizeof(macro_item));
+
+/* fprintf(stderr, "%s: '%s' '%s'\n", __FUNCTION__, name, val); */
+m->next = NULL;
+m->command_line = command_line;
+m->namelen = Ustrlen(name);
+m->replen = Ustrlen(val);
+m->name = string_copy(name);
+m->replacement = string_copy(val);
+if (mlast)
+ mlast->next = m;
+else
+ macros = m;
+mlast = m;
+if (!macros_user)
+ macros_user = m;
+return m;
+}
+
+
/* This function is called when a line that starts with an upper case letter is
encountered. The argument "line" should contain a complete logical line, and
start with the first letter of the macro name. The macro name and the
just skip this definition. It's an error to attempt to redefine a macro without
redef set to TRUE, or to redefine a macro when it hasn't been defined earlier.
It is also an error to define a macro whose name begins with the name of a
-previously defined macro.
+previously defined macro. This is the requirement that make using a tree
+for macros hard; we must check all macros for the substring. Perhaps a
+sorted list, and a bsearch, would work?
Note: it is documented that the other way round works. */
-if ((m = macro_search_prefix(name)))
+for (m = macros; m; m = m->next)
{
- if (m->namelen < namelen) /* substring match */
+ if (Ustrcmp(m->name, name) == 0)
{
- log_write(0, LOG_CONFIG|LOG_PANIC, "\"%s\" cannot be defined as "
- "a macro because previously defined macro \"%s\" is a substring",
- name, m->tnode.name);
- return FALSE;
+ if (!m->command_line && !redef)
+ {
+ log_write(0, LOG_CONFIG|LOG_PANIC, "macro \"%s\" is already "
+ "defined (use \"==\" if you want to redefine it", name);
+ return FALSE;
+ }
+ break;
}
- /* exact match */
- if (!m->command_line && !redef)
+
+ if (m->namelen < namelen && Ustrstr(name, m->name) != NULL)
{
- log_write(0, LOG_CONFIG|LOG_PANIC, "macro \"%s\" is already "
- "defined (use \"==\" if you want to redefine it", name);
+ log_write(0, LOG_CONFIG|LOG_PANIC, "\"%s\" cannot be defined as "
+ "a macro because previously defined macro \"%s\" is a substring",
+ name, m->name);
return FALSE;
}
- if (m->command_line) /* overriding cmdline definition */
- return TRUE;
+ /* We cannot have this test, because it is documented that a substring
+ macro is permitted (there is even an example).
+ *
+ * if (m->namelen > namelen && Ustrstr(m->name, name) != NULL)
+ * log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "\"%s\" cannot be defined as "
+ * "a macro because it is a substring of previously defined macro \"%s\"",
+ * name, m->name);
+ */
}
+/* Check for an overriding command-line definition. */
+
+if (m && m->command_line) return TRUE;
+
/* Redefinition must refer to an existing macro. */
if (redef)
if (m)
{
m->replen = Ustrlen(s);
- m->tnode.data.ptr = string_copy(s);
+ m->replacement = string_copy(s);
}
else
{
/* Process line for macros. The line is in big_buffer starting at offset len.
Expand big_buffer if needed. Handle definitions of new macros, and
-imacro expansions, rewriting the line in thw buffer.
+macro expansions, rewriting the line in the buffer.
Arguments:
len Offset in buffer of start of line
{
uschar * ss = big_buffer + len;
uschar * s;
+macro_item * m;
/* Find the true start of the physical line - leading spaces are always
ignored. */
if (*s != '=') s = ss; /* Not a macro definition */
}
-/* Scan the line (from after XXX= if present), replacing any macros. Rescan
-after replacement for any later-defined macros. */
+/* Skip leading chars which cannot start a macro name, to avoid multiple
+pointless rescans in Ustrstr calls. */
+
+while (*s && !isupper(*s) && !(*s == '_' && isupper(s[1]))) s++;
+
+/* For each defined macro, scan the line (from after XXX= if present),
+replacing all occurrences of the macro. */
*macro_found = FALSE;
-while (*s)
+if (*s) for (m = *s == '_' ? macros : macros_user; m; m = m->next)
{
- if (isupper(*s) || *s == '_' && isupper(s[1]))
- {
- macro_item * m;
- unsigned mnum = 0;
+ uschar * p, *pp;
+ uschar * t;
- while ((m = macro_search_largest_prefix(s)) && m->m_number > mnum)
- {
- uschar * pp;
- int moveby;
+ while (*s && !isupper(*s) && !(*s == '_' && isupper(s[1]))) s++;
+ if (!*s) break;
- /* Expand the buffer if necessary */
+ t = s;
+ while ((p = Ustrstr(t, m->name)) != NULL)
+ {
+ int moveby;
- while (*newlen - m->namelen + m->replen + 1 > big_buffer_size)
- {
- int newsize = big_buffer_size + BIG_BUFFER_SIZE;
- uschar *newbuffer = store_malloc(newsize);
- memcpy(newbuffer, big_buffer, *newlen + 1);
- s = newbuffer + (s - big_buffer);
- ss = newbuffer + (ss - big_buffer);
- big_buffer_size = newsize;
- store_free(big_buffer);
- big_buffer = newbuffer;
- }
+/* fprintf(stderr, "%s: matched '%s' in '%s'\n", __FUNCTION__, m->name, ss); */
+ /* Expand the buffer if necessary */
+
+ while (*newlen - m->namelen + m->replen + 1 > big_buffer_size)
+ {
+ int newsize = big_buffer_size + BIG_BUFFER_SIZE;
+ uschar *newbuffer = store_malloc(newsize);
+ memcpy(newbuffer, big_buffer, *newlen + 1);
+ p = newbuffer + (p - big_buffer);
+ s = newbuffer + (s - big_buffer);
+ ss = newbuffer + (ss - big_buffer);
+ t = newbuffer + (t - big_buffer);
+ big_buffer_size = newsize;
+ store_free(big_buffer);
+ big_buffer = newbuffer;
+ }
- /* Shuffle the remaining characters up or down in the buffer before
- copying in the replacement text. Don't rescan the replacement for this
- same macro. */
+ /* Shuffle the remaining characters up or down in the buffer before
+ copying in the replacement text. Don't rescan the replacement for this
+ same macro. */
- pp = s + m->namelen;
- if ((moveby = m->replen - m->namelen) != 0)
- {
- memmove(s + m->replen, pp, (big_buffer + *newlen) - pp + 1);
- *newlen += moveby;
- }
- Ustrncpy(s, m->tnode.data.ptr, m->replen);
- *macro_found = TRUE;
- mnum = m->m_number;
+ pp = p + m->namelen;
+ if ((moveby = m->replen - m->namelen) != 0)
+ {
+ memmove(p + m->replen, pp, (big_buffer + *newlen) - pp + 1);
+ *newlen += moveby;
}
+ Ustrncpy(p, m->replacement, m->replen);
+ t = p + m->replen;
+ while (*t && !isupper(*t) && !(*t == '_' && isupper(t[1]))) t++;
+ *macro_found = TRUE;
}
- s++;
}
/* An empty macro replacement at the start of a line could mean that ss no
last one more than the offset of the last entry in optop
no_labels do not show "foo = " at the start.
-Returns: nothing
+Returns: boolean success
*/
-static void
+static BOOL
print_ol(optionlist *ol, uschar *name, void *options_block,
optionlist *oltop, int last, BOOL no_labels)
{
uschar *s;
uschar name2[64];
-if (ol == NULL)
+if (!ol)
{
printf("%s is not a known option\n", name);
- return;
+ return FALSE;
}
/* Non-admin callers cannot see options that have been flagged secure by the
"hide" prefix. */
-if (!admin_user && (ol->type & opt_secure) != 0)
+if (!admin_user && ol->type & opt_secure)
{
if (no_labels)
printf("%s\n", hidden);
else
printf("%s = %s\n", name, hidden);
- return;
+ return TRUE;
}
/* Else show the value of the option */
value = ol->value;
-if (options_block != NULL)
+if (options_block)
{
- if ((ol->type & opt_public) == 0)
+ if (!(ol->type & opt_public))
options_block = (void *)(((driver_instance *)options_block)->options_block);
value = (void *)(US options_block + (long int)value);
}
{
case opt_stringptr:
case opt_rewrite: /* Show the text value */
- s = *((uschar **)value);
- if (!no_labels) printf("%s = ", name);
- printf("%s\n", (s == NULL)? US"" : string_printing2(s, FALSE));
- break;
+ s = *(USS value);
+ if (!no_labels) printf("%s = ", name);
+ printf("%s\n", s ? string_printing2(s, FALSE) : US"");
+ break;
case opt_int:
- if (!no_labels) printf("%s = ", name);
- printf("%d\n", *((int *)value));
- break;
+ if (!no_labels) printf("%s = ", name);
+ printf("%d\n", *((int *)value));
+ break;
case opt_mkint:
{
printf("%d\n", x);
}
}
- break;
+ break;
case opt_Kint:
{
int x = *((int *)value);
if (!no_labels) printf("%s = ", name);
if (x == 0) printf("0\n");
- else if ((x & 1023) == 0) printf("%dM\n", x >> 10);
- else printf("%dK\n", x);
+ else if ((x & 1023) == 0) printf("%dM\n", x >> 10);
+ else printf("%dK\n", x);
}
- break;
+ break;
case opt_octint:
- if (!no_labels) printf("%s = ", name);
- printf("%#o\n", *((int *)value));
- break;
+ if (!no_labels) printf("%s = ", name);
+ printf("%#o\n", *((int *)value));
+ break;
/* Can be negative only when "unset", in which case integer */
printf("\n");
}
}
- break;
+ break;
/* If the numerical value is unset, try for the string value */
case opt_expand_uid:
- if (! *get_set_flag(name, oltop, last, options_block))
- {
- sprintf(CS name2, "*expand_%.50s", name);
- ol2 = find_option(name2, oltop, last);
- if (ol2 != NULL)
+ if (! *get_set_flag(name, oltop, last, options_block))
{
- void *value2 = ol2->value;
- if (options_block != NULL)
- value2 = (void *)(US options_block + (long int)value2);
- s = *((uschar **)value2);
- if (!no_labels) printf("%s = ", name);
- printf("%s\n", (s == NULL)? US"" : string_printing(s));
- break;
+ sprintf(CS name2, "*expand_%.50s", name);
+ if ((ol2 = find_option(name2, oltop, last)))
+ {
+ void *value2 = ol2->value;
+ if (options_block)
+ value2 = (void *)(US options_block + (long int)value2);
+ s = *(USS value2);
+ if (!no_labels) printf("%s = ", name);
+ printf("%s\n", s ? string_printing(s) : US"");
+ break;
+ }
}
- }
- /* Else fall through */
+ /* Else fall through */
case opt_uid:
- if (!no_labels) printf("%s = ", name);
- if (! *get_set_flag(name, oltop, last, options_block))
- printf("\n");
- else
- {
- pw = getpwuid(*((uid_t *)value));
- if (pw == NULL)
- printf("%ld\n", (long int)(*((uid_t *)value)));
- else printf("%s\n", pw->pw_name);
- }
- break;
+ if (!no_labels) printf("%s = ", name);
+ if (! *get_set_flag(name, oltop, last, options_block))
+ printf("\n");
+ else
+ if ((pw = getpwuid(*((uid_t *)value))))
+ printf("%s\n", pw->pw_name);
+ else
+ printf("%ld\n", (long int)(*((uid_t *)value)));
+ break;
/* If the numerical value is unset, try for the string value */
case opt_expand_gid:
- if (! *get_set_flag(name, oltop, last, options_block))
- {
- sprintf(CS name2, "*expand_%.50s", name);
- ol2 = find_option(name2, oltop, last);
- if (ol2 != NULL && (ol2->type & opt_mask) == opt_stringptr)
+ if (! *get_set_flag(name, oltop, last, options_block))
{
- void *value2 = ol2->value;
- if (options_block != NULL)
- value2 = (void *)(US options_block + (long int)value2);
- s = *((uschar **)value2);
- if (!no_labels) printf("%s = ", name);
- printf("%s\n", (s == NULL)? US"" : string_printing(s));
- break;
+ sprintf(CS name2, "*expand_%.50s", name);
+ if ( (ol2 = find_option(name2, oltop, last))
+ && (ol2->type & opt_mask) == opt_stringptr)
+ {
+ void *value2 = ol2->value;
+ if (options_block)
+ value2 = (void *)(US options_block + (long int)value2);
+ s = *(USS value2);
+ if (!no_labels) printf("%s = ", name);
+ printf("%s\n", s ? string_printing(s) : US"");
+ break;
+ }
}
- }
- /* Else fall through */
+ /* Else fall through */
case opt_gid:
- if (!no_labels) printf("%s = ", name);
- if (! *get_set_flag(name, oltop, last, options_block))
- printf("\n");
- else
- {
- gr = getgrgid(*((int *)value));
- if (gr == NULL)
- printf("%ld\n", (long int)(*((int *)value)));
- else printf("%s\n", gr->gr_name);
- }
- break;
+ if (!no_labels) printf("%s = ", name);
+ if (! *get_set_flag(name, oltop, last, options_block))
+ printf("\n");
+ else
+ if ((gr = getgrgid(*((int *)value))))
+ printf("%s\n", gr->gr_name);
+ else
+ printf("%ld\n", (long int)(*((int *)value)));
+ break;
case opt_uidlist:
- uidlist = *((uid_t **)value);
- if (!no_labels) printf("%s =", name);
- if (uidlist != NULL)
- {
- int i;
- uschar sep = ' ';
- if (no_labels) sep = '\0';
- for (i = 1; i <= (int)(uidlist[0]); i++)
+ uidlist = *((uid_t **)value);
+ if (!no_labels) printf("%s =", name);
+ if (uidlist)
{
- uschar *name = NULL;
- pw = getpwuid(uidlist[i]);
- if (pw != NULL) name = US pw->pw_name;
- if (sep != '\0') printf("%c", sep);
- if (name != NULL) printf("%s", name);
- else printf("%ld", (long int)(uidlist[i]));
- sep = ':';
+ int i;
+ uschar sep = no_labels ? '\0' : ' ';
+ for (i = 1; i <= (int)(uidlist[0]); i++)
+ {
+ uschar *name = NULL;
+ if ((pw = getpwuid(uidlist[i]))) name = US pw->pw_name;
+ if (sep != '\0') printf("%c", sep);
+ if (name) printf("%s", name);
+ else printf("%ld", (long int)(uidlist[i]));
+ sep = ':';
+ }
}
- }
- printf("\n");
- break;
+ printf("\n");
+ break;
case opt_gidlist:
- gidlist = *((gid_t **)value);
- if (!no_labels) printf("%s =", name);
- if (gidlist != NULL)
- {
- int i;
- uschar sep = ' ';
- if (no_labels) sep = '\0';
- for (i = 1; i <= (int)(gidlist[0]); i++)
+ gidlist = *((gid_t **)value);
+ if (!no_labels) printf("%s =", name);
+ if (gidlist)
{
- uschar *name = NULL;
- gr = getgrgid(gidlist[i]);
- if (gr != NULL) name = US gr->gr_name;
- if (sep != '\0') printf("%c", sep);
- if (name != NULL) printf("%s", name);
- else printf("%ld", (long int)(gidlist[i]));
- sep = ':';
+ int i;
+ uschar sep = no_labels ? '\0' : ' ';
+ for (i = 1; i <= (int)(gidlist[0]); i++)
+ {
+ uschar *name = NULL;
+ if ((gr = getgrgid(gidlist[i]))) name = US gr->gr_name;
+ if (sep != '\0') printf("%c", sep);
+ if (name) printf("%s", name);
+ else printf("%ld", (long int)(gidlist[i]));
+ sep = ':';
+ }
}
- }
- printf("\n");
- break;
+ printf("\n");
+ break;
case opt_time:
- if (!no_labels) printf("%s = ", name);
- printf("%s\n", readconf_printtime(*((int *)value)));
- break;
+ if (!no_labels) printf("%s = ", name);
+ printf("%s\n", readconf_printtime(*((int *)value)));
+ break;
case opt_timelist:
{
int *list = (int *)value;
if (!no_labels) printf("%s = ", name);
for (i = 0; i < list[1]; i++)
- printf("%s%s", (i == 0)? "" : ":", readconf_printtime(list[i+2]));
+ printf("%s%s", i == 0 ? "" : ":", readconf_printtime(list[i+2]));
printf("\n");
}
- break;
+ break;
case opt_bit:
- printf("%s%s\n", ((*((int *)value)) & (1 << ((ol->type >> 16) & 31)))?
- "" : "no_", name);
- break;
+ printf("%s%s\n", ((*((int *)value)) & (1 << ((ol->type >> 16) & 31)))?
+ "" : "no_", name);
+ break;
case opt_expand_bool:
- sprintf(CS name2, "*expand_%.50s", name);
- ol2 = find_option(name2, oltop, last);
- if (ol2 != NULL && ol2->value != NULL)
- {
- void *value2 = ol2->value;
- if (options_block != NULL)
- value2 = (void *)(US options_block + (long int)value2);
- s = *((uschar **)value2);
- if (s != NULL)
+ sprintf(CS name2, "*expand_%.50s", name);
+ if ((ol2 = find_option(name2, oltop, last)) && ol2->value)
{
- if (!no_labels) printf("%s = ", name);
- printf("%s\n", string_printing(s));
- break;
+ void *value2 = ol2->value;
+ if (options_block)
+ value2 = (void *)(US options_block + (long int)value2);
+ s = *(USS value2);
+ if (s)
+ {
+ if (!no_labels) printf("%s = ", name);
+ printf("%s\n", string_printing(s));
+ break;
+ }
+ /* s == NULL => string not set; fall through */
}
- /* s == NULL => string not set; fall through */
- }
- /* Fall through */
+ /* Fall through */
case opt_bool:
case opt_bool_verify:
case opt_bool_set:
- printf("%s%s\n", (*((BOOL *)value))? "" : "no_", name);
- break;
+ printf("%s%s\n", (*((BOOL *)value))? "" : "no_", name);
+ break;
}
+return TRUE;
}
type NULL or driver type name, as described above
no_labels avoid the "foo = " at the start of an item
-Returns: nothing
+Returns: Boolean success
*/
-void
+BOOL
readconf_print(uschar *name, uschar *type, BOOL no_labels)
{
BOOL names_only = FALSE;
macro_item *m;
int size = 0;
-if (type == NULL)
+if (!type)
{
if (*name == '+')
{
&hostlist_anchor, &localpartlist_anchor };
for (i = 0; i < 4; i++)
- {
- t = tree_search(*(anchors[i]), name+1);
- if (t != NULL)
+ if ((t = tree_search(*(anchors[i]), name+1)))
{
found = TRUE;
if (no_labels)
printf("%slist %s = %s\n", types[i], name+1,
((namedlist_block *)(t->data.ptr))->string);
}
- }
if (!found)
printf("no address, domain, host, or local part list called \"%s\" "
"exists\n", name+1);
- return;
+ return found;
}
if ( Ustrcmp(name, "configure_file") == 0
|| Ustrcmp(name, "config_file") == 0)
{
printf("%s\n", CS config_main_filename);
- return;
+ return TRUE;
}
if (Ustrcmp(name, "all") == 0)
{
for (ol = optionlist_config;
ol < optionlist_config + nelem(optionlist_config); ol++)
- {
- if ((ol->type & opt_hidden) == 0)
- print_ol(ol, US ol->name, NULL,
- optionlist_config, nelem(optionlist_config),
- no_labels);
- }
- return;
+ if (!(ol->type & opt_hidden))
+ (void) print_ol(ol, US ol->name, NULL,
+ optionlist_config, nelem(optionlist_config),
+ no_labels);
+ return TRUE;
}
if (Ustrcmp(name, "local_scan") == 0)
{
- #ifndef LOCAL_SCAN_HAS_OPTIONS
+#ifndef LOCAL_SCAN_HAS_OPTIONS
printf("local_scan() options are not supported\n");
- #else
+ return FALSE;
+#else
for (ol = local_scan_options;
ol < local_scan_options + local_scan_options_count; ol++)
- {
- print_ol(ol, US ol->name, NULL, local_scan_options,
- local_scan_options_count, no_labels);
- }
- #endif
- return;
+ (void) print_ol(ol, US ol->name, NULL, local_scan_options,
+ local_scan_options_count, no_labels);
+ return TRUE;
+#endif
}
if (Ustrcmp(name, "config") == 0)
{
print_config(admin_user, no_labels);
- return;
+ return TRUE;
}
if (Ustrcmp(name, "routers") == 0)
type = US"transport";
name = NULL;
}
-
else if (Ustrcmp(name, "authenticators") == 0)
{
type = US"authenticator";
name = NULL;
}
-
else if (Ustrcmp(name, "macros") == 0)
{
type = US"macro";
name = NULL;
}
-
else if (Ustrcmp(name, "router_list") == 0)
{
type = US"router";
name = NULL;
names_only = TRUE;
}
-
else if (Ustrcmp(name, "transport_list") == 0)
{
type = US"transport";
name = NULL;
names_only = TRUE;
}
-
else if (Ustrcmp(name, "authenticator_list") == 0)
{
type = US"authenticator";
name = NULL;
names_only = TRUE;
}
-
else if (Ustrcmp(name, "macro_list") == 0)
{
type = US"macro";
name = NULL;
names_only = TRUE;
}
-
else if (Ustrcmp(name, "environment") == 0)
{
if (environ)
puts(CS *p);
}
}
- return;
+ return TRUE;
}
else
- {
- print_ol(find_option(name, optionlist_config, nelem(optionlist_config)),
+ return print_ol(find_option(name,
+ optionlist_config, nelem(optionlist_config)),
name, NULL, optionlist_config, nelem(optionlist_config), no_labels);
- return;
- }
}
/* Handle the options for a router or transport. Skip options that are flagged
if (!admin_user)
{
fprintf(stderr, "exim: permission denied\n");
- exit(EXIT_FAILURE);
+ return FALSE;
}
+ for (m = macros; m; m = m->next)
+ if (!name || Ustrcmp(name, m->name) == 0)
+ {
+ if (names_only)
+ printf("%s\n", CS m->name);
+ else
+ printf("%s=%s\n", CS m->name, CS m->replacement);
+ if (name)
+ return TRUE;
+ }
+ if (!name) return TRUE;
- if (name)
- if ((m = macro_search(name)))
- macro_print(m->tnode.name, m->tnode.data.ptr, (void *)(long)names_only);
- else
- printf("%s %s not found\n", type, name);
- else
- tree_walk(tree_macros, macro_print, (void *)(long)names_only);
-
- return;
+ printf("%s %s not found\n", type, name);
+ return FALSE;
}
if (names_only)
{
for (; d; d = d->next) printf("%s\n", CS d->name);
- return;
+ return TRUE;
}
/* Either search for a given driver, or print all of them */
for (; d; d = d->next)
{
+ BOOL rc = FALSE;
if (!name)
printf("\n%s %s:\n", d->name, type);
- else if (Ustrcmp(d->name, name) != 0)
- continue;
+ else if (Ustrcmp(d->name, name) != 0) continue;
for (ol = ol2; ol < ol2 + size; ol++)
- if ((ol->type & opt_hidden) == 0)
- print_ol(ol, US ol->name, d, ol2, size, no_labels);
+ if (!(ol->type & opt_hidden))
+ rc |= print_ol(ol, US ol->name, d, ol2, size, no_labels);
for (ol = d->info->options;
ol < d->info->options + *(d->info->options_count); ol++)
- if ((ol->type & opt_hidden) == 0)
- print_ol(ol, US ol->name, d, d->info->options, *(d->info->options_count), no_labels);
+ if (!(ol->type & opt_hidden))
+ rc |= print_ol(ol, US ol->name, d, d->info->options,
+ *d->info->options_count, no_labels);
- if (name) return;
+ if (name) return rc;
}
-if (name) printf("%s %s not found\n", type, name);
+if (!name) return TRUE;
+
+printf("%s %s not found\n", type, name);
+return FALSE;
}