From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id DE103386180B for ; Fri, 30 Aug 2024 17:21:37 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DE103386180B Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org DE103386180B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1725038506; cv=none; b=PPAYMeisbXbuA577MUQiH8t0kTWiy4w2tYKNtcUCFkAN1yCGMvl2NMt8QcO+LL1CK4Yf50QBe+NFG3AiHgZjnb+HHuy6kJeOildXqdcYcZi1OgKxZ8/65l98TigJy3nraNDiTCjp8/dsuadXRVAc0NEFw0iuMDp9NYAQFHJviPk= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1725038506; c=relaxed/simple; bh=Sy1Cjw1y0mV+/4/8tMlq+BE/Hhfx1ALdqYTnAErtXPM=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=p4N36xdqBANNYqLSL8Z2H4CaKwaMSTNqoi5KI9Tuqhn8Sl8kY8HaJjArr8JxSqBjpPhvWRQPjnc3X9iiqo8YSPNDS8DieT/+kBVfH9nWcXWHtfUfqqpqeOKG+CVat5+5fYLpOK6rcJH+Y0JhyYBi49Riernad+3gsdOrWxUhWgA= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1725038497; h=from:from:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type:in-reply-to:in-reply-to: references:references; bh=lXbB5W5M2Lv+DDcWbQDRQ4+lcwDr5K8q2koDdPL0O58=; b=ixj49InWbq3NqJ/x4ozLyUynwMd0Bh32SWGVPYS6MxU6YKeNYJHYyAPVf2ZFqzXpDUdkdF y0/r2eRiyENlFks9ttcbxHLWdi5zSeBx8OzuUw49ye6lO3UiglFYw+ERP837pS9TI7iTDR 5USOgbxkW+YrWiY5Icvu3/yx+9vJbfU= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-282-wnzFz6QNNtibTa42o7Ux2g-1; Fri, 30 Aug 2024 13:21:36 -0400 X-MC-Unique: wnzFz6QNNtibTa42o7Ux2g-1 Received: from mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.40]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 811AF19560AB for ; Fri, 30 Aug 2024 17:21:35 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.45.224.29]) by mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 8A50B19560A3; Fri, 30 Aug 2024 17:21:34 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.17.1/8.17.1) with ESMTPS id 47UHLVpm1737772 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Fri, 30 Aug 2024 19:21:31 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 47UHLVBM1737771; Fri, 30 Aug 2024 19:21:31 +0200 Date: Fri, 30 Aug 2024 19:21:31 +0200 From: Jakub Jelinek To: Joseph Myers Cc: Marek Polacek , gcc-patches@gcc.gnu.org, Jason Merrill Subject: [PATCH] libcpp, v3: Add support for gnu::offset #embed/__has_embed parameter Message-ID: Reply-To: Jakub Jelinek References: MIME-Version: 1.0 In-Reply-To: X-Scanned-By: MIMEDefang 3.0 on 10.30.177.40 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=us-ascii Content-Disposition: inline X-Spam-Status: No, score=-3.6 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On Fri, Aug 16, 2024 at 04:58:58PM +0000, Joseph Myers wrote: > On Thu, 15 Aug 2024, Jakub Jelinek wrote: > > > + else > > + { > > + if (res > INTTYPE_MAXIMUM (off_t)) > > + cpp_error_with_line (pfile, CPP_DL_ERROR, loc, 0, > > + "too large 'gnu::offset' argument"); > > Having a testcase for this diagnostic would be a good idea. Also one for > a negative argument for gnu::offset (the errors for negative arguments are > already tested for limit, but I think testing that for gnu::offset is a > good idea as well). Here is an updated patch which does that: 2024-08-30 Jakub Jelinek libcpp/ * internal.h (struct cpp_embed_params): Add offset member. * directives.cc (EMBED_PARAMS): Add gnu::offset entry. (enum embed_param_kind): Add NUM_EMBED_STD_PARAMS. (_cpp_parse_embed_params): Use NUM_EMBED_STD_PARAMS rather than NUM_EMBED_PARAMS when parsing standard parameters. Parse gnu::offset parameter. * files.cc (struct _cpp_file): Add offset member. (_cpp_stack_embed): Handle params->offset. gcc/ * doc/cpp.texi (Binary Resource Inclusion): Document gnu::offset #embed parameter. gcc/testsuite/ * c-c++-common/cpp/embed-15.c: New test. * c-c++-common/cpp/embed-16.c: New test. * gcc.dg/cpp/embed-5.c: New test. --- libcpp/internal.h.jj 2024-08-15 10:29:44.422065173 +0200 +++ libcpp/internal.h 2024-08-15 11:26:00.726026264 +0200 @@ -630,7 +630,7 @@ struct cpp_embed_params { location_t loc; bool has_embed; - cpp_num_part limit; + cpp_num_part limit, offset; cpp_embed_params_tokens prefix, suffix, if_empty; }; --- libcpp/directives.cc.jj 2024-08-15 11:02:56.653279609 +0200 +++ libcpp/directives.cc 2024-08-15 11:39:49.476685559 +0200 @@ -1014,13 +1014,15 @@ skip_balanced_token_seq (cpp_reader *pfi EMBED_PARAM (LIMIT, "limit") \ EMBED_PARAM (PREFIX, "prefix") \ EMBED_PARAM (SUFFIX, "suffix") \ - EMBED_PARAM (IF_EMPTY, "if_empty") + EMBED_PARAM (IF_EMPTY, "if_empty") \ + EMBED_PARAM (GNU_OFFSET, "offset") enum embed_param_kind { #define EMBED_PARAM(c, s) EMBED_PARAM_##c, EMBED_PARAMS #undef EMBED_PARAM - NUM_EMBED_PARAMS + NUM_EMBED_PARAMS, + NUM_EMBED_STD_PARAMS = EMBED_PARAM_IF_EMPTY + 1 }; static struct { int len; const char *name; } embed_params[NUM_EMBED_PARAMS] = { @@ -1120,7 +1122,18 @@ _cpp_parse_embed_params (cpp_reader *pfi size_t param_kind = -1; if (param_prefix == NULL) { - for (size_t i = 0; i < NUM_EMBED_PARAMS; ++i) + for (size_t i = 0; i < NUM_EMBED_STD_PARAMS; ++i) + if (param_name_len == embed_params[i].len + && memcmp (param_name, embed_params[i].name, + param_name_len) == 0) + { + param_kind = i; + break; + } + } + else if (param_prefix_len == 3 && memcmp (param_prefix, "gnu", 3) == 0) + { + for (size_t i = NUM_EMBED_STD_PARAMS; i < NUM_EMBED_PARAMS; ++i) if (param_name_len == embed_params[i].len && memcmp (param_name, embed_params[i].name, param_name_len) == 0) @@ -1157,12 +1170,23 @@ _cpp_parse_embed_params (cpp_reader *pfi if (param_kind != (size_t) -1 && token->type != CPP_OPEN_PAREN) cpp_error_with_line (pfile, CPP_DL_ERROR, loc, 0, "expected '('"); - else if (param_kind == EMBED_PARAM_LIMIT) + else if (param_kind == EMBED_PARAM_LIMIT + || param_kind == EMBED_PARAM_GNU_OFFSET) { - if (params->has_embed && pfile->op_stack == NULL) - _cpp_expand_op_stack (pfile); - params->limit = _cpp_parse_expr (pfile, "#embed", token); - token = _cpp_get_token_no_padding (pfile); + if (params->has_embed && pfile->op_stack == NULL) + _cpp_expand_op_stack (pfile); + cpp_num_part res = _cpp_parse_expr (pfile, "#embed", token); + if (param_kind == EMBED_PARAM_LIMIT) + params->limit = res; + else + { + if (res > INTTYPE_MAXIMUM (off_t)) + cpp_error_with_line (pfile, CPP_DL_ERROR, loc, 0, + "too large 'gnu::offset' argument"); + else + params->offset = res; + } + token = _cpp_get_token_no_padding (pfile); } else if (token->type == CPP_OPEN_PAREN) { --- libcpp/files.cc.jj 2024-08-15 10:29:44.479064462 +0200 +++ libcpp/files.cc 2024-08-15 11:26:00.727026251 +0200 @@ -90,6 +90,9 @@ struct _cpp_file /* Size for #embed, perhaps smaller than st.st_size. */ size_t limit; + /* Offset for #embed. */ + off_t offset; + /* File descriptor. Invalid if -1, otherwise open. */ int fd; @@ -1243,8 +1246,11 @@ _cpp_stack_embed (cpp_reader *pfile, con _cpp_file *orig_file = file; if (file->buffer_valid && (!S_ISREG (file->st.st_mode) - || (file->limit < file->st.st_size + (size_t) 0 - && file->limit < params->limit))) + || file->offset + (cpp_num_part) 0 > params->offset + || (file->limit < file->st.st_size - file->offset + (size_t) 0 + && (params->offset - file->offset > (cpp_num_part) file->limit + || file->limit - (params->offset + - file->offset) < params->limit)))) { bool found = false; if (S_ISREG (file->st.st_mode)) @@ -1257,8 +1263,13 @@ _cpp_stack_embed (cpp_reader *pfile, con && strcmp (file->path, file->next_file->path) == 0) { file = file->next_file; - if (file->limit >= file->st.st_size + (size_t) 0 - || file->limit >= params->limit) + if (file->offset + (cpp_num_part) 0 <= params->offset + && (file->limit >= (file->st.st_size - file->offset + + (size_t) 0) + || (params->offset + - file->offset <= (cpp_num_part) file->limit + && file->limit - (params->offset + - file->offset) >= params->limit))) { found = true; break; @@ -1314,8 +1325,10 @@ _cpp_stack_embed (cpp_reader *pfile, con if (regular) { cpp_num_part limit; - if (file->st.st_size + (cpp_num_part) 0 < params->limit) - limit = file->st.st_size; + if (file->st.st_size + (cpp_num_part) 0 < params->offset) + limit = 0; + else if (file->st.st_size - params->offset < params->limit) + limit = file->st.st_size - params->offset; else limit = params->limit; if (params->has_embed) @@ -1326,6 +1339,14 @@ _cpp_stack_embed (cpp_reader *pfile, con "%s is too large", file->path); goto fail; } + if (lseek (file->fd, params->offset, SEEK_CUR) + != (off_t) params->offset) + { + cpp_errno_filename (pfile, CPP_DL_ERROR, file->path, + params->loc); + goto fail; + } + file->offset = params->offset; file->limit = limit; size = limit; } @@ -1338,6 +1359,38 @@ _cpp_stack_embed (cpp_reader *pfile, con buf = XNEWVEC (uchar, size ? size : 1); total = 0; + if (!regular && params->offset) + { + uchar *buf2 = buf; + ssize_t size2 = size; + cpp_num_part total2 = params->offset; + + if (params->offset > 8 * 1024 && size < 8 * 1024) + { + size2 = 32 * 1024; + buf2 = XNEWVEC (uchar, size2); + } + do + { + if ((cpp_num_part) size2 > total2) + size2 = total2; + count = read (file->fd, buf2, size2); + if (count < 0) + { + cpp_errno_filename (pfile, CPP_DL_ERROR, file->path, + params->loc); + if (buf2 != buf) + free (buf2); + free (buf); + goto fail; + } + total2 -= count; + } + while (total2); + if (buf2 != buf) + free (buf2); + } + while ((count = read (file->fd, buf + total, size - total)) > 0) { total += count; @@ -1378,7 +1431,10 @@ _cpp_stack_embed (cpp_reader *pfile, con file->limit = total; } else if (!regular) - file->limit = total; + { + file->offset = params->offset; + file->limit = total; + } file->buffer_start = buf; file->buffer = buf; @@ -1387,9 +1443,22 @@ _cpp_stack_embed (cpp_reader *pfile, con file->fd = -1; } else if (params->has_embed) - return file->limit && params->limit ? 1 : 2; + { + if (params->offset - file->offset > file->limit) + return 2; + size_t limit = file->limit - (params->offset - file->offset); + return limit && params->limit ? 1 : 2; + } + const uchar *buffer = file->buffer; size_t limit = file->limit; + if (params->offset - file->offset > limit) + limit = 0; + else + { + buffer += params->offset - file->offset; + limit -= params->offset - file->offset; + } if (params->limit < limit) limit = params->limit; @@ -1413,20 +1482,20 @@ _cpp_stack_embed (cpp_reader *pfile, con size_t len = 0; for (size_t i = 0; i < limit; ++i) { - if (file->buffer[i] < 10) + if (buffer[i] < 10) len += 2; - else if (file->buffer[i] < 100) + else if (buffer[i] < 100) len += 3; #if UCHAR_MAX == 255 else len += 4; #else - else if (file->buffer[i] < 1000) + else if (buffer[i] < 1000) len += 4; else { char buf[64]; - len += sprintf (buf, "%d", file->buffer[i]) + 1; + len += sprintf (buf, "%d", buffer[i]) + 1; } #endif if (len > INTTYPE_MAXIMUM (ssize_t)) @@ -1480,7 +1549,7 @@ _cpp_stack_embed (cpp_reader *pfile, con if (i == 0) tok->flags |= PREV_WHITE; tok->val.str.text = s; - tok->val.str.len = sprintf ((char *) s, "%d", file->buffer[i]); + tok->val.str.len = sprintf ((char *) s, "%d", buffer[i]); s += tok->val.str.len + 1; if (tok == &pfile->directive_result) tok = toks; --- gcc/doc/cpp.texi.jj 2024-08-15 10:29:44.567063363 +0200 +++ gcc/doc/cpp.texi 2024-08-15 11:26:00.728026239 +0200 @@ -3966,8 +3966,8 @@ treated the same), followed by parameter with currently supported standard parameters @code{limit}, @code{prefix}, @code{suffix} and @code{if_empty}, or implementation defined parameters specified by a unique vendor prefix followed by @code{::} followed by -name of the parameter. GCC will use the @code{gnu} prefix but currently -doesn't support any extensions. +name of the parameter. GCC uses the @code{gnu} prefix for vendor +parameters and currently supports the @code{gnu::offset} parameter. The @code{limit} parameter argument is a constant expression which specifies the maximum number of bytes included by the directive, @@ -3977,6 +3977,10 @@ that sequence is not empty and @code{if_ sequence which is used as expansion for @code{#embed} directive if the resource is empty. +The @code{gnu::offset} parameter argument is a constant expression +which specifies how many bytes to skip from the start of the resource. +@code{limit} is then counted from that position. + The @code{#embed} directive is not supported in the Traditional Mode (@pxref{Traditional Mode}). --- gcc/testsuite/c-c++-common/cpp/embed-15.c.jj 2024-08-15 11:26:00.728026239 +0200 +++ gcc/testsuite/c-c++-common/cpp/embed-15.c 2024-08-15 11:26:00.728026239 +0200 @@ -0,0 +1,88 @@ +/* { dg-do run } */ +/* { dg-options "--embed-dir=${srcdir}/c-c++-common/cpp/embed-dir" } */ +/* { dg-additional-options "-std=gnu99" { target c } } */ + +#if __has_embed (__FILE__ gnu::offset (4 + FOOBAR) limit (3)) != __STDC_EMBED_FOUND__ +#error "__has_embed fail" +#endif + +#embed limit(1) gnu::offset (0) prefix(int a = ) suffix (;) +#embed limit(1) __gnu__::offset (1 * 1) prefix(int b = ) suffix (;) +#embed limit(1) gnu::__offset__ (1 + 1) prefix(int c = ) suffix (;) +#embed __limit__(1) __gnu__::__offset__ (1 + (1 \ + + 1)) __prefix__(int d = ) __suffix__ (;) +const unsigned char e[] = { + #embed limit(5) gnu::offset (999) +}; +const unsigned char f[] = { + #embed limit(7) gnu::offset (998) +}; +const unsigned char g[] = { + #embed limit(8) gnu::offset (998) +}; +const unsigned char h[] = { + #embed limit(8) gnu::offset (997) +}; +const unsigned char i[] = { + #embed limit(9) gnu::offset (997) +}; +const unsigned char j[] = { + #embed limit(30) gnu::offset (990) +}; +const unsigned char k[] = { + #embed limit(26) gnu::offset (992) +}; +const unsigned char l[] = { + #embed +}; +const unsigned char m[] = { + #embed __limit__ (1000) __gnu__::__offset__ (32) +}; +#if __has_embed ( limit(5) gnu::offset (999)) != __STDC_EMBED_FOUND__ \ + || __has_embed ( limit(5) gnu::offset (999)) != __STDC_EMBED_FOUND__ \ + || __has_embed ( limit(7) gnu::offset (998)) != __STDC_EMBED_FOUND__ \ + || __has_embed ( limit(8) gnu::offset (998)) != __STDC_EMBED_FOUND__ \ + || __has_embed ( limit(8) gnu::offset (997)) != __STDC_EMBED_FOUND__ \ + || __has_embed ( limit(9) gnu::offset (997)) != __STDC_EMBED_FOUND__ \ + || __has_embed ( limit(30) gnu::offset (990)) != __STDC_EMBED_FOUND__ \ + || __has_embed ( limit(26) gnu::offset (992)) != __STDC_EMBED_FOUND__ \ + || __has_embed () != __STDC_EMBED_FOUND__ \ + || __has_embed ( limit(26) gnu::offset (992)) != __STDC_EMBED_FOUND__ +#error "__has_embed fail" +#endif + +#ifdef __cplusplus +#define C "C" +#else +#define C +#endif +extern C void abort (void); +extern C int memcmp (const void *, const void *, __SIZE_TYPE__); + +int +main () +{ + if (a != 'H' || b != 'e' || c != 'n' || d != 'r') + abort (); + if (sizeof (e) != 5 + || sizeof (f) != 7 + || sizeof (g) != 8 + || sizeof (h) != 8 + || sizeof (i) != 9 + || sizeof (j) != 30 + || sizeof (k) != 26 + || sizeof (l) < 1032 + || sizeof (m) != 1000) + abort (); + if (memcmp (e, l + 999, 5) + || memcmp (f, l + 998, 7) + || memcmp (g, l + 998, 8) + || memcmp (h, l + 997, 8) + || memcmp (i, l + 997, 9) + || memcmp (j, l + 990, 30) + || memcmp (k, l + 992, 26) + || memcmp (m, l + 32, 1000)) + abort (); + if (l[0] != 'H' || l[1] != 'e' || l[2] != 'n' || l[3] != 'r') + abort (); +} --- gcc/testsuite/c-c++-common/cpp/embed-16.c.jj 2024-08-15 11:26:00.728026239 +0200 +++ gcc/testsuite/c-c++-common/cpp/embed-16.c 2024-08-30 17:40:18.543982478 +0200 @@ -0,0 +1,31 @@ +/* { dg-do preprocess } */ +/* { dg-options "" } */ + +#embed __FILE__ gnu::offset(1) gnu::offset(1) /* { dg-error "duplicate embed parameter 'gnu::offset'" } */ +#embed __FILE__ gnu::offset prefix() suffix() /* { dg-error "expected '\\\('" } */ +#embed __FILE__ gnu::offset (1 / 0) /* { dg-error "division by zero in #embed" } */ +#embed __FILE__ __gnu__::__offset__ (+ + +) /* { dg-error "operator '\\\+' has no right operand" } */ +#define FOO 1 +#embed __FILE__ gnu::offset(0 + defined(FOO)) /* { dg-error "'defined' in #embed parameter" } */ +#embed __FILE__ gnu::offset (-1) /* { dg-error "negative embed parameter operand" } */ +#embed __FILE__ gnu::offset (-42) /* { dg-error "negative embed parameter operand" } */ +#embed __FILE__ gnu::offset (-9223372036854775807 - 1) /* { dg-error "negative embed parameter operand" } */ +#embed __FILE__ gnu::offset (18446744073709551615ULL) /* { dg-error "too large 'gnu::offset' argument" } */ +#if 1 + __has_embed (__FILE__ gnu::offset(1) __gnu__::__offset__(1)) /* { dg-error "duplicate embed parameter 'gnu::offset'" } */ +#endif +#if 1 + __has_embed (__FILE__ __gnu__::__offset__ prefix() suffix()) /* { dg-error "expected '\\\('" } */ +#endif +#if 1 + __has_embed (__FILE__ gnu::offset(1/0)) /* { dg-error "division by zero in #embed" } */ +#endif +#if 1 + __has_embed (__FILE__ gnu::offset(+ + +)) /* { dg-error "operator '\\\+' has no right operand" } */ +#endif +#if 1 + __has_embed (__FILE__ gnu::offset(0 + defined(FOO))) /* { dg-error "'defined' in #embed parameter" } */ +#endif +#if 1 + __has_embed (__FILE__ gnu::offset (-1)) /* { dg-error "negative embed parameter operand" } */ +#endif +#if 1 + __has_embed (__FILE__ gnu::offset (-42)) /* { dg-error "negative embed parameter operand" } */ +#endif +#if 1 + __has_embed (__FILE__ gnu::offset (-9223372036854775807 - 1)) /* { dg-error "negative embed parameter operand" } */ +#endif +#if 1 + __has_embed (__FILE__ gnu::offset (18446744073709551615ULL)) /* { dg-error "too large 'gnu::offset' argument" } */ +#endif --- gcc/testsuite/gcc.dg/cpp/embed-5.c.jj 2024-08-15 11:26:00.728026239 +0200 +++ gcc/testsuite/gcc.dg/cpp/embed-5.c 2024-08-15 11:26:00.728026239 +0200 @@ -0,0 +1,4 @@ +/* { dg-do run } */ +/* { dg-options "-std=c23 -pedantic-errors --embed-dir=${srcdir}/c-c++-common/cpp/embed-dir" } */ + +#include "../../c-c++-common/cpp/embed-15.c" Jakub