2021-10-01 05:37:01

by Masahiro Yamada

[permalink] [raw]
Subject: [PATCH 01/10] kconfig: remove 'const' from the return type of sym_escape_string_value()

sym_escape_string_value() returns a malloc'ed memory, but as
(const char *). So, it must be casted to (void *) when it is free'd.
This is odd.

The return type of sym_escape_string_value() should be (char *).

I exploited that free(NULL) has no effect.

Signed-off-by: Masahiro Yamada <[email protected]>
---

scripts/kconfig/conf.c | 15 +++++++--------
scripts/kconfig/confdata.c | 27 ++++++++++++++-------------
scripts/kconfig/lkc_proto.h | 2 +-
scripts/kconfig/symbol.c | 3 ++-
4 files changed, 24 insertions(+), 23 deletions(-)

diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index 5d84b44a2a2a..310fdd408793 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -647,17 +647,16 @@ static void check_conf(struct menu *menu)
switch (input_mode) {
case listnewconfig:
if (sym->name) {
- const char *str;
+ const char *val = sym_get_string_value(sym);
+ char *escaped = NULL;

if (sym->type == S_STRING) {
- str = sym_get_string_value(sym);
- str = sym_escape_string_value(str);
- printf("%s%s=%s\n", CONFIG_, sym->name, str);
- free((void *)str);
- } else {
- str = sym_get_string_value(sym);
- printf("%s%s=%s\n", CONFIG_, sym->name, str);
+ escaped = sym_escape_string_value(val);
+ val = escaped;
}
+
+ printf("%s%s=%s\n", CONFIG_, sym->name, val);
+ free(escaped);
}
break;
case helpnewconfig:
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index cf72680cd769..9b2271eb43d6 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -728,21 +728,22 @@ static struct conf_printer header_printer_cb =
static void conf_write_symbol(FILE *fp, struct symbol *sym,
struct conf_printer *printer, void *printer_arg)
{
- const char *str;
+ const char *val;
+ char *escaped = NULL;

- switch (sym->type) {
- case S_UNKNOWN:
- break;
- case S_STRING:
- str = sym_get_string_value(sym);
- str = sym_escape_string_value(str);
- printer->print_symbol(fp, sym, str, printer_arg);
- free((void *)str);
- break;
- default:
- str = sym_get_string_value(sym);
- printer->print_symbol(fp, sym, str, printer_arg);
+ if (sym->type == S_UNKNOWN)
+ return;
+
+ val = sym_get_string_value(sym);
+
+ if (sym->type == S_STRING) {
+ escaped = sym_escape_string_value(val);
+ val = escaped;
}
+
+ printer->print_symbol(fp, sym, val, printer_arg);
+
+ free(escaped);
}

static void
diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h
index a11626bdc421..e6955df49973 100644
--- a/scripts/kconfig/lkc_proto.h
+++ b/scripts/kconfig/lkc_proto.h
@@ -18,7 +18,7 @@ extern struct symbol * symbol_hash[SYMBOL_HASHSIZE];

struct symbol * sym_lookup(const char *name, int flags);
struct symbol * sym_find(const char *name);
-const char * sym_escape_string_value(const char *in);
+char *sym_escape_string_value(const char *in);
struct symbol ** sym_re_search(const char *pattern);
const char * sym_type_name(enum symbol_type type);
void sym_calc_value(struct symbol *sym);
diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c
index 5844d636d38f..6bf8665a6a0f 100644
--- a/scripts/kconfig/symbol.c
+++ b/scripts/kconfig/symbol.c
@@ -871,7 +871,8 @@ struct symbol *sym_find(const char *name)
return symbol;
}

-const char *sym_escape_string_value(const char *in)
+/* The returned pointer must be freed on the caller side */
+char *sym_escape_string_value(const char *in)
{
const char *p;
size_t reslen;
--
2.30.2


2021-10-01 05:37:17

by Masahiro Yamada

[permalink] [raw]
Subject: [PATCH 09/10] kconfig: refactor conf_touch_dep()

If this function fails to touch a dummy header due to missing parent
directory, then it creates it and touches the file again.

This was needed because CONFIG_FOO_BAR was previously tracked by
include/config/foo/bar.h. (include/config/foo/ may not exist here)

This is no longer the case since commit 0e0345b77ac4 ("kbuild: redo
fake deps at include/config/*.h"); now all the fake headers are placed
right under include/config/, like include/config/FOO_BAR.

Do not try to create parent directory, include/config/, which already
exists.

Signed-off-by: Masahiro Yamada <[email protected]>
---

scripts/kconfig/confdata.c | 22 ++++------------------
1 file changed, 4 insertions(+), 18 deletions(-)

diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index 75e45e69d660..9ece2f3b61a6 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -130,31 +130,17 @@ static size_t depfile_prefix_len;
/* touch depfile for symbol 'name' */
static int conf_touch_dep(const char *name)
{
- int fd, ret;
- char *d;
+ int fd;

/* check overflow: prefix + name + '\0' must fit in buffer. */
if (depfile_prefix_len + strlen(name) + 1 > sizeof(depfile_path))
return -1;

- d = depfile_path + depfile_prefix_len;
- strcpy(d, name);
+ strcpy(depfile_path + depfile_prefix_len, name);

- /* Assume directory path already exists. */
fd = open(depfile_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
- if (fd == -1) {
- if (errno != ENOENT)
- return -1;
-
- ret = make_parent_dir(depfile_path);
- if (ret)
- return ret;
-
- /* Try it again. */
- fd = open(depfile_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
- if (fd == -1)
- return -1;
- }
+ if (fd == -1)
+ return -1;
close(fd);

return 0;
--
2.30.2

2021-10-01 06:00:34

by Masahiro Yamada

[permalink] [raw]
Subject: [PATCH 03/10] kconfig: refactor conf_write_symbol()

I do not think 'struct conf_printer' is so useful.

Add simple functions, print_symbol_for_*() to write out one symbol.

Signed-off-by: Masahiro Yamada <[email protected]>
---

scripts/kconfig/confdata.c | 136 ++++++++++++++++---------------------
1 file changed, 57 insertions(+), 79 deletions(-)

diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index ed1bb8ba971b..ce11e7442d12 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -11,6 +11,7 @@
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -159,10 +160,6 @@ static int conf_touch_dep(const char *name)
return 0;
}

-struct conf_printer {
- void (*print_symbol)(FILE *, struct symbol *, const char *, void *);
-};
-
static void conf_warning(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));

@@ -629,91 +626,52 @@ static void conf_write_heading(FILE *fp, const struct comment_style *cs)
* This printer is used when generating the resulting configuration after
* kconfig invocation and `defconfig' files. Unset symbol might be omitted by
* passing a non-NULL argument to the printer.
- *
*/
-static void
-kconfig_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
-{
-
- switch (sym->type) {
- case S_BOOLEAN:
- case S_TRISTATE:
- if (*value == 'n') {
- bool skip_unset = (arg != NULL);
-
- if (!skip_unset)
- fprintf(fp, "# %s%s is not set\n",
- CONFIG_, sym->name);
- return;
- }
- break;
- default:
- break;
- }
-
- fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, value);
-}
+enum output_n { OUTPUT_N, OUTPUT_N_AS_UNSET, OUTPUT_N_NONE };

-static struct conf_printer kconfig_printer_cb =
+static void __print_symbol(FILE *fp, struct symbol *sym, enum output_n output_n,
+ bool escape_string)
{
- .print_symbol = kconfig_print_symbol,
-};
+ const char *val;
+ char *escaped = NULL;

-/*
- * Header printer
- *
- * This printer is used when generating the `include/generated/autoconf.h' file.
- */
-static void
-header_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
-{
+ if (sym->type == S_UNKNOWN)
+ return;

- switch (sym->type) {
- case S_BOOLEAN:
- case S_TRISTATE: {
- const char *suffix = "";
+ val = sym_get_string_value(sym);

- switch (*value) {
- case 'n':
- break;
- case 'm':
- suffix = "_MODULE";
- /* fall through */
- default:
- fprintf(fp, "#define %s%s%s 1\n",
- CONFIG_, sym->name, suffix);
- }
- break;
+ if ((sym->type == S_BOOLEAN || sym->type == S_TRISTATE) &&
+ output_n != OUTPUT_N && *val == 'n') {
+ if (output_n == OUTPUT_N_AS_UNSET)
+ fprintf(fp, "# %s%s is not set\n", CONFIG_, sym->name);
+ return;
}
- case S_HEX: {
- const char *prefix = "";

- if (value[0] != '0' || (value[1] != 'x' && value[1] != 'X'))
- prefix = "0x";
- fprintf(fp, "#define %s%s %s%s\n",
- CONFIG_, sym->name, prefix, value);
- break;
- }
- case S_STRING:
- case S_INT:
- fprintf(fp, "#define %s%s %s\n",
- CONFIG_, sym->name, value);
- break;
- default:
- break;
+ if (sym->type == S_STRING && escape_string) {
+ escaped = sym_escape_string_value(val);
+ val = escaped;
}

+ fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, val);
+
+ free(escaped);
}

-static struct conf_printer header_printer_cb =
+static void print_symbol_for_dotconfig(FILE *fp, struct symbol *sym)
{
- .print_symbol = header_print_symbol,
-};
+ __print_symbol(fp, sym, OUTPUT_N_AS_UNSET, true);
+}
+
+static void print_symbol_for_autoconf(FILE *fp, struct symbol *sym)
+{
+ __print_symbol(fp, sym, OUTPUT_N_NONE, true);
+}

-static void conf_write_symbol(FILE *fp, struct symbol *sym,
- struct conf_printer *printer, void *printer_arg)
+static void print_symbol_for_c(FILE *fp, struct symbol *sym)
{
const char *val;
+ const char *sym_suffix = "";
+ const char *val_prefix = "";
char *escaped = NULL;

if (sym->type == S_UNKNOWN)
@@ -721,12 +679,32 @@ static void conf_write_symbol(FILE *fp, struct symbol *sym,

val = sym_get_string_value(sym);

- if (sym->type == S_STRING) {
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ switch (*val) {
+ case 'n':
+ return;
+ case 'm':
+ sym_suffix = "_MODULE";
+ /* fall through */
+ default:
+ val = "1";
+ }
+ break;
+ case S_HEX:
+ if (val[0] != '0' || (val[1] != 'x' && val[1] != 'X'))
+ val_prefix = "0x";
+ break;
+ case S_STRING:
escaped = sym_escape_string_value(val);
val = escaped;
+ default:
+ break;
}

- printer->print_symbol(fp, sym, val, printer_arg);
+ fprintf(fp, "#define %s%s%s %s%s\n", CONFIG_, sym->name, sym_suffix,
+ val_prefix, val);

free(escaped);
}
@@ -787,7 +765,7 @@ int conf_write_defconfig(const char *filename)
goto next_menu;
}
}
- conf_write_symbol(out, sym, &kconfig_printer_cb, NULL);
+ print_symbol_for_dotconfig(out, sym);
}
next_menu:
if (menu->list != NULL) {
@@ -874,7 +852,7 @@ int conf_write(const char *name)
need_newline = false;
}
sym->flags |= SYMBOL_WRITTEN;
- conf_write_symbol(out, sym, &kconfig_printer_cb, NULL);
+ print_symbol_for_dotconfig(out, sym);
}

next:
@@ -1060,8 +1038,8 @@ int conf_write_autoconf(int overwrite)
continue;

/* write symbols to auto.conf and autoconf.h */
- conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1);
- conf_write_symbol(out_h, sym, &header_printer_cb, NULL);
+ print_symbol_for_autoconf(out, sym);
+ print_symbol_for_c(out_h, sym);
}
fclose(out);
fclose(out_h);
--
2.30.2

