Shellminator V3.0.1
Simple Terminal
Loading...
Searching...
No Matches
Example 205 Commander Cowsay

Getting Started with Flags in Command-Line Arguments

In this example, we’ll explore flags —a special type of command-line argument. Flags are position-independent and don’t require a value. At first, this might seem a little strange, but not all arguments need to have an associated value. Sometimes, simply including the flag in the command is enough to change how the program behaves.

A great example of this is the cowsay command in Linux, which is actually a fun little easter egg! We’ll be implementing a simplified version of it to see flags in action.

What Should Our cowsay Command Do?

The cowsay command is quite flexible. Here’s how it works:

  • If no arguments are provided, it simply prints a cow saying booo.
  • If the user provides a text argument, the cow will say that instead.
  • Additionally, we can change the eyes of the cow using flags:
    • --tired or -t → gives the cow tired eyes.
    • --wink or -w → makes the cow wink.
    • --greedy or -g → gives the cow money-shaped eyes.

Implementing the Argument Handling

We won’t dive into the graphics rendering here—you can check the full implementation below. Instead, let’s focus on how we handle command-line arguments using flag detection.

First, we define our arguments:

// The text argument is optional but must be the first argument if provided.
Argument text(args, 0);
// These are the optional flags for modifying the cow’s eyes.
Argument tired_flag(args, 't', "tired");
Argument wink_flag(args, 'w', "wink");
Argument greedy_flag(args, 'g', "greedy");

For flags, the only thing that matters is **whether they are present or not**—we don’t parse values for them, we just check if they exist. That’s why we use the find() method:

// Check if the flags exist in the command.
tired_flag.find();
wink_flag.find();
greedy_flag.find();

When we need to determine which flag was provided, we use the isFound() method. Since multiple flags can be given at once, we should establish a priority order to avoid unpredictable behavior. In our case, the priority (from highest to lowest) is:

  1. Tired (-t)
  2. Wink (-w)
  3. Greedy (-g)
  4. Default (normal eyes, if no flag is given)

Now, let’s apply this logic in our implementation:

// The eye type is determined based on priority.
if (tired_flag.isFound()) {
caller->print(tired_eyes);
}
else if (wink_flag.isFound()) {
caller->print(wink_eyes);
}
else if (greedy_flag.isFound()) {
caller->print(greedy_eyes);
}
// If no flags are present, use the default eyes.
else {
caller->print(default_eyes);
}

Flags are a simple yet powerful way to modify a program’s behavior without requiring additional values. In this example, we used them to change our cow’s eye expression dynamically.

Live Demo

Here are a few suggested commands you might want to try in the demo above:

  • cowsay "hello bello" --tired → Displays "hello bello" with tired eyes.
  • cowsay --tired --wink → Shows the default text with tired eyes (not winking), since the tired flag has higher priority.
  • cowsay --wink --tired → Same as the previous one, proving that flag order doesn’t matter.
  • cowsay -g → Displays the cow with money-shaped eyes.
  • cowsay 10 → The text will be "10" because the first argument is treated as a string, not a number.
  • cowsay 10.5 → Same as above, but with a decimal number.
  • cowsay → Runs with the default behavior.

Whole Code

/*
* Created on Aug 10 2020
*
* Copyright (c) 2023 - Daniel Hajnal
* hajnal.daniel96@gmail.com
* This file is part of the Shellminator project.
* Modified 2023.05.13
*/
#include "Shellminator.hpp"
#include "Commander-API.hpp"
#include "Commander-Arguments.hpp"
// Not an average looking cow for sure
const char* cow_top = " \\ ^__^\r\n \\ ";
const char* cow_bottom = "\\_______\r\n (__)\\ )\\/\\\r\n ||----w |\r\n || ||\r\n";
// Eye configurations
const char* default_eyes = "(oo)";
const char* tired_eyes = "(--)";
const char* wink_eyes = "(-O)";
const char* greedy_eyes = "($$)";
// We have to create an object from Commander class.
Commander commander;
bool cowsay_func( char *args, CommandCaller* caller );
Commander::systemCommand_t API_tree[] = {
systemCommand( "cowsay", "Like the Linux command. Just give it a string.\r\n\tflags: --greedy\r\n\t --tired", cowsay_func )
};
// Create a ShellminatorCommanderInterface object, and initialize it to use Serial
// System init section.
void setup(){
Serial.begin(115200);
// Clear the terminal
shell.clear();
commander.attachDebugChannel( &Serial );
commander.attachTree( API_tree );
commander.init();
shell.attachCommander( &commander );
// Initialize shell object.
shell.begin( "arnold" );
}
// Infinite loop.
void loop(){
// Process the new data.
shell.update();
}
bool cowsay_func(char *args, CommandCaller* caller ){
char textBuffer[ 30 ];
int i;
// The text argument is place dependent but optional.
// It has to be the first argument when it is given.
Argument text( args, 0 );
// These are the optional flags for the command.
Argument tired_flag( args, 't', "tired" );
Argument wink_flag( args, 'w', "wink" );
Argument greedy_flag( args, 'g', "greedy" );
// Try to find if these flags can be found in the command.
tired_flag.find();
wink_flag.find();
greedy_flag.find();
caller -> print( "< " );
// Draw the user text when it is given.
if( text.parseString( textBuffer ) ){
caller -> print( textBuffer );
caller -> println( " >" );
caller -> print( ' ' );
for( i = strlen( textBuffer ) + 2; i > 0 ; i-- ){
caller -> print( '-' );
}
}
// Draw the default text.
else{
caller -> print( "booo >\r\n ------" );
}
caller -> println();
caller -> print( cow_top );
// The eye drawing has a priority in case multiple flags are given.
// Check each flag from top priority and draw the corresponding eyes.
if( tired_flag.isFound() ){
caller -> print( tired_eyes );
}
else if( wink_flag.isFound() ){
caller -> print( wink_eyes );
}
else if( greedy_flag.isFound() ){
caller -> print( greedy_eyes );
}
// If no flags are given, draw dhe default eyes.
else{
caller -> print( default_eyes );
}
caller -> print( cow_bottom );
return true;
}