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