2021-10-01 06:03:14

by Masahiro Yamada

[permalink] [raw]
Subject: [PATCH 02/10] kconfig: refactor conf_write_heading()

All the call sites of conf_write_heading() pass NULL to the third
argument, and it is not used in the function.

Also, the print_comment hooks are doing much more complex than
needed.

Rewrite the code.

Signed-off-by: Masahiro Yamada <[email protected]>
---

scripts/kconfig/confdata.c | 95 +++++++++++++-------------------------
1 file changed, 33 insertions(+), 62 deletions(-)

diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index 9b2271eb43d6..ed1bb8ba971b 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -161,7 +161,6 @@ static int conf_touch_dep(const char *name)

struct conf_printer {
void (*print_symbol)(FILE *, struct symbol *, const char *, void *);
- void (*print_comment)(FILE *, const char *, void *);
};

static void conf_warning(const char *fmt, ...)
@@ -594,6 +593,36 @@ int conf_read(const char *name)
return 0;
}

+struct comment_style {
+ const char *comment_line;
+ const char *comment_block_begin;
+ const char *comment_block_end;
+};
+
+static const struct comment_style comment_style_pound = {
+ .comment_line = "#",
+ .comment_block_begin = "#",
+ .comment_block_end = "#",
+};
+
+static const struct comment_style comment_style_c = {
+ .comment_line = " *",
+ .comment_block_begin = "/*",
+ .comment_block_end = " */",
+};
+
+static void conf_write_heading(FILE *fp, const struct comment_style *cs)
+{
+ fprintf(fp, "%s\n", cs->comment_block_begin);
+
+ fprintf(fp, "%s Automatically generated file; DO NOT EDIT.\n",
+ cs->comment_line);
+
+ fprintf(fp, "%s %s\n", cs->comment_line, rootmenu.prompt->text);
+
+ fprintf(fp, "%s\n", cs->comment_block_end);
+}
+
/*
* Kconfig configuration printer
*
@@ -625,30 +654,9 @@ kconfig_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, value);
}

-static void
-kconfig_print_comment(FILE *fp, const char *value, void *arg)
-{
- const char *p = value;
- size_t l;
-
- for (;;) {
- l = strcspn(p, "\n");
- fprintf(fp, "#");
- if (l) {
- fprintf(fp, " ");
- xfwrite(p, l, 1, fp);
- p += l;
- }
- fprintf(fp, "\n");
- if (*p++ == '\0')
- break;
- }
-}
-
static struct conf_printer kconfig_printer_cb =
{
.print_symbol = kconfig_print_symbol,
- .print_comment = kconfig_print_comment,
};

/*
@@ -697,32 +705,9 @@ header_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)

}

-static void
-header_print_comment(FILE *fp, const char *value, void *arg)
-{
- const char *p = value;
- size_t l;
-
- fprintf(fp, "/*\n");
- for (;;) {
- l = strcspn(p, "\n");
- fprintf(fp, " *");
- if (l) {
- fprintf(fp, " ");
- xfwrite(p, l, 1, fp);
- p += l;
- }
- fprintf(fp, "\n");
- if (*p++ == '\0')
- break;
- }
- fprintf(fp, " */\n");
-}
-
static struct conf_printer header_printer_cb =
{
.print_symbol = header_print_symbol,
- .print_comment = header_print_comment,
};

