123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- Module Name:
- test.c
- Abstract:
- This module implements the test (aka left square bracket [) program.
- Author:
- Evan Green 15-Jun-2013
- Environment:
- POSIX
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/lib/types.h>
- #include <assert.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/stat.h>
- #include "swlib.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define TEST_VERSION_MAJOR 1
- #define TEST_VERSION_MINOR 0
- #define TEST_USAGE \
- "usage: test [<test>]\n" \
- " [ <test> ]\n" \
- "The test utility performs basic file, integer, and string tests to \n" \
- "augment the shell's functionality. Options are: \n" \
- " --help -- Show this help and exit.\n" \
- " --version -- Show the application version and exit.\n\n" \
- "Valid tests are:\n" \
- " -b file -- The file exists and is a block device.\n" \
- " -c file -- The file exists and is a character device.\n" \
- " -d file -- The file exists and is a directory.\n" \
- " -f file -- The file exists and is a regular file.\n" \
- " -g file -- The file exists and has its set-group-ID flag set.\n" \
- " -h file -- The file exists and is a symbolic link.\n" \
- " -L file -- The file exists and is a symbolic link (same as -h).\n" \
- " -p file -- The file exists and is a FIFO.\n" \
- " -r file -- The file exists and is readable.\n" \
- " -S file -- The file exists and is a socket.\n" \
- " -s file -- The file exists and has a size greater than zero.\n" \
- " -t file_descriptor -- The file descriptor is valid and points to a \n" \
- " terminal device.\n" \
- " -u file -- The file exists and has its set-user-ID flag set.\n" \
- " -w file -- The file exists and is writable.\n" \
- " -x file -- The file exists and is executable.\n" \
- " file1 -fe file2 -- True if file1 and file2 have the same device and \n" \
- " file serial numbers.\n" \
- " file1 -nt file2 -- True if file1 has a later modification date than \n" \
- " file2.\n" \
- " file1 -ot file2 -- True if file1 has an earlier modification date \n" \
- " than file2.\n" \
- " -n string -- True if the length of the given string is non-zero.\n" \
- " -z string -- True if the length of the string is zero.\n" \
- " string -- True if the string is not the null string.\n" \
- " string1 = string2 -- True if the two strings are identical.\n" \
- " string1 != string2 -- True if the two strings are not identical.\n" \
- " number1 -eq number2 -- True if the two numbers are equal.\n" \
- " number1 -ne number2 -- True if the two numbers are not equal.\n" \
- " number1 -gt number2 -- True if number1 is greater than number2.\n" \
- " number1 -ge number2 -- True if number1 is greater than or equal to \n" \
- " number2.\n" \
- " number1 -lt number2 -- True if number1 is less than number2.\n" \
- " number1 -le number2 -- True if number1 is less than or equal to " \
- "number2.\n\n" \
- "Additionally, tests can be combined in the following ways:\n" \
- " expression1 -a expression2 -- True if both expression1 and \n" \
- " expression2 are true. This has a higher precedence than -o.\n" \
- " expression1 -o expression2 -- True if either expression1 or \n" \
- " expression2 are true.\n" \
- " ! expression - True if the expression is false.\n" \
- " ( expression ) - True if the inner expression is True. Parentheses \n" \
- " can be used to alter the normal associativity and precedence.\n\n" \
- //
- // Define away the test utility return values since they're backwards and
- // therefore a little tough to look at.
- //
- #define TEST_UTILITY_FALSE 1
- #define TEST_UTILITY_TRUE 0
- #define TEST_UTILITY_ERROR 2
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- typedef enum _TEST_UTILITY_TEST {
- TestUtilityInvalid,
- TestUtilityBang,
- TestUtilityOpenParentheses,
- TestUtilityCloseParentheses,
- TestUtilityAnd,
- TestUtilityOr,
- TestFileMinValue,
- TestFileIsBlockDevice,
- TestFileIsCharacterDevice,
- TestFileIsDirectory,
- TestFileExists,
- TestFileIsRegularFile,
- TestFileHasSetGroupId,
- TestFileIsSymbolicLink,
- TestFileIsFifo,
- TestFileCanRead,
- TestFileIsSocket,
- TestFileIsNonEmpty,
- TestFileDescriptorIsTerminal,
- TestFileHasSetUserId,
- TestFileCanWrite,
- TestFileCanExecute,
- TestFileEqual,
- TestFileNewer,
- TestFileOlder,
- TestFileMaxValue,
- TestStringMinValue,
- TestStringNonZeroLength,
- TestStringZeroLength,
- TestStringEquals,
- TestStringNotEquals,
- TestStringMaxValue,
- TestIntegerMinValue,
- TestIntegerEquals,
- TestIntegerNotEquals,
- TestIntegerGreaterThan,
- TestIntegerGreaterThanOrEqualTo,
- TestIntegerLessThan,
- TestIntegerLessThanOrEqualTo,
- TestIntegerMaxValue,
- } TEST_UTILITY_TEST, *PTEST_UTILITY_TEST;
- typedef enum _TEST_PARSE_ELEMENT_TYPE {
- TestParseElementInvalid,
- TestParseElementOperator,
- TestParseElementToken,
- TestParseElementResult,
- TestParseElementEnd,
- } TEST_PARSE_ELEMENT_TYPE, *PTEST_PARSE_ELEMENT_TYPE;
- typedef struct _TEST_PARSE_ELEMENT {
- TEST_PARSE_ELEMENT_TYPE Type;
- PSTR Token;
- TEST_UTILITY_TEST Operator;
- INT Result;
- } TEST_PARSE_ELEMENT, *PTEST_PARSE_ELEMENT;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- INT
- TestEvaluateExpression (
- INT ArgumentCount,
- CHAR **Arguments
- );
- BOOL
- TestShiftOrReduce (
- PTEST_PARSE_ELEMENT Stack,
- ULONG StackSize,
- PTEST_PARSE_ELEMENT NextElement,
- PBOOL Shift
- );
- BOOL
- TestReduce (
- PTEST_PARSE_ELEMENT Stack,
- PULONG StackSize
- );
- TEST_UTILITY_TEST
- TestGetOperator (
- PSTR String
- );
- ULONG
- TestGetOperandCount (
- TEST_UTILITY_TEST Operator
- );
- ULONG
- TestGetOperatorPrecedence (
- TEST_UTILITY_TEST Operator
- );
- INT
- TestEvaluateUnaryOperator (
- TEST_UTILITY_TEST Operator,
- PSTR Operand
- );
- INT
- TestEvaluateBinaryOperator (
- TEST_UTILITY_TEST Operator,
- PSTR LeftOperand,
- PSTR RightOperand
- );
- INT
- TestEvaluateStringTest (
- TEST_UTILITY_TEST Operator,
- PSTR String1,
- PSTR String2
- );
- INT
- TestEvaluateIntegerTest (
- TEST_UTILITY_TEST Operator,
- PSTR LeftIntegerString,
- PSTR RightIntegerString
- );
- INT
- TestEvaluateAndOr (
- TEST_UTILITY_TEST Operator,
- INT Left,
- INT Right
- );
- VOID
- TestConvertTokenToResult (
- PTEST_PARSE_ELEMENT Element
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- PSTR TestOperatorStrings[] = {
- NULL,
- "!",
- "(",
- ")",
- "-a",
- "-o",
- NULL,
- "-b",
- "-c",
- "-d",
- "-e",
- "-f",
- "-g",
- "-h",
- "-p",
- "-r",
- "-S",
- "-s",
- "-t",
- "-u",
- "-w",
- "-x",
- "-fe",
- "-nt",
- "-ot",
- NULL,
- NULL,
- "-n",
- "-z",
- "=",
- "!=",
- NULL,
- NULL,
- "-eq",
- "-ne",
- "-gt",
- "-ge",
- "-lt",
- "-le",
- NULL,
- };
- BOOL TestDebugPrintEvaluations = FALSE;
- //
- // ------------------------------------------------------------------ Functions
- //
- INT
- TestMain (
- INT ArgumentCount,
- CHAR **Arguments
- )
- /*++
- Routine Description:
- This routine implements the test application entry point.
- Arguments:
- ArgumentCount - Supplies the number of arguments on the command line.
- Arguments - Supplies an array of pointers to strings representing the
- arguments.
- Return Value:
- 0 or 1 if the evaluation succeeds.
- >1 on failure.
- --*/
- {
- PSTR LastSlash;
- INT Result;
- //
- // Look for --help or --version as the only argument.
- //
- if (ArgumentCount == 2) {
- if (strcmp(Arguments[1], "--help") == 0) {
- printf(TEST_USAGE);
- return TEST_UTILITY_ERROR;
- } else if (strcmp(Arguments[1], "--version") == 0) {
- SwPrintVersion(TEST_VERSION_MAJOR, TEST_VERSION_MINOR);
- return TEST_UTILITY_ERROR;
- }
- }
- //
- // Look for an open bracket.
- //
- if (ArgumentCount == 0) {
- return TEST_UTILITY_ERROR;
- }
- LastSlash = strrchr(Arguments[0], '/');
- if (LastSlash == NULL) {
- LastSlash = Arguments[0];
- } else {
- LastSlash += 1;
- }
- if (strchr(LastSlash, '[') != NULL) {
- //
- // There had better be a closing bracket.
- //
- if (strcmp(Arguments[ArgumentCount - 1], "]") != 0) {
- SwPrintError(0, Arguments[ArgumentCount - 1], "Expected ']'");
- return TEST_UTILITY_ERROR;
- }
- ArgumentCount -= 1;
- }
- if (ArgumentCount == 0) {
- return TEST_UTILITY_FALSE;
- }
- Result = TestEvaluateExpression(ArgumentCount - 1, Arguments + 1);
- return Result;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- INT
- TestEvaluateExpression (
- INT ArgumentCount,
- CHAR **Arguments
- )
- /*++
- Routine Description:
- This routine evaluates an expression for the test application.
- Arguments:
- ArgumentCount - Supplies the number of remaining arguments on the command
- line.
- Arguments - Supplies an array of pointers to strings representing the
- arguments.
- ArgumentsConsumed - Supplies a pointer where the number of arguments
- consumed will be returned.
- Return Value:
- 0 or 1 if the evaluation succeeds.
- >1 on failure.
- --*/
- {
- ULONG ArgumentIndex;
- TEST_PARSE_ELEMENT NextElement;
- TEST_UTILITY_TEST Operator;
- PTEST_PARSE_ELEMENT ParseStack;
- BOOL Result;
- INT ReturnValue;
- BOOL Shift;
- ULONG StackIndex;
- ParseStack = NULL;
- //
- // Zero arguments represents an implicit test for a non-null string
- // failing.
- //
- if (ArgumentCount == 0) {
- return TEST_UTILITY_FALSE;
- //
- // For a single argument test to see if it's non-null.
- //
- } else if (ArgumentCount == 1) {
- if (strlen(Arguments[0]) != 0) {
- return TEST_UTILITY_TRUE;
- } else {
- return TEST_UTILITY_FALSE;
- }
- //
- // For two arguments, process a unary operator or a bang plus an implicit
- // non-null test.
- //
- } else if (ArgumentCount == 2) {
- Operator = TestGetOperator(Arguments[0]);
- if (Operator == TestUtilityBang) {
- if (strlen(Arguments[1]) != 0) {
- return TEST_UTILITY_FALSE;
- } else {
- return TEST_UTILITY_TRUE;
- }
- }
- if ((Operator == TestUtilityInvalid) ||
- (TestGetOperandCount(Operator) != 1)) {
- SwPrintError(0, NULL, "%s: Unary operator expected", Arguments[0]);
- return TEST_UTILITY_ERROR;
- }
- return TestEvaluateUnaryOperator(Operator, Arguments[1]);
- //
- // For three arguments:
- // * If $2 is a binary primary, perform the binary test of $1 and $3.
- // * If $1 is '!', negate the two-argument test of $2 and $3.
- // * If $1 is '(' and $3 is ')', perfomr the unary test of $2.
- //
- } else if (ArgumentCount == 3) {
- Operator = TestGetOperator(Arguments[1]);
- if ((Operator != TestUtilityInvalid) &&
- (TestGetOperandCount(Operator) == 2)) {
- ReturnValue = TestEvaluateBinaryOperator(Operator,
- Arguments[0],
- Arguments[2]);
- return ReturnValue;
- } else {
- Operator = TestGetOperator(Arguments[0]);
- if (Operator == TestUtilityBang) {
- ReturnValue = TestEvaluateExpression(ArgumentCount - 1,
- Arguments + 1);
- if (ReturnValue == TEST_UTILITY_FALSE) {
- return TEST_UTILITY_TRUE;
- } else if (ReturnValue == TEST_UTILITY_TRUE) {
- return TEST_UTILITY_FALSE;
- } else {
- return ReturnValue;
- }
- } else if (Operator == TestUtilityOpenParentheses) {
- Operator = TestGetOperator(Arguments[2]);
- if (Operator != TestUtilityCloseParentheses) {
- SwPrintError(0,
- NULL,
- "%s: Close parentheses expected",
- Arguments[2]);
- return TEST_UTILITY_ERROR;
- }
- ReturnValue = TestEvaluateExpression(ArgumentCount - 2,
- Arguments + 1);
- return ReturnValue;
- }
- }
- //
- // For four arguments:
- // * If $1 is '!', negate the three-argument test of $2, $3, and $4.
- // * If $1 is '(' and $4 is ')', perform the two argument test of $2 and $3.
- //
- } else if (ArgumentCount == 4) {
- Operator = TestGetOperator(Arguments[0]);
- if (Operator == TestUtilityBang) {
- ReturnValue = TestEvaluateExpression(ArgumentCount - 1,
- Arguments + 1);
- if (ReturnValue == TEST_UTILITY_FALSE) {
- return TEST_UTILITY_TRUE;
- } else if (ReturnValue == TEST_UTILITY_TRUE) {
- return TEST_UTILITY_FALSE;
- } else {
- return ReturnValue;
- }
- } else if (Operator == TestUtilityOpenParentheses) {
- Operator = TestGetOperator(Arguments[3]);
- if (Operator == TestUtilityCloseParentheses) {
- ReturnValue = TestEvaluateExpression(ArgumentCount - 2,
- Arguments + 1);
- return ReturnValue;
- }
- }
- }
- //
- // Allocate the space for a basic parse stack.
- //
- ParseStack = malloc(sizeof(TEST_PARSE_ELEMENT) * ArgumentCount);
- if (ParseStack == NULL) {
- SwPrintError(errno, NULL, "Failed to allocate");
- return TEST_UTILITY_ERROR;
- }
- memset(ParseStack, 0, sizeof(TEST_PARSE_ELEMENT) * ArgumentCount);
- ArgumentIndex = 0;
- StackIndex = 0;
- while (TRUE) {
- if (ArgumentIndex == ArgumentCount) {
- NextElement.Type = TestParseElementEnd;
- } else {
- //
- // Get the next parse stack element.
- //
- Operator = TestGetOperator(Arguments[ArgumentIndex]);
- if (Operator != TestUtilityInvalid) {
- NextElement.Type = TestParseElementOperator;
- NextElement.Operator = Operator;
- } else {
- NextElement.Type = TestParseElementToken;
- }
- NextElement.Token = Arguments[ArgumentIndex];
- //
- // Ensure that if this is a binary operator, there is a token
- // before it.
- //
- if ((Operator != TestUtilityInvalid) &&
- (TestGetOperandCount(Operator) == 2)) {
- if ((StackIndex == 0) ||
- (ParseStack[StackIndex - 1].Type !=
- TestParseElementToken)) {
- SwPrintError(0,
- Arguments[ArgumentIndex],
- "Binary operator used without left argument");
- ReturnValue = TEST_UTILITY_ERROR;
- goto EvaluateExpressionEnd;
- }
- }
- }
- while (TRUE) {
- Result = TestShiftOrReduce(ParseStack,
- StackIndex,
- &NextElement,
- &Shift);
- if (Result == FALSE) {
- ReturnValue = TEST_UTILITY_ERROR;
- goto EvaluateExpressionEnd;
- }
- if (Shift != FALSE) {
- break;
- }
- Result = TestReduce(ParseStack, &StackIndex);
- if (Result == FALSE) {
- ReturnValue = TEST_UTILITY_ERROR;
- goto EvaluateExpressionEnd;
- }
- if ((NextElement.Type == TestParseElementEnd) &&
- (StackIndex == 1) &&
- (ParseStack[0].Type == TestParseElementResult)) {
- ReturnValue = ParseStack[0].Result;
- goto EvaluateExpressionEnd;
- }
- }
- //
- // Shift this next element onto the stack.
- //
- memcpy(&(ParseStack[StackIndex]),
- &NextElement,
- sizeof(TEST_PARSE_ELEMENT));
- StackIndex += 1;
- ArgumentIndex += 1;
- }
- EvaluateExpressionEnd:
- if (ParseStack != NULL) {
- free(ParseStack);
- }
- return ReturnValue;
- }
- BOOL
- TestShiftOrReduce (
- PTEST_PARSE_ELEMENT Stack,
- ULONG StackSize,
- PTEST_PARSE_ELEMENT NextElement,
- PBOOL Shift
- )
- /*++
- Routine Description:
- This routine determines whether the test parser should shift another
- element onto the stack or reduce what it's got.
- Arguments:
- Stack - Supplies a pointer to the parse stack.
- StackSize - Supplies the number of elements on the stack.
- NextElement - Supplies a pointer to the next element that would be
- shifted onto the stack.
- Shift - Supplies a pointer where a boolean will be returned indicating
- whether or not to shift (TRUE) or reduce (FALSE).
- Return Value:
- TRUE on success.
- FALSE if there was a parsing error.
- --*/
- {
- TEST_UTILITY_TEST NextOperator;
- TEST_UTILITY_TEST Operator;
- if (StackSize == 0) {
- *Shift = TRUE;
- return TRUE;
- }
- if (NextElement->Type == TestParseElementEnd) {
- *Shift = FALSE;
- return TRUE;
- }
- switch (Stack[StackSize - 1].Type) {
- case TestParseElementOperator:
- Operator = Stack[StackSize - 1].Operator;
- if (Operator == TestUtilityOpenParentheses) {
- *Shift = TRUE;
- return TRUE;
- }
- if (Operator == TestUtilityCloseParentheses) {
- *Shift = FALSE;
- return TRUE;
- }
- //
- // If it's an and, or, or bang, just shift.
- //
- if ((Operator == TestUtilityAnd) || (Operator == TestUtilityOr) ||
- (Operator == TestUtilityBang)) {
- *Shift = TRUE;
- return TRUE;
- }
- //
- // If it's an operator, then all other operators are turned off
- // except for bang.
- //
- if ((NextElement->Type == TestParseElementOperator) &&
- (NextElement->Operator == TestUtilityBang)) {
- *Shift = TRUE;
- return TRUE;
- }
- NextElement->Type = TestParseElementToken;
- *Shift = TRUE;
- return TRUE;
- case TestParseElementToken:
- //
- // If the next thing isn't an operator this is an error.
- //
- if (NextElement->Type != TestParseElementOperator) {
- SwPrintError(0,
- NULL,
- "Expected an operator, got %s",
- NextElement->Token);
- return FALSE;
- }
- //
- // If there's only one element on the stack just shift. That next
- // argument had better be a binary argument.
- //
- NextOperator = NextElement->Operator;
- if (StackSize == 1) {
- if (TestGetOperandCount(NextOperator) != 2) {
- SwPrintError(0, NULL, "Expected a binary operator");
- }
- *Shift = TRUE;
- return TRUE;
- }
- //
- // There's more than one element on the stack, so get the operator
- // back there and compare with this upcoming operator.
- //
- assert(Stack[StackSize - 2].Type == TestParseElementOperator);
- Operator = Stack[StackSize - 2].Operator;
- if (Operator == TestUtilityOpenParentheses) {
- *Shift = TRUE;
- } else if (TestGetOperatorPrecedence(Operator) >=
- TestGetOperatorPrecedence(NextOperator)) {
- *Shift = FALSE;
- } else {
- *Shift = TRUE;
- }
- return TRUE;
- case TestParseElementResult:
- //
- // If the next thing isn't an operator this is an error.
- //
- if (NextElement->Type != TestParseElementOperator) {
- SwPrintError(0,
- NULL,
- "Expected an operator, got %s",
- NextElement->Token);
- return FALSE;
- }
- //
- // The upcoming operator had better be an and or an or.
- //
- NextOperator = NextElement->Operator;
- if ((NextOperator != TestUtilityAnd) &&
- (NextOperator != TestUtilityOr) &&
- (NextOperator != TestUtilityCloseParentheses)) {
- SwPrintError(0, NULL, "Expected end of expression");
- return FALSE;
- }
- if (StackSize == 1) {
- *Shift = TRUE;
- return TRUE;
- }
- //
- // The operator back there had better be an and, or, or bang.
- //
- Operator = Stack[StackSize - 2].Operator;
- assert((Operator == TestUtilityAnd) || (Operator == TestUtilityOr) ||
- (Operator == TestUtilityBang) ||
- (Operator == TestUtilityOpenParentheses));
- //
- // Figure out which is more important and shift or reduce.
- //
- if (Operator == TestUtilityOpenParentheses) {
- *Shift = TRUE;
- } else if (TestGetOperatorPrecedence(Operator) >=
- TestGetOperatorPrecedence(NextOperator)) {
- *Shift = FALSE;
- } else {
- *Shift = TRUE;
- }
- return TRUE;
- default:
- break;
- }
- assert(FALSE);
- return FALSE;
- }
- BOOL
- TestReduce (
- PTEST_PARSE_ELEMENT Stack,
- PULONG StackSize
- )
- /*++
- Routine Description:
- This routine attempts to reduce the stack by performing the topmost
- operation.
- Arguments:
- Stack - Supplies a pointer to the parse stack.
- StackSize - Supplies the number of elements on the stack.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- ULONG OperandCount;
- TEST_UTILITY_TEST Operator;
- BOOL Result;
- INT ReturnValue;
- assert(*StackSize != 0);
- //
- // If there's a single element on the stack or the stack looks like
- // ( something, then just evaluate the top item.
- //
- if ((*StackSize == 1) ||
- ((Stack[*StackSize - 1].Type == TestParseElementToken) &&
- (Stack[*StackSize - 2].Type == TestParseElementOperator) &&
- (Stack[*StackSize - 2].Operator == TestUtilityOpenParentheses))) {
- if (Stack[0].Type != TestParseElementToken) {
- SwPrintError(0, NULL, "Expected token");
- return FALSE;
- }
- TestConvertTokenToResult(&(Stack[0]));
- }
- //
- // If the topmost thing is a close parentheses, reduce until finding the
- // corresponding open parentheses.
- //
- if (Stack[*StackSize - 1].Type == TestParseElementOperator) {
- if (Stack[*StackSize - 1].Operator != TestUtilityCloseParentheses) {
- SwPrintError(0, NULL, "Argument expected");
- return FALSE;
- }
- *StackSize -= 1;
- while (TRUE) {
- if (*StackSize < 2) {
- SwPrintError(0, NULL, "Unexpected close parentheses");
- return FALSE;
- }
- if ((Stack[*StackSize - 1].Type == TestParseElementResult) &&
- (Stack[*StackSize - 2].Type == TestParseElementOperator) &&
- (Stack[*StackSize - 2].Operator ==
- TestUtilityOpenParentheses)) {
- *StackSize -= 1;
- Stack[*StackSize - 1].Type = TestParseElementResult;
- Stack[*StackSize - 1].Result = Stack[*StackSize].Result;
- return TRUE;
- }
- Result = TestReduce(Stack, StackSize);
- if (Result == FALSE) {
- return FALSE;
- }
- }
- }
- assert(Stack[*StackSize - 2].Type == TestParseElementOperator);
- //
- // Handle the logical operators !, AND, and OR right here.
- //
- Operator = Stack[*StackSize - 2].Operator;
- if (Operator == TestUtilityBang) {
- assert(Stack[*StackSize - 1].Type == TestParseElementResult);
- ReturnValue = Stack[*StackSize - 1].Result;
- if (ReturnValue == TEST_UTILITY_TRUE) {
- ReturnValue = TEST_UTILITY_FALSE;
- } else if (ReturnValue == TEST_UTILITY_FALSE) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- if (TestDebugPrintEvaluations != FALSE) {
- SwPrintError(0,
- NULL,
- "%d <== [!] %d\n",
- ReturnValue,
- Stack[*StackSize - 1].Result);
- }
- *StackSize -= 1;
- Stack[*StackSize - 1].Result = ReturnValue;
- Stack[*StackSize - 1].Type = TestParseElementResult;
- return TRUE;
- } else if ((Operator == TestUtilityAnd) || (Operator == TestUtilityOr)) {
- TestConvertTokenToResult(&(Stack[*StackSize - 3]));
- TestConvertTokenToResult(&(Stack[*StackSize - 1]));
- assert(Stack[*StackSize - 3].Type == TestParseElementResult);
- assert(Stack[*StackSize - 1].Type == TestParseElementResult);
- ReturnValue = TestEvaluateAndOr(Operator,
- Stack[*StackSize - 3].Result,
- Stack[*StackSize - 1].Result);
- *StackSize -= 2;
- Stack[*StackSize - 1].Result = ReturnValue;
- Stack[*StackSize - 1].Type = TestParseElementResult;
- return TRUE;
- }
- //
- // It's not logical, it must be an expression with tokens.
- //
- assert(Stack[*StackSize - 1].Type == TestParseElementToken);
- //
- // Try for a unary operator.
- //
- OperandCount = TestGetOperandCount(Operator);
- if (OperandCount == 1) {
- ReturnValue = TestEvaluateUnaryOperator(Operator,
- Stack[*StackSize - 1].Token);
- *StackSize -= 1;
- //
- // Process a binary operator.
- //
- } else {
- assert(OperandCount == 2);
- ReturnValue = TestEvaluateBinaryOperator(Operator,
- Stack[*StackSize - 3].Token,
- Stack[*StackSize - 1].Token);
- *StackSize -= 2;
- }
- Stack[*StackSize - 1].Result = ReturnValue;
- Stack[*StackSize - 1].Type = TestParseElementResult;
- return TRUE;
- }
- TEST_UTILITY_TEST
- TestGetOperator (
- PSTR String
- )
- /*++
- Routine Description:
- This routine converts the given string into an operator.
- Arguments:
- String - Supplies the argument string.
- Return Value:
- 0 or 1 if the evaluation succeeds.
- >1 on failure.
- --*/
- {
- if (String[0] == '\0') {
- return TestUtilityInvalid;
- }
- if ((strcmp(String, "=") == 0) || (strcmp(String, "==") == 0)) {
- return TestStringEquals;
- }
- if (strcmp(String, "(") == 0) {
- return TestUtilityOpenParentheses;
- }
- if (strcmp(String, ")") == 0) {
- return TestUtilityCloseParentheses;
- }
- if (strcmp(String, "!") == 0) {
- return TestUtilityBang;
- }
- if (strcmp(String, "!=") == 0) {
- return TestStringNotEquals;
- }
- //
- // All others start with a -.
- //
- if (String[0] != '-') {
- return TestUtilityInvalid;
- }
- String += 1;
- if (strcmp(String, "a") == 0) {
- return TestUtilityAnd;
- }
- if (strcmp(String, "b") == 0) {
- return TestFileIsBlockDevice;
- }
- if (strcmp(String, "c") == 0) {
- return TestFileIsCharacterDevice;
- }
- if (strcmp(String, "d") == 0) {
- return TestFileIsDirectory;
- }
- if (strcmp(String, "e") == 0) {
- return TestFileExists;
- }
- if (strcmp(String, "f") == 0) {
- return TestFileIsRegularFile;
- }
- if (strcmp(String, "g") == 0) {
- return TestFileHasSetGroupId;
- }
- if (strcmp(String, "h") == 0) {
- return TestFileIsSymbolicLink;
- }
- if (strcmp(String, "L") == 0) {
- return TestFileIsSymbolicLink;
- }
- if (strcmp(String, "o") == 0) {
- return TestUtilityOr;
- }
- if (strcmp(String, "p") == 0) {
- return TestFileIsFifo;
- }
- if (strcmp(String, "r") == 0) {
- return TestFileCanRead;
- }
- if (strcmp(String, "S") == 0) {
- return TestFileIsSocket;
- }
- if (strcmp(String, "s") == 0) {
- return TestFileIsNonEmpty;
- }
- if (strcmp(String, "t") == 0) {
- return TestFileDescriptorIsTerminal;
- }
- if (strcmp(String, "u") == 0) {
- return TestFileHasSetUserId;
- }
- if (strcmp(String, "w") == 0) {
- return TestFileCanWrite;
- }
- if (strcmp(String, "x") == 0) {
- return TestFileCanExecute;
- }
- if (strcmp(String, "fe") == 0) {
- return TestFileEqual;
- }
- if (strcmp(String, "nt") == 0) {
- return TestFileNewer;
- }
- if (strcmp(String, "ot") == 0) {
- return TestFileOlder;
- }
- if (strcmp(String, "n") == 0) {
- return TestStringNonZeroLength;
- }
- if (strcmp(String, "z") == 0) {
- return TestStringZeroLength;
- }
- if (strcmp(String, "eq") == 0) {
- return TestIntegerEquals;
- }
- if (strcmp(String, "ne") == 0) {
- return TestIntegerNotEquals;
- }
- if (strcmp(String, "gt") == 0) {
- return TestIntegerGreaterThan;
- }
- if (strcmp(String, "ge") == 0) {
- return TestIntegerGreaterThanOrEqualTo;
- }
- if (strcmp(String, "lt") == 0) {
- return TestIntegerLessThan;
- }
- if (strcmp(String, "le") == 0) {
- return TestIntegerLessThanOrEqualTo;
- }
- return TestUtilityInvalid;
- }
- ULONG
- TestGetOperandCount (
- TEST_UTILITY_TEST Operator
- )
- /*++
- Routine Description:
- This routine returns the number of operands needed for the given operator.
- Arguments:
- Operator - Supplies the operator.
- Return Value:
- 0 on failure.
- 1 or 2 for unary and binary operators.
- --*/
- {
- if ((Operator > TestIntegerMinValue) && (Operator < TestIntegerMaxValue)) {
- return 2;
- }
- if ((Operator == TestStringEquals) || (Operator == TestStringNotEquals) ||
- (Operator == TestUtilityAnd) || (Operator == TestUtilityOr)) {
- return 2;
- }
- if ((Operator > TestFileMinValue) && (Operator < TestFileMaxValue)) {
- if ((Operator == TestFileEqual) || (Operator == TestFileNewer) ||
- (Operator == TestFileOlder)) {
- return 2;
- }
- return 1;
- }
- if ((Operator == TestStringZeroLength) ||
- (Operator == TestStringNonZeroLength) ||
- (Operator == TestUtilityBang)) {
- return 1;
- }
- return 0;
- }
- ULONG
- TestGetOperatorPrecedence (
- TEST_UTILITY_TEST Operator
- )
- /*++
- Routine Description:
- This routine returns precedence of the given operator.
- Arguments:
- Operator - Supplies the operator.
- Return Value:
- Returns the precedence. Higher values are higher precedence.
- --*/
- {
- if (Operator == TestUtilityCloseParentheses) {
- return 0;
- }
- if (Operator == TestUtilityOr) {
- return 1;
- }
- if (Operator == TestUtilityAnd) {
- return 2;
- }
- if (Operator == TestUtilityBang) {
- return 3;
- }
- if ((Operator == TestStringEquals) || (Operator == TestStringNotEquals)) {
- return 6;
- }
- if (Operator == TestUtilityOpenParentheses) {
- return 7;
- }
- if (TestGetOperandCount(Operator) == 2) {
- return 4;
- }
- return 5;
- }
- INT
- TestEvaluateUnaryOperator (
- TEST_UTILITY_TEST Operator,
- PSTR Operand
- )
- /*++
- Routine Description:
- This routine evaluates a unary operator.
- Arguments:
- Operator - Supplies the operator.
- Operand - Supplies the operand.
- Return Value:
- Returns the result of the operation.
- --*/
- {
- INT Error;
- SWISS_FILE_TEST FileTest;
- INT Result;
- INT ReturnValue;
- if ((Operator > TestFileMinValue) && (Operator < TestFileMaxValue)) {
- switch (Operator) {
- case TestFileIsBlockDevice:
- FileTest = FileTestIsBlockDevice;
- break;
- case TestFileIsCharacterDevice:
- FileTest = FileTestIsCharacterDevice;
- break;
- case TestFileIsDirectory:
- FileTest = FileTestIsDirectory;
- break;
- case TestFileExists:
- FileTest = FileTestExists;
- break;
- case TestFileIsRegularFile:
- FileTest = FileTestIsRegularFile;
- break;
- case TestFileHasSetGroupId:
- FileTest = FileTestHasSetGroupId;
- break;
- case TestFileIsSymbolicLink:
- FileTest = FileTestIsSymbolicLink;
- break;
- case TestFileIsFifo:
- FileTest = FileTestIsFifo;
- break;
- case TestFileCanRead:
- FileTest = FileTestCanRead;
- break;
- case TestFileIsSocket:
- FileTest = FileTestIsSocket;
- break;
- case TestFileIsNonEmpty:
- FileTest = FileTestIsNonEmpty;
- break;
- case TestFileDescriptorIsTerminal:
- FileTest = FileTestDescriptorIsTerminal;
- break;
- case TestFileHasSetUserId:
- FileTest = FileTestHasSetUserId;
- break;
- case TestFileCanWrite:
- FileTest = FileTestCanWrite;
- break;
- case TestFileCanExecute:
- FileTest = FileTestCanExecute;
- break;
- default:
- assert(FALSE);
- return TEST_UTILITY_ERROR;
- }
- ReturnValue = TEST_UTILITY_FALSE;
- Result = SwEvaluateFileTest(FileTest, Operand, &Error);
- if (Error != 0) {
- ReturnValue = TEST_UTILITY_ERROR;
- } else if (Result != FALSE) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- } else if ((Operator == TestStringZeroLength) ||
- (Operator == TestStringNonZeroLength)) {
- ReturnValue = TestEvaluateStringTest(Operator, Operand, NULL);
- } else {
- assert(FALSE);
- ReturnValue = TEST_UTILITY_ERROR;
- }
- if (TestDebugPrintEvaluations != FALSE) {
- SwPrintError(0,
- NULL,
- "%d <== [%s] \"%s\"\n",
- ReturnValue,
- TestOperatorStrings[Operator],
- Operand);
- }
- return ReturnValue;
- }
- INT
- TestEvaluateBinaryOperator (
- TEST_UTILITY_TEST Operator,
- PSTR LeftOperand,
- PSTR RightOperand
- )
- /*++
- Routine Description:
- This routine evaluates a binary operator.
- Arguments:
- Operator - Supplies the operator.
- LeftOperand - Supplies the left operand.
- RightOperand - Supplies the right operand.
- Return Value:
- Returns the result of the operation.
- --*/
- {
- INT LeftResult;
- struct stat LeftStat;
- INT ReturnValue;
- INT RightResult;
- struct stat RightStat;
- if ((Operator > TestFileMinValue) && (Operator < TestFileMaxValue)) {
- memset(&LeftStat, 0, sizeof(struct stat));
- memset(&RightStat, 0, sizeof(struct stat));
- SwStat(LeftOperand, TRUE, &LeftStat);
- SwStat(RightOperand, TRUE, &RightStat);
- ReturnValue = TEST_UTILITY_FALSE;
- switch (Operator) {
- case TestFileEqual:
- if ((LeftStat.st_dev == RightStat.st_dev) &&
- (LeftStat.st_ino == RightStat.st_ino)) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- break;
- case TestFileNewer:
- if (LeftStat.st_mtime > RightStat.st_mtime) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- break;
- case TestFileOlder:
- if (LeftStat.st_mtime < RightStat.st_mtime) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- break;
- default:
- assert(FALSE);
- ReturnValue = TEST_UTILITY_ERROR;
- break;
- }
- } else if ((Operator > TestStringMinValue) &&
- (Operator < TestStringMaxValue)) {
- ReturnValue = TestEvaluateStringTest(Operator,
- LeftOperand,
- RightOperand);
- } else if ((Operator > TestIntegerMinValue) &&
- (Operator < TestIntegerMaxValue)) {
- ReturnValue = TestEvaluateIntegerTest(Operator,
- LeftOperand,
- RightOperand);
- } else if ((Operator == TestUtilityAnd) || (Operator == TestUtilityOr)) {
- LeftResult = TEST_UTILITY_FALSE;
- RightResult = TEST_UTILITY_FALSE;
- if (strlen(LeftOperand) != 0) {
- LeftResult = TEST_UTILITY_TRUE;
- }
- if (strlen(RightOperand) != 0) {
- RightResult = TEST_UTILITY_TRUE;
- }
- ReturnValue = TestEvaluateAndOr(Operator, LeftResult, RightResult);
- } else {
- assert(FALSE);
- ReturnValue = TEST_UTILITY_ERROR;
- }
- if (TestDebugPrintEvaluations != FALSE) {
- SwPrintError(0,
- NULL,
- "%d <== \"%s\" [%s] \"%s\"\n",
- ReturnValue,
- LeftOperand,
- TestOperatorStrings[Operator],
- RightOperand);
- }
- return ReturnValue;
- }
- INT
- TestEvaluateStringTest (
- TEST_UTILITY_TEST Operator,
- PSTR String1,
- PSTR String2
- )
- /*++
- Routine Description:
- This routine evaluates a string test.
- Arguments:
- Operator - Supplies the operator.
- String1 - Supplies a pointer to the first string operand.
- String2 - Supplies a pointer to the second string operand. Not all
- operators require a second operand.
- Return Value:
- Returns the result of the operation.
- --*/
- {
- INT ReturnValue;
- ReturnValue = TEST_UTILITY_FALSE;
- switch (Operator) {
- case TestStringNonZeroLength:
- if (strlen(String1) != 0) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- break;
- case TestStringZeroLength:
- if (strlen(String1) == 0) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- break;
- case TestStringEquals:
- assert((String1 != NULL) && (String2 != NULL));
- if (strcmp(String1, String2) == 0) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- break;
- case TestStringNotEquals:
- assert((String1 != NULL) && (String2 != NULL));
- if (strcmp(String1, String2) != 0) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- break;
- default:
- assert(FALSE);
- break;
- }
- return ReturnValue;
- }
- INT
- TestEvaluateIntegerTest (
- TEST_UTILITY_TEST Operator,
- PSTR LeftIntegerString,
- PSTR RightIntegerString
- )
- /*++
- Routine Description:
- This routine evaluates an integer test.
- Arguments:
- Operator - Supplies the operator.
- LeftIntegerString - Supplies a pointer to the string containing the left
- numeric value.
- RightIntegerString - Supplies a pointer to the string containing the right
- numeric value.
- Return Value:
- Returns the result of the operation.
- --*/
- {
- PSTR AfterScan;
- INT Left;
- INT ReturnValue;
- INT Right;
- //
- // Convert the strings to integers.
- //
- Left = strtol(LeftIntegerString, &AfterScan, 10);
- if ((Left == 0) && (AfterScan == LeftIntegerString)) {
- SwPrintError(0, NULL, "%s: Invalid integer", LeftIntegerString);
- return TEST_UTILITY_ERROR;
- }
- Right = strtol(RightIntegerString, &AfterScan, 10);
- if ((Right == 0) && (AfterScan == RightIntegerString)) {
- SwPrintError(0, NULL, "%s: Invalid integer", RightIntegerString);
- return TEST_UTILITY_ERROR;
- }
- ReturnValue = TEST_UTILITY_FALSE;
- switch (Operator) {
- case TestIntegerEquals:
- if (Left == Right) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- break;
- case TestIntegerNotEquals:
- if (Left != Right) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- break;
- case TestIntegerGreaterThan:
- if (Left > Right) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- break;
- case TestIntegerGreaterThanOrEqualTo:
- if (Left >= Right) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- break;
- case TestIntegerLessThan:
- if (Left < Right) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- break;
- case TestIntegerLessThanOrEqualTo:
- if (Left <= Right) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- break;
- default:
- assert(FALSE);
- break;
- }
- return ReturnValue;
- }
- INT
- TestEvaluateAndOr (
- TEST_UTILITY_TEST Operator,
- INT Left,
- INT Right
- )
- /*++
- Routine Description:
- This routine evaluates and AND (-a) or and OR (-o) operator.
- Arguments:
- Operator - Supplies the operator, which should be AND or OR.
- Left - Supplies the left result, a TEST_UTILITY_* value.
- Right - Supplies the right result, a TEST_UTILITY_* value.
- Return Value:
- Returns the TEST_UTILITY_* result.
- --*/
- {
- INT ReturnValue;
- assert((Operator == TestUtilityAnd) || (Operator == TestUtilityOr));
- ReturnValue = TEST_UTILITY_FALSE;
- if (Operator == TestUtilityAnd) {
- if ((Left == TEST_UTILITY_TRUE) && (Right == TEST_UTILITY_TRUE)) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- } else {
- if ((Left == TEST_UTILITY_TRUE) || (Right == TEST_UTILITY_TRUE)) {
- ReturnValue = TEST_UTILITY_TRUE;
- }
- }
- if (TestDebugPrintEvaluations != FALSE) {
- SwPrintError(0,
- NULL,
- "%d <== %d [%s] %d\n",
- ReturnValue,
- Left,
- TestOperatorStrings[Operator],
- Right);
- }
- return ReturnValue;
- }
- VOID
- TestConvertTokenToResult (
- PTEST_PARSE_ELEMENT Element
- )
- /*++
- Routine Description:
- This routine converts a token type parse element into a result element,
- which is non-zero if the given token has a non-zero length.
- Arguments:
- Element - Supplies a pointer to the token element to convert into a result.
- Return Value:
- None.
- --*/
- {
- //
- // Skip it if it's already a result.
- //
- if (Element->Type == TestParseElementResult) {
- return;
- }
- //
- // Only tokens are handled here, there's no converting an operator to a
- // result.
- //
- assert(Element->Type == TestParseElementToken);
- if (strlen(Element->Token) != 0) {
- Element->Result = TEST_UTILITY_TRUE;
- } else {
- Element->Result = TEST_UTILITY_FALSE;
- }
- Element->Type = TestParseElementResult;
- return;
- }
|