2 A simple "fuzzer" application for OrderedCollectionLib, reading commands from
3 the standard input, and writing results to the standard output.
5 Make sure you configure your platform so that the console stderr device is
6 visible to the user (or else run the program from wherever stderr is visible
7 per default, eg. serial line).
9 Copyright (C) 2014, Red Hat, Inc.
10 Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
12 This program and the accompanying materials are licensed and made available
13 under the terms and conditions of the BSD License which accompanies this
14 distribution. The full text of the license may be found at
15 http://opensource.org/licenses/bsd-license.
17 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
18 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
21 #include <assert.h> // assert()
22 #include <errno.h> // errno
23 #include <limits.h> // INT_MIN
24 #include <stdio.h> // fgets()
25 #include <stdlib.h> // EXIT_FAILURE
26 #include <string.h> // strerror()
27 #include <unistd.h> // getopt()
29 #include <Library/OrderedCollectionLib.h>
32 // We allow the user to select between stdin+stdout and regular input+output
33 // files via command line options. We don't rely on shell redirection for two
36 // - The "old shell" doesn't support input redirection (<a, <);
38 // - The "new shell" supports input redirection (<a, <), but those redirections
39 // break fgets(stdin). Both when redirecting stdin from an ASCII file (<a),
40 // and when redirecting stdin from a UCS-2 file (<), the very first fgets()
41 // spirals into an infinite loop, spewing ^@ on the serial console.
43 // Performing fopen() manually (made available to the user with the -i option),
44 // and reading from that stream with fgets() work under both old and new shell.
45 // Only ASCII encoded files are supported.
47 static FILE *Input
, *Output
;
51 // Define a (potentially aggregate) key type.
58 // The user structure includes the key as one of its fields. (There can be
59 // several, optionally differently typed, keys, if we link user structures into
60 // several collections, with different comparators.)
63 unsigned char Supplementary1
[4];
65 unsigned short Supplementary2
[2];
70 Compare a standalone key against a user structure containing an embedded key.
72 @param[in] StandaloneKey Pointer to the bare key.
74 @param[in] UserStruct Pointer to the user structure with the embedded
77 @retval <0 If StandaloneKey compares less than UserStruct's key.
79 @retval 0 If StandaloneKey compares equal to UserStruct's key.
81 @retval >0 If StandaloneKey compares greater than UserStruct's key.
87 IN CONST VOID
*StandaloneKey
,
88 IN CONST VOID
*UserStruct
91 const USER_KEY
*CmpKey
;
92 const USER_STRUCT
*CmpStruct
;
94 CmpKey
= StandaloneKey
;
95 CmpStruct
= UserStruct
;
97 return CmpKey
->Value
< CmpStruct
->Key
.Value
? -1 :
98 CmpKey
->Value
> CmpStruct
->Key
.Value
? 1 :
104 Comparator function type for two user structures.
106 @param[in] UserStruct1 Pointer to the first user structure.
108 @param[in] UserStruct2 Pointer to the second user structure.
110 @retval <0 If UserStruct1 compares less than UserStruct2.
112 @retval 0 If UserStruct1 compares equal to UserStruct2.
114 @retval >0 If UserStruct1 compares greater than UserStruct2.
120 IN CONST VOID
*UserStruct1
,
121 IN CONST VOID
*UserStruct2
124 const USER_STRUCT
*CmpStruct1
;
126 CmpStruct1
= UserStruct1
;
127 return KeyCompare (&CmpStruct1
->Key
, UserStruct2
);
132 Empty the collection by iterating forward through its entries.
134 This function demonstrates that iterators different from the one being
135 removed remain valid.
137 @param[in,out] Collection The collection to empty.
141 IN OUT ORDERED_COLLECTION
*Collection
144 ORDERED_COLLECTION_ENTRY
*Entry
;
146 Entry
= OrderedCollectionMin (Collection
);
147 while (Entry
!= NULL
) {
148 ORDERED_COLLECTION_ENTRY
*Next
;
150 USER_STRUCT
*UserStruct
;
152 Next
= OrderedCollectionNext (Entry
);
153 OrderedCollectionDelete (Collection
, Entry
, &Ptr
);
156 fprintf (Output
, "%s: %d: removed\n", __FUNCTION__
, UserStruct
->Key
.Value
);
165 Empty the collection by iterating backward through its entries.
167 This function demonstrates that iterators different from the one being
168 removed remain valid.
170 @param[in,out] Collection The collection to empty.
174 IN OUT ORDERED_COLLECTION
*Collection
177 ORDERED_COLLECTION_ENTRY
*Entry
;
179 Entry
= OrderedCollectionMax (Collection
);
180 while (Entry
!= NULL
) {
181 ORDERED_COLLECTION_ENTRY
*Prev
;
183 USER_STRUCT
*UserStruct
;
185 Prev
= OrderedCollectionPrev (Entry
);
186 OrderedCollectionDelete (Collection
, Entry
, &Ptr
);
189 fprintf (Output
, "%s: %d: removed\n", __FUNCTION__
, UserStruct
->Key
.Value
);
198 List the user structures linked into the collection, in increasing order.
200 @param[in] Collection The collection to list.
204 IN ORDERED_COLLECTION
*Collection
207 ORDERED_COLLECTION_ENTRY
*Entry
;
209 for (Entry
= OrderedCollectionMin (Collection
); Entry
!= NULL
;
210 Entry
= OrderedCollectionNext (Entry
)) {
211 USER_STRUCT
*UserStruct
;
213 UserStruct
= OrderedCollectionUserStruct (Entry
);
214 fprintf (Output
, "%s: %d\n", __FUNCTION__
, UserStruct
->Key
.Value
);
220 List the user structures linked into the collection, in decreasing order.
222 @param[in] Collection The collection to list.
226 IN ORDERED_COLLECTION
*Collection
229 ORDERED_COLLECTION_ENTRY
*Entry
;
231 for (Entry
= OrderedCollectionMax (Collection
); Entry
!= NULL
;
232 Entry
= OrderedCollectionPrev (Entry
)) {
233 USER_STRUCT
*UserStruct
;
235 UserStruct
= OrderedCollectionUserStruct (Entry
);
236 fprintf (Output
, "%s: %d\n", __FUNCTION__
, UserStruct
->Key
.Value
);
242 Create a new user structure and attempt to insert it into the collection.
244 @param[in] Value The key value of the user structure to create.
246 @param[in,out] Collection The collection to insert the new user structure
252 IN OUT ORDERED_COLLECTION
*Collection
255 USER_STRUCT
*UserStruct
, *UserStruct2
;
256 RETURN_STATUS Status
;
257 ORDERED_COLLECTION_ENTRY
*Entry
;
259 UserStruct
= calloc (1, sizeof *UserStruct
);
260 if (UserStruct
== NULL
) {
261 fprintf (Output
, "%s: %d: calloc(): out of memory\n", __FUNCTION__
, Value
);
265 UserStruct
->Key
.Value
= Value
;
266 Status
= OrderedCollectionInsert (Collection
, &Entry
, UserStruct
);
269 case RETURN_OUT_OF_RESOURCES
:
270 fprintf (Output
, "%s: %d: OrderedCollectionInsert(): out of memory\n",
271 __FUNCTION__
, Value
);
272 goto ReleaseUserStruct
;
274 case RETURN_ALREADY_STARTED
:
275 UserStruct2
= OrderedCollectionUserStruct (Entry
);
276 assert (UserStruct
!= UserStruct2
);
277 assert (UserStruct2
->Key
.Value
== Value
);
278 fprintf (Output
, "%s: %d: already exists\n", __FUNCTION__
,
279 UserStruct2
->Key
.Value
);
280 goto ReleaseUserStruct
;
283 assert (Status
== RETURN_SUCCESS
);
287 assert (OrderedCollectionUserStruct (Entry
) == UserStruct
);
288 fprintf (Output
, "%s: %d: inserted\n", __FUNCTION__
, Value
);
297 Look up a user structure by key in the collection and print it.
299 @param[in] Value The key of the user structure to find.
301 @param[in] Collection The collection to search for the user structure with
307 IN ORDERED_COLLECTION
*Collection
310 USER_KEY StandaloneKey
;
311 ORDERED_COLLECTION_ENTRY
*Entry
;
312 USER_STRUCT
*UserStruct
;
314 StandaloneKey
.Value
= Value
;
315 Entry
= OrderedCollectionFind (Collection
, &StandaloneKey
);
318 fprintf (Output
, "%s: %d: not found\n", __FUNCTION__
, Value
);
322 UserStruct
= OrderedCollectionUserStruct (Entry
);
323 assert (UserStruct
->Key
.Value
== StandaloneKey
.Value
);
324 fprintf (Output
, "%s: %d: found\n", __FUNCTION__
, UserStruct
->Key
.Value
);
329 Look up a user structure by key in the collection and delete it.
331 @param[in] Value The key of the user structure to find.
333 @param[in] Collection The collection to search for the user structure with
339 IN ORDERED_COLLECTION
*Collection
342 USER_KEY StandaloneKey
;
343 ORDERED_COLLECTION_ENTRY
*Entry
;
345 USER_STRUCT
*UserStruct
;
347 StandaloneKey
.Value
= Value
;
348 Entry
= OrderedCollectionFind (Collection
, &StandaloneKey
);
351 fprintf (Output
, "%s: %d: not found\n", __FUNCTION__
, Value
);
355 OrderedCollectionDelete (Collection
, Entry
, &Ptr
);
358 assert (UserStruct
->Key
.Value
== StandaloneKey
.Value
);
359 fprintf (Output
, "%s: %d: removed\n", __FUNCTION__
, UserStruct
->Key
.Value
);
366 void (*Function
) (ORDERED_COLLECTION
*Collection
);
367 const char *Description
;
372 void (*Function
) (int Value
, ORDERED_COLLECTION
*Collection
);
373 const char *Description
;
376 static const KEYLESS_COMMAND KeylessCommands
[] = {
377 { "forward-empty", CmdForwardEmpty
,
378 "empty the collection iterating forward" },
379 { "fe", CmdForwardEmpty
,
380 "shorthand for forward-empty" },
381 { "backward-empty", CmdBackwardEmpty
,
382 "empty the collection iterating backward" },
383 { "be", CmdBackwardEmpty
,
384 "shorthand for backward-empty" },
385 { "forward-list", CmdForwardList
,
386 "list contents, iterating forward" },
387 { "fl", CmdForwardList
,
388 "shorthand for forward-list" },
389 { "backward-list", CmdBackwardList
,
390 "list contents, iterating backward" },
391 { "bl", CmdBackwardList
,
392 "shorthand for backward-list" },
396 static const KEYED_COMMAND KeyedCommands
[] = {
397 { "insert ", CmdInsert
, "insert value into collection" },
398 { "i ", CmdInsert
, "shorthand for insert" },
399 { "find ", CmdFind
, "find value in collection" },
400 { "f ", CmdFind
, "shorthand for find" },
401 { "delete ", CmdDelete
, "delete value from collection" },
402 { "d ", CmdDelete
, "shorthand for delete" },
408 List the supported commands on stderr.
415 const KEYLESS_COMMAND
*KeylessCmd
;
416 const KEYED_COMMAND
*KeyedCmd
;
418 fprintf (stderr
, "Supported commands:\n\n");
419 for (KeylessCmd
= KeylessCommands
; KeylessCmd
->Command
!= NULL
;
421 fprintf (stderr
, "%-14s: %s\n", KeylessCmd
->Command
,
422 KeylessCmd
->Description
);
424 for (KeyedCmd
= KeyedCommands
; KeyedCmd
->Command
!= NULL
; ++KeyedCmd
) {
425 fprintf (stderr
, "%-9s<int>: %s\n", KeyedCmd
->Command
,
426 KeyedCmd
->Description
);
432 Configure stdio FILEs that we'll use for input and output.
434 @param[in] ArgC The number of elements in ArgV, from main(). The environment
435 is required to ensure ArgC >= 1 (ie. that the program name,
436 ArgV[0], is available).
438 @param[in] ArgV Command line argument list, from main().
446 char *InputName
, *OutputName
;
456 switch (getopt (ArgC
, ArgV
, ":i:o:h")) {
467 "%1$s: simple OrderedCollectionLib tester\n"
469 "Usage: 1. %1$s [-i InputFile] [-o OutputFile]\n"
473 " -i InputFile : read commands from InputFile\n"
474 " (will read from stdin if absent)\n"
475 " -o OutputFile: write command responses to OutputFile\n"
476 " (will write to stdout if absent)\n"
477 " -h : print this help and exit\n"
483 // The current "compatibility" getopt() implementation doesn't support optopt,
484 // but it gracefully degrades these branches to the others (one of the optarg
485 // ones or the excess operands one).
489 fprintf (stderr
, "%s: option -%c requires an argument; pass -h for "
490 "help\n", ArgV
[0], optopt
);
494 fprintf (stderr
, "%s: unknown option -%c; pass -h for help\n", ArgV
[0],
500 if (optind
!= ArgC
) {
501 fprintf (stderr
, "%s: excess operands on command line; pass -h for "
513 if (InputName
== NULL
) {
516 Input
= fopen (InputName
, "r");
518 fprintf (stderr
, "%s: fopen(\"%s\", \"r\"): %s\n", ArgV
[0], InputName
,
524 if (OutputName
== NULL
) {
527 Output
= fopen (OutputName
, "w");
528 if (Output
== NULL
) {
529 fprintf (stderr
, "%s: fopen(\"%s\", \"w\"): %s\n", ArgV
[0], OutputName
,
536 // When reading commands from the standard input, assume interactive mode,
537 // and list the supported commands. However, delay this until both streams
540 if (InputName
== NULL
) {
553 ORDERED_COLLECTION
*Collection
;
556 SetupInputOutput (ArgC
, ArgV
);
558 Collection
= OrderedCollectionInit (UserStructCompare
, KeyCompare
);
559 if (Collection
== NULL
) {
560 fprintf (stderr
, "%s: OrderedCollectionInit(): out of memory\n",
565 RetVal
= EXIT_SUCCESS
;
566 while (fgets (Line
, sizeof Line
, Input
) != NULL
) {
568 const KEYLESS_COMMAND
*KeylessCmd
;
569 const KEYED_COMMAND
*KeyedCmd
;
571 Length
= strlen (Line
);
573 if (Line
[Length
- 1] != '\n') {
574 fprintf (stderr
, "%s: overlong line\n", __FUNCTION__
);
575 RetVal
= EXIT_FAILURE
;
582 Line
[Length
- 1] = '\0';
583 if (Length
>= 2 && Line
[Length
- 2] == '\r') {
584 Line
[Length
- 2] = '\0';
587 // Ignore empty lines and comments.
589 if (Line
[0] == '\0' || Line
[0] == '#') {
590 if (Input
!= stdin
) {
592 // ... but echo them back in non-interactive mode.
594 fprintf (Output
, "%s\n", Line
);
600 // Ironically, this is the kind of loop that should be replaced with an
601 // ORDERED_COLLECTION.
603 for (KeylessCmd
= KeylessCommands
; KeylessCmd
->Command
!= NULL
;
605 if (strcmp (KeylessCmd
->Command
, Line
) == 0) {
606 KeylessCmd
->Function (Collection
);
610 if (KeylessCmd
->Command
!= NULL
) {
614 for (KeyedCmd
= KeyedCommands
; KeyedCmd
->Command
!= NULL
; ++KeyedCmd
) {
617 CmdLength
= strlen (KeyedCmd
->Command
);
618 assert (CmdLength
>= 2);
619 if (strncmp (KeyedCmd
->Command
, Line
, CmdLength
) == 0) {
620 char *CommandArg
, *EndPtr
;
623 CommandArg
= Line
+ CmdLength
;
625 Value
= strtol (CommandArg
, &EndPtr
, 10);
626 if (EndPtr
== CommandArg
|| // no conversion performed
627 errno
!= 0 || // not in long's range, etc
628 *EndPtr
!= '\0' || // final string not empty
629 Value
< INT_MIN
|| Value
> INT_MAX
// parsed long not in int range
631 fprintf (stderr
, "%s: %.*s: \"%s\": not an int\n", __FUNCTION__
,
632 (int)(CmdLength
- 1), Line
, CommandArg
);
634 KeyedCmd
->Function (Value
, Collection
);
640 if (KeyedCmd
->Command
!= NULL
) {
644 fprintf (stderr
, "%s: \"%s\": unknown command\n", __FUNCTION__
, Line
);
647 if (RetVal
== EXIT_SUCCESS
&& ferror (Input
)) {
648 fprintf (stderr
, "%s: fgets(): %s\n", __FUNCTION__
, strerror (errno
));
649 RetVal
= EXIT_FAILURE
;
652 CmdForwardEmpty (Collection
);
653 OrderedCollectionUninit (Collection
);