/** @file | |
A simple "fuzzer" application for OrderedCollectionLib, reading commands from | |
the standard input, and writing results to the standard output. | |
Make sure you configure your platform so that the console stderr device is | |
visible to the user (or else run the program from wherever stderr is visible | |
per default, eg. serial line). | |
Copyright (C) 2014, Red Hat, Inc. | |
Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials are licensed and made available | |
under the terms and conditions of the BSD License which accompanies this | |
distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license. | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT | |
WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include <assert.h> // assert() | |
#include <errno.h> // errno | |
#include <limits.h> // INT_MIN | |
#include <stdio.h> // fgets() | |
#include <stdlib.h> // EXIT_FAILURE | |
#include <string.h> // strerror() | |
#include <unistd.h> // getopt() | |
#include <Library/OrderedCollectionLib.h> | |
// | |
// We allow the user to select between stdin+stdout and regular input+output | |
// files via command line options. We don't rely on shell redirection for two | |
// reasons: | |
// | |
// - The "old shell" doesn't support input redirection (<a, <); | |
// | |
// - The "new shell" supports input redirection (<a, <), but those redirections | |
// break fgets(stdin). Both when redirecting stdin from an ASCII file (<a), | |
// and when redirecting stdin from a UCS-2 file (<), the very first fgets() | |
// spirals into an infinite loop, spewing ^@ on the serial console. | |
// | |
// Performing fopen() manually (made available to the user with the -i option), | |
// and reading from that stream with fgets() work under both old and new shell. | |
// Only ASCII encoded files are supported. | |
// | |
static FILE *Input, *Output; | |
// | |
// Define a (potentially aggregate) key type. | |
// | |
typedef struct { | |
int Value; | |
} USER_KEY; | |
// | |
// The user structure includes the key as one of its fields. (There can be | |
// several, optionally differently typed, keys, if we link user structures into | |
// several collections, with different comparators.) | |
// | |
typedef struct { | |
unsigned char Supplementary1[4]; | |
USER_KEY Key; | |
unsigned short Supplementary2[2]; | |
} USER_STRUCT; | |
/** | |
Compare a standalone key against a user structure containing an embedded key. | |
@param[in] StandaloneKey Pointer to the bare key. | |
@param[in] UserStruct Pointer to the user structure with the embedded | |
key. | |
@retval <0 If StandaloneKey compares less than UserStruct's key. | |
@retval 0 If StandaloneKey compares equal to UserStruct's key. | |
@retval >0 If StandaloneKey compares greater than UserStruct's key. | |
**/ | |
static | |
INTN | |
EFIAPI | |
KeyCompare ( | |
IN CONST VOID *StandaloneKey, | |
IN CONST VOID *UserStruct | |
) | |
{ | |
const USER_KEY *CmpKey; | |
const USER_STRUCT *CmpStruct; | |
CmpKey = StandaloneKey; | |
CmpStruct = UserStruct; | |
return CmpKey->Value < CmpStruct->Key.Value ? -1 : | |
CmpKey->Value > CmpStruct->Key.Value ? 1 : | |
0; | |
} | |
/** | |
Comparator function type for two user structures. | |
@param[in] UserStruct1 Pointer to the first user structure. | |
@param[in] UserStruct2 Pointer to the second user structure. | |
@retval <0 If UserStruct1 compares less than UserStruct2. | |
@retval 0 If UserStruct1 compares equal to UserStruct2. | |
@retval >0 If UserStruct1 compares greater than UserStruct2. | |
**/ | |
static | |
INTN | |
EFIAPI | |
UserStructCompare ( | |
IN CONST VOID *UserStruct1, | |
IN CONST VOID *UserStruct2 | |
) | |
{ | |
const USER_STRUCT *CmpStruct1; | |
CmpStruct1 = UserStruct1; | |
return KeyCompare (&CmpStruct1->Key, UserStruct2); | |
} | |
/** | |
Empty the collection by iterating forward through its entries. | |
This function demonstrates that iterators different from the one being | |
removed remain valid. | |
@param[in,out] Collection The collection to empty. | |
**/ | |
static void | |
CmdForwardEmpty ( | |
IN OUT ORDERED_COLLECTION *Collection | |
) | |
{ | |
ORDERED_COLLECTION_ENTRY *Entry; | |
Entry = OrderedCollectionMin (Collection); | |
while (Entry != NULL) { | |
ORDERED_COLLECTION_ENTRY *Next; | |
void *Ptr; | |
USER_STRUCT *UserStruct; | |
Next = OrderedCollectionNext (Entry); | |
OrderedCollectionDelete (Collection, Entry, &Ptr); | |
UserStruct = Ptr; | |
fprintf (Output, "%s: %d: removed\n", __FUNCTION__, UserStruct->Key.Value); | |
free (UserStruct); | |
Entry = Next; | |
} | |
} | |
/** | |
Empty the collection by iterating backward through its entries. | |
This function demonstrates that iterators different from the one being | |
removed remain valid. | |
@param[in,out] Collection The collection to empty. | |
**/ | |
static void | |
CmdBackwardEmpty ( | |
IN OUT ORDERED_COLLECTION *Collection | |
) | |
{ | |
ORDERED_COLLECTION_ENTRY *Entry; | |
Entry = OrderedCollectionMax (Collection); | |
while (Entry != NULL) { | |
ORDERED_COLLECTION_ENTRY *Prev; | |
void *Ptr; | |
USER_STRUCT *UserStruct; | |
Prev = OrderedCollectionPrev (Entry); | |
OrderedCollectionDelete (Collection, Entry, &Ptr); | |
UserStruct = Ptr; | |
fprintf (Output, "%s: %d: removed\n", __FUNCTION__, UserStruct->Key.Value); | |
free (UserStruct); | |
Entry = Prev; | |
} | |
} | |
/** | |
List the user structures linked into the collection, in increasing order. | |
@param[in] Collection The collection to list. | |
**/ | |
static void | |
CmdForwardList ( | |
IN ORDERED_COLLECTION *Collection | |
) | |
{ | |
ORDERED_COLLECTION_ENTRY *Entry; | |
for (Entry = OrderedCollectionMin (Collection); Entry != NULL; | |
Entry = OrderedCollectionNext (Entry)) { | |
USER_STRUCT *UserStruct; | |
UserStruct = OrderedCollectionUserStruct (Entry); | |
fprintf (Output, "%s: %d\n", __FUNCTION__, UserStruct->Key.Value); | |
} | |
} | |
/** | |
List the user structures linked into the collection, in decreasing order. | |
@param[in] Collection The collection to list. | |
**/ | |
static void | |
CmdBackwardList ( | |
IN ORDERED_COLLECTION *Collection | |
) | |
{ | |
ORDERED_COLLECTION_ENTRY *Entry; | |
for (Entry = OrderedCollectionMax (Collection); Entry != NULL; | |
Entry = OrderedCollectionPrev (Entry)) { | |
USER_STRUCT *UserStruct; | |
UserStruct = OrderedCollectionUserStruct (Entry); | |
fprintf (Output, "%s: %d\n", __FUNCTION__, UserStruct->Key.Value); | |
} | |
} | |
/** | |
Create a new user structure and attempt to insert it into the collection. | |
@param[in] Value The key value of the user structure to create. | |
@param[in,out] Collection The collection to insert the new user structure | |
into. | |
**/ | |
static void | |
CmdInsert ( | |
IN int Value, | |
IN OUT ORDERED_COLLECTION *Collection | |
) | |
{ | |
USER_STRUCT *UserStruct, *UserStruct2; | |
RETURN_STATUS Status; | |
ORDERED_COLLECTION_ENTRY *Entry; | |
UserStruct = calloc (1, sizeof *UserStruct); | |
if (UserStruct == NULL) { | |
fprintf (Output, "%s: %d: calloc(): out of memory\n", __FUNCTION__, Value); | |
return; | |
} | |
UserStruct->Key.Value = Value; | |
Status = OrderedCollectionInsert (Collection, &Entry, UserStruct); | |
switch (Status) { | |
case RETURN_OUT_OF_RESOURCES: | |
fprintf (Output, "%s: %d: OrderedCollectionInsert(): out of memory\n", | |
__FUNCTION__, Value); | |
goto ReleaseUserStruct; | |
case RETURN_ALREADY_STARTED: | |
UserStruct2 = OrderedCollectionUserStruct (Entry); | |
assert (UserStruct != UserStruct2); | |
assert (UserStruct2->Key.Value == Value); | |
fprintf (Output, "%s: %d: already exists\n", __FUNCTION__, | |
UserStruct2->Key.Value); | |
goto ReleaseUserStruct; | |
default: | |
assert (Status == RETURN_SUCCESS); | |
break; | |
} | |
assert (OrderedCollectionUserStruct (Entry) == UserStruct); | |
fprintf (Output, "%s: %d: inserted\n", __FUNCTION__, Value); | |
return; | |
ReleaseUserStruct: | |
free (UserStruct); | |
} | |
/** | |
Look up a user structure by key in the collection and print it. | |
@param[in] Value The key of the user structure to find. | |
@param[in] Collection The collection to search for the user structure with | |
the key. | |
**/ | |
static void | |
CmdFind ( | |
IN int Value, | |
IN ORDERED_COLLECTION *Collection | |
) | |
{ | |
USER_KEY StandaloneKey; | |
ORDERED_COLLECTION_ENTRY *Entry; | |
USER_STRUCT *UserStruct; | |
StandaloneKey.Value = Value; | |
Entry = OrderedCollectionFind (Collection, &StandaloneKey); | |
if (Entry == NULL) { | |
fprintf (Output, "%s: %d: not found\n", __FUNCTION__, Value); | |
return; | |
} | |
UserStruct = OrderedCollectionUserStruct (Entry); | |
assert (UserStruct->Key.Value == StandaloneKey.Value); | |
fprintf (Output, "%s: %d: found\n", __FUNCTION__, UserStruct->Key.Value); | |
} | |
/** | |
Look up a user structure by key in the collection and delete it. | |
@param[in] Value The key of the user structure to find. | |
@param[in] Collection The collection to search for the user structure with | |
the key. | |
**/ | |
static void | |
CmdDelete ( | |
IN int Value, | |
IN ORDERED_COLLECTION *Collection | |
) | |
{ | |
USER_KEY StandaloneKey; | |
ORDERED_COLLECTION_ENTRY *Entry; | |
void *Ptr; | |
USER_STRUCT *UserStruct; | |
StandaloneKey.Value = Value; | |
Entry = OrderedCollectionFind (Collection, &StandaloneKey); | |
if (Entry == NULL) { | |
fprintf (Output, "%s: %d: not found\n", __FUNCTION__, Value); | |
return; | |
} | |
OrderedCollectionDelete (Collection, Entry, &Ptr); | |
UserStruct = Ptr; | |
assert (UserStruct->Key.Value == StandaloneKey.Value); | |
fprintf (Output, "%s: %d: removed\n", __FUNCTION__, UserStruct->Key.Value); | |
free (UserStruct); | |
} | |
typedef struct { | |
const char *Command; | |
void (*Function) (ORDERED_COLLECTION *Collection); | |
const char *Description; | |
} KEYLESS_COMMAND; | |
typedef struct { | |
const char *Command; | |
void (*Function) (int Value, ORDERED_COLLECTION *Collection); | |
const char *Description; | |
} KEYED_COMMAND; | |
static const KEYLESS_COMMAND KeylessCommands[] = { | |
{ "forward-empty", CmdForwardEmpty, | |
"empty the collection iterating forward" }, | |
{ "fe", CmdForwardEmpty, | |
"shorthand for forward-empty" }, | |
{ "backward-empty", CmdBackwardEmpty, | |
"empty the collection iterating backward" }, | |
{ "be", CmdBackwardEmpty, | |
"shorthand for backward-empty" }, | |
{ "forward-list", CmdForwardList, | |
"list contents, iterating forward" }, | |
{ "fl", CmdForwardList, | |
"shorthand for forward-list" }, | |
{ "backward-list", CmdBackwardList, | |
"list contents, iterating backward" }, | |
{ "bl", CmdBackwardList, | |
"shorthand for backward-list" }, | |
{ NULL } | |
}; | |
static const KEYED_COMMAND KeyedCommands[] = { | |
{ "insert ", CmdInsert, "insert value into collection" }, | |
{ "i ", CmdInsert, "shorthand for insert" }, | |
{ "find ", CmdFind, "find value in collection" }, | |
{ "f ", CmdFind, "shorthand for find" }, | |
{ "delete ", CmdDelete, "delete value from collection" }, | |
{ "d ", CmdDelete, "shorthand for delete" }, | |
{ NULL } | |
}; | |
/** | |
List the supported commands on stderr. | |
**/ | |
static void | |
ListCommands ( | |
void | |
) | |
{ | |
const KEYLESS_COMMAND *KeylessCmd; | |
const KEYED_COMMAND *KeyedCmd; | |
fprintf (stderr, "Supported commands:\n\n"); | |
for (KeylessCmd = KeylessCommands; KeylessCmd->Command != NULL; | |
++KeylessCmd) { | |
fprintf (stderr, "%-14s: %s\n", KeylessCmd->Command, | |
KeylessCmd->Description); | |
} | |
for (KeyedCmd = KeyedCommands; KeyedCmd->Command != NULL; ++KeyedCmd) { | |
fprintf (stderr, "%-9s<int>: %s\n", KeyedCmd->Command, | |
KeyedCmd->Description); | |
} | |
} | |
/** | |
Configure stdio FILEs that we'll use for input and output. | |
@param[in] ArgC The number of elements in ArgV, from main(). The environment | |
is required to ensure ArgC >= 1 (ie. that the program name, | |
ArgV[0], is available). | |
@param[in] ArgV Command line argument list, from main(). | |
**/ | |
static void | |
SetupInputOutput ( | |
IN int ArgC, | |
IN char **ArgV | |
) | |
{ | |
char *InputName, *OutputName; | |
int Loop; | |
assert (ArgC >= 1); | |
InputName = NULL; | |
OutputName = NULL; | |
Loop = 1; | |
while (Loop) { | |
switch (getopt (ArgC, ArgV, ":i:o:h")) { | |
case 'i': | |
InputName = optarg; | |
break; | |
case 'o': | |
OutputName = optarg; | |
break; | |
case 'h': | |
fprintf (stderr, | |
"%1$s: simple OrderedCollectionLib tester\n" | |
"\n" | |
"Usage: 1. %1$s [-i InputFile] [-o OutputFile]\n" | |
" 2. %1$s -h\n" | |
"\n" | |
"Options:\n" | |
" -i InputFile : read commands from InputFile\n" | |
" (will read from stdin if absent)\n" | |
" -o OutputFile: write command responses to OutputFile\n" | |
" (will write to stdout if absent)\n" | |
" -h : print this help and exit\n" | |
"\n", ArgV[0]); | |
ListCommands (); | |
exit (EXIT_SUCCESS); | |
// | |
// The current "compatibility" getopt() implementation doesn't support optopt, | |
// but it gracefully degrades these branches to the others (one of the optarg | |
// ones or the excess operands one). | |
// | |
#if 0 | |
case ':': | |
fprintf (stderr, "%s: option -%c requires an argument; pass -h for " | |
"help\n", ArgV[0], optopt); | |
exit (EXIT_FAILURE); | |
case '?': | |
fprintf (stderr, "%s: unknown option -%c; pass -h for help\n", ArgV[0], | |
optopt); | |
exit (EXIT_FAILURE); | |
#endif | |
case -1: | |
if (optind != ArgC) { | |
fprintf (stderr, "%s: excess operands on command line; pass -h for " | |
"help\n", ArgV[0]); | |
exit (EXIT_FAILURE); | |
} | |
Loop = 0; | |
break; | |
default: | |
assert (0); | |
} | |
} | |
if (InputName == NULL) { | |
Input = stdin; | |
} else { | |
Input = fopen (InputName, "r"); | |
if (Input == NULL) { | |
fprintf (stderr, "%s: fopen(\"%s\", \"r\"): %s\n", ArgV[0], InputName, | |
strerror (errno)); | |
exit (EXIT_FAILURE); | |
} | |
} | |
if (OutputName == NULL) { | |
Output = stdout; | |
} else { | |
Output = fopen (OutputName, "w"); | |
if (Output == NULL) { | |
fprintf (stderr, "%s: fopen(\"%s\", \"w\"): %s\n", ArgV[0], OutputName, | |
strerror (errno)); | |
exit (EXIT_FAILURE); | |
} | |
} | |
// | |
// When reading commands from the standard input, assume interactive mode, | |
// and list the supported commands. However, delay this until both streams | |
// are set up. | |
// | |
if (InputName == NULL) { | |
ListCommands (); | |
} | |
} | |
int | |
main ( | |
IN int ArgC, | |
IN char **ArgV | |
) | |
{ | |
int RetVal; | |
ORDERED_COLLECTION *Collection; | |
char Line[256]; | |
SetupInputOutput (ArgC, ArgV); | |
Collection = OrderedCollectionInit (UserStructCompare, KeyCompare); | |
if (Collection == NULL) { | |
fprintf (stderr, "%s: OrderedCollectionInit(): out of memory\n", | |
__FUNCTION__); | |
return EXIT_FAILURE; | |
} | |
RetVal = EXIT_SUCCESS; | |
while (fgets (Line, sizeof Line, Input) != NULL) { | |
size_t Length; | |
const KEYLESS_COMMAND *KeylessCmd; | |
const KEYED_COMMAND *KeyedCmd; | |
Length = strlen (Line); | |
assert (Length > 0); | |
if (Line[Length - 1] != '\n') { | |
fprintf (stderr, "%s: overlong line\n", __FUNCTION__); | |
RetVal = EXIT_FAILURE; | |
break; | |
} | |
// | |
// Strip [\r]\n. | |
// | |
Line[Length - 1] = '\0'; | |
if (Length >= 2 && Line[Length - 2] == '\r') { | |
Line[Length - 2] = '\0'; | |
} | |
// | |
// Ignore empty lines and comments. | |
// | |
if (Line[0] == '\0' || Line[0] == '#') { | |
if (Input != stdin) { | |
// | |
// ... but echo them back in non-interactive mode. | |
// | |
fprintf (Output, "%s\n", Line); | |
} | |
continue; | |
} | |
// | |
// Ironically, this is the kind of loop that should be replaced with an | |
// ORDERED_COLLECTION. | |
// | |
for (KeylessCmd = KeylessCommands; KeylessCmd->Command != NULL; | |
++KeylessCmd) { | |
if (strcmp (KeylessCmd->Command, Line) == 0) { | |
KeylessCmd->Function (Collection); | |
break; | |
} | |
} | |
if (KeylessCmd->Command != NULL) { | |
continue; | |
} | |
for (KeyedCmd = KeyedCommands; KeyedCmd->Command != NULL; ++KeyedCmd) { | |
size_t CmdLength; | |
CmdLength = strlen (KeyedCmd->Command); | |
assert (CmdLength >= 2); | |
if (strncmp (KeyedCmd->Command, Line, CmdLength) == 0) { | |
char *CommandArg, *EndPtr; | |
long Value; | |
CommandArg = Line + CmdLength; | |
errno = 0; | |
Value = strtol (CommandArg, &EndPtr, 10); | |
if (EndPtr == CommandArg || // no conversion performed | |
errno != 0 || // not in long's range, etc | |
*EndPtr != '\0' || // final string not empty | |
Value < INT_MIN || Value > INT_MAX // parsed long not in int range | |
) { | |
fprintf (stderr, "%s: %.*s: \"%s\": not an int\n", __FUNCTION__, | |
(int)(CmdLength - 1), Line, CommandArg); | |
} else { | |
KeyedCmd->Function (Value, Collection); | |
} | |
break; | |
} | |
} | |
if (KeyedCmd->Command != NULL) { | |
continue; | |
} | |
fprintf (stderr, "%s: \"%s\": unknown command\n", __FUNCTION__, Line); | |
} | |
if (RetVal == EXIT_SUCCESS && ferror (Input)) { | |
fprintf (stderr, "%s: fgets(): %s\n", __FUNCTION__, strerror (errno)); | |
RetVal = EXIT_FAILURE; | |
} | |
CmdForwardEmpty (Collection); | |
OrderedCollectionUninit (Collection); | |
return RetVal; | |
} |