Merge changes from 3.16 and below into libcom/master
This commit is contained in:
@@ -12,6 +12,7 @@ include $(TOP)/configure/CONFIG
|
||||
|
||||
PROD_LIBS += Com
|
||||
PROD_SYS_LIBS_WIN32 += ws2_32 advapi32 user32
|
||||
PROD_SYS_LIBS_solaris += socket nsl
|
||||
|
||||
TESTPROD_HOST += epicsUnitTestTest
|
||||
epicsUnitTestTest_SRCS += epicsUnitTestTest.c
|
||||
@@ -48,8 +49,6 @@ TESTS += epicsMathTest
|
||||
|
||||
TESTPROD_HOST += epicsMMIOTest
|
||||
epicsMMIOTest_SRCS += epicsMMIOTest.c
|
||||
epicsMMIOTest_SYS_LIBS_solaris = socket
|
||||
epicsMMIOTest_SYS_LIBS_WIN32 = ws2_32 user32
|
||||
testHarness_SRCS += epicsMMIOTest.c
|
||||
TESTS += epicsMMIOTest
|
||||
|
||||
@@ -65,8 +64,6 @@ TESTS += epicsEnvTest
|
||||
|
||||
TESTPROD_HOST += epicsErrlogTest
|
||||
epicsErrlogTest_SRCS += epicsErrlogTest.c
|
||||
epicsErrlogTest_SYS_LIBS_solaris = socket
|
||||
epicsErrlogTest_SYS_LIBS_WIN32 = ws2_32 user32
|
||||
testHarness_SRCS += epicsErrlogTest.c
|
||||
TESTS += epicsErrlogTest
|
||||
|
||||
@@ -82,8 +79,6 @@ TESTS += epicsStdlibTest
|
||||
|
||||
TESTPROD_HOST += epicsSockResolveTest
|
||||
epicsSockResolveTest_SRCS += epicsSockResolveTest.c
|
||||
epicsSockResolveTest_SYS_LIBS_solaris = socket
|
||||
epicsSockResolveTest_SYS_LIBS_WIN32 = ws2_32 user32
|
||||
testHarness_SRCS += epicsSockResolveTest.c
|
||||
TESTS += epicsSockResolveTest
|
||||
|
||||
@@ -198,9 +193,6 @@ TESTS += taskwdTest
|
||||
TESTPROD_HOST += blockingSockTest
|
||||
blockingSockTest_SRCS += blockingSockTest.cpp
|
||||
testHarness_SRCS += blockingSockTest.cpp
|
||||
# needed when its an object library build
|
||||
blockingSockTest_SYS_LIBS_WIN32 = ws2_32 advapi32 user32
|
||||
blockingSockTest_SYS_LIBS_solaris = socket
|
||||
TESTS += blockingSockTest
|
||||
|
||||
TESTPROD_HOST += epicsMessageQueueTest
|
||||
@@ -218,6 +210,18 @@ ipAddrToAsciiTest_SRCS += ipAddrToAsciiTest.cpp
|
||||
testHarness_SRCS += ipAddrToAsciiTest.cpp
|
||||
TESTS += ipAddrToAsciiTest
|
||||
|
||||
TESTPROD_HOST += osiSockTest
|
||||
osiSockTest_SRCS += osiSockTest.c
|
||||
testHarness_SRCS += osiSockTest.c
|
||||
TESTS += osiSockTest
|
||||
|
||||
ifneq ($(OS_CLASS),WIN32)
|
||||
# This test can only be run on a build host, and is broken on Windows
|
||||
TESTPROD_HOST += yajl_test
|
||||
yajl_test_SRCS += yajl_test.c
|
||||
TESTS += yajlTest
|
||||
endif
|
||||
|
||||
# The testHarness runs all the test programs in a known working order.
|
||||
testHarness_SRCS += epicsRunLibComTests.c
|
||||
|
||||
@@ -262,4 +266,3 @@ cvtFastPerform_SRCS += cvtFastPerform.cpp
|
||||
testHarness_SRCS += cvtFastPerform.cpp
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ int epicsInlineTest(void);
|
||||
int ipAddrToAsciiTest(void);
|
||||
int macDefExpandTest(void);
|
||||
int macLibTest(void);
|
||||
int osiSockTest(void);
|
||||
int ringBytesTest(void);
|
||||
int ringPointerTest(void);
|
||||
int taskwdTest(void);
|
||||
@@ -104,6 +105,7 @@ void epicsRunLibComTests(void)
|
||||
runTest(ipAddrToAsciiTest);
|
||||
runTest(macDefExpandTest);
|
||||
runTest(macLibTest);
|
||||
runTest(osiSockTest);
|
||||
runTest(ringBytesTest);
|
||||
runTest(ringPointerTest);
|
||||
runTest(taskwdTest);
|
||||
|
||||
74
test/osiSockTest.c
Normal file
74
test/osiSockTest.c
Normal file
@@ -0,0 +1,74 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "osiSock.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
/* This could easily be generalized to test more options */
|
||||
void udpBroadcast(SOCKET s, int put)
|
||||
{
|
||||
int status;
|
||||
int flag = put;
|
||||
osiSocklen_t len = sizeof(flag);
|
||||
|
||||
status = setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&flag, len);
|
||||
testOk(status >= 0, "setsockopt BROADCAST := %d", put);
|
||||
|
||||
status = getsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&flag, &len);
|
||||
testOk(status >= 0 && len == sizeof(flag) && !flag == !put,
|
||||
"getsockopt BROADCAST => %d", flag);
|
||||
}
|
||||
|
||||
void multiCastLoop(SOCKET s, int put)
|
||||
{
|
||||
int status;
|
||||
osiSockOptMcastLoop_t flag = put;
|
||||
osiSocklen_t len = sizeof(flag);
|
||||
|
||||
status = setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||
(char *)&flag, len);
|
||||
testOk(status >= 0, "setsockopt MULTICAST_LOOP := %d", put);
|
||||
|
||||
status = getsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&flag, &len);
|
||||
testOk(status >= 0 && len == sizeof(flag) && !flag == !put,
|
||||
"getsockopt MULTICAST_LOOP => %d", (int) flag);
|
||||
}
|
||||
|
||||
void udpSockTest(void)
|
||||
{
|
||||
SOCKET s;
|
||||
|
||||
s = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
testOk(s != INVALID_SOCKET, "epicsSocketCreate INET, DGRAM, 0");
|
||||
|
||||
udpBroadcast(s, 1);
|
||||
udpBroadcast(s, 0);
|
||||
|
||||
multiCastLoop(s, 1);
|
||||
multiCastLoop(s, 0);
|
||||
|
||||
epicsSocketDestroy(s);
|
||||
}
|
||||
|
||||
|
||||
MAIN(osiSockTest)
|
||||
{
|
||||
int status;
|
||||
testPlan(10);
|
||||
|
||||
status = osiSockAttach();
|
||||
testOk(status, "osiSockAttach");
|
||||
|
||||
udpSockTest();
|
||||
|
||||
osiSockRelease();
|
||||
return testDone();
|
||||
}
|
||||
@@ -238,9 +238,11 @@ MAIN(ringPointerTest)
|
||||
|
||||
testPlan(37);
|
||||
testSingle();
|
||||
epicsThreadSetPriority(epicsThreadGetIdSelf(), epicsThreadPriorityScanLow);
|
||||
if (prio)
|
||||
epicsThreadSetPriority(epicsThreadGetIdSelf(), epicsThreadPriorityScanLow);
|
||||
testPair(0);
|
||||
testPair(1);
|
||||
epicsThreadSetPriority(epicsThreadGetIdSelf(), prio);
|
||||
if (prio)
|
||||
epicsThreadSetPriority(epicsThreadGetIdSelf(), prio);
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "taskwd.h"
|
||||
#include "errlog.h"
|
||||
@@ -17,50 +18,78 @@
|
||||
#include "epicsUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
/* This is a unique prefix used for the names of the test threads */
|
||||
#define baseName "testTask"
|
||||
|
||||
void monInsert(void *usr, epicsThreadId tid)
|
||||
{
|
||||
testPass("monInsert(tid=%p)", (void *)tid);
|
||||
char tname[32];
|
||||
|
||||
epicsThreadGetName(tid, tname, sizeof(tname));
|
||||
if (strncmp(tname, baseName, strlen(baseName)) == 0)
|
||||
testPass("monInsert(thread='%s')", tname);
|
||||
else
|
||||
testDiag("monInsert(thread='%s')", tname);
|
||||
}
|
||||
|
||||
void monNotify(void *usr, epicsThreadId tid, int suspended)
|
||||
{
|
||||
testPass("monNotify(tid=%p, suspended=%d)", (void *)tid, suspended);
|
||||
char tname[32];
|
||||
|
||||
epicsThreadGetName(tid, tname, sizeof(tname));
|
||||
testPass("monNotify(thread='%s', suspended=%d)", tname, suspended);
|
||||
epicsThreadResume(tid);
|
||||
}
|
||||
|
||||
void monRemove(void *usr, epicsThreadId tid)
|
||||
{
|
||||
testPass("monRemove(tid=%p)", (void *)tid);
|
||||
char tname[32];
|
||||
|
||||
epicsThreadGetName(tid, tname, sizeof(tname));
|
||||
if (strncmp(tname, baseName, strlen(baseName)) == 0)
|
||||
testPass("monRemove(thread='%s')", tname);
|
||||
else
|
||||
testDiag("monRemove(thread='%s')", tname);
|
||||
}
|
||||
|
||||
taskwdMonitor monFuncs = {monInsert, monNotify, monRemove};
|
||||
|
||||
void anyNotify(void *usr, epicsThreadId tid)
|
||||
{
|
||||
testPass("anyNotify(tid=%p)", (void *)tid);
|
||||
char tname[32];
|
||||
|
||||
epicsThreadGetName(tid, tname, sizeof(tname));
|
||||
if (strncmp(tname, baseName, strlen(baseName)) == 0)
|
||||
testPass("anyNotify(thread='%s')", tname);
|
||||
else
|
||||
testDiag("anyNotify(thread='%s')", tname);
|
||||
}
|
||||
|
||||
void taskNotify(void *usr)
|
||||
{
|
||||
testPass("taskNotify");
|
||||
const char *id = (const char *) usr;
|
||||
|
||||
testPass("taskNotify id='%s'", id);
|
||||
}
|
||||
|
||||
void testTask1(void *arg)
|
||||
{
|
||||
taskwdInsert(0, taskNotify, NULL);
|
||||
epicsThreadSleep(10.0);
|
||||
taskwdInsert(0, taskNotify, "1");
|
||||
epicsThreadSleep(14.0);
|
||||
testDiag("Task 1 cleaning up");
|
||||
taskwdRemove(0);
|
||||
}
|
||||
|
||||
void testTask2(void *arg)
|
||||
{
|
||||
taskwdInsert(0, taskNotify, NULL);
|
||||
testDiag("Task suspending");
|
||||
taskwdInsert(0, taskNotify, "2");
|
||||
epicsThreadSleep(1.0);
|
||||
testDiag("Task 2 suspending");
|
||||
epicsThreadSuspendSelf();
|
||||
epicsThreadSleep(1.0);
|
||||
testDiag("Alive again");
|
||||
epicsThreadSleep(10.0);
|
||||
testDiag("Task 2 alive again");
|
||||
epicsThreadSleep(6.0);
|
||||
testDiag("Task 2 cleaning up");
|
||||
taskwdRemove(0);
|
||||
}
|
||||
|
||||
@@ -73,12 +102,12 @@ MAIN(taskwdTest)
|
||||
taskwdMonitorAdd(&monFuncs, NULL);
|
||||
taskwdAnyInsert(NULL, anyNotify, NULL);
|
||||
|
||||
epicsThreadCreate("testTask1", epicsThreadPriorityMax,
|
||||
epicsThreadCreate(baseName "1", epicsThreadPriorityMax,
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
testTask1, NULL);
|
||||
epicsThreadSleep(1.0);
|
||||
|
||||
epicsThreadCreate("testTask2", epicsThreadPriorityMax,
|
||||
epicsThreadCreate(baseName "2", epicsThreadPriorityMax,
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
testTask2, NULL);
|
||||
|
||||
@@ -91,4 +120,3 @@ MAIN(taskwdTest)
|
||||
eltc(1);
|
||||
return testDone();
|
||||
}
|
||||
|
||||
|
||||
51
test/yajlTest.plt
Normal file
51
test/yajlTest.plt
Normal file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# This is a test director for running yajl JSON parser tests.
|
||||
# The tests are actually defined in the yajlTestCases.pm module,
|
||||
# which is generated from the yajl cases by yajlTestConverter.pl
|
||||
|
||||
use strict;
|
||||
use Test::More;
|
||||
use IO::Handle;
|
||||
use IPC::Open3;
|
||||
|
||||
# Load test cases
|
||||
use lib "..";
|
||||
use yajlTestCases;
|
||||
|
||||
my @cases = cases();
|
||||
plan tests => scalar @cases;
|
||||
|
||||
# The yajl_test program reads JSON from stdin and sends a description
|
||||
# of what it got to stdout, with errors going to stderr. We merge the
|
||||
# two output streams for the purpose of checking the test results.
|
||||
my $prog = './yajl_test';
|
||||
$prog .= '.exe' if ($^O eq 'MSWin32') || ($^O eq 'cygwin');
|
||||
|
||||
foreach my $case (@cases) {
|
||||
my $name = $case->{name};
|
||||
my @opts = @{$case->{opts}};
|
||||
my @input = @{$case->{input}};
|
||||
my @gives = @{$case->{gives}};
|
||||
|
||||
my ($rx, $tx);
|
||||
my $pid = open3($tx, $rx, 0, $prog, @opts);
|
||||
|
||||
# Send the test case, then EOF
|
||||
print $tx join "\n", @input;
|
||||
close $tx;
|
||||
|
||||
# Receive the result
|
||||
my @result;
|
||||
while (!$rx->eof) {
|
||||
chomp(my $line = <$rx>);
|
||||
push @result, $line;
|
||||
}
|
||||
close $rx;
|
||||
|
||||
# Clean up the child process
|
||||
waitpid $pid, 0;
|
||||
|
||||
# Report the result of this test case
|
||||
is_deeply(\@result, \@gives, $name);
|
||||
}
|
||||
3069
test/yajlTestCases.pm
Normal file
3069
test/yajlTestCases.pm
Normal file
File diff suppressed because it is too large
Load Diff
98
test/yajlTestConverter.pl
Executable file
98
test/yajlTestConverter.pl
Executable file
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# This script converts the parsing test cases from the yajl release tree
|
||||
# into the yajlTestCases module as used by libCom/test/yajlTest.plt
|
||||
#
|
||||
# Re-do this conversion and commit after checking out a new version of yajl
|
||||
# from https://github.com/lloyd/yajl as follows:
|
||||
# $ cd <base>/src/libCom/test
|
||||
# $ perl yajlTestConverter.pl /path/to/yajl
|
||||
# The tests are saved into the file yajlTestCases.pm in the src/libCom/test
|
||||
# directory which will be read by the yajlTest.t test script.
|
||||
|
||||
use Data::Dumper;
|
||||
|
||||
my $yajl = shift @ARGV
|
||||
or die "Usage: $0 /path/to/yajl\n";
|
||||
|
||||
my @files = glob "$yajl/test/parsing/cases/*.json";
|
||||
|
||||
my $caseFile = 'yajlTestCases.pm';
|
||||
|
||||
my @cases;
|
||||
|
||||
for my $file (@files) {
|
||||
$file =~ m|/([afn][cgmp]_)?([^/]*)\.json$|;
|
||||
my $allow = $1;
|
||||
my $name = $2;
|
||||
next if $name eq '';
|
||||
|
||||
my $case = { name => $name };
|
||||
|
||||
if ($allow eq 'ac_') {
|
||||
$case->{opts} = ['-c'];
|
||||
}
|
||||
elsif ($allow eq 'ag_') {
|
||||
$case->{opts} = ['-g'];
|
||||
}
|
||||
elsif ($allow eq 'am_') {
|
||||
$case->{opts} = ['-m'];
|
||||
}
|
||||
elsif ($allow eq 'ap_') {
|
||||
$case->{opts} = ['-p'];
|
||||
}
|
||||
else {
|
||||
$case->{opts} = [];
|
||||
}
|
||||
|
||||
my $input = slurp($file);
|
||||
my @input = split "\n", $input;
|
||||
push @input, '' if $input =~ m/\n$/;
|
||||
$case->{input} = \@input;
|
||||
|
||||
my @gives = split "\n", slurp("$file.gold");
|
||||
$case->{gives} = \@gives;
|
||||
|
||||
push @cases, $case;
|
||||
}
|
||||
|
||||
# Configure Dumper() output
|
||||
$Data::Dumper::Pad = ' ';
|
||||
$Data::Dumper::Indent = 1;
|
||||
$Data::Dumper::Useqq = 1;
|
||||
$Data::Dumper::Quotekeys = 0;
|
||||
$Data::Dumper::Sortkeys = sub { return ['name', 'opts', 'input', 'gives'] };
|
||||
|
||||
my $data = Dumper(\@cases);
|
||||
|
||||
open my $out, '>', $caseFile
|
||||
or die "Can't open/create $caseFile: $@\n";
|
||||
print $out <<"EOF";
|
||||
# Parser test cases from https://github.com/lloyd/yajl
|
||||
#
|
||||
# This file is generated, DO NOT EDIT!
|
||||
#
|
||||
# See comments in yajlTestConverter.pl for instructions on
|
||||
# how to regenerate this file from the original yajl sources.
|
||||
|
||||
sub cases {
|
||||
my$data
|
||||
return \@{\$VAR1};
|
||||
}
|
||||
|
||||
1;
|
||||
EOF
|
||||
|
||||
close $out
|
||||
or die "Problem writing $caseFile: $@\n";
|
||||
|
||||
exit 0;
|
||||
|
||||
|
||||
sub slurp {
|
||||
my ($file) = @_;
|
||||
open my $in, '<', $file
|
||||
or die "Can't open file $file: $!\n";
|
||||
my $contents = do { local $/; <$in> };
|
||||
return $contents;
|
||||
}
|
||||
294
test/yajl_test.c
Normal file
294
test/yajl_test.c
Normal file
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <yajl_parse.h>
|
||||
#include <yajl_gen.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/* memory debugging routines */
|
||||
typedef struct
|
||||
{
|
||||
unsigned int numFrees;
|
||||
unsigned int numMallocs;
|
||||
/* XXX: we really need a hash table here with per-allocation
|
||||
* information */
|
||||
} yajlTestMemoryContext;
|
||||
|
||||
/* cast void * into context */
|
||||
#define TEST_CTX(vptr) ((yajlTestMemoryContext *) (vptr))
|
||||
|
||||
static void yajlTestFree(void * ctx, void * ptr)
|
||||
{
|
||||
assert(ptr != NULL);
|
||||
TEST_CTX(ctx)->numFrees++;
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void * yajlTestMalloc(void * ctx, size_t sz)
|
||||
{
|
||||
assert(sz != 0);
|
||||
TEST_CTX(ctx)->numMallocs++;
|
||||
return malloc(sz);
|
||||
}
|
||||
|
||||
static void * yajlTestRealloc(void * ctx, void * ptr, size_t sz)
|
||||
{
|
||||
if (ptr == NULL) {
|
||||
assert(sz != 0);
|
||||
TEST_CTX(ctx)->numMallocs++;
|
||||
} else if (sz == 0) {
|
||||
TEST_CTX(ctx)->numFrees++;
|
||||
}
|
||||
|
||||
return realloc(ptr, sz);
|
||||
}
|
||||
|
||||
|
||||
/* begin parsing callback routines */
|
||||
#define BUF_SIZE 2048
|
||||
|
||||
static int test_yajl_null(void *ctx)
|
||||
{
|
||||
printf("null\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int test_yajl_boolean(void * ctx, int boolVal)
|
||||
{
|
||||
printf("bool: %s\n", boolVal ? "true" : "false");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int test_yajl_integer(void *ctx, long long integerVal)
|
||||
{
|
||||
printf("integer: %lld\n", integerVal);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int test_yajl_double(void *ctx, double doubleVal)
|
||||
{
|
||||
printf("double: %g\n", doubleVal);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int test_yajl_string(void *ctx, const unsigned char * stringVal,
|
||||
size_t stringLen)
|
||||
{
|
||||
printf("string: '");
|
||||
fwrite(stringVal, 1, stringLen, stdout);
|
||||
printf("'\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int test_yajl_map_key(void *ctx, const unsigned char * stringVal,
|
||||
size_t stringLen)
|
||||
{
|
||||
char * str = (char *) malloc(stringLen + 1);
|
||||
str[stringLen] = 0;
|
||||
memcpy(str, stringVal, stringLen);
|
||||
printf("key: '%s'\n", str);
|
||||
free(str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int test_yajl_start_map(void *ctx)
|
||||
{
|
||||
printf("map open '{'\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int test_yajl_end_map(void *ctx)
|
||||
{
|
||||
printf("map close '}'\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int test_yajl_start_array(void *ctx)
|
||||
{
|
||||
printf("array open '['\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int test_yajl_end_array(void *ctx)
|
||||
{
|
||||
printf("array close ']'\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static yajl_callbacks callbacks = {
|
||||
test_yajl_null,
|
||||
test_yajl_boolean,
|
||||
test_yajl_integer,
|
||||
test_yajl_double,
|
||||
NULL,
|
||||
test_yajl_string,
|
||||
test_yajl_start_map,
|
||||
test_yajl_map_key,
|
||||
test_yajl_end_map,
|
||||
test_yajl_start_array,
|
||||
test_yajl_end_array
|
||||
};
|
||||
|
||||
static void usage(const char * progname)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s [options]\n"
|
||||
"Parse input from stdin as JSON and ouput parsing details "
|
||||
"to stdout\n"
|
||||
" -b set the read buffer size\n"
|
||||
" -c allow comments\n"
|
||||
" -g allow *g*arbage after valid JSON text\n"
|
||||
" -h print this help message\n"
|
||||
" -m allows the parser to consume multiple JSON values\n"
|
||||
" from a single string separated by whitespace\n"
|
||||
" -p partial JSON documents should not cause errors\n",
|
||||
progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char ** argv)
|
||||
{
|
||||
yajl_handle hand;
|
||||
const char * fileName = NULL;
|
||||
static unsigned char * fileData = NULL;
|
||||
FILE *file;
|
||||
size_t bufSize = BUF_SIZE;
|
||||
yajl_status stat;
|
||||
size_t rd;
|
||||
int i, j;
|
||||
|
||||
/* memory allocation debugging: allocate a structure which collects
|
||||
* statistics */
|
||||
yajlTestMemoryContext memCtx = { 0,0 };
|
||||
|
||||
/* memory allocation debugging: allocate a structure which holds
|
||||
* allocation routines */
|
||||
yajl_alloc_funcs allocFuncs = {
|
||||
yajlTestMalloc,
|
||||
yajlTestRealloc,
|
||||
yajlTestFree,
|
||||
(void *) NULL
|
||||
};
|
||||
|
||||
allocFuncs.ctx = (void *) &memCtx;
|
||||
|
||||
/* allocate the parser */
|
||||
hand = yajl_alloc(&callbacks, &allocFuncs, NULL);
|
||||
|
||||
/* check arguments. We expect exactly one! */
|
||||
for (i=1;i<argc;i++) {
|
||||
if (!strcmp("-c", argv[i])) {
|
||||
yajl_config(hand, yajl_allow_comments, 1);
|
||||
} else if (!strcmp("-b", argv[i])) {
|
||||
if (++i >= argc) usage(argv[0]);
|
||||
|
||||
/* validate integer */
|
||||
for (j=0;j<(int)strlen(argv[i]);j++) {
|
||||
if (argv[i][j] <= '9' && argv[i][j] >= '0') continue;
|
||||
fprintf(stderr, "-b requires an integer argument. '%s' "
|
||||
"is invalid\n", argv[i]);
|
||||
usage(argv[0]);
|
||||
}
|
||||
|
||||
bufSize = atoi(argv[i]);
|
||||
if (!bufSize) {
|
||||
fprintf(stderr, "%zu is an invalid buffer size\n",
|
||||
bufSize);
|
||||
}
|
||||
} else if (!strcmp("-g", argv[i])) {
|
||||
yajl_config(hand, yajl_allow_trailing_garbage, 1);
|
||||
} else if (!strcmp("-h", argv[i])) {
|
||||
usage(argv[0]);
|
||||
} else if (!strcmp("-m", argv[i])) {
|
||||
yajl_config(hand, yajl_allow_multiple_values, 1);
|
||||
} else if (!strcmp("-p", argv[i])) {
|
||||
yajl_config(hand, yajl_allow_partial_values, 1);
|
||||
} else {
|
||||
fileName = argv[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fileData = (unsigned char *) malloc(bufSize);
|
||||
|
||||
if (fileData == NULL) {
|
||||
fprintf(stderr,
|
||||
"failed to allocate read buffer of %zu bytes, exiting.",
|
||||
bufSize);
|
||||
yajl_free(hand);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
if (fileName)
|
||||
{
|
||||
file = fopen(fileName, "r");
|
||||
}
|
||||
else
|
||||
{
|
||||
file = stdin;
|
||||
}
|
||||
for (;;) {
|
||||
rd = fread((void *) fileData, 1, bufSize, file);
|
||||
|
||||
if (rd == 0) {
|
||||
if (!feof(file)) {
|
||||
fprintf(stderr, "error reading from '%s'\n", fileName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* read file data, now pass to parser */
|
||||
stat = yajl_parse(hand, fileData, rd);
|
||||
|
||||
if (stat != yajl_status_ok) break;
|
||||
}
|
||||
|
||||
stat = yajl_complete_parse(hand);
|
||||
if (stat != yajl_status_ok)
|
||||
{
|
||||
unsigned char * str = yajl_get_error(hand, 0, fileData, rd);
|
||||
fflush(stdout);
|
||||
fprintf(stderr, "%s", (char *) str);
|
||||
yajl_free_error(hand, str);
|
||||
}
|
||||
|
||||
yajl_free(hand);
|
||||
free(fileData);
|
||||
|
||||
if (fileName)
|
||||
{
|
||||
fclose(file);
|
||||
}
|
||||
/* finally, print out some memory statistics */
|
||||
|
||||
/* (lth) only print leaks here, as allocations and frees may vary depending
|
||||
* on read buffer size, causing false failures.
|
||||
*
|
||||
* printf("allocations:\t%u\n", memCtx.numMallocs);
|
||||
* printf("frees:\t\t%u\n", memCtx.numFrees);
|
||||
*/
|
||||
fflush(stderr);
|
||||
fflush(stdout);
|
||||
printf("memory leaks:\t%u\n", memCtx.numMallocs - memCtx.numFrees);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user