One of the burdens of being a programmer is that few people are witness to your brilliance. You can write the keenest code since the Countess of Lovelace and it’s likely no one will ever appreciate your genius. But don’t let this limitation stop you!
I’m always looking for clever ways to improve my code. Some of this obsession borders on obfuscation, but the process always beings with the bulky way to code a problem. Work continues as I try to add efficiency and elegance.
I wrote the following code to measure tab stops on the terminal screen. In Linux, you use the tabs command to set the stops. The default is eight spaces, though you can set the interval to any amount or even set discrete tab stops at specific positions. My program shows where they’re set:
2024_07_27-Lesson-a.c
#include <stdio.h> int main() { int x; /* output header */ for( x=0; x<79; x++ ) { if( x%10==0 ) putchar('|'); else printf("%d",x%10); } putchar('\n'); /* show tab stops */ printf("Zero\n"); printf("\tOne\n"); printf("\t\tTwo\n"); printf("\t\t\tThree\n"); printf("\t\t\t\tFour\n"); printf("\t\t\t\t\tFive\n"); printf("\t\t\t\t\t\tSix\n"); printf("\t\t\t\t\t\t\tSeven\n"); return(0); }
Here’s sample output:
|123456789|123456789|123456789|123456789|123456789|123456789|123456789|12345678
Zero
One
Two
Three
Four
Five
Six
Seven
To me, this program provides a useful tool, but I don’t like the repetitive printf() statements. Further, it would help if the header were output between each line to better gauge the tab stop positions.
The first improvement I made to the code is to extract the header into its own function. I then put the tab output routine into a loop. This change didn’t make the code tighter, but it improved the output:
2024_07_27-Lesson-b.c
#include <stdio.h> /* output header */ void header(void) { int x; for( x=0; x<79; x++ ) { if( x%10==0 ) putchar('|'); else printf("%d",x%10); } putchar('\n'); } int main() { int x,t; char *count[8] = { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven" }; /* show tab stops */ for( x=0; x<8; x++ ) { header(); for(t=0; t<x; t++ ) putchar('\t'); printf("%s\n",count[x]); } return(0); }
Here is the updated output:
|123456789|123456789|123456789|123456789|123456789|123456789|123456789|12345678
Zero
|123456789|123456789|123456789|123456789|123456789|123456789|123456789|12345678
One
|123456789|123456789|123456789|123456789|123456789|123456789|123456789|12345678
Two
|123456789|123456789|123456789|123456789|123456789|123456789|123456789|12345678
Three
|123456789|123456789|123456789|123456789|123456789|123456789|123456789|12345678
Four
|123456789|123456789|123456789|123456789|123456789|123456789|123456789|12345678
Five
|123456789|123456789|123456789|123456789|123456789|123456789|123456789|12345678
Six
|123456789|123456789|123456789|123456789|123456789|123456789|123456789|12345678
Seven
I remained unhappy with this update. The output is crowded. So I added a toggle to the header() function. An on/off value is read to determine whether the digits are output between the pipe characters. Two defined constants, ON
and OFF
, determine when to output the digits:
2024_07_27-Lesson-c.c
#include <stdio.h> #define ON 1 #define OFF 0 /* output header */ void header(int a) { int x; for( x=0; x<79; x++ ) { if( x%10==0 ) putchar('|'); else { if(a) printf("%d",x%10); else putchar(' '); } } putchar('\n'); } int main() { int x,t; char *count[8] = { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven" }; /* show tab stops */ for( x=0; x<8; x++ ) { x==0 ? header(ON): header(OFF); for(t=0; t<x; t++ ) putchar('\t'); printf("%s\n",count[x]); } return(0); }
Here is the updated and final output:
|123456789|123456789|123456789|123456789|123456789|123456789|123456789|12345678
Zero
| | | | | | | |
One
| | | | | | | |
Two
| | | | | | | |
Three
| | | | | | | |
Four
| | | | | | | |
Five
| | | | | | | |
Six
| | | | | | | |
Seven
I’m pleased with the result. Further, I think I’m clever for devising some interesting solutions. My goal wasn’t to make the code more compact (which would have been sweet), but to make it more efficient. Regardless, the final program is the point. As long as it’s doing the job and doesn’t pose an unintended risk to the system, I’m good.
I donʼt know how you do it, but the challenges you come up with on a weekly basis is really impressive and I like this one.
The only thing that could probably be improved would be to use
ioctl(STDIN_FILENO, TIOCGWINSZ, &winsize);
to determine the terminals width and height and to also use the"\x1B[6n\n"
ANSI escape sequence to get the cursors position after outputting"\r\t"
, thereby determining the systems TAB width size at runtime.That being said I realize why you didnʼt go this route—all the necessary code to do this would only distract from the main idea and blow up the the programs LOC to probably twice or even thrice of the current solution.
Thank you!
I like your thoughts on obtaining a tab stop. Using the window size would be good – and relevant today as a mouse swipe can easily change a terminal window’s size.
The ANSI sequence is cool, but keep in mind that the tabs command can set tab stops at irregular intervals. Of course, all this dust is silly because the tabs command option -d coughs up the current tab stops anyway:
----+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8
*-------*-------*-------*-------*-------*-------*-------*-------*-------*-------
* * * * * * * * * *
It even adjusts the output width to match the window, so there’s some magic in there!
Still, it’s a fun exercise and something I’ve used in the past.
Interesting, I didn’t know that you could set varying TAB positions (like on an electronic typewriter).
Then again, I must admit that I have never really played with the tabs command.