Files
epics-base/modules/libcom/test/aslibtest.c
george-mcintyre 9b532540bf Introduce Future Proofing to ACF parsing.
- Allow ACF files to contain sections that are unsupported
- introduce a pragma disable-strict-parsing to disable strict parsing and allow files to be accepted

Add tests for future proof parser functionaluty

Expanded tests for Access Security configurations to validate parsing behaviors for unsupported elements. Added detailed cases covering invalid formats, unsupported modifications, and acceptable configurations while maintaining future-proofing and backward compatibility assurances.

Refactor parser to handle generic items and floating-point tokens

Replaced "unsupported elements" with a more flexible "generic items" mechanism to enhance parser extensibility. Added support for floating-point numbers in parsing rules. Updated tests and rules to reflect these changes, ensuring better handling of nested and unknown configurations.

Add warnings for unsupported ACF elements and update tests

Introduced `yywarn` to log warnings for unsupported ACF elements, rule permissions, and conditions in `asLib.y`. Updated test configuration to replace generic placeholders with more descriptive names. Adjusted test cases and reduced the test plan count to reflect the changes.

Remove unused strictParsing logic and improve error handling.

Removed strictParsing variable and related code as it was no longer in use. Enhanced error messaging and parsing logic for handling unsupported elements. Updated test cases for better clarity and expanded coverage of edge cases.

Enhance ASLib parsing with Int64/Float64 support.

Updated ASLib to utilize 64-bit integers and floats for improved parsing precision. Replaced legacy parsing methods with epics-friendly functions, improved error handling, and adjusted tests and grammar accordingly.

Refactor AS parser to separate non-rule keywords

Introduced a new `non_rule_keyword` type and moved `tokenASG` and `tokenRULE` under it for better logical grouping. This improves parser clarity and maintains a clean separation of keyword types.

Add rule-specific parsing for generic block elements

Introduce new rules for parsing rule-specific generic block elements and their names. This enhances the grammar to handle additional cases while maintaining memory management and error handling. Adjust references to use the new rule where applicable.

Refactor grammar rules in asLib.y to remove remaining shift/reduce conflict

Reorganize and refine grammar rules for handling generic elements, blocks, and lists. Simplifies the structure while introducing clearer distinctions between different constructs. This enhances maintainability and removes ambiguities in parsing.
2025-11-23 14:03:47 -08:00

793 lines
20 KiB
C

