Jim Lawless' Blog


Yet Another Enhanced Echo Command

Originally published on: Sun, 06 Sep 2009 16:12:42 +0000

Please note! If you're having difficulties compiling the C source code presented below, please see my post: Compiling C from the Command Line with Pelles C

Although a number of more robust scripting options are available, I still write batch files in the Windows environment. I enjoy the simplicity but sometimes find myself frustrated with something that I perceive to be a limitation.

Usually, if I hit a wall trying to perform something that can be a cumbersome in a batch file, I will often just write a separate program or script to perform the task.

In some cases, when I need to construct an output text file, I find myself opting for Perl or AWK or something similar if I need to embed special-characters in the output. The greater-than '>' and less-than '<' symbols can be a bit troublesome in a batch file because they handle redirection. This makes building an XML file a little tricky. The & symbol can sometimes cause problems too as it is supposed to be used to chain commands together.

As a solution to my own problem, I wrote this simple version of the batch echo command called echoj. While the EXE provided in a link later in this text is intended for use in a Windows environment, the C code should be portable to other environments.

Here's the C source:

echoj.c


// echoj
// An enhanced batch "echo" command that allows
// data to be written to or appended to a file. Binary characters
// can be specified in hex notation with a leading '$' symbol.
//
// License: MIT / X11
// Copyright (c) 2009 by James K. Lawless
// jimbo@radiks.net http://www.radiks.net/~jimbo
// http://www.mailsend-online.com
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.


#include <stdio.h>
#include <string.h>
#include <ctype.h>

void syntax(void);

int main(int argc,char **argv) {
   FILE *out;
   char *fileName;
   char *fileMode;
   int firstString;
   int i;
   char hexWrk[3];
   int c;
   
   
   if(argc<2) {
      syntax();
      return 1;
   }
   out=stdout;
   fileName=NULL;
   fileMode="w";
   
   for(i=1;i<argc;i++) {
      if(*argv[i]!='-') {
         firstString=i;
         break;
      }
      switch( *(argv[i]+1)) {
         case 'v':
         case 'V':
            if(i==(argc-1)) {
               fprintf(stderr,"Missing argument for -v parameter");
               return 1;
            }
return atoi(argv[i+1]);
            break;
         case 'f':
         case 'F':
            if(i==(argc-1)) {
               fprintf(stderr,"Missing argument for -f parameter");
               return 1;
            }
            fileName=argv[i+1];
            i++;
            break;
         case 'm':
         case 'M':
            if(i==(argc-1)) {
               fprintf(stderr,"Missing argument for -m parameter");
               return 1;
            }
            fileMode=argv[i+1];
            i++;
            break;
      }
   }
   
   if(fileName!=NULL) {
      out=fopen(fileName,fileMode);
      if(out==NULL) {
         fprintf(stderr,"Unable to open file %s in mode %s\n",fileName,fileMode);
         return 1;
      }
   }
      // now, process the rest of the strings as output.
   for(i=firstString;i<argc;i++) {
      if(* (argv[i])=='$') {
         if(strlen(argv[i])==3) {
            hexWrk[0]=*(argv[i]+1);
            hexWrk[1]=*(argv[i]+2);
            hexWrk[2]=0;
            sscanf(hexWrk,"%02x",&c);
            fputc(c,out);
            continue;
         }
      }
      fwrite(argv[i],1,strlen(argv[i]),out);
   }
   if(fileName!=NULL)
      fclose(out);
   return 0;
}

void syntax(void) {
   printf("echoj by Jim Lawless jimbo@radiks.net\n");
   printf("Syntax:\techoj [ -f filename -m file-open-mode -v verification-int ] strings\n");
   printf("\n");
   printf("Valid file open modes:\n");
   printf(" \"a\" Append to specified file in text mode.\n");
   printf(" \"ab\" Append to specified file in binary mode.\n");
   printf(" \"w\" (default) Write (overwrite) specified file in text mode.\n");
   printf(" \"wb\" Write (overwrite) specified file in binary mode.\n\n");
   printf("If -f is not specified, output is written to standard output.\n");
   printf("Strings should be in double-quotes if they contain spaces.\n\n");
   printf("Binary characters can be specified in two-digit hexadecimal notation\n");
   printf("using a '$' at the beginning.\n\n");
   printf("Example:\n");
   printf("echoj \"Here are greater-than and less-than signs: \" $3c \" \" $3e $0a\n\n");
   printf("For more information, including documentation for the -v option,\n");
   printf("please read the related blog post at this shortened URL:\nhttp://bit.ly/2eqbvR\n");
}

If you run echoj from a command-prompt with no arguments, you should see the following help screen:


echoj by Jim Lawless jimbo@radiks.net
Syntax: echoj [ -f filename -m file-open-mode -v verification-int ] strings

Valid file open modes:
   "a" Append to specified file in text mode.
   "ab" Append to specified file in binary mode.
   "w" (default) Write (overwrite) specified file in text mode.
   "wb" Write (overwrite) specified file in binary mode.

If -f is not specified, output is written to standard output.
Strings should be in double-quotes if they contain spaces.

Binary characters can be specified in two-digit hexadecimal notation
using a '$' at the beginning.

Example:
echoj "Here are greater-than and less-than signs: " $3c " " $3e $0a

For more information, including documentation for the -v option,
please read the related blog post at this shortened URL:
http://bit.ly/2eqbvR

Let's examine the different options and different ways to use echoj.

