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 SPDX-License-Identifier: BSD-2-Clause-Patent
15 #include <assert.h> // assert()
16 #include <errno.h> // errno
17 #include <limits.h> // INT_MIN
18 #include <stdio.h> // fgets()
19 #include <stdlib.h> // EXIT_FAILURE
20 #include <string.h> // strerror()
21 #include <unistd.h> // getopt()
23 #include <Library/OrderedCollectionLib.h>
26 // We allow the user to select between stdin+stdout and regular input+output
27 // files via command line options. We don't rely on shell redirection for two
30 // - The "old shell" doesn't support input redirection (<a, <);
32 // - The "new shell" supports input redirection (<a, <), but those redirections
33 // break fgets(stdin). Both when redirecting stdin from an ASCII file (<a),
34 // and when redirecting stdin from a UCS-2 file (<), the very first fgets()
35 // spirals into an infinite loop, spewing ^@ on the serial console.
37 // Performing fopen() manually (made available to the user with the -i option),
38 // and reading from that stream with fgets() work under both old and new shell.
39 // Only ASCII encoded files are supported.
41 static FILE *Input
, *Output
;
45 // Define a (potentially aggregate) key type.
52 // The user structure includes the key as one of its fields. (There can be
53 // several, optionally differently typed, keys, if we link user structures into
54 // several collections, with different comparators.)
57 unsigned char Supplementary1
[4];
59 unsigned short Supplementary2
[2];
64 Compare a standalone key against a user structure containing an embedded key.
66 @param[in] StandaloneKey Pointer to the bare key.
68 @param[in] UserStruct Pointer to the user structure with the embedded
71 @retval <0 If StandaloneKey compares less than UserStruct's key.
73 @retval 0 If StandaloneKey compares equal to UserStruct's key.
75 @retval >0 If StandaloneKey compares greater than UserStruct's key.
81 IN CONST VOID
*StandaloneKey
,
82 IN CONST VOID
*UserStruct
85 const USER_KEY
*CmpKey
;
86 const USER_STRUCT
*CmpStruct
;
88 CmpKey
= StandaloneKey
;
89 CmpStruct
= UserStruct
;
91 return CmpKey
->Value
< CmpStruct
->Key
.Value
? -1 :
92 CmpKey
->Value
> CmpStruct
->Key
.Value
? 1 :
98 Comparator function type for two user structures.
100 @param[in] UserStruct1 Pointer to the first user structure.
102 @param[in] UserStruct2 Pointer to the second user structure.
104 @retval <0 If UserStruct1 compares less than UserStruct2.
106 @retval 0 If UserStruct1 compares equal to UserStruct2.
108 @retval >0 If UserStruct1 compares greater than UserStruct2.
114 IN CONST VOID
*UserStruct1
,
115 IN CONST VOID
*UserStruct2
118 const USER_STRUCT
*CmpStruct1
;
120 CmpStruct1
= UserStruct1
;
121 return KeyCompare (&CmpStruct1
->Key
, UserStruct2
);
126 Empty the collection by iterating forward through its entries.
128 This function demonstrates that iterators different from the one being
129 removed remain valid.
131 @param[in,out] Collection The collection to empty.
135 IN OUT ORDERED_COLLECTION
*Collection
138 ORDERED_COLLECTION_ENTRY
*Entry
;
140 Entry
= OrderedCollectionMin (Collection
);
141 while (Entry
!= NULL
) {
142 ORDERED_COLLECTION_ENTRY
*Next
;
144 USER_STRUCT
*UserStruct
;
146 Next
= OrderedCollectionNext (Entry
);
147 OrderedCollectionDelete (Collection
, Entry
, &Ptr
);
150 fprintf (Output
, "%s: %d: removed\n", __FUNCTION__
, UserStruct
->Key
.Value
);
159 Empty the collection by iterating backward through its entries.
161 This function demonstrates that iterators different from the one being
162 removed remain valid.
164 @param[in,out] Collection The collection to empty.
168 IN OUT ORDERED_COLLECTION
*Collection
171 ORDERED_COLLECTION_ENTRY
*Entry
;
173 Entry
= OrderedCollectionMax (Collection
);
174 while (Entry
!= NULL
) {
175 ORDERED_COLLECTION_ENTRY
*Prev
;
177 USER_STRUCT
*UserStruct
;
179 Prev
= OrderedCollectionPrev (Entry
);
180 OrderedCollectionDelete (Collection
, Entry
, &Ptr
);
183 fprintf (Output
, "%s: %d: removed\n", __FUNCTION__
, UserStruct
->Key
.Value
);
192 List the user structures linked into the collection, in increasing order.
194 @param[in] Collection The collection to list.
198 IN ORDERED_COLLECTION
*Collection
201 ORDERED_COLLECTION_ENTRY
*Entry
;
203 for (Entry
= OrderedCollectionMin (Collection
); Entry
!= NULL
;
204 Entry
= OrderedCollectionNext (Entry
)) {
205 USER_STRUCT
*UserStruct
;
207 UserStruct
= OrderedCollectionUserStruct (Entry
);
208 fprintf (Output
, "%s: %d\n", __FUNCTION__
, UserStruct
->Key
.Value
);
214 List the user structures linked into the collection, in decreasing order.
216 @param[in] Collection The collection to list.
220 IN ORDERED_COLLECTION
*Collection
223 ORDERED_COLLECTION_ENTRY
*Entry
;
225 for (Entry
= OrderedCollectionMax (Collection
); Entry
!= NULL
;
226 Entry
= OrderedCollectionPrev (Entry
)) {
227 USER_STRUCT
*UserStruct
;
229 UserStruct
= OrderedCollectionUserStruct (Entry
);
230 fprintf (Output
, "%s: %d\n", __FUNCTION__
, UserStruct
->Key
.Value
);
236 Create a new user structure and attempt to insert it into the collection.
238 @param[in] Value The key value of the user structure to create.
240 @param[in,out] Collection The collection to insert the new user structure
246 IN OUT ORDERED_COLLECTION
*Collection
249 USER_STRUCT
*UserStruct
, *UserStruct2
;
250 RETURN_STATUS Status
;
251 ORDERED_COLLECTION_ENTRY
*Entry
;
253 UserStruct
= calloc (1, sizeof *UserStruct
);
254 if (UserStruct
== NULL
) {
255 fprintf (Output
, "%s: %d: calloc(): out of memory\n", __FUNCTION__
, Value
);
259 UserStruct
->Key
.Value
= Value
;
260 Status
= OrderedCollectionInsert (Collection
, &Entry
, UserStruct
);
263 case RETURN_OUT_OF_RESOURCES
:
264 fprintf (Output
, "%s: %d: OrderedCollectionInsert(): out of memory\n",
265 __FUNCTION__
, Value
);
266 goto ReleaseUserStruct
;
268 case RETURN_ALREADY_STARTED
:
269 UserStruct2
= OrderedCollectionUserStruct (Entry
);
270 assert (UserStruct
!= UserStruct2
);
271 assert (UserStruct2
->Key
.Value
== Value
);
272 fprintf (Output
, "%s: %d: already exists\n", __FUNCTION__
,
273 UserStruct2
->Key
.Value
);
274 goto ReleaseUserStruct
;
277 assert (Status
== RETURN_SUCCESS
);
281 assert (OrderedCollectionUserStruct (Entry
) == UserStruct
);
282 fprintf (Output
, "%s: %d: inserted\n", __FUNCTION__
, Value
);
291 Look up a user structure by key in the collection and print it.
293 @param[in] Value The key of the user structure to find.
295 @param[in] Collection The collection to search for the user structure with
301 IN ORDERED_COLLECTION
*Collection
304 USER_KEY StandaloneKey
;
305 ORDERED_COLLECTION_ENTRY
*Entry
;
306 USER_STRUCT
*UserStruct
;
308 StandaloneKey
.Value
= Value
;
309 Entry
= OrderedCollectionFind (Collection
, &StandaloneKey
);
312 fprintf (Output
, "%s: %d: not found\n", __FUNCTION__
, Value
);
316 UserStruct
= OrderedCollectionUserStruct (Entry
);
317 assert (UserStruct
->Key
.Value
== StandaloneKey
.Value
);
318 fprintf (Output
, "%s: %d: found\n", __FUNCTION__
, UserStruct
->Key
.Value
);
323 Look up a user structure by key in the collection and delete it.
325 @param[in] Value The key of the user structure to find.
327 @param[in] Collection The collection to search for the user structure with
333 IN ORDERED_COLLECTION
*Collection
336 USER_KEY StandaloneKey
;
337 ORDERED_COLLECTION_ENTRY
*Entry
;
339 USER_STRUCT
*UserStruct
;
341 StandaloneKey
.Value
= Value
;
342 Entry
= OrderedCollectionFind (Collection
, &StandaloneKey
);
345 fprintf (Output
, "%s: %d: not found\n", __FUNCTION__
, Value
);
349 OrderedCollectionDelete (Collection
, Entry
, &Ptr
);
352 assert (UserStruct
->Key
.Value
== StandaloneKey
.Value
);
353 fprintf (Output
, "%s: %d: removed\n", __FUNCTION__
, UserStruct
->Key
.Value
);
360 void (*Function
) (ORDERED_COLLECTION
*Collection
);
361 const char *Description
;
366 void (*Function
) (int Value
, ORDERED_COLLECTION
*Collection
);
367 const char *Description
;
370 static const KEYLESS_COMMAND KeylessCommands
[] = {
371 { "forward-empty", CmdForwardEmpty
,
372 "empty the collection iterating forward" },
373 { "fe", CmdForwardEmpty
,
374 "shorthand for forward-empty" },
375 { "backward-empty", CmdBackwardEmpty
,
376 "empty the collection iterating backward" },
377 { "be", CmdBackwardEmpty
,
378 "shorthand for backward-empty" },
379 { "forward-list", CmdForwardList
,
380 "list contents, iterating forward" },
381 { "fl", CmdForwardList
,
382 "shorthand for forward-list" },
383 { "backward-list", CmdBackwardList
,
384 "list contents, iterating backward" },
385 { "bl", CmdBackwardList
,
386 "shorthand for backward-list" },
390 static const KEYED_COMMAND KeyedCommands
[] = {
391 { "insert ", CmdInsert
, "insert value into collection" },
392 { "i ", CmdInsert
, "shorthand for insert" },
393 { "find ", CmdFind
, "find value in collection" },
394 { "f ", CmdFind
, "shorthand for find" },
395 { "delete ", CmdDelete
, "delete value from collection" },
396 { "d ", CmdDelete
, "shorthand for delete" },
402 List the supported commands on stderr.
409 const KEYLESS_COMMAND
*KeylessCmd
;
410 const KEYED_COMMAND
*KeyedCmd
;
412 fprintf (stderr
, "Supported commands:\n\n");
413 for (KeylessCmd
= KeylessCommands
; KeylessCmd
->Command
!= NULL
;
415 fprintf (stderr
, "%-14s: %s\n", KeylessCmd
->Command
,
416 KeylessCmd
->Description
);
418 for (KeyedCmd
= KeyedCommands
; KeyedCmd
->Command
!= NULL
; ++KeyedCmd
) {
419 fprintf (stderr
, "%-9s<int>: %s\n", KeyedCmd
->Command
,
420 KeyedCmd
->Description
);
426 Configure stdio FILEs that we'll use for input and output.
428 @param[in] ArgC The number of elements in ArgV, from main(). The environment
429 is required to ensure ArgC >= 1 (ie. that the program name,
430 ArgV[0], is available).
432 @param[in] ArgV Command line argument list, from main().
440 char *InputName
, *OutputName
;
450 switch (getopt (ArgC
, ArgV
, ":i:o:h")) {
461 "%1$s: simple OrderedCollectionLib tester\n"
463 "Usage: 1. %1$s [-i InputFile] [-o OutputFile]\n"
467 " -i InputFile : read commands from InputFile\n"
468 " (will read from stdin if absent)\n"
469 " -o OutputFile: write command responses to OutputFile\n"
470 " (will write to stdout if absent)\n"
471 " -h : print this help and exit\n"
477 // The current "compatibility" getopt() implementation doesn't support optopt,
478 // but it gracefully degrades these branches to the others (one of the optarg
479 // ones or the excess operands one).
483 fprintf (stderr
, "%s: option -%c requires an argument; pass -h for "
484 "help\n", ArgV
[0], optopt
);
488 fprintf (stderr
, "%s: unknown option -%c; pass -h for help\n", ArgV
[0],
494 if (optind
!= ArgC
) {
495 fprintf (stderr
, "%s: excess operands on command line; pass -h for "
507 if (InputName
== NULL
) {
510 Input
= fopen (InputName
, "r");
512 fprintf (stderr
, "%s: fopen(\"%s\", \"r\"): %s\n", ArgV
[0], InputName
,
518 if (OutputName
== NULL
) {
521 Output
= fopen (OutputName
, "w");
522 if (Output
== NULL
) {
523 fprintf (stderr
, "%s: fopen(\"%s\", \"w\"): %s\n", ArgV
[0], OutputName
,
530 // When reading commands from the standard input, assume interactive mode,
531 // and list the supported commands. However, delay this until both streams
534 if (InputName
== NULL
) {
547 ORDERED_COLLECTION
*Collection
;
550 SetupInputOutput (ArgC
, ArgV
);
552 Collection
= OrderedCollectionInit (UserStructCompare
, KeyCompare
);
553 if (Collection
== NULL
) {
554 fprintf (stderr
, "%s: OrderedCollectionInit(): out of memory\n",
559 RetVal
= EXIT_SUCCESS
;
560 while (fgets (Line
, sizeof Line
, Input
) != NULL
) {
562 const KEYLESS_COMMAND
*KeylessCmd
;
563 const KEYED_COMMAND
*KeyedCmd
;
565 Length
= strlen (Line
);
567 if (Line
[Length
- 1] != '\n') {
568 fprintf (stderr
, "%s: overlong line\n", __FUNCTION__
);
569 RetVal
= EXIT_FAILURE
;
576 Line
[Length
- 1] = '\0';
577 if (Length
>= 2 && Line
[Length
- 2] == '\r') {
578 Line
[Length
- 2] = '\0';
581 // Ignore empty lines and comments.
583 if (Line
[0] == '\0' || Line
[0] == '#') {
584 if (Input
!= stdin
) {
586 // ... but echo them back in non-interactive mode.
588 fprintf (Output
, "%s\n", Line
);
594 // Ironically, this is the kind of loop that should be replaced with an
595 // ORDERED_COLLECTION.
597 for (KeylessCmd
= KeylessCommands
; KeylessCmd
->Command
!= NULL
;
599 if (strcmp (KeylessCmd
->Command
, Line
) == 0) {
600 KeylessCmd
->Function (Collection
);
604 if (KeylessCmd
->Command
!= NULL
) {
608 for (KeyedCmd
= KeyedCommands
; KeyedCmd
->Command
!= NULL
; ++KeyedCmd
) {
611 CmdLength
= strlen (KeyedCmd
->Command
);
612 assert (CmdLength
>= 2);
613 if (strncmp (KeyedCmd
->Command
, Line
, CmdLength
) == 0) {
614 char *CommandArg
, *EndPtr
;
617 CommandArg
= Line
+ CmdLength
;
619 Value
= strtol (CommandArg
, &EndPtr
, 10);
620 if (EndPtr
== CommandArg
|| // no conversion performed
621 errno
!= 0 || // not in long's range, etc
622 *EndPtr
!= '\0' || // final string not empty
623 Value
< INT_MIN
|| Value
> INT_MAX
// parsed long not in int range
625 fprintf (stderr
, "%s: %.*s: \"%s\": not an int\n", __FUNCTION__
,
626 (int)(CmdLength
- 1), Line
, CommandArg
);
628 KeyedCmd
->Function (Value
, Collection
);
634 if (KeyedCmd
->Command
!= NULL
) {
638 fprintf (stderr
, "%s: \"%s\": unknown command\n", __FUNCTION__
, Line
);
641 if (RetVal
== EXIT_SUCCESS
&& ferror (Input
)) {
642 fprintf (stderr
, "%s: fgets(): %s\n", __FUNCTION__
, strerror (errno
));
643 RetVal
= EXIT_FAILURE
;
646 CmdForwardEmpty (Collection
);
647 OrderedCollectionUninit (Collection
);