From d7a6d947dfdbb41d806f4bd65f8a74bffb5acbb4 Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Mon, 13 Feb 2023 10:48:05 -0500 Subject: [PATCH] fix for $_ when executing $PROMPT_COMMAND; fix for extra line in history after here-document in command substitution --- CWRU/CWRU.chlog | 37 ++++++++++++++--- bashline.c | 21 ++++------ builtins/declare.def | 16 ++++++-- doc/bash.1 | 4 +- doc/bashref.texi | 4 +- examples/loadables/mkdir.c | 80 +++++++++++++++++++++++-------------- execute_cmd.c | 3 +- execute_cmd.h | 1 + parse.y | 24 ++++++++++- po/zh_TW.gmo | Bin 168812 -> 168819 bytes po/zh_TW.po | 20 +++++----- 11 files changed, 139 insertions(+), 71 deletions(-) diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 51dbb384b..8f41fe846 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -5272,8 +5272,9 @@ arrayfunc.c --- parse.y - discard_until: now returns the last character read. If we get an - EOF while processing a comment, return EOF - - shell_getc: if discard_until returns EOF, return yacc_EOF + EOF while processing a comment, return EOF (rarely happens; + shell_getc returns EOF only under certain circumstances) + - read_token: if discard_until returns EOF, return yacc_EOF input.h - bclearerror: new macro, clears the B_ERROR flag in the buffered @@ -5294,7 +5295,31 @@ doc/{bash.1,bashref.texi} 2/8 --- bashline.c - - alias_expand_line: if alias_expand doesn't change rl_line_buffer, - just return right away without changing rl_point. Inspired by a - report from Addison Brendtro - - history_expand_line: ditto + - set_up_new__line: if the new line doesn't change rl_line_buffer, + just return right away without changing rl_point. Affects + alias-expand-line, history-expand-line, and history-and-alias-expand-line. + Inspired by a report from + Addison Brendtro + +builtins/declare.def + - declare_internal: make multiple calls to `local -' at the same + context have no effect after the first one. That way we don't keep + overwriting the saved option set. Report and fix from + Emanuele Torre + + 2/10 + ---- +execute_cmd.[ch] + - bind_lastarg: now a public function + +parse.y + - execute_variable_command: call bind_lastarg instead of bind_variable + to avoid exporting $_ if allexport is set. Fix from + Emanuele Torre + +bashhist.c + - history_delimiting_chars: if we're parsing some kind of delimited + construct that's *not* a quoted string, don't bother adding an extra + newline to the history if the current history entry already ends in + a newline. From https://savannah.gnu.org/support/?110838 via + Ganapathi Kamath diff --git a/bashline.c b/bashline.c index f46657593..dfe5ab9bf 100644 --- a/bashline.c +++ b/bashline.c @@ -2713,6 +2713,13 @@ set_up_new_line (char *new_line) { int old_point, at_end; + /* If we didn't expand anything, don't change anything. */ + if (STREQ (new_line, rl_line_buffer)) + { + free (new_line); + return; + } + old_point = rl_point; at_end = rl_point == rl_end; @@ -2741,13 +2748,6 @@ alias_expand_line (int count, int ignore) new_line = alias_expand (rl_line_buffer); - /* If we didn't expand anything, don't change anything. */ - if (new_line && STREQ (new_line, rl_line_buffer)) - { - free (new_line); - return (0); - } - if (new_line) { set_up_new_line (new_line); @@ -2770,13 +2770,6 @@ history_expand_line (int count, int ignore) new_line = history_expand_line_internal (rl_line_buffer); - /* If we didn't expand anything, don't change anything. */ - if (new_line && STREQ (new_line, rl_line_buffer)) - { - free (new_line); - return (0); - } - if (new_line) { set_up_new_line (new_line); diff --git a/builtins/declare.def b/builtins/declare.def index f7dac7287..28100de1b 100644 --- a/builtins/declare.def +++ b/builtins/declare.def @@ -409,11 +409,19 @@ declare_internal (WORD_LIST *list, int local_var) if (local_var && variable_context && STREQ (name, "-")) { + int o; + + o = localvar_inherit; + localvar_inherit = 0; var = make_local_variable ("-", 0); - FREE (value_cell (var)); /* just in case */ - value = get_current_options (); - var_setvalue (var, value); - VSETATTR (var, att_invisible); + localvar_inherit = o; + + if (value_cell (var) == NULL) /* no duplicate instances */ + { + value = get_current_options (); + var_setvalue (var, value); + VSETATTR (var, att_invisible); + } NEXT_VARIABLE (); } diff --git a/doc/bash.1 b/doc/bash.1 index 4efb67652..376e347e0 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -9278,7 +9278,9 @@ is used within a function, it causes the variable to have a visible scope restricted to that function and its children. If \fIname\fP is \-, the set of shell options is made local to the function in which \fBlocal\fP is invoked: shell options changed using the -\fBset\fP builtin inside the function are restored to their original values +\fBset\fP builtin inside the function +after the call to \fBlocal\fP +are restored to their original values when the function returns. The restore is effected as if a series of \fBset\fP commands were executed to restore the values that were in place before the function. diff --git a/doc/bashref.texi b/doc/bashref.texi index 5b44ce74e..bd24d7151 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -4787,7 +4787,9 @@ The @var{option} can be any of the options accepted by @code{declare}. children. If @var{name} is @samp{-}, the set of shell options is made local to the function in which @code{local} is invoked: shell options changed using -the @code{set} builtin inside the function are restored to their original +the @code{set} builtin inside the function +after the call to @code{local} +are restored to their original values when the function returns. The restore is effected as if a series of @code{set} commands were executed to restore the values that were in place before the function. diff --git a/examples/loadables/mkdir.c b/examples/loadables/mkdir.c index b0df8f0d6..55ffbd07d 100644 --- a/examples/loadables/mkdir.c +++ b/examples/loadables/mkdir.c @@ -3,7 +3,7 @@ /* See Makefile for compilation details. */ /* - Copyright (C) 1999-2009,2022 Free Software Foundation, Inc. + Copyright (C) 1999-2009,2022-2023 Free Software Foundation, Inc. This file is part of GNU Bash. Bash is free software: you can redistribute it and/or modify @@ -135,10 +135,12 @@ mkdir_builtin (WORD_LIST *list) static int make_path (char *path, int user_mode, int nmode, int parent_mode) { - int oumask; + mode_t oumask; struct stat sb; char *p, *npath; + int tail; + /* If we don't have to do any work, don't do any work. */ if (stat (path, &sb) == 0) { if (S_ISDIR (sb.st_mode) == 0) @@ -166,48 +168,64 @@ make_path (char *path, int user_mode, int nmode, int parent_mode) while (*p == '/') p++; - while (p = strchr (p, '/')) + tail = 0; + while (tail == 0) { - *p = '\0'; - if (stat (npath, &sb) != 0) + if (*p == '\0') + tail = 1; + else + p = strchr (p, '/'); + if (p) + *p = '\0'; + else + tail = 1; + if (mkdir (npath, 0) < 0) { - if (mkdir (npath, 0)) + /* "Each dir operand that names an existing directory shall be + ignored without error." */ + if (errno == EEXIST || errno == EISDIR) { - builtin_error ("cannot create directory `%s': %s", npath, strerror (errno)); - umask (original_umask); - free (npath); - return 1; + int e = errno; + int fail = 0; + + if (stat (npath, &sb) != 0) + { + fail = 1; + builtin_error ("cannot create directory `%s': %s", npath, strerror (e)); + } + else if (e == EEXIST && S_ISDIR (sb.st_mode) == 0) + { + fail = 1; + builtin_error ("`%s': file exists but is not a directory", npath); + } + if (fail) + { + umask (original_umask); + free (npath); + return 1; + } } - if (chmod (npath, parent_mode) != 0) + else { - builtin_error ("cannot chmod directory `%s': %s", npath, strerror (errno)); + builtin_error ("cannot create directory `%s': %s", npath, strerror (errno)); umask (original_umask); free (npath); return 1; } } - else if (S_ISDIR (sb.st_mode) == 0) - { - builtin_error ("`%s': file exists but is not a directory", npath); - umask (original_umask); - free (npath); - return 1; - } - - *p++ = '/'; /* restore slash */ - while (*p == '/') + if (chmod (npath, (tail == 0) ? parent_mode : nmode) != 0) + { + builtin_error ("cannot chmod directory `%s': %s", npath, strerror (errno)); + umask (original_umask); + free (npath); + return 1; + } + if (tail == 0) + *p++ = '/'; /* restore slash */ + while (p && *p == '/') /* skip consecutive slashes or trailing slash */ p++; } - /* Create the final directory component. */ - if (stat (npath, &sb) && mkdir (npath, nmode)) - { - builtin_error ("cannot create directory `%s': %s", npath, strerror (errno)); - umask (original_umask); - free (npath); - return 1; - } - umask (original_umask); free (npath); return 0; diff --git a/execute_cmd.c b/execute_cmd.c index d4c082d04..185404099 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -121,7 +121,6 @@ extern int close (int); /* Static functions defined and used in this file. */ static void close_pipes (int, int); static void do_piping (int, int); -static void bind_lastarg (char *); static int shell_control_structure (enum command_type); static void cleanup_redirects (REDIRECT *); @@ -4000,7 +3999,7 @@ execute_cond_command (COND_COM *cond_command) } #endif /* COND_COMMAND */ -static void +void bind_lastarg (char *arg) { SHELL_VAR *var; diff --git a/execute_cmd.h b/execute_cmd.h index 37e386ab1..d9c3e7b13 100644 --- a/execute_cmd.h +++ b/execute_cmd.h @@ -120,4 +120,5 @@ extern void close_all_files (void); extern void restore_funcarray_state (struct func_array_state *); #endif +extern void bind_lastarg (char *); #endif /* _EXECUTE_CMD_H_ */ diff --git a/parse.y b/parse.y index 379fdaadb..9178e9a7d 100644 --- a/parse.y +++ b/parse.y @@ -2849,7 +2849,7 @@ execute_variable_command (const char *command, const char *vname) parse_and_execute (savestring (command), vname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_NOOPTIMIZE); restore_parser_state (&ps); - bind_variable ("_", last_lastarg, 0); + bind_lastarg (last_lastarg); FREE (last_lastarg); if (token_to_read == '\n') /* reset_parser was called */ @@ -5636,7 +5636,27 @@ history_delimiting_chars (const char *line) last_was_heredoc = 0; if (dstack.delimiter_depth != 0) - return ("\n"); + { + char ch; + HIST_ENTRY *current; + size_t curlen; + + ch = current_delimiter(dstack); + if (shellquote(ch)) + return ("\n"); + else if (ch == '(') /* ) and maybe for other non-quote-char delimiters */ + { + using_history (); + current = previous_history (); + curlen = current ? strlen (current->line) : 0; + /* If we're not reading some kind of quoted string, don't bother + adding a newline if the current history entry already ends in a + newline. It's just extra. */ + return ((curlen > 0 && current->line[curlen - 1] == '\n') ? "" : "\n"); + } + else + return ("\n"); + } /* We look for current_command_line_count == 2 because we are looking to add the first line of the body of the here document (the second line diff --git a/po/zh_TW.gmo b/po/zh_TW.gmo index 9dd7546f07248c44a395e1f7799c47f982dd372b..a56fe55c75266bcd659458f9740b30e4b2516bf1 100644 GIT binary patch delta 3791 zc-k$Nc~q3w8OGmx0T)uE;u737iZR3mQKRH&G$yf_7)?}2jIFgXVF@TGy8<6DFl@s% zM1hEgO0e31rCJ>|s69;@PitzeY1GCofPnGAXSIGzwdC93HyRXPvuovc6E0jzHyTFY~WJslw{+Jh5DLIaL4XCt8 zWVMn4%-`CoMEi(b*{0-8{N7%tB0|XXO;W}4kdr37nPKQcfj+YnY4udj&T6(z$l_wOKsz=7Zb z3xfq1ECog}*%-V61scH1cf1_^aA4{kB?G`+;Pcpb0L;Vs z4-aS{<)M;EeMBT!NZbCMW zhCg^x_aymF@L4!;6zuC`iQEd(QU})`Oww`!2DeVrauo+7Lbb$0>sd3kyklOxP|Gio zWPV7Pv6#1n>mDUegI}3y38$e0i!q0RW1vx_>NVXHqFQh$_=(NmgZ*G%{*HmEg6rU9 z%*QO_!6o1m;0kaU7`KcE3&2;wdN3a6Zh$Xi9`S~j|3K>P;6jr#k>rI)IywS1X<|Rw zW*BI_hZirxz+_|dDlMzQCAJ=W$Ha1IQWff;b%i#56@+ZU{ba^UM= z5Y|I}&X-5Rh$-7x(3G(;YWA1Cr>&l>-hm%Rf``E%@M~}q_`se|h~oMy;7l+&ii*^O zPlAWQ@kYrgD)i9aKRBBEr-M(MsscxWmCj95@HO9Zdx$$7l%yUyWf% z3Suao3k(G7Z0?WY3s2c>kKxPz9mALU$7-2w6a}9(p|$4@3xl~B9E|0`AM6c%-Y4gS zzcL*QG_81C9CF1-42FV5aZI{T!ExXp;`q|*adco{yp|B7U_9@S08PJ%1D`RWXpW~u z7vlNye(M?YMc@Qu*yi^2H1Io{9qVbxcLPH;6&wS;2~GjiHt>Z_8|e6XFxa&41cq#S zLeJcil^D!0o+R+#fdoq2oPJMH!3iF|Q8I1dcisAZ>7 z3~U9vH!`OhHW>#{e_w23W4N`6hWjP)qTxw2JP&-z^!6m?fCF3!mL$>ft4UN~P%0^>Cm02DEn*WS-sZ@AVDwDDSv`j2pF<1efP36TC)2PUtG&-~reBP8@ z8e_Q|e9n|<8ecddosvJ5P6KbH(=lf{lk7L?%(2Vq3|&A5=iwQsf742{GAQBB48GuG z20gr!L1lb5GwDWcW=ES3{uIpHOb5TUd3!To+G`6P7y`aw8WT7XbZ_B3N4C&`i(4!v z&wxzEBqWoD7iID#k(pE?-(GLXWR4ulq+_3D((t8B=0dlf2?0~V^;tZBEsHJv zzgf(M04tj@3e4t36SGzI7gIZ=O340aib!NK5+0^Rea6JEd;+g8Br z?k?bkW1XymrB1fuEGKi}pp(ga+R1Yd?DeUIbRepbt-7|5q1*+|H1*$t!D3Utg?#Dc zA{vM-V%gOe@#6DEbi}uqoK?&$jw)uzwu4K|hea_B^)I24!%EoDCYDf{#U(7;SkN*H zIrhdzo2@16c*jcELcJx-h4H0)X;>*GTwY4a6HEE`+fd4C`Mi|5@U1<6SV{+nxp;1Y zi}N)u)W3;Ifs0=#wKn&<=*Urf<4G5l_};}Nno>qC1DBiRD`UNXUPcGI%lL`br<@Jv z>2jX`SveoHvz&i+m&z>;hE!0}r8cb!D$rU%=`YyytK|OBN*au-+9b6}90v0Uh8XItWC60Wvc?B<0Y zH!r;CrsMbAEa%bH{E+!^H5H8l@x|8D6-{e)6a{&Tf>iawelw1>q z@;co&>OS%oId6XW!SxUGyPEfRRn~i3ORsO=chi;Mb-2L0ZQr&+wX$YHsTx!hU8wr| zc&m=pWE85pKyOQ}w_=+))#CA1?7y+67Duq^2`*AclwVCv*PgnX8^!9&nh`EF$aAtp QwY;yLZcpl6b#KT20m-0~^Z)<= delta 3780 zc-k$Nc~n&Q702)I;fhM?jz-03iMR%+F{rIZjEPo-ntGC&%}7u|L6FTshItIju*|Rw zf@GAE)*1r_4b>`o+;V!-lhfF`CmO6Z5rK&@F*b%{k5&8q@qPcy=l$Mqx%Ym5_uhFw z?g_lTC$MGrNONv)k-knPPm0Jwml9ilk=0-yjIUHGnE_r0H!G2#DkV=~oaa??65}dR z86vUUloVmSqE?9!AaZ%Tl2`G*&8OrCj3eun90Mo*L5U0YuK!8N6wI${Ql>nIjouHH z?7_i*Z&q>-6RG=@RDx?;l)xYP>41_!;A3z&IOw2~6Z$~#G{#>aRx%a`?`u`E4GT{n zS5lAnt3OkchI9T0KE^oZpGxj%o>6i`&!07wEX0E7b4oh#!gWE(8*nK3qLQQFXP1;5 zf}z4Mlr&;Ir;QG^UsX~8eg|F#?f+8Z0aspAGEh5ojY@qF_Ua|_2po-ZzzuR7I2fF5 z;^BEbECnxs9&o7ktep$fEml};0)xO$+IdA6!69H97^od;r-S#~>4EJya6bPb3pug>ge~L+(R?`=EC0ZlEF4VCMlC6(FXB1sQS(6Ke(=f;}>Fh#^Pe z(C}e~G=pbH7?Oea_ECm(>jOp`QiierNkbNEg~l2Z10Dxg;r)PdhTzs@ZHOT+>nnST zhRY`yJxN{%&VmC6!TtfJ$gNOAd~p3(lGa1Z5*h}*tVAIU26@qC#$#P#Tf9BtA`O&)Y;JvW>MqrltisX%CgA+y035_lz! z1WH!~27}cWKTKeRpIB^5VB|j}Fw%fTLn5@I;4FQ$*4S?1;g@*WpU92hS_{+$G78+Q zI~J&0@#u}n6)iCs2|712>HY#f1)kiX{lPJ;o zBu3sRnRh-P{JA!4u_l=Yj#|8$OhXToc~z4(84?2i2AmG2ZeoNDo9OsCaEfl>n|NhY zQhLUwEXTtybW)^nV{-~6ZcCxGT`9aXN#(|I;IrU@R2tl3vCf)rO=ZMw;IF^|n+<8u zih(Vle=~E+m!=&+{hdl-s;E!AQ?q8~qvV0}nFjX;>x?h{@#GmdP?~$fTq# znarW9nO6VEGNhkQvMgqK45~Evw<83ss!bZnT8VA;@SFoVbl_5s z$>iy8=bcQp)9`#dBZ;+BiCk;mXJ?LlWT#`t?KJ$kow?w*=EvmnN+yF@;JRGyzn06E z{$nn4VSwr29SwHypb!V^KGH$Q62ZQ@V}bhr-Rq!%dIueBa030S&Jz zV53PY;Cw>?l{-^Fg}%4u#}smXOd-p9O`-0bs6P`AyR^jMFmPIt(etIVpolH@P!Y4+ zU&I4PxmX1+x!8)cT+D_2E++4%F7CT$%}*+(1FMVKs=dX$%AMd$UH^OVute8yF(VBv zp@Fy(mYuhR2cIjUBM&XkC}kF}F6EWgfXnoUMJWySE2EM_%Gl6C%BW0q8Ot^fH1$Nb zwXoh|QyDwn;WD<+f0r>A#<&@2q?-~hbyM<0H~)TpZdS_)H*?`jYuxRogF`&rH`~MU zs~*(9z7vOsUnpLSyF7H{ptbO050&WhFo~WnCl`Y+>*OnEy`LzjgZ^@UqNxftoCy`& z|GNqXR9C@2yU#03J_J=#(w8jSDycwIC8a-a@lhq$PpzWC_$vM5dd=;sY3$s7ygkqJPJDY_d8eoN=CR!!l?9!(M>?zOJ6hZw zB`&}3J^X&Nqo!*|Vb{+6-*^f;O6vT7Y^>{BtX3K&uDUmi)%XBxt;;J`zF>dj-j3Qz xtnJ*_+EIDH-}o+eV74`)L>*PV8-4!0p1M1w>L1wJdZJ97+@K$Fe^TAQ|3CFjk1PNH diff --git a/po/zh_TW.po b/po/zh_TW.po index 91724f83e..cae843920 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -11,7 +11,7 @@ msgstr "" "Project-Id-Version: bash 5.2-rc1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-01-11 14:50-0500\n" -"PO-Revision-Date: 2022-07-28 01:08+0800\n" +"PO-Revision-Date: 2023-02-09 00:40+0800\n" "Last-Translator: Yi-Jyun Pan \n" "Language-Team: Chinese (traditional) \n" "Language: zh_TW\n" @@ -20,7 +20,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Bugs: Report translation errors to the Language-Team address.\n" -"X-Generator: Poedit 3.1.1\n" +"X-Generator: Poedit 3.2.2\n" #: arrayfunc.c:66 msgid "bad array subscript" @@ -2762,7 +2762,7 @@ msgstr "" " 變更目前目錄至 <目錄>。預設的 <目錄> 是 shell 變數 HOME\n" " 的值。\n" " \n" -" 變數 CDPATH 定義了含有 <目錄> 的目錄搜尋路徑,其中不同的目錄名稱由冒號 (:)分隔。\n" +" 變數 CDPATH 定義了含有 <目錄> 的目錄搜尋路徑,其中不同的目錄名稱由冒號 (:) 分隔。\n" " 一個空的目錄名稱表示目前目錄。如果要切換到的 <目錄> 由斜線 (/) 開頭,則 CDPATH\n" " 變數不會被使用。\n" " \n" @@ -3188,7 +3188,7 @@ msgstr "" " Getopts 被 shell 過程用來將可定位的參數解析為選項。\n" " \n" " <選項字串> 字串包含待識別的選項字母。如果一個字母後面跟\n" -" 著分號,則該選項需要一個參數,這個參數應利用空格與選項分開。\n" +" 著冒號,則該選項需要一個參數,這個參數應利用空格與選項分開。\n" " \n" " 每次呼叫時,getopts 會將下一個選項放到 shell 變數 $name\n" " 中,如果 name 變數不存在則先將其初始化,而下一個待處\n" @@ -5007,22 +5007,22 @@ msgstr "" "常用 shell 變數名稱和使用。\n" " \n" " BASH_VERSION\t目前 Bash 的版本資訊。\n" -" CDPATH\t用於「cd」指令參數搜尋分號分隔的目錄列表\n" +" CDPATH\t傳入「cd」作為引數,冒號分隔的欲搜尋目錄清單\n" " GLOBIGNORE\t路徑擴充套件時忽略的檔名符合模式列表,\n" -" \t\t以分號分隔。\n" -" HISTFILE\t您的指令歷史記錄存放的檔案名稱。\n" +" \t\t以冒號分隔。\n" +" HISTFILE\t您的命令歷史記錄存放的檔案名稱。\n" " HISTFILESIZE\t歷史記錄檔案最多可以儲存的列數。\n" -" HISTSIZE\t一個執行的 shell 最多可以訪問的歷史記錄指令列數。\n" +" HISTSIZE\t一個執行的 shell 最多可以存取的歷史記錄命令列數。\n" " HOME\t您登入目錄的完整路徑。\n" " HOSTNAME\t目前主機的主機名稱。\n" " HOSTTYPE\t目前版本的 BASH 在其之上執行的 CPU 類型。\n" " IGNOREEOF\t控制 shell 收到檔案結束符做為單一輸入後的\n" " \t\t動作。如果設定這個變數,則它的值是 shell 結束之前在\n" " \t\t一個空列上可以連續看到的檔案結束符數量(預設為 10)。\n" -" \t\t未設定時,檔案結束符標誌著輸入的結束。\n" +" \t\t未設定時,檔案結束符旗標著輸入的結束。\n" " MACHTYPE\t描述目前執行 Bash 的系統字串。\n" " MAILCHECK\tBash 檢測新郵件的頻率,以秒為單位。\n" -" MAILPATH\tBash 從中檢測新郵件的檔案列表,以分號分隔。\n" +" MAILPATH\tBash 從中檢測新郵件的檔案列表,以冒號分隔。\n" " OSTYPE\t執行 Bash 的 Unix 版本。\n" " PATH\t當尋找指令時搜尋的目錄列表,以冒號分隔。\n" " PROMPT_COMMAND\t印出每一個主提示符之前執行的命\n" -- 2.47.3