From Tiny C Projects by Dan Gookin

This article talks about writing a C program that writes an appropriate greeting, depending on the time of day, for you when you start your computer.


Take 35% off Tiny C Projects by entering fccgookin into the discount code box at checkout at manning.com.


A Simple Greeting

All major programming projects start out simple and have a tendency to grow into complex, ugly monsters. I’m certain that Excel began its existence as a quick-and-dirty, text mode calculator — and now look at it. Regardless, it’s good programming practice not to begin a project by coding everything you need all at once. No, it’s best to grow the project, starting with something simple and stupid, which is the point of this section.

Coding a greeting

The most basic greetings program you can make is a simple regurgitation of the silly Hello World program that ushers in the pages of every introductory C programming book since Moses. Listing 1 shows the version you could write for your greetings program.

Listing 1 greet01_basic.c

  
 #include <stdio.h>
  
 int main()
 {
     printf("Hello, Dan!\n");
  
     return(0);
 }
 

Don’t build. Don’t run. If you do, use this command to build a program named greetings:

  
 clang -Wall greet01_basic.c -o greetings
 

You may substitute clang with your favorite-yet-inferior compiler. Upon success, the resulting program is named greetings. Set this program into your shell’s startup script, adding the last line that looks like this:

  
 Greetings
 

Ensure that you prefix the program name with a pathname, either the full pathname, like this:

  
 /home/dang/bin/greetings
 

or a relative pathname:

  
 ~/bin/greetings
 

The startup script cannot magically locate program files, unless you specify a path, such as to my personal ~/bin directory, shown in the examples. (I also use my shell startup script to place my personal ~/bin directory on the search path — another Linux trick found in another book somewhere.)

After the startup script is updated, the next terminal window you open runs a startup script that outputs the following line, making your day more cheerful:

  
 Hello, Dan!
 

And if your name isn’t Dan, then the greeting is more puzzling than cheerful.

Adding a name as an argument

The initial version of the greetings program is inflexible. That’s probably why you didn’t code it and are instead eager to modify it with some customization.

Consider the modest improvement offered in Listing 2. This update to the code allows you to present the program with an argument, allowing it to be flexible.

Listing 2 greet02_basic.c

  
 #include <stdio.h>
  
 int main(int argc, char *argv[])
 {
     if( argc<2)    #A
         puts("Hello, you handsome beast!");
     else
         printf("Hello, %s!\n",argv[1]);    #B
  
     return(0);
 }
 

#A The argument count is always 1 for the program name. If so, a default message is output.

#B Whatever was typed after the program name (one word only) is represented as argv[1] and is output here.

Build this code into a program and thrust it into your shell’s startup script as written in the ancient scrolls but also in the preceding section.

  
 greetings Danny
 

The program now outputs the following message when you open a new terminal window:

  
 Hello, Danny!
 

This new message is far more cheerful than the original, but still begging for some improvement.

The Time of Day

One of the first programs I wrote for my old DOS computer greeted me every time I fired up the machine. The program was a bit boring, sadly. To spice it up, inspired by my verbal interactions with humans I encounter in real life, I added code to make the greeting reflect the time of day. You can do so as well with varying degrees of accuracy.

Obtaining the current time

Does anyone really know what time it is? The computer can guess. It keeps semi-accurate time because it touches base with an Internet time server every so often. Otherwise, the computer’s clock would be off by several minutes every day. Trust me, computers make lousy clocks, but this truth doesn’t stop you from plucking the current time from its innards.

The C library is rife with time functions, all defined in the time.h header file. The time_t data type is also defined in the header. For most compilers, the time_t data type is a positive integer value (long data type, printf() placeholder %ld) that stores the Unix epoch, the number of seconds ticking away since midnight January 1, 1970.

The Unix epoch is a great value to use in your greetings program. For example, imagine your joy at seeing — every day when you start the terminal — the following jolly message:

  
 Hello, Danny, it's 1624424373
 

Of course, the time_t value must be manipulated into something a bit more useful. Listing 1 shows some sample code. Be aware that many time functions, such as time() and ctime() used in the code for time01.c, require the address of the time_t variable. Yup, they’re pointers.

Listing 3 time01.c

  
 #include <stdio.h>
 #include <time.h>    #A
  
 int main()
 {
     time_t now;
  
     time(&now);    #B
     printf("The computer thinks it's %ld\n",now);
     printf("%s",ctime(&now));    #C
  
     return(0);
 }
 

#A The time.h header file is required, lest the compiler get cross with you.

#B The time() function requires the time_t variable’s address, prefixed here with the & address-of operator.

#C The ctime() function requires a pointer argument and returns a string appended with a newline.

Here is sample output from the resulting program:

  
 The computer thinks it's 1624424373
 Tue Jun 22 21:59:33 2021
 

The output shows the number of seconds of tick-tocking since 1970. This same value is swallowed by the ctime() function to output a formatted time string. This result may be acceptable in your greetings program, but time data can be customized further. The key to unlocking specific time details is found in the localtime() function, as the code in Listing 4 demonstrates.