/*************************************************************************\
* Copyright (c) 2018 Michael Davidsaver
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include <stdlib.h>
#include <string.h>
#include <testMain.h>
#include <epicsUnitTest.h>
#include <errSymTbl.h>
#include <epicsString.h>
#include <osiFileName.h>
#include <errlog.h>
#include <asLib.h>
static char *asUser,
*asHost;
static int asAsl;
/**
* @brief Test data with Host Access Groups (HAG)
*
* This includes a host access group (HAG) for localhost and a default Access Security Group (ASG)
* with rules for read and write access to the HAG.
*/
static const char hostname_config[] = ""
"HAG(foo) {localhost}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n"
"ASG(rw) {\n"
" RULE(1, WRITE) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* Test data with unsupported elements.
* The unsupported element should be silently ignored, but the rest of the config is processed.
*
* top-unknown-keyword(WELL,FORMED,LIST)
* - valid top level keyword with well-formed arg list
*/
static const char supported_config_1[] = ""
"HAG(foo) {localhost}\n"
"GENERIC(WELL, FORMED, ARG, LIST)\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* Test data with unsupported elements.
* The unsupported element should be silently ignored, but the rest of the config is processed.
*
* top-unknown-keyword(WELL,FORMED,LIST) { WELL,FORMED,LIST }
* - valid top level keyword with well-formed arg list and valid arg list body
*/
static const char supported_config_2[] = ""
"HAG(foo) {localhost}\n"
"SIMPLE(WELL, FORMED, ARG, LIST) {\n"
" WELL, FORMED, LIST\n"
"}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* Test data with unsupported elements.
* The unsupported element should be silently ignored, but the rest of the config is processed.
*
* top-unknown-keyword(WELL,FORMED,LIST) { recursive-body-keyword(WELL,FORMED,LIST) }
* - valid top level keyword with well-formed arg list and valid recursive body
* - includes quoted strings, integers, and floating point numbers
*/
static const char supported_config_3[] = ""
"HAG(foo) {localhost}\n"
"COMPLEX_ARGUMENTS(1, WELL, \"FORMED\", ARG, LIST) {\n"
" ALSO_GENERIC(WELL, FORMED, ARG, LIST, 2.0) \n"
"}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* Test data with unsupported elements.
* The unsupported element should be silently ignored, but the rest of the config is processed.
*
* top-unknown-keyword(WELL,FORMED,LIST) { recursive-body-keyword(WELL,FORMED,LIST) { AND_BODY } }
* - valid top level keyword with well-formed arg list and valid recursive body, with a nested body
* - includes floating point numbers, and an empty arg list
*/
static const char supported_config_4[] = ""
"HAG(foo) {localhost}\n"
"SUB_BLOCKS(1.0, ARGS) {\n"
" ALSO_GENERIC() {\n"
" AND_LIST_BODY\n"
" }\n"
" ANOTHER_GENERIC() {\n"
" BIGGER, LIST, BODY\n"
" }\n"
"}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* Test data with unsupported elements.
* The unsupported element should be silently ignored, but the rest of the config is processed.
*
* top-unknown-keyword(WELL,FORMED,LIST) { recursive-body-keyword(WELL,FORMED,LIST) { AND_RECURSIVE_BODY() {LIST, LIST } }
* - valid top level keyword with well-formed arg list and valid recursive body, with a nested recursion
* - includes floating point numbers, and an empty arg list
*/
static const char supported_config_5[] = ""
"HAG(foo) {localhost}\n"
"RECURSIVE_SUB_BLOCKS(1.0, -2.3, +4.5, ARGS, +2.71828E-23, -2.71828e+23, +12, -13, +-14) {\n"
" ALSO_GENERIC() {\n"
" AND_RECURSIVE(FOO) {\n"
" LIST, BODY\n"
" }\n"
" }\n"
"}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(+1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* Test data with unsupported elements.
* The unsupported element should be silently ignored, but the rest of the config is processed.
*
* top-unknown-keyword(KEYWORD) { KEYWORD(KEYWORD) }
* - valid top level keyword with keyword for args, recursive body name, and arg list
* - top level generic items referenced in RULES, then RULES are ignored
*/
static const char supported_config_6[] = ""
"HAG(foo) {localhost}\n"
"WITH_KEYWORDS(UAG) {\n"
" ASG(HAL, IMP, CALC, RULE)\n"
" HAL(USG, MAL) {\n"
" HAG(foo)\n"
" }\n"
"}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ignored) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" WITH_KEYWORDS(UAG)\n"
" }\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
" RULE(2, WRITE) {\n"
" WITH_KEYWORDS(UAG)\n"
" }\n"
"}\n";
/**
* Test data with unsupported elements.
* The unsupported elements should be silently ignored, and the rule will not match,
* but the rest of the config is processed.
*
* - RULE contains unsupported elements
*/
static const char supported_config_7[] = ""
"HAG(foo) {localhost}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" BAD_PREDICATE(\"x509\")\n"
" BAD_PREDICATE_AS_WELL(\"EPICS Certificate Authority\")\n"
" }\n"
"}\n";
/**
* Test data with unsupported elements.
* The unsupported elements should be silently ignored, and the rule will not match,
* but the rest of the config is processed.
*
* - unexpected permission name in arg list for RULE element ignored
*/
static const char supported_config_8[] = ""
"HAG(foo) {localhost}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, ADDITIONAL_PERMISSION) {\n"
" HAG(foo)\n"
" }\n"
"}\n"
;
/**
* Test data with unsupported elements.
* The unsupported element should cause an error as the format is invalid.
*
* top-unknown-keyword( a b )
* - invalid arg list missing commas
*/
static const char unsupported_config_1[] = ""
"HAG(foo) {localhost}\n"
"GENERIC(not well-formed arg list)\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* Test data with unsupported elements.
* The unsupported element should cause an error as the format is invalid.
*
* top-unknown-keyword(WELL,FORMED,LIST) { a b }
* - invalid string list
*/
static const char unsupported_config_2[] = ""
"HAG(foo) {localhost}\n"
"GENERIC(WELL, FORMED, ARG, LIST) {\n"
" NOT WELL-FORMED BODY\n"
"}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* Test data with unsupported elements.
* The unsupported element should cause an error as the format is invalid.
*
* top-unknown-keyword { a, b }
* - missing parameters (must have at least an empty arg list)
*/
static const char unsupported_config_3[] = ""
"HAG(foo) {localhost}\n"
"GENERIC {\n"
" WELL, FORMED, LIST, BODY\n"
"}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* Test data with unsupported elements.
* The unsupported element should cause an error as the format is invalid.
*
* top-unknown-keyword(WELL,FORMED,LIST) { X, Y(a b c) }
* - bad arg list for recursive body
*/
static const char unsupported_config_4[] = ""
"HAG(foo) {localhost}\n"
"GENERIC(WELL, FORMED, ARG, LIST) {\n"
" BODY(BAD ARG LIST)\n"
"}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* Test data with unsupported elements.
* The unsupported element should cause an error as the format is invalid.
*
* top-unknown-keyword(WELL,FORMED,LIST) { X, Y(a b c) }
* - mix of list and recursive type bodies
*/
static const char unsupported_config_5[] = ""
"HAG(foo) {localhost}\n"
"GENERIC(WELL, FORMED, ARG, LIST) {\n"
" LIST, BODY, MIXED, WITH,\n"
" RECURSIVE_BODY(ARG, LIST)\n"
"}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* The modification to a well known element should cause an error.
*
* - bad arg list for ASG element
*/
static const char unsupported_mod_1[] = ""
"HAG(foo) {localhost}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro BAD ARG LIST) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* The modification to a well known element should cause an error.
*
* - bad arg list for HAG element
*/
static const char unsupported_mod_2[] = ""
"HAG(BAD ARG LIST) {localhost}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* The modification to a well known element should cause an error.
*
* - bad arg list for RULE element
*/
static const char unsupported_mod_3[] = ""
"HAG(foo) {localhost}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0 BAD ARG LIST)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* The modification to a well known element should cause an error.
*
* - bad arg count for ASG element
*/
static const char unsupported_mod_4[] = ""
"HAG(foo) {localhost}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro, UNKNOWN_PERMISSION) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* The modification to a well known element should cause an error.
*
* - unexpected name in arg list for RULE element
*/
static const char unsupported_mod_5[] = ""
"HAG(foo) {localhost}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE, UNKNOWN_FLAG)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* The modification to a well known element should cause an error.
*
* - unexpected recursive body mixed in with HAG string list body
*/
static const char unsupported_mod_6[] = ""
"HAG(foo) {\n"
" localhost,\n"
" NETWORK(\"127.0.0.1\")\n"
"}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* The modification to a well known element should cause an error.
*
* - unexpected recursive body mixed in with UAG string list body
*/
static const char unsupported_mod_7[] = ""
"UAG(foo) {\n"
" alice,\n"
" GROUP(admin)\n"
"}\n"
"ASG(DEFAULT) {\n"
" RULE(0, NONE)\n"
"}\n"
"ASG(ro) {\n"
" RULE(0, NONE)\n"
" RULE(1, READ) {\n"
" HAG(foo)\n"
" }\n"
"}\n";
/**
* Set the username for the authorization tests
*/
static void setUser(const char *name)
{
free(asUser);
asUser = epicsStrDup(name);
}
/**
* Set the hostname for the authorization tests
*/
static void setHost(const char *name)
{
free(asHost);
asHost = epicsStrDup(name);
}
/**
* Test the access control system with the given ASG, user, and hostname
* This will test that the expected access given by mask is granted
* when the Access Security Group (ASG) is interpreted in the
* context of the configured user, host, and Level (asl).
*/
static void testAccess(const char *asg, unsigned mask)
{
ASMEMBERPVT asp = 0; /* aka dbCommon::asp */
ASCLIENTPVT client = 0;
long ret;
ret = asAddMember(&asp, asg);
if(ret) {
testFail("testAccess(ASG:%s, USER:%s, HOST:%s, ASL:%d) -> asAddMember error: %s",
asg, asUser, asHost, asAsl, errSymMsg(ret));
} else {
ret = asAddClient(&client, asp, asAsl, asUser, asHost);
}
if(ret) {
testFail("testAccess(ASG:%s, USER:%s, HOST:%s, ASL:%d) -> asAddClient error: %s",
asg, asUser, asHost, asAsl, errSymMsg(ret));
} else {
unsigned actual = 0;
actual |= asCheckGet(client) ? 1 : 0;
actual |= asCheckPut(client) ? 2 : 0;
testOk(actual==mask, "testAccess(ASG:%s, USER:%s, HOST:%s, ASL:%d) -> %x == %x",
asg, asUser, asHost, asAsl, actual, mask);
}
if(client) asRemoveClient(&client);
if(asp) asRemoveMember(&asp);
}
static void testSyntaxErrors(void)
{
static const char empty[] = "\n#almost empty file\n\n";
long ret;
testDiag("testSyntaxErrors()");
asCheckClientIP = 0;
eltc(0);
ret = asInitMem(empty, NULL);
testOk(ret==S_asLib_badConfig, "load \"empty\" config -> %s", errSymMsg(ret));
eltc(1);
}
static void testHostNames(void)
{
testDiag("testHostNames()");
asCheckClientIP = 0;
testOk1(asInitMem(hostname_config, NULL)==0);
setUser("testing");
setHost("localhost");
asAsl = 0;
testAccess("invalid", 0);
testAccess("DEFAULT", 0);
testAccess("ro", 1);
testAccess("rw", 3);
setHost("127.0.0.1");
testAccess("invalid", 0);
testAccess("DEFAULT", 0);
testAccess("ro", 0);
testAccess("rw", 0);
setHost("guaranteed.invalid.");
testAccess("invalid", 0);
testAccess("DEFAULT", 0);
testAccess("ro", 0);
testAccess("rw", 0);
}
static void testUseIP(void)
{
testDiag("testUseIP()");
asCheckClientIP = 1;
/* still host names in .acf */
testOk1(asInitMem(hostname_config, NULL)==0);
/* now resolved to IPs */
setUser("testing");
setHost("localhost"); /* will not match against resolved IP */
asAsl = 0;
testAccess("invalid", 0);
testAccess("DEFAULT", 0);
testAccess("ro", 0);
testAccess("rw", 0);
setHost("127.0.0.1");
testAccess("invalid", 0);
testAccess("DEFAULT", 0);
testAccess("ro", 1);
testAccess("rw", 3);
setHost("guaranteed.invalid.");
testAccess("invalid", 0);
testAccess("DEFAULT", 0);
testAccess("ro", 0);
testAccess("rw", 0);
}
static void testFutureProofParser(void)
{
long ret;
testDiag("testFutureProofParser()");
asCheckClientIP = 0;
eltc(0); /* Suppress error messages during test */
/* Test parsing should reject unsupported elements badly placed or formed */
ret = asInitMem(unsupported_config_1, NULL);
testOk(ret==S_asLib_badConfig, "parsing rejects invalid arg list missing commas -> %s", errSymMsg(ret));
ret = asInitMem(unsupported_config_2, NULL);
testOk(ret==S_asLib_badConfig, "parsing rejects invalid string list -> %s", errSymMsg(ret));
ret = asInitMem(unsupported_config_3, NULL);
testOk(ret==S_asLib_badConfig, "parsing rejects missing parameters (must have at least an empty arg list) -> %s", errSymMsg(ret));
ret = asInitMem(unsupported_config_4, NULL);
testOk(ret==S_asLib_badConfig, "parsing rejects bad arg list for recursive body -> %s", errSymMsg(ret));
ret = asInitMem(unsupported_config_5, NULL);
testOk(ret==S_asLib_badConfig, "parsing rejects mix of list and recursive type bodies -> %s", errSymMsg(ret));
/* Test supported elements badly modified should be rejected */
ret = asInitMem(unsupported_mod_1, NULL);
testOk(ret==S_asLib_badConfig, "parsing rejects bad arg list for ASG element -> %s", errSymMsg(ret));
ret = asInitMem(unsupported_mod_2, NULL);
testOk(ret==S_asLib_badConfig, "parsing rejects bad arg list for HAG element-> %s", errSymMsg(ret));
ret = asInitMem(unsupported_mod_3, NULL);
testOk(ret==S_asLib_badConfig, "parsing rejects bad arg list for RULE element -> %s", errSymMsg(ret));
ret = asInitMem(unsupported_mod_4, NULL);
testOk(ret==S_asLib_badConfig, "parsing rejects bad arg count for ASG element -> %s", errSymMsg(ret));
ret = asInitMem(unsupported_mod_5, NULL);
testOk(ret==S_asLib_badConfig, "parsing rejects unexpected name in arg list for RULE element -> %s", errSymMsg(ret));
ret = asInitMem(unsupported_mod_6, NULL);
testOk(ret==S_asLib_badConfig, "parsing rejects unexpected recursive body in HAG element body -> %s", errSymMsg(ret));
ret = asInitMem(unsupported_mod_7, NULL);
testOk(ret==S_asLib_badConfig, "parsing rejects unexpected recursive body in UAG element body -> %s", errSymMsg(ret));
eltc(1);
/* Test supported for known elements containing unsupported elements, well-formed and ignored */
setUser("testing");
setHost("localhost");
ret = asInitMem(supported_config_1, NULL);
testOk(ret==0, "unknown elements ignored -> %s", errSymMsg(ret));
if (!ret) {
asAsl = 0;
testAccess("DEFAULT", 0);
testAccess("ro", 1);
}
ret = asInitMem(supported_config_2, NULL);
testOk(ret==0, "unknown elements with body ignored -> %s", errSymMsg(ret));
if (!ret) {
asAsl = 0;
testAccess("DEFAULT", 0);
testAccess("ro", 1);
}
ret = asInitMem(supported_config_3, NULL);
testOk(ret==0, "unknown elements with string and double args and a body, ignored -> %s", errSymMsg(ret));
if (!ret) {
asAsl = 0;
testAccess("DEFAULT", 0);
testAccess("ro", 1);
}
ret = asInitMem(supported_config_4, NULL);
testOk(ret==0, "unknown elements with recursive body ignored -> %s", errSymMsg(ret));
if (!ret) {
asAsl = 0;
testAccess("DEFAULT", 0);
testAccess("ro", 1);
}
ret = asInitMem(supported_config_5, NULL);
testOk(ret==0, "unknown elements with recursive body with recursion ignored -> %s", errSymMsg(ret));
if (!ret) {
asAsl = 0;
testAccess("DEFAULT", 0);
testAccess("ro", 1);
}
ret = asInitMem(supported_config_6, NULL);
testOk(ret==0, "unknown elements with keywords arguments and body names ignored -> %s", errSymMsg(ret));
if (!ret) {
asAsl = 0;
testAccess("DEFAULT", 0);
testAccess("ignored", 0);
testAccess("ro", 1);
}
ret = asInitMem(supported_config_7, NULL);
testOk(ret==0, "rules with unknown elements ignored -> %s", errSymMsg(ret));
if (!ret) {
asAsl = 0;
testAccess("DEFAULT", 0);
testAccess("ro", 0);
}
ret = asInitMem(supported_config_8, NULL);
testOk(ret==0, "rules with unknown permission names ignored -> %s", errSymMsg(ret));
if (!ret) {
asAsl = 0;
testAccess("DEFAULT", 0);
testAccess("ro", 0);
}
}
MAIN(aslibtest)
{
testPlan(64);
testSyntaxErrors();
testFutureProofParser();
testHostNames();
testUseIP();
errlogFlush();
return testDone();
}