]> git.proxmox.com Git - mirror_edk2.git/blame - AppPkg/Applications/OrderedCollectionTest/OrderedCollectionTest.c
AppPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / AppPkg / Applications / OrderedCollectionTest / OrderedCollectionTest.c
CommitLineData
424d8455
LE
1/** @file\r
2 A simple "fuzzer" application for OrderedCollectionLib, reading commands from\r
3 the standard input, and writing results to the standard output.\r
4\r
5 Make sure you configure your platform so that the console stderr device is\r
6 visible to the user (or else run the program from wherever stderr is visible\r
7 per default, eg. serial line).\r
8\r
9 Copyright (C) 2014, Red Hat, Inc.\r
10 Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>\r
11\r
bcb96695 12 SPDX-License-Identifier: BSD-2-Clause-Patent\r
424d8455
LE
13**/\r
14\r
15#include <assert.h> // assert()\r
16#include <errno.h> // errno\r
17#include <limits.h> // INT_MIN\r
18#include <stdio.h> // fgets()\r
19#include <stdlib.h> // EXIT_FAILURE\r
20#include <string.h> // strerror()\r
21#include <unistd.h> // getopt()\r
22\r
23#include <Library/OrderedCollectionLib.h>\r
24\r
25//\r
26// We allow the user to select between stdin+stdout and regular input+output\r
27// files via command line options. We don't rely on shell redirection for two\r
28// reasons:\r
29//\r
30// - The "old shell" doesn't support input redirection (<a, <);\r
31//\r
32// - The "new shell" supports input redirection (<a, <), but those redirections\r
33// break fgets(stdin). Both when redirecting stdin from an ASCII file (<a),\r
34// and when redirecting stdin from a UCS-2 file (<), the very first fgets()\r
35// spirals into an infinite loop, spewing ^@ on the serial console.\r
36//\r
37// Performing fopen() manually (made available to the user with the -i option),\r
38// and reading from that stream with fgets() work under both old and new shell.\r
39// Only ASCII encoded files are supported.\r
40//\r
41static FILE *Input, *Output;\r
42\r
43\r
44//\r
45// Define a (potentially aggregate) key type.\r
46//\r
47typedef struct {\r
48 int Value;\r
49} USER_KEY;\r
50\r
51//\r
52// The user structure includes the key as one of its fields. (There can be\r
53// several, optionally differently typed, keys, if we link user structures into\r
54// several collections, with different comparators.)\r
55//\r
56typedef struct {\r
57 unsigned char Supplementary1[4];\r
58 USER_KEY Key;\r
59 unsigned short Supplementary2[2];\r
60} USER_STRUCT;\r
61\r
62\r
63/**\r
64 Compare a standalone key against a user structure containing an embedded key.\r
65\r
66 @param[in] StandaloneKey Pointer to the bare key.\r
67\r
68 @param[in] UserStruct Pointer to the user structure with the embedded\r
69 key.\r
70\r
71 @retval <0 If StandaloneKey compares less than UserStruct's key.\r
72\r
73 @retval 0 If StandaloneKey compares equal to UserStruct's key.\r
74\r
75 @retval >0 If StandaloneKey compares greater than UserStruct's key.\r
76**/\r
77static\r
78INTN\r
79EFIAPI\r
80KeyCompare (\r
81 IN CONST VOID *StandaloneKey,\r
82 IN CONST VOID *UserStruct\r
83 )\r
84{\r
85 const USER_KEY *CmpKey;\r
86 const USER_STRUCT *CmpStruct;\r
87\r
88 CmpKey = StandaloneKey;\r
89 CmpStruct = UserStruct;\r
90\r
91 return CmpKey->Value < CmpStruct->Key.Value ? -1 :\r
92 CmpKey->Value > CmpStruct->Key.Value ? 1 :\r
93 0;\r
94}\r
95\r
96\r
97/**\r
98 Comparator function type for two user structures.\r
99\r
100 @param[in] UserStruct1 Pointer to the first user structure.\r
101\r
102 @param[in] UserStruct2 Pointer to the second user structure.\r
103\r
104 @retval <0 If UserStruct1 compares less than UserStruct2.\r
105\r
106 @retval 0 If UserStruct1 compares equal to UserStruct2.\r
107\r
108 @retval >0 If UserStruct1 compares greater than UserStruct2.\r
109**/\r
110static\r
111INTN\r
112EFIAPI\r
113UserStructCompare (\r
114 IN CONST VOID *UserStruct1,\r
115 IN CONST VOID *UserStruct2\r
116 )\r
117{\r
118 const USER_STRUCT *CmpStruct1;\r
119\r
120 CmpStruct1 = UserStruct1;\r
121 return KeyCompare (&CmpStruct1->Key, UserStruct2);\r
122}\r
123\r
124\r
125/**\r
126 Empty the collection by iterating forward through its entries.\r
127\r
128 This function demonstrates that iterators different from the one being\r
129 removed remain valid.\r
130\r
131 @param[in,out] Collection The collection to empty.\r
132**/\r
133static void\r
134CmdForwardEmpty (\r
135 IN OUT ORDERED_COLLECTION *Collection\r
136 )\r
137{\r
138 ORDERED_COLLECTION_ENTRY *Entry;\r
139\r
140 Entry = OrderedCollectionMin (Collection);\r
141 while (Entry != NULL) {\r
142 ORDERED_COLLECTION_ENTRY *Next;\r
143 void *Ptr;\r
144 USER_STRUCT *UserStruct;\r
145\r
146 Next = OrderedCollectionNext (Entry);\r
147 OrderedCollectionDelete (Collection, Entry, &Ptr);\r
148\r
149 UserStruct = Ptr;\r
150 fprintf (Output, "%s: %d: removed\n", __FUNCTION__, UserStruct->Key.Value);\r
151 free (UserStruct);\r
152\r
153 Entry = Next;\r
154 }\r
155}\r
156\r
157\r
158/**\r
159 Empty the collection by iterating backward through its entries.\r
160\r
161 This function demonstrates that iterators different from the one being\r
162 removed remain valid.\r
163\r
164 @param[in,out] Collection The collection to empty.\r
165**/\r
166static void\r
167CmdBackwardEmpty (\r
168 IN OUT ORDERED_COLLECTION *Collection\r
169 )\r
170{\r
171 ORDERED_COLLECTION_ENTRY *Entry;\r
172\r
173 Entry = OrderedCollectionMax (Collection);\r
174 while (Entry != NULL) {\r
175 ORDERED_COLLECTION_ENTRY *Prev;\r
176 void *Ptr;\r
177 USER_STRUCT *UserStruct;\r
178\r
179 Prev = OrderedCollectionPrev (Entry);\r
180 OrderedCollectionDelete (Collection, Entry, &Ptr);\r
181\r
182 UserStruct = Ptr;\r
183 fprintf (Output, "%s: %d: removed\n", __FUNCTION__, UserStruct->Key.Value);\r
184 free (UserStruct);\r
185\r
186 Entry = Prev;\r
187 }\r
188}\r
189\r
190\r
191/**\r
192 List the user structures linked into the collection, in increasing order.\r
193\r
194 @param[in] Collection The collection to list.\r
195**/\r
196static void\r
197CmdForwardList (\r
198 IN ORDERED_COLLECTION *Collection\r
199 )\r
200{\r
201 ORDERED_COLLECTION_ENTRY *Entry;\r
202\r
203 for (Entry = OrderedCollectionMin (Collection); Entry != NULL;\r
204 Entry = OrderedCollectionNext (Entry)) {\r
205 USER_STRUCT *UserStruct;\r
206\r
207 UserStruct = OrderedCollectionUserStruct (Entry);\r
208 fprintf (Output, "%s: %d\n", __FUNCTION__, UserStruct->Key.Value);\r
209 }\r
210}\r
211\r
212\r
213/**\r
214 List the user structures linked into the collection, in decreasing order.\r
215\r
216 @param[in] Collection The collection to list.\r
217**/\r
218static void\r
219CmdBackwardList (\r
220 IN ORDERED_COLLECTION *Collection\r
221 )\r
222{\r
223 ORDERED_COLLECTION_ENTRY *Entry;\r
224\r
225 for (Entry = OrderedCollectionMax (Collection); Entry != NULL;\r
226 Entry = OrderedCollectionPrev (Entry)) {\r
227 USER_STRUCT *UserStruct;\r
228\r
229 UserStruct = OrderedCollectionUserStruct (Entry);\r
230 fprintf (Output, "%s: %d\n", __FUNCTION__, UserStruct->Key.Value);\r
231 }\r
232}\r
233\r
234\r
235/**\r
236 Create a new user structure and attempt to insert it into the collection.\r
237\r
238 @param[in] Value The key value of the user structure to create.\r
239\r
240 @param[in,out] Collection The collection to insert the new user structure\r
241 into.\r
242**/\r
243static void\r
244CmdInsert (\r
245 IN int Value,\r
246 IN OUT ORDERED_COLLECTION *Collection\r
247 )\r
248{\r
249 USER_STRUCT *UserStruct, *UserStruct2;\r
250 RETURN_STATUS Status;\r
251 ORDERED_COLLECTION_ENTRY *Entry;\r
252\r
253 UserStruct = calloc (1, sizeof *UserStruct);\r
254 if (UserStruct == NULL) {\r
255 fprintf (Output, "%s: %d: calloc(): out of memory\n", __FUNCTION__, Value);\r
256 return;\r
257 }\r
258\r
259 UserStruct->Key.Value = Value;\r
260 Status = OrderedCollectionInsert (Collection, &Entry, UserStruct);\r
261\r
262 switch (Status) {\r
263 case RETURN_OUT_OF_RESOURCES:\r
264 fprintf (Output, "%s: %d: OrderedCollectionInsert(): out of memory\n",\r
265 __FUNCTION__, Value);\r
266 goto ReleaseUserStruct;\r
267\r
268 case RETURN_ALREADY_STARTED:\r
269 UserStruct2 = OrderedCollectionUserStruct (Entry);\r
270 assert (UserStruct != UserStruct2);\r
271 assert (UserStruct2->Key.Value == Value);\r
272 fprintf (Output, "%s: %d: already exists\n", __FUNCTION__,\r
273 UserStruct2->Key.Value);\r
274 goto ReleaseUserStruct;\r
275\r
276 default:\r
277 assert (Status == RETURN_SUCCESS);\r
278 break;\r
279 }\r
280\r
281 assert (OrderedCollectionUserStruct (Entry) == UserStruct);\r
282 fprintf (Output, "%s: %d: inserted\n", __FUNCTION__, Value);\r
283 return;\r
284\r
285ReleaseUserStruct:\r
286 free (UserStruct);\r
287}\r
288\r
289\r
290/**\r
291 Look up a user structure by key in the collection and print it.\r
292\r
293 @param[in] Value The key of the user structure to find.\r
294\r
295 @param[in] Collection The collection to search for the user structure with\r
296 the key.\r
297**/\r
298static void\r
299CmdFind (\r
300 IN int Value,\r
301 IN ORDERED_COLLECTION *Collection\r
302 )\r
303{\r
304 USER_KEY StandaloneKey;\r
305 ORDERED_COLLECTION_ENTRY *Entry;\r
306 USER_STRUCT *UserStruct;\r
307\r
308 StandaloneKey.Value = Value;\r
309 Entry = OrderedCollectionFind (Collection, &StandaloneKey);\r
310\r
311 if (Entry == NULL) {\r
312 fprintf (Output, "%s: %d: not found\n", __FUNCTION__, Value);\r
313 return;\r
314 }\r
315\r
316 UserStruct = OrderedCollectionUserStruct (Entry);\r
317 assert (UserStruct->Key.Value == StandaloneKey.Value);\r
318 fprintf (Output, "%s: %d: found\n", __FUNCTION__, UserStruct->Key.Value);\r
319}\r
320\r
321\r
322/**\r
323 Look up a user structure by key in the collection and delete it.\r
324\r
325 @param[in] Value The key of the user structure to find.\r
326\r
327 @param[in] Collection The collection to search for the user structure with\r
328 the key.\r
329**/\r
330static void\r
331CmdDelete (\r
332 IN int Value,\r
333 IN ORDERED_COLLECTION *Collection\r
334 )\r
335{\r
336 USER_KEY StandaloneKey;\r
337 ORDERED_COLLECTION_ENTRY *Entry;\r
338 void *Ptr;\r
339 USER_STRUCT *UserStruct;\r
340\r
341 StandaloneKey.Value = Value;\r
342 Entry = OrderedCollectionFind (Collection, &StandaloneKey);\r
343\r
344 if (Entry == NULL) {\r
345 fprintf (Output, "%s: %d: not found\n", __FUNCTION__, Value);\r
346 return;\r
347 }\r
348\r
349 OrderedCollectionDelete (Collection, Entry, &Ptr);\r
350\r
351 UserStruct = Ptr;\r
352 assert (UserStruct->Key.Value == StandaloneKey.Value);\r
353 fprintf (Output, "%s: %d: removed\n", __FUNCTION__, UserStruct->Key.Value);\r
354 free (UserStruct);\r
355}\r
356\r
357\r
358typedef struct {\r
359 const char *Command;\r
360 void (*Function) (ORDERED_COLLECTION *Collection);\r
361 const char *Description;\r
362} KEYLESS_COMMAND;\r
363\r
364typedef struct {\r
365 const char *Command;\r
366 void (*Function) (int Value, ORDERED_COLLECTION *Collection);\r
367 const char *Description;\r
368} KEYED_COMMAND;\r
369\r
370static const KEYLESS_COMMAND KeylessCommands[] = {\r
371 { "forward-empty", CmdForwardEmpty,\r
372 "empty the collection iterating forward" },\r
373 { "fe", CmdForwardEmpty,\r
374 "shorthand for forward-empty" },\r
375 { "backward-empty", CmdBackwardEmpty,\r
376 "empty the collection iterating backward" },\r
377 { "be", CmdBackwardEmpty,\r
378 "shorthand for backward-empty" },\r
379 { "forward-list", CmdForwardList,\r
380 "list contents, iterating forward" },\r
381 { "fl", CmdForwardList,\r
382 "shorthand for forward-list" },\r
383 { "backward-list", CmdBackwardList,\r
384 "list contents, iterating backward" },\r
385 { "bl", CmdBackwardList,\r
386 "shorthand for backward-list" },\r
387 { NULL }\r
388};\r
389\r
390static const KEYED_COMMAND KeyedCommands[] = {\r
391 { "insert ", CmdInsert, "insert value into collection" },\r
392 { "i ", CmdInsert, "shorthand for insert" },\r
393 { "find ", CmdFind, "find value in collection" },\r
394 { "f ", CmdFind, "shorthand for find" },\r
395 { "delete ", CmdDelete, "delete value from collection" },\r
396 { "d ", CmdDelete, "shorthand for delete" },\r
397 { NULL }\r
398};\r
399\r
400\r
401/**\r
402 List the supported commands on stderr.\r
403**/\r
404static void\r
405ListCommands (\r
406 void\r
407 )\r
408{\r
409 const KEYLESS_COMMAND *KeylessCmd;\r
410 const KEYED_COMMAND *KeyedCmd;\r
411\r
412 fprintf (stderr, "Supported commands:\n\n");\r
413 for (KeylessCmd = KeylessCommands; KeylessCmd->Command != NULL;\r
414 ++KeylessCmd) {\r
415 fprintf (stderr, "%-14s: %s\n", KeylessCmd->Command,\r
416 KeylessCmd->Description);\r
417 }\r
418 for (KeyedCmd = KeyedCommands; KeyedCmd->Command != NULL; ++KeyedCmd) {\r
419 fprintf (stderr, "%-9s<int>: %s\n", KeyedCmd->Command,\r
420 KeyedCmd->Description);\r
421 }\r
422}\r
423\r
424\r
425/**\r
426 Configure stdio FILEs that we'll use for input and output.\r
427\r
428 @param[in] ArgC The number of elements in ArgV, from main(). The environment\r
429 is required to ensure ArgC >= 1 (ie. that the program name,\r
430 ArgV[0], is available).\r
431\r
432 @param[in] ArgV Command line argument list, from main().\r
433**/\r
434static void\r
435SetupInputOutput (\r
436 IN int ArgC,\r
437 IN char **ArgV\r
438 )\r
439{\r
440 char *InputName, *OutputName;\r
441 int Loop;\r
442\r
443 assert (ArgC >= 1);\r
444\r
445 InputName = NULL;\r
446 OutputName = NULL;\r
447 Loop = 1;\r
448\r
449 while (Loop) {\r
450 switch (getopt (ArgC, ArgV, ":i:o:h")) {\r
451 case 'i':\r
452 InputName = optarg;\r
453 break;\r
454\r
455 case 'o':\r
456 OutputName = optarg;\r
457 break;\r
458\r
459 case 'h':\r
460 fprintf (stderr,\r
461 "%1$s: simple OrderedCollectionLib tester\n"\r
462 "\n"\r
463 "Usage: 1. %1$s [-i InputFile] [-o OutputFile]\n"\r
464 " 2. %1$s -h\n"\r
465 "\n"\r
466 "Options:\n"\r
467 " -i InputFile : read commands from InputFile\n"\r
468 " (will read from stdin if absent)\n"\r
469 " -o OutputFile: write command responses to OutputFile\n"\r
470 " (will write to stdout if absent)\n"\r
471 " -h : print this help and exit\n"\r
472 "\n", ArgV[0]);\r
473 ListCommands ();\r
474 exit (EXIT_SUCCESS);\r
475\r
476//\r
477// The current "compatibility" getopt() implementation doesn't support optopt,\r
478// but it gracefully degrades these branches to the others (one of the optarg\r
479// ones or the excess operands one).\r
480//\r
481#if 0\r
482 case ':':\r
483 fprintf (stderr, "%s: option -%c requires an argument; pass -h for "\r
484 "help\n", ArgV[0], optopt);\r
485 exit (EXIT_FAILURE);\r
486\r
487 case '?':\r
488 fprintf (stderr, "%s: unknown option -%c; pass -h for help\n", ArgV[0],\r
489 optopt);\r
490 exit (EXIT_FAILURE);\r
491#endif\r
492\r
493 case -1:\r
494 if (optind != ArgC) {\r
495 fprintf (stderr, "%s: excess operands on command line; pass -h for "\r
496 "help\n", ArgV[0]);\r
497 exit (EXIT_FAILURE);\r
498 }\r
499 Loop = 0;\r
500 break;\r
501\r
502 default:\r
503 assert (0);\r
504 }\r
505 }\r
506\r
507 if (InputName == NULL) {\r
508 Input = stdin;\r
509 } else {\r
510 Input = fopen (InputName, "r");\r
511 if (Input == NULL) {\r
512 fprintf (stderr, "%s: fopen(\"%s\", \"r\"): %s\n", ArgV[0], InputName,\r
513 strerror (errno));\r
514 exit (EXIT_FAILURE);\r
515 }\r
516 }\r
517\r
518 if (OutputName == NULL) {\r
519 Output = stdout;\r
520 } else {\r
521 Output = fopen (OutputName, "w");\r
522 if (Output == NULL) {\r
523 fprintf (stderr, "%s: fopen(\"%s\", \"w\"): %s\n", ArgV[0], OutputName,\r
524 strerror (errno));\r
525 exit (EXIT_FAILURE);\r
526 }\r
527 }\r
528\r
529 //\r
530 // When reading commands from the standard input, assume interactive mode,\r
531 // and list the supported commands. However, delay this until both streams\r
532 // are set up.\r
533 //\r
534 if (InputName == NULL) {\r
535 ListCommands ();\r
536 }\r
537}\r
538\r
539\r
540int\r
541main (\r
542 IN int ArgC,\r
543 IN char **ArgV\r
544 )\r
545{\r
546 int RetVal;\r
547 ORDERED_COLLECTION *Collection;\r
548 char Line[256];\r
549\r
550 SetupInputOutput (ArgC, ArgV);\r
551\r
552 Collection = OrderedCollectionInit (UserStructCompare, KeyCompare);\r
553 if (Collection == NULL) {\r
554 fprintf (stderr, "%s: OrderedCollectionInit(): out of memory\n",\r
555 __FUNCTION__);\r
556 return EXIT_FAILURE;\r
557 }\r
558\r
559 RetVal = EXIT_SUCCESS;\r
560 while (fgets (Line, sizeof Line, Input) != NULL) {\r
561 size_t Length;\r
562 const KEYLESS_COMMAND *KeylessCmd;\r
563 const KEYED_COMMAND *KeyedCmd;\r
564\r
565 Length = strlen (Line);\r
566 assert (Length > 0);\r
567 if (Line[Length - 1] != '\n') {\r
568 fprintf (stderr, "%s: overlong line\n", __FUNCTION__);\r
569 RetVal = EXIT_FAILURE;\r
570 break;\r
571 }\r
572\r
573 //\r
574 // Strip [\r]\n.\r
575 //\r
576 Line[Length - 1] = '\0';\r
577 if (Length >= 2 && Line[Length - 2] == '\r') {\r
578 Line[Length - 2] = '\0';\r
579 }\r
580 //\r
581 // Ignore empty lines and comments.\r
582 //\r
583 if (Line[0] == '\0' || Line[0] == '#') {\r
584 if (Input != stdin) {\r
585 //\r
586 // ... but echo them back in non-interactive mode.\r
587 //\r
588 fprintf (Output, "%s\n", Line);\r
589 }\r
590 continue;\r
591 }\r
592\r
593 //\r
594 // Ironically, this is the kind of loop that should be replaced with an\r
595 // ORDERED_COLLECTION.\r
596 //\r
597 for (KeylessCmd = KeylessCommands; KeylessCmd->Command != NULL;\r
598 ++KeylessCmd) {\r
599 if (strcmp (KeylessCmd->Command, Line) == 0) {\r
600 KeylessCmd->Function (Collection);\r
601 break;\r
602 }\r
603 }\r
604 if (KeylessCmd->Command != NULL) {\r
605 continue;\r
606 }\r
607\r
608 for (KeyedCmd = KeyedCommands; KeyedCmd->Command != NULL; ++KeyedCmd) {\r
609 size_t CmdLength;\r
610\r
611 CmdLength = strlen (KeyedCmd->Command);\r
612 assert (CmdLength >= 2);\r
613 if (strncmp (KeyedCmd->Command, Line, CmdLength) == 0) {\r
614 char *CommandArg, *EndPtr;\r
615 long Value;\r
616\r
617 CommandArg = Line + CmdLength;\r
618 errno = 0;\r
619 Value = strtol (CommandArg, &EndPtr, 10);\r
620 if (EndPtr == CommandArg || // no conversion performed\r
621 errno != 0 || // not in long's range, etc\r
622 *EndPtr != '\0' || // final string not empty\r
623 Value < INT_MIN || Value > INT_MAX // parsed long not in int range\r
624 ) {\r
625 fprintf (stderr, "%s: %.*s: \"%s\": not an int\n", __FUNCTION__,\r
626 (int)(CmdLength - 1), Line, CommandArg);\r
627 } else {\r
628 KeyedCmd->Function (Value, Collection);\r
629 }\r
630\r
631 break;\r
632 }\r
633 }\r
634 if (KeyedCmd->Command != NULL) {\r
635 continue;\r
636 }\r
637\r
638 fprintf (stderr, "%s: \"%s\": unknown command\n", __FUNCTION__, Line);\r
639 }\r
640\r
641 if (RetVal == EXIT_SUCCESS && ferror (Input)) {\r
642 fprintf (stderr, "%s: fgets(): %s\n", __FUNCTION__, strerror (errno));\r
643 RetVal = EXIT_FAILURE;\r
644 }\r
645\r
646 CmdForwardEmpty (Collection);\r
647 OrderedCollectionUninit (Collection);\r
648 return RetVal;\r
649}\r