Listing 4 time02.c

  
 #include <stdio.h>
 #include <time.h>
  
 int main()
 {
     time_t now;
     struct tm *clock;    #A
  
     time(&now);
     clock = localtime(&now);
     puts("Time details:");
     printf(" Day of the year: %d\n",clock->tm_yday);
     printf(" Day of the week: %d\n",clock->tm_wday);    #B
     printf("            Year: %d\n",clock->tm_year+1900);    #C
     printf("           Month: %d\n",clock->tm_mon+1);    #D
     printf("Day of the month: %d\n",clock->tm_mday);
     printf("            Hour: %d\n",clock->tm_hour);
     printf("          Minute: %d\n",clock->tm_min);
     printf("          Second: %d\n",clock->tm_sec);
  
     return(0);
 }
 

A# Because localtime() returns a pointer, it’s best to declare the structure as a pointer.

B# The first day of the week is 0 for Sunday

C# You must add 1900 to the tm_year member to get the current year. You will forget this.

D# The tm_mon member ranges from 0 to 11.

I formatted the code in Listing 4 with oodles of spaces so that you could easily identify the tm structure’s members. These variables represent the time tidbits that the localtime() function extracts from a time_t value. Ensure that you remember to adjust some values as shown in Listing 2: The year value tm_year must be increased by 1900 to reflect the current, valid year; the month value tm_mon starts with zero, not one.

The output is trivial, so I need not show it — unless you send me a check for $5. Still, the point of the code is to show how you can obtain useful time information with which to properly pepper your terminal greetings.

Mixing in the general time of day

The program I wrote years ago for my DOS computer was called GREET.COM. It was part of my computer’s AUTOEXEC.BAT program, which ran each time I started my trusty ol’ IBM PC. Because I’m fond of nostalgic things, I’ve kept a copy of the program. Written in x86 Assembly, it still runs under DOSBox. Ah, the sweet perfume of the digital past. Smells like ozone.

Alas, I don’t have the source code for my GREET.COM program. From memory (and disassembly), I see that the code fetches the current hour of the day and outputs an appropriate time-of-day greeting: good morning, good afternoon, or good evening. You can code the same trick — though in C for your current computer and not in x86 Assembly for an ancient IBM PC.

Pulling together resources from the first chunk of this chapter, Listing 5 shows a current version of my old greetings program.

Listing 5 greet03_time.c

  
 #include <stdio.h>
 #include <time.h>
  
 int main(int argc, char *argv[])
 {
     time_t now;
     struct tm *clock;
     int hour;
  
     time(&now);
     clock = localtime(&now);
     hour = clock->tm_hour;    #A
  
     printf("Good ");
     if( hour < 12 )    #B
         printf("morning");
     else if( hour < 17 )    #C
         printf("afternoon");
     else    #D
         printf("evening");
  
     if( argc>1 )    #E
         printf(", %s",argv[1]);
  
     putchar('\n');
  
     return(0);
 }
 

#A This statement is a convenience to avoid using clock->tm_hour over and over

#B Before noon, say “Good morning”

#C From noon to 5:00 PM, say “Good afternoon”

#D Otherwise, it’s evening

#E Check for and output the first command line argument

Assuming that the built program is named greetings, that the user types in Danny as the command line argument, and that it’s 4 o’clock in the afternoon, here is the code’s output:

  
 Good afternoon, Danny
 

This code effectively replicates what I wrote decades ago as my GREET.COM program. The output is a cheery, time-relevant greetings given the current time of day.

For extra humor, you can add a test for early hours, such as midnight to 4:00 AM. Output some whimsical text such as “Working late?” or “Are you still up?” Oh, the jocularity! I hope your sides don’t hurt.

Adding specific time info

Another way to treat yourself when you open a terminal window is to output a detailed time string. The simple way to accomplish this task is to output the greeting followed by a time string generated by the ctime() function. Here are the two relevant lines of code:

  
 printf(“Good day, %s\n”,argv[1]);
 printf(“It’s %s”,ctime(&now);
 

These two statements should give you an idea of what the code might look like. Still, the program is lazy. Better to incorporate the strftime() function, which formats a timestamp string according to your specifications.

The strftime() function works like printf(), with a special string that formats time information. The function’s output is saved in a buffer, which your code can use later. The code shown in Listing 6 demonstrates.

Listing 6 greet04_time.c

  
 #include <stdio.h>
 #include <time.h>
  
 int main(int argc, char *argv[])
 {
     time_t now;
     struct tm *clock;
     char time_string[64];    #A
  
     time(&now);
     clock = localtime(&now);    #B
  
     strftime(time_string,64,"Today is %A, %B %d, %Y%nIt is %r%n",clock);
  
     printf("Greetings");
     if( argc>1 )
         printf(", %s",argv[1]);
     printf("!\n%s",time_string);
  
     return(0);
 }
 

#A Storage for the string filled by the strftime() function.

#B You must fill a localtime() tm structure to make the strftime() function work.

You can review the man page for strftime() to discover all the fun placeholders and what they do. Like the printf() function, the placeholders are prefixed by a % character. Any other text in the formatting string is output as-is. Here are the highlights from the strftime() statement in Listing 6:



The output reflects the time string generated and stored in the time_string[] buffer. The time string appears after the general greeting as covered earlier in this article:

  
 Greetings, Danny!
 Today is Wednesday, June 23, 2021
 It is 04:24:47 PM
 

At this point, someone might say that all this output can easily be accomplished by using a shell scripting language, which is the native tongue of the shell startup and configuration file anyway. Still, as a C programmer your job is to offer more insight and pow

er to the greeting. Such additions aren’t possible when using a sad little shell scripting language.

That’s all for this article. If you want to learn more about the book, check it out on Manning’s liveBook platform here.