{"id":5730,"date":"2023-02-04T00:01:03","date_gmt":"2023-02-04T08:01:03","guid":{"rendered":"https:\/\/c-for-dummies.com\/blog\/?p=5730"},"modified":"2024-03-09T09:38:12","modified_gmt":"2024-03-09T17:38:12","slug":"counting-terminal-rows-and-columns","status":"publish","type":"post","link":"https:\/\/c-for-dummies.com\/blog\/?p=5730","title":{"rendered":"Counting Terminal Rows and Columns"},"content":{"rendered":"<p>I enjoy programming a computer the old fashioned way, in the text mode terminal window. Yes, it&#8217;s now a sad little &#8220;app&#8221; floating on a graphical screen. This burden doesn&#8217;t remove the charm, but it does raise an interesting issue when you try to get text mode output just right: How many rows and columns are there in the terminal window?<br \/>\n<!--more--><br \/>\nIn ancient times, the text mode window size was fixed and consistent. On an IBM PC, it was 80 columns in 25 rows. My TRS-80 Model III had a terminal screen of 64 columns by 16 rows. Later computers let you adjust the terminal size. Even today in Windows, Linux, or macOS, the terminal app&#8217;s dimensions are adjustable &mdash; sometimes just by re-sizing the window.<\/p>\n<p>Various methods exist to fetch the terminal size, returning the number of character columns and rows so that your program can neatly output text. The best way to address screen size, as well as all text mode programming, is to use the NCurses library. Time for a shameless plug for my <a href=\"http:\/\/NCurses eBook\" rel=\"noopener\" target=\"_blank\">NCurses eBook<\/a>. Other (less expensive to you and less profitable for me) methods exist as well.<\/p>\n<p>At the prompt, use the <em>tput<\/em> command to divine the number of rows (lines) and columns:<\/p>\n<p><code>$ tput lines && tput cols<br \/>\n24<br \/>\n80<\/code><\/p>\n<p>If the terminal is running the <em>bash<\/em> shell, variables <code>$LINES<\/code> and <code>$COLUMNS<\/code> hold the details:<\/p>\n<p><code>$ echo $LINES && echo $COLUMNS<br \/>\n24<br \/>\n80<\/code><\/p>\n<p>These shell variables aren&#8217;t the same as environment variables, so you can&#8217;t read the environment to fetch their values.<\/p>\n<p>Nope, you must use the <code>ioctl()<\/code> function to dig into the terminal&#8217;s guts and obtain the line (row) and column sizes for the current terminal. This method may be cryptic, but it&#8217;s consistent.<\/p>\n<p>The <em>ioctl()<\/em> function is defined in the <code>sys\/ioctl.h<\/code> header file. It reads details about special files &mdash; and remember that all devices in Unix\/Linux\/macOS are treated as files. The terminal is just another file, but you need the <em>ioctl()<\/em> (input\/output control) function to obtain or set these details. Here is the <em>man<\/em> page format:<\/p>\n<p><code>int ioctl(int fildes, unsigned long request, ...);<\/code><\/p>\n<p>The first argument, <code><em>fildes<\/em><\/code>, represents an open file descriptor. The second argument, <code><em>request<\/em><\/code>, is the specific information required, expressed as a defined constant. The final argument(s) hold the results. The return value is zero upon success or an error value otherwise.<\/p>\n<p>To read the current terminal&#8217;s data, the <code><em>filedes<\/em><\/code> is <code>STDOUT_FILENO<\/code>. This defined constant, declared in the <code>unistd.h<\/code> header, represents the current output device.<\/p>\n<p>The second argument is <code>TIOCGWINSZ<\/code>, which I assume stands for Terminal Input\/Output Current Golly Window Size, though I have my doubts about the word &#8220;Golly.&#8221; Regardless, use this argument to obtain information about the terminal window.<\/p>\n<p>The final argument is the address of a <em>winsize<\/em> structure to hold the results. Structure members <em>ws_row<\/em> and <em>ws_col<\/em> contain the row and column (line) values. Here&#8217;s code:<\/p>\n<h3><a href=\"https:\/\/github.com\/dangookin\/C-For-Dummies-Blog\/blob\/master\/2023_02_04-Lesson.c\" rel=\"noopener\" target=\"_blank\">2023_02_04-Lesson.c<\/a><\/h3>\n<pre class=\"screen\">\r\n#include &lt;stdio.h&gt;\r\n#include &lt;unistd.h&gt;\r\n#include &lt;sys\/ioctl.h&gt;\r\n\r\nint main()\r\n{\r\n    struct winsize w;\r\n\r\n    ioctl(STDOUT_FILENO, TIOCGWINSZ, &amp;w);\r\n\r\n    printf(\"This terminal window is %d rows by %d columns\\n\",\r\n            w.ws_row,\r\n            w.ws_col\r\n          );\r\n\r\n    return(0);\r\n}<\/pre>\n<p>The <em>ioctl()<\/em> function at Line 9 reads the terminal information, stuffing the details into <em>winsize<\/em> variable <code>w<\/code>. The <em>printf()<\/em> statement outputs the current terminal size values:<\/p>\n<p><code>This terminal window is 24 rows by 80 columns<\/code><\/p>\n<p>The <em>ioctl()<\/em> function is powerful and, obviously, handy. Yet it seems scary. Any apprehension probably comes from the function&#8217;s scant documentation. I would love to probe the <em>winsize<\/em> structure further, but without a well written guide or further details, yes it&#8217;s scary.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Now that terminal screens are virtual, knowing its number of rows and columns isn&#8217;t a predictable thing. <a href=\"https:\/\/c-for-dummies.com\/blog\/?p=5730\">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-5730","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\/5730","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=5730"}],"version-history":[{"count":5,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/5730\/revisions"}],"predecessor-version":[{"id":6302,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/5730\/revisions\/6302"}],"wp:attachment":[{"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5730"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5730"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5730"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}