From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 22E12C3DA7A for ; Fri, 6 Jan 2023 15:50:36 +0000 (UTC) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id BFFC3853FD; Fri, 6 Jan 2023 16:50:33 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmx.de Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; secure) header.d=gmx.de header.i=@gmx.de header.b="oJ9Or0ZR"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id D6D8C8549F; Fri, 6 Jan 2023 16:50:31 +0100 (CET) Received: from mout.gmx.net (mout.gmx.net [212.227.15.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id DE464853E0 for ; Fri, 6 Jan 2023 16:50:26 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmx.de Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=xypron.glpk@gmx.de DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.de; s=s31663417; t=1673020225; bh=o/sfYBDaY+XGL6Uob/2if0UHCIribT4YQWq36d6L2lA=; h=X-UI-Sender-Class:Date:Subject:To:Cc:References:From:In-Reply-To; b=oJ9Or0ZRiXlQfgeXj7MEMwaUr/PKmkhzo7jQggkGLX/6vjOgpbp9Q+pgl4HYVZnek owKw/zHlBhw3artvvCkDeQv1qO4rLnoUspnrNPd9Hl9n3R1UX3VvbY8MlywWckPB4N iS5j8LQosEnDRMdOiqGjnYbtPsto8cvA5ZVP7wzGh9eKCQ3tQgESC+T1t1lOmoqwff TRTcflTCjJpKVNun/cjmeyIgjcj1JUyYmbpfxRt3voO4Tk/8yDIjyVel6yFB3EOKj8 Yx9NNvexLEgv1BBdhuPzp4/VSPhPU/yNaHqmfO7PuwYgiZfr5tBnZEMgndqNhYK2Ue klXvCRPxKAMLg== X-UI-Sender-Class: 724b4f7f-cbec-4199-ad4e-598c01a50d3a Received: from [192.168.123.67] ([88.152.145.137]) by mail.gmx.net (mrgmx004 [212.227.17.190]) with ESMTPSA (Nemesis) id 1MTiU3-1pLm0428gn-00U2Jh; Fri, 06 Jan 2023 16:50:25 +0100 Message-ID: <55849b76-29b0-a20e-8ea7-1b384f37f611@gmx.de> Date: Fri, 6 Jan 2023 16:50:24 +0100 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.6.0 Subject: Re: [PATCH v3 02/25] cli: Move readline character-processing to a state machine Content-Language: en-US To: Simon Glass Cc: Anatolij Gustschin , Tom Rini , U-Boot Mailing List References: <20230106145243.411626-1-sjg@chromium.org> <20230106145243.411626-3-sjg@chromium.org> From: Heinrich Schuchardt In-Reply-To: <20230106145243.411626-3-sjg@chromium.org> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: quoted-printable X-Provags-ID: V03:K1:9vhY9FOEQ+YRPJKFC8J+U3ROtFNIxYCao9yhHoBjJEBPbaiMpT3 PNGA7kT/GOMEDztIKK7ABhOImx6u0ojwhWiptHAqiCid43aqGPZldZzAPu+lIP6eJ0rU9fj LyHtTzX8pGToDN/eN7CzZTfvyXgFFuqq7AR5DYhH+G5XIWlX1dQvypI8U3SpA+lWzim6W8M G+LzCBN60PlHxzAjktDQQ== UI-OutboundReport: notjunk:1;M01:P0:k2N3uF5+SN0=;8RKd1gKKMf4sb0Rs8Huvs8sQmo9 Weapg248q91wv8PJ1wSunNpNZ1emp2zZv6/G33qOn1DPG6Z2ax3zyjg7ohr7Iv7TRhk6Jhvvi Td8X371sUlAX96BGO2wcoBYyCgXdyXTLKMhQHVfliDHfgnZge5g01OkBYsbKFip+6w6E6iGcF Y71whHki6g9zn/BJjR75PehEsagbbIcDLxtH3egcIQNr+opdhfstDnmFMVkp67HQL+Ud6aXVV yXo2MyQh8uPf3Cah4FFG892DygZ6nqmo1GAacM7MZhwpGopqSkDEjrvLE1Z1WU8gcu7Rl+/YY 4ripaJksgXiAHijAb/h5pzqtpvr9Na4CZFvP/f2VQ0Rwrl+vtapOpvX9zWzRqxte0onEQJk4Z Wm5DFDQazt8Cnjiol9UfL5uEngjW17SWV6HkZjh2oUosJtcba2JQ0O+beYhsC/evp4rGISLy0 Vtwh2cQdnQ0SiU3C7yOkkHP/6SPyq/gy196W3YaEntsz7Kx2HV+5ondUbdJk46gtA2mZgoEFL ODDNZrxvL2n9nk1COlnSjwUhN1a40lWKj3IvbjMVmzrxO1Lwh+u+jxFslSJz9wXBCrZKdxvED xGa5l+jYyL0D1uLal839jcw8Brjtq19V99J6AGJYxLqZytOK7f1BQCfn0l0iHIjJ87fcyc5qe gmorCjIuC+aco+93KfsIlb5Tj8/Nhp5Aa+7Cmypu2YhBBI3OtZvecm2u9IIERfCbCpiGfKbFy h6lWnAxse/JwUETypNePao5JQp9NGGlxcFW3KwxU1OHF2OQWiD7MWzQIzY5JDWAW9waWwD9Ee DPn3zK9FaqHoWcbPD1YpX0mV8WusrJEbwDMinBaYsKKJmcmxgQZlwnuAZCFlELzvGTmamfDIx kQpR6EDyLqYMdQdPmy+NgKuAx3MKBjm8AFobvXMg3tDRQqjSLgKfLKHUzj7JPYYeHKnqFu6gX 9spLikKkXAnJZ44XLd7gFRjEKPk= X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.6 at phobos.denx.de X-Virus-Status: Clean On 1/6/23 15:52, Simon Glass wrote: > The current cread_line() function is very long. It handles the escape > processing inline. The menu command does similar processing but at the > character level, so there is some duplication. > > Split the character processing into a new function cli_ch_process() whic= h > processes individual characters and returns the resulting input characte= r, > taking account of escape sequences. It requires the caller to set up and > maintain its state. > > Update cread_line() to use this new function. > > The only intended functional change is that an invalid escape sequence > does not add invalid/control characters into the input buffer, but inste= ad > discards these. > > Signed-off-by: Simon Glass > --- > > (no changes since v1) > > common/Makefile | 6 +- > common/cli_getch.c | 204 ++++++++++++++++++++++++++++++++++++++++++ > common/cli_readline.c | 150 +++++-------------------------- > include/cli.h | 72 +++++++++++++++ > 4 files changed, 301 insertions(+), 131 deletions(-) > create mode 100644 common/cli_getch.c > > diff --git a/common/Makefile b/common/Makefile > index 20addfb244c..67485e77a04 100644 > --- a/common/Makefile > +++ b/common/Makefile > @@ -38,7 +38,7 @@ obj-$(CONFIG_SPLASH_SOURCE) +=3D splash_source.o > obj-$(CONFIG_MENU) +=3D menu.o > obj-$(CONFIG_UPDATE_COMMON) +=3D update.o > obj-$(CONFIG_USB_KEYBOARD) +=3D usb_kbd.o > -obj-$(CONFIG_CMDLINE) +=3D cli_readline.o cli_simple.o > +obj-$(CONFIG_CMDLINE) +=3D cli_getch.o cli_readline.o cli_simple.o > > endif # !CONFIG_SPL_BUILD > > @@ -93,8 +93,8 @@ obj-y +=3D eeprom/eeprom_field.o eeprom/eeprom_layout.= o > endif > > obj-y +=3D cli.o > -obj-$(CONFIG_FSL_DDR_INTERACTIVE) +=3D cli_simple.o cli_readline.o > -obj-$(CONFIG_STM32MP1_DDR_INTERACTIVE) +=3D cli_simple.o cli_readline.o > +obj-$(CONFIG_FSL_DDR_INTERACTIVE) +=3D cli_getch.o cli_simple.o cli_rea= dline.o > +obj-$(CONFIG_STM32MP1_DDR_INTERACTIVE) +=3D cli_getch.o cli_simple.o cl= i_readline.o > obj-$(CONFIG_DFU_OVER_USB) +=3D dfu.o > obj-y +=3D command.o > obj-$(CONFIG_$(SPL_TPL_)LOG) +=3D log.o > diff --git a/common/cli_getch.c b/common/cli_getch.c > new file mode 100644 > index 00000000000..9eeea7fef29 > --- /dev/null > +++ b/common/cli_getch.c > @@ -0,0 +1,204 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * (C) Copyright 2000 > + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. > + * > + * Copyright 2022 Google LLC > + */ > + > +#include > +#include > + > +/** > + * enum cli_esc_state_t - indicates what to do with an escape character > + * > + * @ESC_REJECT: Invalid escape sequence, so the esc_save[] characters a= re > + * returned from each subsequent call to cli_ch_esc() > + * @ESC_SAVE: Character should be saved in esc_save until we have anoth= er one > + * @ESC_CONVERTED: Escape sequence has been completed and the resulting > + * character is available > + */ > +enum cli_esc_state_t { > + ESC_REJECT, > + ESC_SAVE, > + ESC_CONVERTED > +}; > + > +void cli_ch_init(struct cli_ch_state *cch) > +{ > + memset(cch, '\0', sizeof(*cch)); > +} > + > +/** > + * cli_ch_esc() - Process a character in an ongoing escape sequence > + * > + * @cch: State information > + * @ichar: Character to process > + * @actp: Returns the action to take > + * Returns: Output character if *actp is ESC_CONVERTED, else 0 > + */ > +static int cli_ch_esc(struct cli_ch_state *cch, int ichar, > + enum cli_esc_state_t *actp) > +{ > + enum cli_esc_state_t act =3D ESC_REJECT; > + > + switch (cch->esc_len) { > + case 1: > + if (ichar =3D=3D '[' || ichar =3D=3D 'O') > + act =3D ESC_SAVE; > + break; > + case 2: > + switch (ichar) { > + case 'D': /* <- key */ > + ichar =3D CTL_CH('b'); > + act =3D ESC_CONVERTED; > + break; /* pass off to ^B handler */ > + case 'C': /* -> key */ > + ichar =3D CTL_CH('f'); > + act =3D ESC_CONVERTED; > + break; /* pass off to ^F handler */ > + case 'H': /* Home key */ > + ichar =3D CTL_CH('a'); > + act =3D ESC_CONVERTED; > + break; /* pass off to ^A handler */ > + case 'F': /* End key */ > + ichar =3D CTL_CH('e'); > + act =3D ESC_CONVERTED; > + break; /* pass off to ^E handler */ > + case 'A': /* up arrow */ > + ichar =3D CTL_CH('p'); > + act =3D ESC_CONVERTED; > + break; /* pass off to ^P handler */ > + case 'B': /* down arrow */ > + ichar =3D CTL_CH('n'); > + act =3D ESC_CONVERTED; > + break; /* pass off to ^N handler */ > + case '1': > + case '2': > + case '3': > + case '4': > + case '7': > + case '8': > + if (cch->esc_save[1] =3D=3D '[') { > + /* see if next character is ~ */ > + act =3D ESC_SAVE; > + } > + break; > + } > + break; > + case 3: > + switch (ichar) { > + case '~': > + switch (cch->esc_save[2]) { > + case '3': /* Delete key */ > + ichar =3D CTL_CH('d'); > + act =3D ESC_CONVERTED; > + break; /* pass to ^D handler */ > + case '1': /* Home key */ > + case '7': > + ichar =3D CTL_CH('a'); > + act =3D ESC_CONVERTED; > + break; /* pass to ^A handler */ > + case '4': /* End key */ > + case '8': > + ichar =3D CTL_CH('e'); > + act =3D ESC_CONVERTED; > + break; /* pass to ^E handler */ > + } > + break; > + case '0': > + if (cch->esc_save[2] =3D=3D '2') > + act =3D ESC_SAVE; > + break; > + } > + break; > + case 4: > + switch (ichar) { > + case '0': > + case '1': > + act =3D ESC_SAVE; > + break; /* bracketed paste */ > + } > + break; > + case 5: > + if (ichar =3D=3D '~') { /* bracketed paste */ > + ichar =3D 0; > + act =3D ESC_CONVERTED; > + } > + } > + > + *actp =3D act; > + > + return act =3D=3D ESC_CONVERTED ? ichar : 0; > +} > + > +int cli_ch_process(struct cli_ch_state *cch, int ichar) > +{ > + /* > + * ichar=3D0x0 when error occurs in U-Boot getchar() or when the calle= r > + * wants to check if there are more characters saved in the escape > + * sequence > + */ > + if (!ichar) { > + if (cch->emit_upto) { > + if (cch->emit_upto < cch->esc_len) > + return cch->esc_save[cch->emit_upto++]; > + cch->emit_upto =3D 0; > + } > + return 0; > + } else if (ichar =3D=3D -ETIMEDOUT) { > + /* > + * If we are in an escape sequence but nothing has followed the > + * Escape character, then the user probably just pressed the > + * Escape key. Return it and clear the sequence. > + */ > + if (cch->esc_len) { > + cch->esc_len =3D 0; > + return '\e'; > + } > + > + /* Otherwise there is nothing to return */ > + return 0; > + } > + > + if (ichar =3D=3D '\n' || ichar =3D=3D '\r') > + return '\n'; > + > + /* handle standard linux xterm esc sequences for arrow key, etc. */ > + if (cch->esc_len !=3D 0) { > + enum cli_esc_state_t act; > + > + ichar =3D cli_ch_esc(cch, ichar, &act); > + > + switch (act) { > + case ESC_SAVE: > + /* save this character and return nothing */ > + cch->esc_save[cch->esc_len++] =3D ichar; > + return 0; > + case ESC_REJECT: > + /* > + * invalid escape sequence, start returning the > + * characters in it > + */ > + cch->esc_save[cch->esc_len++] =3D ichar; > + return cch->esc_save[cch->emit_upto++]; > + case ESC_CONVERTED: > + /* valid escape sequence, return the resulting char */ > + cch->esc_len =3D 0; > + return ichar; > + } > + } > + > + if (ichar =3D=3D '\e') { > + if (!cch->esc_len) { > + cch->esc_save[cch->esc_len] =3D ichar; > + cch->esc_len =3D 1; > + } else { > + puts("impossible condition #876\n"); > + cch->esc_len =3D 0; > + } > + return 0; > + } > + > + return ichar; > +} > diff --git a/common/cli_readline.c b/common/cli_readline.c > index d6444f5fc1d..709e9c3d38b 100644 > --- a/common/cli_readline.c > +++ b/common/cli_readline.c > @@ -62,7 +62,6 @@ static char *delete_char (char *buffer, char *p, int *= colp, int *np, int plen) > > #define putnstr(str, n) printf("%.*s", (int)n, str) > > -#define CTL_CH(c) ((c) - 'a' + 1) > #define CTL_BACKSPACE ('\b') > #define DEL ((char)255) > #define DEL7 ((char)127) > @@ -252,156 +251,53 @@ static void cread_add_str(char *str, int strsize,= int insert, > static int cread_line(const char *const prompt, char *buf, unsigned in= t *len, > int timeout) > { > + struct cli_ch_state s_cch, *cch =3D &s_cch; > unsigned long num =3D 0; > unsigned long eol_num =3D 0; > unsigned long wlen; > char ichar; > int insert =3D 1; > - int esc_len =3D 0; > - char esc_save[8]; > int init_len =3D strlen(buf); > int first =3D 1; > > + cli_ch_init(cch); > + > if (init_len) > cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); > > while (1) { > - if (bootretry_tstc_timeout()) > - return -2; /* timed out */ > - if (first && timeout) { > - uint64_t etime =3D endtick(timeout); > - > - while (!tstc()) { /* while no incoming data */ > - if (get_ticks() >=3D etime) > - return -2; /* timed out */ > - schedule(); > + /* Check for saved characters */ > + ichar =3D cli_ch_process(cch, 0); > + > + if (!ichar) { > + if (bootretry_tstc_timeout()) > + return -2; /* timed out */ > + if (first && timeout) { > + u64 etime =3D endtick(timeout); > + > + while (!tstc()) { /* while no incoming data */ > + if (get_ticks() >=3D etime) > + return -2; /* timed out */ > + schedule(); > + } > + first =3D 0; > } > - first =3D 0; > + > + ichar =3D getcmd_getch(); > } > > - ichar =3D getcmd_getch(); > + ichar =3D cli_ch_process(cch, ichar); > > /* ichar=3D0x0 when error occurs in U-Boot getc */ > if (!ichar) > continue; > > - if ((ichar =3D=3D '\n') || (ichar =3D=3D '\r')) { > + if (ichar =3D=3D '\n') { > putc('\n'); > break; > } > > - /* > - * handle standard linux xterm esc sequences for arrow key, etc. > - */ > - if (esc_len !=3D 0) { > - enum { ESC_REJECT, ESC_SAVE, ESC_CONVERTED } act =3D ESC_REJECT; > - > - if (esc_len =3D=3D 1) { > - if (ichar =3D=3D '[' || ichar =3D=3D 'O') > - act =3D ESC_SAVE; > - } else if (esc_len =3D=3D 2) { > - switch (ichar) { > - case 'D': /* <- key */ > - ichar =3D CTL_CH('b'); > - act =3D ESC_CONVERTED; > - break; /* pass off to ^B handler */ We have similar code to decode escape sequences and Unicode letters in efi_cin_read_key_stroke_ex(). The big difference in the EFI code is that it does not use dummy control characters but uses a structure: struct efi_key_data { =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0struct efi_input_key key; =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0struct efi_key_state key_= state; }; with struct efi_input_key { =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0u16 scan_code; =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0s16 unicode_char; }; The special keys are represented as numbers in scan_code while characters are represented in unicode_char. Separating special keys from characters is much cleaner than creating dummy control characters. Anyway you would be able to first create the structure above and then in a separate step to convert to control characters. The code below misses to interprete some special keys like FN1 - FN12 which could be quite useful for navigating a UI. Best regards Heinrich > - case 'C': /* -> key */ > - ichar =3D CTL_CH('f'); > - act =3D ESC_CONVERTED; > - break; /* pass off to ^F handler */ > - case 'H': /* Home key */ > - ichar =3D CTL_CH('a'); > - act =3D ESC_CONVERTED; > - break; /* pass off to ^A handler */ > - case 'F': /* End key */ > - ichar =3D CTL_CH('e'); > - act =3D ESC_CONVERTED; > - break; /* pass off to ^E handler */ > - case 'A': /* up arrow */ > - ichar =3D CTL_CH('p'); > - act =3D ESC_CONVERTED; > - break; /* pass off to ^P handler */ > - case 'B': /* down arrow */ > - ichar =3D CTL_CH('n'); > - act =3D ESC_CONVERTED; > - break; /* pass off to ^N handler */ > - case '1': > - case '2': > - case '3': > - case '4': > - case '7': > - case '8': > - if (esc_save[1] =3D=3D '[') { > - /* see if next character is ~ */ > - act =3D ESC_SAVE; > - } > - break; > - } > - } else if (esc_len =3D=3D 3) { > - switch (ichar) { > - case '~': > - switch (esc_save[2]) { > - case '3': /* Delete key */ > - ichar =3D CTL_CH('d'); > - act =3D ESC_CONVERTED; > - break; /* pass to ^D handler */ > - case '1': /* Home key */ > - case '7': > - ichar =3D CTL_CH('a'); > - act =3D ESC_CONVERTED; > - break; /* pass to ^A handler */ > - case '4': /* End key */ > - case '8': > - ichar =3D CTL_CH('e'); > - act =3D ESC_CONVERTED; > - break; /* pass to ^E handler */ > - } > - break; > - case '0': > - if (esc_save[2] =3D=3D '2') > - act =3D ESC_SAVE; > - break; > - } > - } else if (esc_len =3D=3D 4) { > - switch (ichar) { > - case '0': > - case '1': > - act =3D ESC_SAVE; > - break; /* bracketed paste */ > - } > - } else if (esc_len =3D=3D 5) { > - if (ichar =3D=3D '~') { /* bracketed paste */ > - ichar =3D 0; > - act =3D ESC_CONVERTED; > - } > - } > - switch (act) { > - case ESC_SAVE: > - esc_save[esc_len++] =3D ichar; > - continue; > - case ESC_REJECT: > - esc_save[esc_len++] =3D ichar; > - cread_add_str(esc_save, esc_len, insert, > - &num, &eol_num, buf, *len); > - esc_len =3D 0; > - continue; > - case ESC_CONVERTED: > - esc_len =3D 0; > - break; > - } > - } > - > switch (ichar) { > - case 0x1b: > - if (esc_len =3D=3D 0) { > - esc_save[esc_len] =3D ichar; > - esc_len =3D 1; > - } else { > - puts("impossible condition #876\n"); > - esc_len =3D 0; > - } > - break; > - > case CTL_CH('a'): > BEGINNING_OF_LINE(); > break; > @@ -470,8 +366,6 @@ static int cread_line(const char *const prompt, char= *buf, unsigned int *len, > { > char *hline; > > - esc_len =3D 0; > - > if (ichar =3D=3D CTL_CH('p')) > hline =3D hist_prev(); > else > diff --git a/include/cli.h b/include/cli.h > index ba5b8ebd36e..863519e4b13 100644 > --- a/include/cli.h > +++ b/include/cli.h > @@ -7,6 +7,21 @@ > #ifndef __CLI_H > #define __CLI_H > > +#include > + > +/** > + * struct cli_ch_state - state information for reading cmdline characte= rs > + * > + * @esc_len: Number of escape characters read so far > + * @esc_save: Escape characters collected so far > + * @emit_upto: Next character to emit from esc_save (0 if not emitting) > + */ > +struct cli_ch_state { > + int esc_len; > + char esc_save[8]; > + int emit_upto; > +}; > + > /** > * Go into the command loop > * > @@ -154,5 +169,62 @@ void cli_loop(void); > void cli_init(void); > > #define endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbcl= k()) > +#define CTL_CH(c) ((c) - 'a' + 1) > + > +/** > + * cli_ch_init() - Set up the initial state to process input characters > + * > + * @cch: State to set up > + */ > +void cli_ch_init(struct cli_ch_state *cch); > + > +/** > + * cli_ch_process() - Process an input character > + * > + * When @ichar is 0, this function returns any characters from an inval= id escape > + * sequence which are still pending in the buffer > + * > + * Otherwise it processes the input character. If it is an escape chara= cter, > + * then an escape sequence is started and the function returns 0. If we= are in > + * the middle of an escape sequence, the character is processed and may= result > + * in returning 0 (if more characters are needed) or a valid character = (if > + * @ichar finishes the sequence). > + * > + * If @ichar is a valid character and there is no escape sequence in pr= ogress, > + * then it is returned as is. > + * > + * If the Enter key is pressed, '\n' is returned. > + * > + * Usage should be like this:: > + * > + * struct cli_ch_state cch; > + * > + * cli_ch_init(cch); > + * do > + * { > + * int ichar, ch; > + * > + * ichar =3D cli_ch_process(cch, 0); > + * if (!ichar) { > + * ch =3D getchar(); > + * ichar =3D cli_ch_process(cch, ch); > + * } > + * (handle the ichar character) > + * } while (!done) > + * > + * If tstc() is used to look for keypresses, this function can be calle= d with > + * @ichar set to -ETIMEDOUT if there is no character after 5-10ms. This= allows > + * the ambgiuity between the Escape key and the arrow keys (which gener= ate an > + * escape character followed by other characters) to be resolved. > + * > + * @cch: Current state > + * @ichar: Input character to process, or 0 if none, or -ETIMEDOUT if n= o > + * character has been received within a small number of milliseconds (t= his > + * cancels any existing escape sequence and allows pressing the Escape = key to > + * work) > + * Returns: Resulting input character after processing, 0 if none, '\e'= if > + * an existing escape sequence was cancelled > + */ > +int cli_ch_process(struct cli_ch_state *cch, int ichar); > > #endif