| /** @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; | |
| } |