static void conf_write_symbol(FILE *fp, struct symbol *sym,
@@ -746,20 +731,6 @@ static void conf_write_symbol(FILE *fp, struct symbol *sym,
free(escaped);
}

-static void
-conf_write_heading(FILE *fp, struct conf_printer *printer, void *printer_arg)
-{
- char buf[256];
-
- snprintf(buf, sizeof(buf),
- "\n"
- "Automatically generated file; DO NOT EDIT.\n"
- "%s\n",
- rootmenu.prompt->text);
-
- printer->print_comment(fp, buf, printer_arg);
-}
-
/*
* Write out a minimal config.
* All values that has default values are skipped as this is redundant.
@@ -876,7 +847,7 @@ int conf_write(const char *name)
if (!out)
return 1;

- conf_write_heading(out, &kconfig_printer_cb, NULL);
+ conf_write_heading(out, &comment_style_pound);

if (!conf_get_changed())
sym_clear_all_valid();
@@ -1080,8 +1051,8 @@ int conf_write_autoconf(int overwrite)
return 1;
}

- conf_write_heading(out, &kconfig_printer_cb, NULL);
- conf_write_heading(out_h, &header_printer_cb, NULL);
+ conf_write_heading(out, &comment_style_pound);
+ conf_write_heading(out_h, &comment_style_c);

for_all_symbols(i, sym) {
sym_calc_value(sym);
--
2.30.2

2021-10-05 15:40:20

by Masahiro Yamada

[permalink] [raw]
Subject: Re: [PATCH 01/10] kconfig: remove 'const' from the return type of sym_escape_string_value()

On Fri, Oct 1, 2021 at 2:33 PM Masahiro Yamada <[email protected]> wrote:
>
> sym_escape_string_value() returns a malloc'ed memory, but as
> (const char *). So, it must be casted to (void *) when it is free'd.
> This is odd.
>
> The return type of sym_escape_string_value() should be (char *).
>
> I exploited that free(NULL) has no effect.
>
> Signed-off-by: Masahiro Yamada <[email protected]>
> ---


All applied to linux-kbuild.

I pushed 10/10 v3 to the for-next branch.


>
> scripts/kconfig/conf.c | 15 +++++++--------
> scripts/kconfig/confdata.c | 27 ++++++++++++++-------------
> scripts/kconfig/lkc_proto.h | 2 +-
> scripts/kconfig/symbol.c | 3 ++-
> 4 files changed, 24 insertions(+), 23 deletions(-)
>
> diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
> index 5d84b44a2a2a..310fdd408793 100644
> --- a/scripts/kconfig/conf.c
> +++ b/scripts/kconfig/conf.c
> @@ -647,17 +647,16 @@ static void check_conf(struct menu *menu)
> switch (input_mode) {
> case listnewconfig:
> if (sym->name) {
> - const char *str;
> + const char *val = sym_get_string_value(sym);
> + char *escaped = NULL;
>
> if (sym->type == S_STRING) {
> - str = sym_get_string_value(sym);
> - str = sym_escape_string_value(str);
> - printf("%s%s=%s\n", CONFIG_, sym->name, str);
> - free((void *)str);
> - } else {
> - str = sym_get_string_value(sym);
> - printf("%s%s=%s\n", CONFIG_, sym->name, str);
> + escaped = sym_escape_string_value(val);
> + val = escaped;
> }
> +
> + printf("%s%s=%s\n", CONFIG_, sym->name, val);
> + free(escaped);
> }
> break;
> case helpnewconfig:
> diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
> index cf72680cd769..9b2271eb43d6 100644
> --- a/scripts/kconfig/confdata.c
> +++ b/scripts/kconfig/confdata.c
> @@ -728,21 +728,22 @@ static struct conf_printer header_printer_cb =
> static void conf_write_symbol(FILE *fp, struct symbol *sym,
> struct conf_printer *printer, void *printer_arg)
> {
> - const char *str;
> + const char *val;
> + char *escaped = NULL;
>
> - switch (sym->type) {
> - case S_UNKNOWN:
> - break;
> - case S_STRING:
> - str = sym_get_string_value(sym);
> - str = sym_escape_string_value(str);
> - printer->print_symbol(fp, sym, str, printer_arg);
> - free((void *)str);
> - break;
> - default:
> - str = sym_get_string_value(sym);
> - printer->print_symbol(fp, sym, str, printer_arg);
> + if (sym->type == S_UNKNOWN)
> + return;
> +
> + val = sym_get_string_value(sym);
> +
> + if (sym->type == S_STRING) {
> + escaped = sym_escape_string_value(val);
> + val = escaped;
> }
> +
> + printer->print_symbol(fp, sym, val, printer_arg);
> +
> + free(escaped);
> }
>
> static void
> diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h
> index a11626bdc421..e6955df49973 100644
> --- a/scripts/kconfig/lkc_proto.h
> +++ b/scripts/kconfig/lkc_proto.h
> @@ -18,7 +18,7 @@ extern struct symbol * symbol_hash[SYMBOL_HASHSIZE];
>
> struct symbol * sym_lookup(const char *name, int flags);
> struct symbol * sym_find(const char *name);
> -const char * sym_escape_string_value(const char *in);
> +char *sym_escape_string_value(const char *in);
> struct symbol ** sym_re_search(const char *pattern);
> const char * sym_type_name(enum symbol_type type);
> void sym_calc_value(struct symbol *sym);
> diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c
> index 5844d636d38f..6bf8665a6a0f 100644
> --- a/scripts/kconfig/symbol.c
> +++ b/scripts/kconfig/symbol.c
> @@ -871,7 +871,8 @@ struct symbol *sym_find(const char *name)
> return symbol;
> }
>
> -const char *sym_escape_string_value(const char *in)
> +/* The returned pointer must be freed on the caller side */
> +char *sym_escape_string_value(const char *in)
> {
> const char *p;
> size_t reslen;
> --
> 2.30.2
>


--
Best Regards
Masahiro Yamada

2021-10-28 05:19:10

by Boris Kolpackov

[permalink] [raw]
Subject: Re: [PATCH 03/10] kconfig: refactor conf_write_symbol()

Masahiro Yamada <[email protected]> writes:

> +static void __print_symbol(FILE *fp, struct symbol *sym, enum output_n output_n,

Identifier that start with double underscore are reserved. The same
goes for __conf_write_autoconf() in another patch.

2021-11-05 11:25:09

by Masahiro Yamada

[permalink] [raw]
Subject: Re: [PATCH 03/10] kconfig: refactor conf_write_symbol()

On Thu, Oct 28, 2021 at 2:16 PM Boris Kolpackov <[email protected]> wrote:
>
> Masahiro Yamada <[email protected]> writes:
>
> > +static void __print_symbol(FILE *fp, struct symbol *sym, enum output_n output_n,
>
> Identifier that start with double underscore are reserved. The same
> goes for __conf_write_autoconf() in another patch.


Without this patch, there are some functions that start with double underscores.

For example,

__expr_eliminate_eq() in scripts/kconfig/expr.c
__expand_string() in scripts/kconfig/preprocess.c

Are they problematic as well?


--
Best Regards
Masahiro Yamada

2021-11-05 15:43:36

by Boris Kolpackov

[permalink] [raw]
Subject: Re: [PATCH 03/10] kconfig: refactor conf_write_symbol()

Masahiro Yamada <[email protected]> writes:

> Without this patch, there are some functions that start with double
> underscores.
>
> For example,
>
> __expr_eliminate_eq() in scripts/kconfig/expr.c
> __expand_string() in scripts/kconfig/preprocess.c
>
> Are they problematic as well?

Yes, they could be potentially problematic. Identifiers that start
with double underscores or with a single underscore followed by an
uppercase letter are reserved for use by the C implementation (for
example, as a pre-defined macro).