A small self-contained alternative to readline and libedit

Related tags

CLI linenoise


A minimal, zero-config, BSD licensed, readline replacement used in Redis, MongoDB, and Android.

  • Single and multi line editing mode with the usual key bindings implemented.
  • History handling.
  • Completion.
  • Hints (suggestions at the right of the prompt as you type).
  • About 1,100 lines of BSD license source code.
  • Only uses a subset of VT100 escapes (ANSI.SYS compatible).

Can a line editing library be 20k lines of code?

Line editing with some support for history is a really important feature for command line utilities. Instead of retyping almost the same stuff again and again it's just much better to hit the up arrow and edit on syntax errors, or in order to try a slightly different command. But apparently code dealing with terminals is some sort of Black Magic: readline is 30k lines of code, libedit 20k. Is it reasonable to link small utilities to huge libraries just to get a minimal support for line editing?

So what usually happens is either:

  • Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (Real world example of this problem: Tclsh).
  • Smaller programs not using a configure script not supporting line editing at all (A problem we had with Redis-cli for instance).

The result is a pollution of binaries without line editing support.

So I spent more or less two hours doing a reality check resulting in this little library: is it really needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporting line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to Linenoise if not.

Terminals, in 2010.

Apparently almost every terminal you can happen to use today has some kind of support for basic VT100 escape sequences. So I tried to write a lib using just very basic VT100 features. The resulting library appears to work everywhere I tried to use it, and now can work even on ANSI.SYS compatible terminals, since no VT220 specific sequences are used anymore.

The library is currently about 1100 lines of code. In order to use it in your project just look at the example.c file in the source distribution, it is trivial. Linenoise is BSD code, so you can use both in free software and commercial software.

Tested with...

  • Linux text only console ($TERM = linux)
  • Linux KDE terminal application ($TERM = xterm)
  • Linux xterm ($TERM = xterm)
  • Linux Buildroot ($TERM = vt100)
  • Mac OS X iTerm ($TERM = xterm)
  • Mac OS X default Terminal.app ($TERM = xterm)
  • OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen)
  • IBM AIX 6.1
  • FreeBSD xterm ($TERM = xterm)
  • Emacs comint mode ($TERM = dumb)

Please test it everywhere you can and report back!

Let's push this forward!

Patches should be provided in the respect of Linenoise sensibility for small easy to understand code.

Send feedbacks to antirez at gmail


Linenoise is very easy to use, and reading the example shipped with the library should get you up to speed ASAP. Here is a list of API calls and how to use them.

char *linenoise(const char *prompt);

This is the main Linenoise call: it shows the user a prompt with line editing and history capabilities. The prompt you specify is used as a prompt, that is, it will be printed to the left of the cursor. The library returns a buffer with the line composed by the user, or NULL on end of file or when there is an out of memory condition.

When a tty is detected (the user is actually typing into a terminal session) the maximum editable line length is LINENOISE_MAX_LINE. When instead the standard input is not a tty, which happens every time you redirect a file to a program, or use it in an Unix pipeline, there are no limits to the length of the line that can be returned.

The returned line should be freed with the free() standard system call. However sometimes it could happen that your program uses a different dynamic allocation library, so you may also used linenoiseFree to make sure the line is freed with the same allocator it was created.

The canonical loop used by a program using Linenoise will be something like this:

while((line = linenoise("hello> ")) != NULL) {
    printf("You wrote: %s\n", line);
    linenoiseFree(line); /* Or just free(line) if you use libc malloc. */

Single line VS multi line editing

By default, Linenoise uses single line editing, that is, a single row on the screen will be used, and as the user types more, the text will scroll towards left to make room. This works if your program is one where the user is unlikely to write a lot of text, otherwise multi line editing, where multiple screens rows are used, can be a lot more comfortable.

In order to enable multi line editing use the following API call:


You can disable it using 0 as argument.


Linenoise supporst history, so that the user does not have to retype again and again the same things, but can use the down and up arrows in order to search and re-edit already inserted lines of text.

The followings are the history API calls:

int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(const char *filename);

Use linenoiseHistoryAdd every time you want to add a new element to the top of the history (it will be the first the user will see when using the up arrow).

Note that for history to work, you have to set a length for the history (which is zero by default, so history will be disabled if you don't set a proper one). This is accomplished using the linenoiseHistorySetMaxLen function.

Linenoise has direct support for persisting the history into an history file. The functions linenoiseHistorySave and linenoiseHistoryLoad do just that. Both functions return -1 on error and 0 on success.

Mask mode

Sometimes it is useful to allow the user to type passwords or other secrets that should not be displayed. For such situations linenoise supports a "mask mode" that will just replace the characters the user is typing with * characters, like in the following example:

$ ./linenoise_example
hello> get mykey
echo: 'get mykey'
hello> /mask
hello> *********

You can enable and disable mask mode using the following two functions:

void linenoiseMaskModeEnable(void);
void linenoiseMaskModeDisable(void);


Linenoise supports completion, which is the ability to complete the user input when she or he presses the <TAB> key.

In order to use completion, you need to register a completion callback, which is called every time the user presses <TAB>. Your callback will return a list of items that are completions for the current string.

The following is an example of registering a completion callback:


The completion must be a function returning void and getting as input a const char pointer, which is the line the user has typed so far, and a linenoiseCompletions object pointer, which is used as argument of linenoiseAddCompletion in order to add completions inside the callback. An example will make it more clear:

void completion(const char *buf, linenoiseCompletions *lc) {
    if (buf[0] == 'h') {
        linenoiseAddCompletion(lc,"hello there");

Basically in your completion callback, you inspect the input, and return a list of items that are good completions by using linenoiseAddCompletion.

If you want to test the completion feature, compile the example program with make, run it, type h and press <TAB>.


Linenoise has a feature called hints which is very useful when you use Linenoise in order to implement a REPL (Read Eval Print Loop) for a program that accepts commands and arguments, but may also be useful in other conditions.

The feature shows, on the right of the cursor, as the user types, hints that may be useful. The hints can be displayed using a different color compared to the color the user is typing, and can also be bold.

For example as the user starts to type "git remote add", with hints it's possible to show on the right of the prompt a string <name> <url>.

The feature works similarly to the history feature, using a callback. To register the callback we use:


The callback itself is implemented like this:

char *hints(const char *buf, int *color, int *bold) {
    if (!strcasecmp(buf,"git remote add")) {
        *color = 35;
        *bold = 0;
        return " <name> <url>";
    return NULL;

The callback function returns the string that should be displayed or NULL if no hint is available for the text the user currently typed. The returned string will be trimmed as needed depending on the number of columns available on the screen.

It is possible to return a string allocated in dynamic way, by also registering a function to deallocate the hint string once used:

void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);

The free hint callback will just receive the pointer and free the string as needed (depending on how the hits callback allocated it).

As you can see in the example above, a color (in xterm color terminal codes) can be provided together with a bold attribute. If no color is set, the current terminal foreground color is used. If no bold attribute is set, non-bold text is printed.

Color codes are:

red = 31
green = 32
yellow = 33
blue = 34
magenta = 35
cyan = 36
white = 37;

Screen handling

Sometimes you may want to clear the screen as a result of something the user typed. You can do this by calling the following function:

void linenoiseClearScreen(void);

Related projects

  • Linenoise NG is a fork of Linenoise that aims to add more advanced features like UTF-8 support, Windows support and other features. Uses C++ instead of C as development language.
  • Linenoise-swift is a reimplementation of Linenoise written in Swift.
  • Improve compatibility with VT100 and windows ANSI.SYS terminals

    Improve compatibility with VT100 and windows ANSI.SYS terminals

    I notice that using a windows terminal program (CRT) in its default VT100 or any of its ANSI modes has display issues. I also notice issues when running telnet from a WIN7 command prompt.

    Looking at the Wikipedia page for ANSI Escape Codes, I note that the CHA (ESC [ n G) sequence is not part of ANSI.SYS and I believe was not part of the VT100 command set (I think it was added in the VT220).

    Looking at the code, it appears that cu

    rrently it is used only to set the cursor to the first column. I replaced all of thos occurances with an CUB sequence to send the cursor back 999 coulums. This seems to work fine with all of the terminals that I tried. It may also be possible to replace the CHA 0 with a \r instead. Here is a patch:

    diff --git a/linenoise.c b/linenoise.c
    index aef5cdd..b5404b2 100644
    --- a/linenoise.c
    +++ b/linenoise.c
    @@ -56,10 +56,6 @@
      * flickering effect with some slow terminal, but the lesser sequences
      * the more compatible.
    - * CHA (Cursor Horizontal Absolute)
    - *    Sequence: ESC [ n G
    - *    Effect: moves cursor to column n
    - *
      * EL (Erase Line)
      *    Sequence: ESC [ n K
      *    Effect: if n is 0 or missing, clear from cursor to end of line
    @@ -68,7 +64,19 @@
      * CUF (CUrsor Forward)
      *    Sequence: ESC [ n C
    - *    Effect: moves cursor forward of n chars
    + *    Effect: moves cursor forward n chars
    + *
    + * CUB (CUrsor Backward)
    + *    Sequence: ESC [ n D
    + *    Effect: moves cursor backward n chars
    + * 
    + * The following is used to get the terminal width if getting
    + * the width with the TIOCGWINSZ ioctl fails
    + *
    + * DSR (Device Status Report)
    + *    Sequence: ESC [ 6 n
    + *    Effect: reports the current cusor position as ESC [ n ; m R
    + *            where n is the row and m is the column
      * When multi line mode is enabled, we also use an additional escape
      * sequence. However multi line editing is disabled by default.
    @@ -471,7 +479,7 @@ static void refreshSingleLine(struct linenoiseState *l) {
         /* Cursor to left edge */
    -    snprintf(seq,64,"\x1b[0G");
    +    snprintf(seq,64,"\x1b[999D");
         /* Write the prompt and the current buffer content */
    @@ -480,7 +488,7 @@ static void refreshSingleLine(struct linenoiseState *l) {
         /* Move cursor to original position. */
    -    snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen));
    +    snprintf(seq,64,"\x1b[999D\x1b[%dC", (int)(pos+plen));
         if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
    @@ -515,13 +523,13 @@ static void refreshMultiLine(struct linenoiseState *l) {
         /* Now for every row clear it, go up. */
         for (j = 0; j < old_rows-1; j++) {
    -        snprintf(seq,64,"\x1b[0G\x1b[0K\x1b[1A");
    +        snprintf(seq,64,"\x1b[999D\x1b[0K\x1b[1A");
         /* Clean the top line. */
    -    snprintf(seq,64,"\x1b[0G\x1b[0K");
    +    snprintf(seq,64,"\x1b[999D\x1b[0K");
         /* Write the prompt and the current buffer content */
    @@ -536,7 +544,7 @@ static void refreshMultiLine(struct linenoiseState *l) {
    -        snprintf(seq,64,"\x1b[0G");
    +        snprintf(seq,64,"\x1b[999D");
             if (rows > (int)l->maxrows) l->maxrows = rows;
    @@ -555,7 +563,7 @@ static void refreshMultiLine(struct linenoiseState *l) {
         /* Set column. */
         lndebug("set col %d", 1+((plen+(int)l->pos) % (int)l->cols));
    -    snprintf(seq,64,"\x1b[%dG", 1+((plen+(int)l->pos) % (int)l->cols));
    +    snprintf(seq,64,"\x1b[999D\x1b[%dC", 1+((plen+(int)l->pos) % (int)l->cols));
    @@ -904,7 +912,7 @@ void linenoisePrintKeyCodes(void) {
             printf("'%c' %02x (%d) (type quit to exit)\n",
                 isprint(c) ? c : '?', (int)c, (int)c);
    -        printf("\x1b[0G"); /* Go left edge manually, we are in raw mode. */
    +        printf("\x1b[999D"); /* Go left edge manually, we are in raw mode. */
    opened by welash 10
  • Clarifying Free Software vs 'Commercial'

    Clarifying Free Software vs 'Commercial'

    Free Software is not incompatible at all with commercial use. Indeed, quite the opposite; were I running a company based on open code, it would be significantly to my advantage to choose a copyleft license. But that's beside the point.

    Free Software, then, is not disjunct from Commercial, and it would be more appropriate to say that the BSD permits use in Free Software or Proprietary use-cases, because that's the real difference between, say, the GPL and BSD.

    opened by cathalgarvey 9
  • Newline (\n) not doing \r on Linux ($TERM = xterm)

    Newline (\n) not doing \r on Linux ($TERM = xterm)

    I feel like I must be doing something wrong as I can't really imagine being the first to notice this behavior. So I apologize in advance if I'm just being silly here.

    I'm using linenoise in a multi-threaded application that accepts user-input on terminal but can also output text on its own (for example, in case of errors it prints an error message). We used to use libreadline, but it's no longer possible because of its license.

    The error messages are just printed as:

    std::cout << "Line one\n";
    std::cout << "Line two\n";

    With linenoise, the output is as follows:

    Line one
            Line two

    So 'Line two' is on a new line, but it starts right after 'Line one', rather than at the beginning of the line. It is as if the implicit \r is missing. It's solved by using "\r\n" instead of \n as newline but it's kind of awkward to change this all over the place.

    Note: clearly, whenever something is printed as a result of user-input, everything is fine. e.g.

    > help
    No problem
    with newlines

    Is this a known issue? Can I work around it without having to sed all of my \n into \r\n?

    Thank you for your time.

    opened by thelamb 5
  • ANSI colors in prompt string

    ANSI colors in prompt string

    I'm using https://github.com/hoelzro/lua-linenoise and noticed that the prompt string cannot contain non-printed characters without messing up tab completion, which must be due to incorrect string length calculation. My use case is ANSI coloring (256 spectrum).


    opened by aleclarson 4
  • Add explicit NULL for variadic macro lndebug

    Add explicit NULL for variadic macro lndebug

    When building projects using linenoise with strict warnings, gcc complains about the lndebug macro being called with no parameter.

    Adding an explicit NULL satisfies the compiler and removes the warning.

    (See discussion here: http://unixpapa.com/incnote/variadic.html and here: http://stackoverflow.com/q/3576396/26702.)

    opened by telemachus 4
  • Make linenoise.h C++ compatible

    Make linenoise.h C++ compatible

    I've made a few changes to linenoise.h so it can be included in a C++ application.

    Also I ran into a bug with the escape sequence processing on a "real" serial connection where the first byte of the sequence could be lost if the read call returns 1 rather than 2 bytes.

    opened by nickg 4
  • linenoise eats a serial port

    linenoise eats a serial port

    Using redis on an ARM embedded-system; however, when opening redis-cli on the serial console, I cannot see any characters, and also input doesn't seem to work, as typing commands blind doesn't do anything either. Also, all control signals are consumed as well so I have to kill off the process to get control of the serial port back.

    Using redis-cli over SSH to the same device works fine. After logging in I see there is a .rediscli-history file that has the commands that I was typing over SSH, but nothing for the serial port.

    opened by ventureresearch 4
  • add mask input mode

    add mask input mode

    As mentioned in https://github.com/antirez/redis/pull/6834 I try to add mask input mode to linenoise. Add two functions: linenoiseMaskModeEnable linenoiseMaskModeDisable

    And in ./linenoise_example You can use /mask to enable input masking, and use /unmask to disable it.

    Signed-off-by: lifubang [email protected]

    opened by lifubang 3
  • Fix compilation in non C99/C11 mode.

    Fix compilation in non C99/C11 mode.

    This fixes an accidental drift towards C99/C11 dependencies.

    Noticed this when Redis builds started to fail on older Debian systems (gcc 4.9.2). Apparently deps/Makefile also happens to not propagate CFLAGS so -std=c11 is omitted, resulting with build failures:

    MAKE linenoise
    cd linenoise && make
    make[3]: Entering directory '/work/redis-unstable/deps/linenoise'
    cc  -Wall -Os -g  -c linenoise.c
    linenoise.c: In function 'refreshMultiLine':
    linenoise.c:601:9: error: 'for' loop initial declarations are only allowed in C99 or C11 mode
             for (uint i = 0; i < l->len; i++) abAppend(&ab,"*",1);
    opened by yossigo 2
  • CMake and Build as System Library

    CMake and Build as System Library

    I created a cmake file to enable integration of this with a larger cmake project using ExternalProject. This maintains compatibility. This also allows linenoise to be installed a system library and dynamically linked. I am willing to add conditional compilation for the static target.

    opened by HSchmale16 2
  • linenoiseRemoteRefereshLine()


    We've been using linenoise in a larger multi-threaded project. When another thread uses printf to display some message and a linenoise prompt is on the screen, the message prints at the end of our current prompt, and no new prompt is displayed resulting in ugly output.

    Our new function allows other processes to remotely refresh the linenoise line. Thereby they can print their string on its own line, then reshesh the prompt so everything looks nice.

    This is accomplished by replacing read in several places with a function that uses select to wait on two file descriptors, stdin and a control pipe that we create. linenoiseRemoteRefreshLine() pushes a new control character (ctrl-r) into our control pipe; when that is received, a flag is set that calls for refresh line to be called.

    opened by garethspor 2
  • Linenoise does not work with IntelliJ debug console

    Linenoise does not work with IntelliJ debug console


    When switching my application from libreadline to linenoise, I noticed that I can no longer debug my application from the IntelliJ console (terminal works alright). The line input just doesn't end, and newline just goes to the next line and I can just keep typing.

    I would have opened this issue at IntelliJ, but all others I have tried (libedit, tecla, replxx majorly) works fine with IntelliJ debug console. What could be the problem?? Can this be fixes??

    It would be nice to be able to debug my application from my IDE.

    opened by mcfriend99 2
  • Avoid TOCTOU in linenoiseHistorySave

    Avoid TOCTOU in linenoiseHistorySave

    Before this commit the linenoiseHistorySave performed fopen(filename, ...) and chmod(filename, ...) and this creates a time of use vs time of check vulnerability.

    I have not checked whether this can be exploited, but the fix is trivial here: we can just use fchmod with the opened file descriptor and this is what this commit changes :).

    Btw this was found with https://codeql.github.com/ and its https://codeql.github.com/codeql-query-help/cpp/cpp-toctou-race-condition/ rule when scanning a bigger project that used linenoise as a dependency.

    opened by disconnect3d 0
  • Unnecessary (and risky) usage of atexit()

    Unnecessary (and risky) usage of atexit()

    As of now, linenoise uses atexit() to call free a bunch of times. It seems completely unnecessary do so, because the OS will already reclaim that memory at exit (and far more efficiently, too). Also, using atexit() for this purpose means that if some user of the library decides to use atexit() to save linenoise history to a file, it won't work because linenoise's atexit() will practically always be called later (because it's only called once the first line is processed), and therefore its atexit() set function would be called first once the program exits, making the history written to file complete garbage.

    I would suggest simply removing the freeHistory() call from linenoiseAtExit(), or removing the function altogether.

    opened by Forkelt 0
Salvatore Sanfilippo
Computer programmer based in Sicily, Italy. I mostly write OSS software. Born 1977. Not a puritan.
Salvatore Sanfilippo
A readline and libedit replacement that supports UTF-8, syntax highlighting, hints and Windows and is BSD licensed.

Read Evaluate Print Loop ++ A small, portable GNU readline replacement for Linux, Windows and MacOS which is capable of handling UTF-8 characters. Unl

Marcin Konarski 581 Sep 15, 2022
Alternative firmware for IP cameras based on the HiSilicon (and other) SoC's

OpenIPC v2.1 (experimental, buildroot based..) Alternative firmware for IP cameras based on the HiSilicon (and other) SoC's More information about the

OpenIPC 283 Sep 17, 2022
A (relatively) small node library to clone and pull git repositories in a standalone manner thanks to libgit2, powered by WebAssembly and Emscripten

simple-git-wasm A (relatively) small node library to clone and pull git repositories in a standalone manner thanks to libgit2, powered by WebAssembly

Powercord 20 May 20, 2022
Small header only C++ library for writing multiplatform terminal applications

Terminal Terminal is small header only library for writing terminal applications. It works on Linux, macOS and Windows (in the native cmd.exe console)

Jupyter Xeus 238 Sep 22, 2022
cpp-progressbar is a small console program I wrote in c++. 3 themes are available

cpp-progressbar cpp-progressbar is a small console program I wrote in c++. 3 themes are available (this program only works on linux) Instalation Downl

Zielino 3 Jun 17, 2022
Minimal but open SDK for developing small command line tools.

Minimal SDK for macOS This repository provides the basis to build a cross compiler for macOS. With it, you can compile small command line tools from a

Ayke 9 Mar 18, 2022
A simple header-only C++ argument parser library. Supposed to be flexible and powerful, and attempts to be compatible with the functionality of the Python standard argparse library (though not necessarily the API).

args Note that this library is essentially in maintenance mode. I haven't had the time to work on it or give it the love that it deserves. I'm not add

Taylor C. Richberger 1k Sep 18, 2022
CLI11 is a command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface.

CLI11: Command line parser for C++11 What's new • Documentation • API Reference CLI11 is a command line parser for C++11 and beyond that provides a ri

null 2.3k Sep 16, 2022
A simple header-only C++ argument parser library. Supposed to be flexible and powerful, and attempts to be compatible with the functionality of the Python standard argparse library (though not necessarily the API).

args Note that this library is essentially in maintenance mode. I haven't had the time to work on it or give it the love that it deserves. I'm not add

Taylor C. Richberger 896 Aug 31, 2021
Yori is a CMD replacement shell that supports backquotes, job control, and improves tab completion, file matching, aliases, command history, and more.

Yori is a CMD replacement shell that supports backquotes, job control, and improves tab completion, file matching, aliases, command history, and more.

Malcolm Smith 1.1k Sep 17, 2022
Free open-source modern C++17 / C++20 framework to create console, forms (GUI like WinForms) and unit test applications on Microsoft Windows, Apple macOS and Linux.

xtd Modern C++17/20 framework to create console (CLI), forms (GUI like WinForms) and tunit (unit tests like Microsoft Unit Testing Framework) applicat

Gammasoft 368 Sep 11, 2022
Flexible and fast Z-shell plugin manager that will allow installing everything from GitHub and other sites.

ZINIT News Zinit Wiki Quick Start Install Automatic Installation (Recommended) Manual Installation Usage Introduction Plugins and snippets Upgrade Zin

z-shell 25 Jul 9, 2022
2021 Fall Comp2012h Final Project. A Plant-Vs-Zombie style desktop game with beautiful graphics and sound effects. Developer: thomas914, mitester and tiliuau.

Underperforming Students vs Teachers Table of Contents Underperforming Students vs Teachers Table of Contents Authors Code Conventions Workflow Class

null 3 Apr 14, 2022
Google's common Java, C++ and JavaScript library for parsing, formatting, and validating international phone numbers.

What is it? Google's common Java, C++ and JavaScript library for parsing, formatting, and validating international phone numbers. The Java version is

Google 14.2k Sep 22, 2022
A CLI program that helps you find classes and plan pre-requisites. Written in C++ and Python.

CourseHelper A CLI program created to help you prepare for course registration. Note: At the moment, this project is built specifically for other UCLA

Kyle Chui 1 Jan 25, 2022
A simple to use, composable, command line parser for C++ 11 and beyond

Clara v1.1.5 !! This repository is unmaintained. Go here for a fork that is somewhat maintained. !! A simple to use, composable, command line parser f

Catch Org 652 Aug 27, 2022
A simple to use, composable, command line parser for C++ 11 and beyond

Lyra A simple to use, composing, header only, command line arguments parser for C++ 11 and beyond. Obtain License Standards Stats Tests License Distri

Build Frameworks Group 360 Sep 16, 2022
Terminal calculator made for programmers working with multiple number representations, sizes, and overall close to the bits

Programmer calculator The programmer calculator is a simple terminal tool designed to give maximum efficiency and flexibility to the programmer workin

romes 166 Sep 8, 2022
A single header C++ library for parsing command line arguments and options with minimal amount of code

Quick Arg Parser Tired of unwieldy tools like getopt or argp? Quick Arg Parser is a single header C++ library for parsing command line arguments

null 46 Aug 10, 2022