The -v option is intended to provide a simple way to verify that echoj.exe is in your command processor's execution path. It accepts a number as an argument and terminates the echoj process immediately, returning that number.

In a batch file, you might leverage feature in this manner.


echoj.exe -v 42
if "%ERRORLEVEL%"=="42" goto keepgoing
rem error logic here
goto getout
...
:keepgoing
...
:getout

Without any parameters, echoj can be useful when needing to write special characters to the console. Consider the following example.


echoj "Tab1" $09 "Tab2" $0a

The $09 code is the ASCII code for the TAB character. echoj will translate any string on the command line ( after the optional parameters ) from its hexadecimal representation into binary if:

    The string is exactly three characters in length. The string begins with a '$' symbol.

Note that echoj doesn't work like the normal echo. If you enter:


echoj Hey there, everybody!

You'll likely see:


heythere,everybody!

echoj looks for parameters separated by spaces. If you need to include spaces in a parameter, enclose that parameter in double-quotes.

So, the TAB example would also work the same way if we wrote:


echoj Tab1 $09 Tab2 $0a

...and would also work if we quote the hexadecimal paramters.


echoj Tab1 "$09" Tab2 "$0a"

You can display the greater-than, less-than, ampersand, and pipe characters by using $3c, $3e, $26, and $7c respectively.


echoj $3c " " $3e " " $26 " " $7c

Also note that echoj does not provide it's own newline sequence as output. We do this ourselves by placing the code for an ASCII linefeed ($0a) as the last string paramter. Try this example.


echoj Line1 $0a $0a Line3 $0a $0a $0a Line6

You should see:


Line1

Line3


Line6

You may wonder why we don't also specify a carriage-return ($0d) character with each linefeed. For Windows, most of the C libraries seem to translate the $0a character into a combination of $0d and $0a when going to the console or when a file is opened in text mode. The only situations where we really need to concern ourselves with the carriage-return character will be when we are using echoj to write to binary files and we need to issue a newline sequence.

The -f and -m options can be used to write to files as an option to using the standard command-processor's redirection symbols.

Consider the following example:


echoj -f tmp.txt Line1 $0a $0a Line3 $0a $0a $0a Line6
type tmp.txt

The above code writes the same sequence of lines from a prior example to the text file tmp.txt. The default file open mode that echoj uses is "w" for write new text file. This mode opens a file as output and deletes any existing contents if the file had already existed.

Let's append the same line to the file by specifying mode "a" ( append to text file ):


echoj -f tmp.txt -m "a" Line1 $0a $0a Line3 $0a $0a $0a Line6
type tmp.txt

In the output file, you should now see:


Line1

Line3


Line6Line1

Line3


Line6

Let's use the binary options to build a small .com file called alphabet.com. ( You can see the raw assembly source to this file in this blog's topmost masthead image. )


echoj -f alphabet.com -m "wb" $b2 $41 $b9 $1a $00 $b4 $02 $cd
echoj -f alphabet.com -m "ab" $21 $49 $74 $06 $88 $c2 $fe $c2
echoj -f alphabet.com -m "ab" $eb $f3 $b8 $00 $4c $cd $21

If you now execute alphabet.com you should see the alphabet on the console in upper-case.

These file open mode strings are passed in verbatim to the C fopen() function, so echoj will respond to modes such as "a+b" ( append binary ), but I've yet to really experiment with any options beyond the four listed in the syntax-help screen.

The source and executable file for echoj above can be downloaded in a single archive at: http://www.mailsend-online.com/wp/echoj.zip

Unless otherwise noted, all code and text entries are Copyright ©2009 by James K. Lawless



Views expressed in this blog are those of the author and do not necessary reflect those of the author's employer. Views expressed in the comments are those of the responding individual.

stumbleupon Save to StumbleUpon
digg Digg it
reddit Save to Reddit
facebook Share on Facebook
twitter Share on Twitter
aolfav More bookmarks


Previous post: Internet Protocols and Rhino JavaScript
Next post:Command-Line Image Format Conversion


About Jim ...


Click **here**
to try out MailWrench;
a command-line SMTP /
SMTPS (Google Gmail)
mailer for Windows.


Follow me on Twitter

http://twitter.com/lawlessGuy


Recent Posts

A JavaScript REPL for Android Devices

MailSend is Free

My Blog Engine

The October 10th Bug

A Review of Kevin Mitnick's Book Ghost in the Wires

Spellbound by Web Programming

Backlinks to my Blog Posts

Play MP3 Files with Python on Windows


Random Posts

A Data Manipulation Library for TAP

Cheating the LZW

Getting the Windows Console Text Color

An SMTP Server Simulator in Perl

An Interview with Tom Zimmer: Forth System Developer

WSH2EXE part 2

The Protection Racket

A TCP Command Line Interface in Rhino JavaScript

Throwaway Software: HangUp

Speeding up JRuby with NailGun


Full List of Posts

http://www.mailsend-online.com/bloglist.htm


Recent Posts from my Other Blog

Remembering Dr. San Guinary

Why Some Web Sites will go Dark on Jan 18th

SNL Superhero Skit

More Ruby Games

My Ruby Game Challenge Entry

Steal this Bookmarklet

Nerd Toys

Learn New Jargon, You Must

Spot the Wiebe

Tech Magazine Glory Days

Book Review : Paull Allen - Idea Man

A 90's Experiment in Online Systems - The U.S. West CommunityLink Service