{"id":7357,"date":"2026-01-24T00:01:30","date_gmt":"2026-01-24T08:01:30","guid":{"rendered":"https:\/\/c-for-dummies.com\/blog\/?p=7357"},"modified":"2026-01-17T11:42:17","modified_gmt":"2026-01-17T19:42:17","slug":"direct-terminal-input","status":"publish","type":"post","link":"https:\/\/c-for-dummies.com\/blog\/?p=7357","title":{"rendered":"Direct Terminal Input"},"content":{"rendered":"<p>While <a href=\"https:\/\/c-for-dummies.com\/blog\/?p=7350\">raw terminal input<\/a> allows standard I\/O functions to capture uncooked text, another approach for reading the terminal may also capture a few uncooked morsels. This process involves using low-level file I\/O commands. These functions are <a href=\"https:\/\/c-for-dummies.com\/blog\/?p=4046\"><em>read()<\/em><\/a> and <a href=\"https:\/\/c-for-dummies.com\/blog\/?p=4052\"><em>write()<\/em><\/a>, which are part of the POSIX standard.<br \/>\n<!--more--><br \/>\nAs I explained in <a href=\"https:\/\/c-for-dummies.com\/blog\/?p=4036\">another post<\/a>, the popular <em>fopen()<\/em> function reads formatted data, which is what the <em>f<\/em> stands for. On the other hand, the <em>open()<\/em> function reads data in the raw. But how raw?<\/p>\n<p>The following code uses the <em>read()<\/em> and <em>write()<\/em> functions to process standard I\/O. A prompt is output, input is gathered, then the text input is sent to standard output.<\/p>\n<h3><a href=\"https:\/\/github.com\/dangookin\/C-For-Dummies-Blog\/blob\/master\/2026_01_24-Lesson.c\" rel=\"noopener\" target=\"_blank\">2026_01_24-Lesson.c<\/a><\/h3>\n<pre class=\"screen\">\r\n#include &lt;stdio.h&gt;\r\n#include &lt;unistd.h&gt;\r\n\r\nint main()\r\n{\r\n    char prompt[] = \"Type away: \";\r\n    char buffer[BUFSIZ];\r\n    int ch,offset;\r\n\r\n    <span class=\"comments\">\/* output the prompt *\/<\/span>\r\n    write(fileno(stdout),prompt,sizeof(prompt));\r\n\r\n    <span class=\"comments\">\/* read standard input until the newline *\/<\/span>\r\n    offset = 0;\r\n    do\r\n    {\r\n        read(fileno(stdin),&amp;ch,1);\r\n        buffer[offset] = ch;\r\n        offset++;\r\n        <span class=\"comments\">\/* test for overflow *\/<\/span>\r\n        if( offset&gt;BUFSIZ )\r\n            break;\r\n    }\r\n    while( ch!='\\n' );\r\n\r\n    <span class=\"comments\">\/* output the buffer *\/<\/span>\r\n    write(fileno(stdout),buffer,offset);\r\n\r\n    <span class=\"comments\">\/* clean-up *\/<\/span>\r\n    return 0;\r\n}<\/pre>\n<p>The first use of the <em>write()<\/em> function is to send a prompt to standard output:<\/p>\n<p><code>write(fileno(stdout),prompt,sizeof(prompt));<\/code><\/p>\n<p>Here I use <code>fileno(stdout)<\/code> to obtain the <a href=\"https:\/\/c-for-dummies.com\/blog\/?p=1952\">file number<\/a> for standard output. The <code>unistd.h<\/code> header file also defines the <code>STDOUT_FILENO<\/code> constant, which serves the same purpose.<\/p>\n<p>The second argument is the address of the string to send, <code>prompt[]<\/code>. This argument is followed by the number of characters to write, obtained by using the <em>sizeof<\/em> operator. The value returned doesn&#8217;t include the terminating null character, though the null character isn&#8217;t necessary for writing a string to standard output in this manner.<\/p>\n<p>A <em>do-while<\/em> loop reads standard input until the newline is fetched or the buffer is full. The <em>read()<\/em> function fetches one character at a time from the standard input device&#8217;s file descriptor:<\/p>\n<p><code>read(fileno(stdin),&amp;ch,1);<\/code><\/p>\n<p>Again, I use the <em>fileno()<\/em> function where I could instead use the <code>STDIN_FILENO<\/code> defined constant. Next comes the address of the buffer to store input, a single integer variable <code>ch<\/code> in this example. The final argument is the number of characters to read, one.<\/p>\n<p>The character input is stored in the buffer, then a check is made for overflow. The loop repeats until the newline is captured.<\/p>\n<p>The program&#8217;s final task is to output the characters input. I use the <em>write()<\/em> function again, with the input <code>buffer<\/code> specified and variable <code>output<\/code> as the buffer&#8217;s size. This approach works even though no null character terminates the buffer as variable <code>output<\/code> holds the buffer&#8217;s size. (Technically, the buffer holds characters only, not a valid string.)<\/p>\n<p>Here&#8217;s a sample run:<\/p>\n<pre>Type away: Hello, there!^H^H^H^H^H^H^H^H\r\nHello, there!<\/pre>\n<p>After I typed <strong>Hello, there!,<\/strong> I pressed Ctrl+H to backspace &mdash; but the program didn&#8217;t backspace! Instead, you see a series of ^H (control-H) characters appear. These characters don&#8217;t appear in the output, but they dwell in the buffer, captured by the <em>read()<\/em> function. Other control characters appear onscreen as well, though they&#8217;re not saved in the buffer. In fact, the Backspace key on a PC keyboard generates the ^? (Del or ASCII 127) character, which isn&#8217;t saved in the buffer.<\/p>\n<p>The weirdness here is that reading standard input in the raw may seem like raw terminal input, but it&#8217;s not. It&#8217;s one step closer, with some characters (such as ^H) put into the buffer but others behaving as they normally do with cooked input. It&#8217;s important to know the difference between these modes to ensure that your program is fetching the data it needs.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Reading the terminal&#8217;s input directly isn&#8217;t the same as raw input, though it&#8217;s kinda similar. <a href=\"https:\/\/c-for-dummies.com\/blog\/?p=7357\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-7357","post","type-post","status-publish","format-standard","hentry","category-main"],"_links":{"self":[{"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/7357","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=7357"}],"version-history":[{"count":3,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/7357\/revisions"}],"predecessor-version":[{"id":7362,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/7357\/revisions\/7362"}],"wp:attachment":[{"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=7357"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=7357"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=7357"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}