Skip to content

Commit

Permalink
Issue #189: Eliminate use of MC_ERROR from json_util.c, and add a jso…
Browse files Browse the repository at this point in the history
…n_util_get_last_err() function to retrieve the error for those callers that care about it.

Add tests and descriptions for the functions in json_util.c
  • Loading branch information
hawicz committed Jun 26, 2016
1 parent 5958917 commit 29ef73f
Show file tree
Hide file tree
Showing 9 changed files with 351 additions and 16 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -47,6 +47,7 @@
/tests/test_printbuf
/tests/test_set_serializer
/tests/test_compare
/tests/test_util_file
/tests/*.vg.out
/tests/*.log
/tests/*.trs
Expand Down
42 changes: 27 additions & 15 deletions json_util.c
Expand Up @@ -12,6 +12,7 @@
#include "config.h"
#undef realloc

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
Expand Down Expand Up @@ -63,15 +64,26 @@
static int sscanf_is_broken = 0;
static int sscanf_is_broken_testdone = 0;
static void sscanf_is_broken_test(void);
static void _set_last_err(const char *err_fmt, ...);

static char _last_err[256] = "";

const char *json_util_get_last_err()
{
if (_last_err[0] == '\0')
return NULL;
return _last_err;
}

static void _set_last_err(const char *err_fmt, ...)
{
va_list ap;
va_start(ap, err_fmt);
// Ignore (attempted) overruns from snprintf
(void)vsnprintf(_last_err, sizeof(_last_err), err_fmt, ap);
va_end(ap);
}

/*
* Create a JSON object from already opened file descriptor.
*
* This function can be helpful, when you opened the file already,
* e.g. when you have a temp file.
* Note, that the fd must be readable at the actual position, i.e.
* use lseek(fd, 0, SEEK_SET) before.
*/
struct json_object* json_object_from_fd(int fd)
{
struct printbuf *pb;
Expand All @@ -80,14 +92,14 @@ struct json_object* json_object_from_fd(int fd)
int ret;

if(!(pb = printbuf_new())) {
MC_ERROR("json_object_from_file: printbuf_new failed\n");
_set_last_err("json_object_from_file: printbuf_new failed\n");
return NULL;
}
while((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) {
printbuf_memappend(pb, buf, ret);
}
if(ret < 0) {
MC_ERROR("json_object_from_fd: error reading fd %d: %s\n", fd, strerror(errno));
_set_last_err("json_object_from_fd: error reading fd %d: %s\n", fd, strerror(errno));
printbuf_free(pb);
return NULL;
}
Expand All @@ -102,7 +114,7 @@ struct json_object* json_object_from_file(const char *filename)
int fd;

if((fd = open(filename, O_RDONLY)) < 0) {
MC_ERROR("json_object_from_file: error opening file %s: %s\n",
_set_last_err("json_object_from_file: error opening file %s: %s\n",
filename, strerror(errno));
return NULL;
}
Expand All @@ -120,12 +132,12 @@ int json_object_to_file_ext(const char *filename, struct json_object *obj, int f
unsigned int wpos, wsize;

if(!obj) {
MC_ERROR("json_object_to_file: object is null\n");
_set_last_err("json_object_to_file: object is null\n");
return -1;
}

if((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) {
MC_ERROR("json_object_to_file: error opening file %s: %s\n",
_set_last_err("json_object_to_file: error opening file %s: %s\n",
filename, strerror(errno));
return -1;
}
Expand All @@ -140,7 +152,7 @@ int json_object_to_file_ext(const char *filename, struct json_object *obj, int f
while(wpos < wsize) {
if((ret = write(fd, json_str + wpos, wsize-wpos)) < 0) {
close(fd);
MC_ERROR("json_object_to_file: error writing file %s: %s\n",
_set_last_err("json_object_to_file: error writing file %s: %s\n",
filename, strerror(errno));
return -1;
}
Expand Down Expand Up @@ -306,7 +318,7 @@ const char *json_type_to_name(enum json_type o_type)
int o_type_int = (int)o_type;
if (o_type_int < 0 || o_type_int >= (int)NELEM(json_type_name))
{
MC_ERROR("json_type_to_name: type %d is out of range [0,%d]\n", o_type, NELEM(json_type_name));
_set_last_err("json_type_to_name: type %d is out of range [0,%d]\n", o_type, NELEM(json_type_name));
return NULL;
}
return json_type_name[o_type];
Expand Down
40 changes: 39 additions & 1 deletion json_util.h
Expand Up @@ -30,14 +30,52 @@ extern "C" {
#define JSON_FILE_BUF_SIZE 4096

/* utility functions */
/**
* Read the full contents of the given file, then convert it to a
* json_object using json_tokener_parse().
*
* Returns -1 if something fails. See json_util_get_last_err() for details.
*/
extern struct json_object* json_object_from_file(const char *filename);

/**
* Create a JSON object from already opened file descriptor.
*
* This function can be helpful, when you opened the file already,
* e.g. when you have a temp file.
* Note, that the fd must be readable at the actual position, i.e.
* use lseek(fd, 0, SEEK_SET) before.
*
* Returns -1 if something fails. See json_util_get_last_err() for details.
*/
extern struct json_object* json_object_from_fd(int fd);

/**
* Equivalent to:
* json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN);
*
* Returns -1 if something fails. See json_util_get_last_err() for details.
*/
extern int json_object_to_file(const char *filename, struct json_object *obj);

/**
* Open and truncate the given file, creating it if necessary, then
* convert the json_object to a string and write it to the file.
*
* Returns -1 if something fails. See json_util_get_last_err() for details.
*/
extern int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags);

/**
* Return the last error from json_object_to_file{,_ext} or
* json_object_from_{file,fd}, or NULL if there is none.
*/
const char *json_util_get_last_err(void);


extern int json_parse_int64(const char *buf, int64_t *retval);
extern int json_parse_double(const char *buf, double *retval);


/**
* Return a string describing the type of the object.
* e.g. "int", or "object", etc...
Expand Down
3 changes: 3 additions & 0 deletions tests/Makefile.am
Expand Up @@ -4,6 +4,7 @@ LDADD= $(LIBJSON_LA)
LIBJSON_LA=$(top_builddir)/libjson-c.la

TESTS=
TESTS+= test_util_file.test
TESTS+= test1.test
TESTS+= test2.test
TESTS+= test4.test
Expand Down Expand Up @@ -32,6 +33,8 @@ check_PROGRAMS += test2Formatted
test2Formatted_SOURCES = test2.c parse_flags.c
test2Formatted_CPPFLAGS = -DTEST_FORMATTED

test_util_file_SOURCES = test_util_file.c strerror_override.c

EXTRA_DIST=
EXTRA_DIST += $(TESTS)

Expand Down
90 changes: 90 additions & 0 deletions tests/strerror_override.c
@@ -0,0 +1,90 @@
#include <errno.h>

/*
* Override strerror() to get consistent output across platforms.
*/

static struct {
int errno_value;
const char *errno_str;
} errno_list[] = {
#define STRINGIFY(x) #x
#define ENTRY(x) {x, &STRINGIFY(undef_ ## x)[6]}
ENTRY(EPERM),
ENTRY(ENOENT),
ENTRY(ESRCH),
ENTRY(EINTR),
ENTRY(EIO),
ENTRY(ENXIO),
ENTRY(E2BIG),
ENTRY(ENOEXEC),
ENTRY(EBADF),
ENTRY(ECHILD),
ENTRY(EDEADLK),
ENTRY(ENOMEM),
ENTRY(EACCES),
ENTRY(EFAULT),
ENTRY(ENOTBLK),
ENTRY(EBUSY),
ENTRY(EEXIST),
ENTRY(EXDEV),
ENTRY(ENODEV),
ENTRY(ENOTDIR),
ENTRY(EISDIR),
ENTRY(EINVAL),
ENTRY(ENFILE),
ENTRY(EMFILE),
ENTRY(ENOTTY),
ENTRY(ETXTBSY),
ENTRY(EFBIG),
ENTRY(ENOSPC),
ENTRY(ESPIPE),
ENTRY(EROFS),
ENTRY(EMLINK),
ENTRY(EPIPE),
ENTRY(EDOM),
ENTRY(ERANGE),
ENTRY(EAGAIN),
{ 0, (char *)0 }
};

#define PREFIX "ERRNO="
static char errno_buf[128] = PREFIX;
const char *strerror(int errno_in)
{
int start_idx;
char digbuf[20];
int ii, jj;

// Avoid standard functions, so we don't need to include any
// headers, or guess at signatures.

for (ii = 0; errno_list[ii].errno_str != (char *)0; ii++)
{
const char *errno_str = errno_list[ii].errno_str;
if (errno_list[ii].errno_value != errno_in)
continue;

for (start_idx = sizeof(PREFIX) - 1, jj = 0; errno_str[jj] != '\0'; jj++, start_idx++)
{
errno_buf[start_idx] = errno_str[jj];
}
errno_buf[start_idx] = '\0';
return errno_buf;
}

// It's not one of the known errno values, return the numeric value.
for (ii = 0; errno_in > 10; errno_in /= 10, ii++)
{
digbuf[ii] = "0123456789"[(errno_in % 10)];
}
digbuf[ii] = "0123456789"[(errno_in % 10)];

// Reverse the digits
for (start_idx = sizeof(PREFIX) - 1 ; ii >= 0; ii--, start_idx++)
{
errno_buf[start_idx] = digbuf[ii];
}
return errno_buf;
}

0 comments on commit 29ef73f

Please sign in to comment.