Files
CoreFreq/x86_64/corefreq-cli-extra.c
2023-07-16 03:33:25 +02:00

220 lines
5.6 KiB
C

/*
* CoreFreq (C) 2015-2023 CYRIL COURTIAT
* Contributors: Andrew Gurinovich ; CyrIng
* Licenses: GPL2
*
* Some ideas taken from https://github.com/cesanta/frozen/
* under Apache 2.0 license
*/
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include "corefreq-cli-extra.h"
#define UNUSED(expr) do { (void)(expr); } while (0)
/* --[ JSON toolkit ]-- */
struct json_state;
enum JSON_STATE {
DEFAULT, IN_ARRAY, IN_ARRAY2, IN_OBJECT, IN_OBJECT2
};
int json_writer_stdout(struct json_state * state, const char *str, size_t len)
{
UNUSED(state);
return fwrite(str, len, 1, stdout);
}
size_t get_utf8_char_len(unsigned char ch)
{
if ((ch & 0x80) == 0)
return 1;
switch (ch & 0xf0) {
case 0xf0:
return 4;
case 0xe0:
return 3;
default:
return 2;
}
}
/* This routine does NOT handle unicode finely - everything above 0x7f should
* be \u0000 encoded, but this requires us to be utf8 capable. Check the
* following tiny libraries(for future improvement):
* https://github.com/git/git/blob/master/utf8.c
* https://github.com/JeffBezanson/cutef8/blob/master/utf8.c
* https://github.com/sheredom/utf8.h
* https://github.com/JuliaStrings/utf8proc
*/
int json_escape(struct json_state *state, const char *p, size_t len)
{
size_t i, cl;
const char *hex_digits = "0123456789abcdef";
const char *specials = "btnvfr";
int n = 0;
for (i = 0; i < len; i++) {
unsigned char ch = ((unsigned char *) p)[i];
if (ch == '"' || ch == '\\') {
n += state->write(state, "\\", 1);
n += state->write(state, p + i, 1);
} else if (ch >= '\b' && ch <= '\r') {
n += state->write(state, "\\", 1);
n += state->write(state, &specials[ch - '\b'], 1);
} else if (isprint(ch)) {
n += state->write(state, p + i, 1);
} else if ((cl = get_utf8_char_len(ch)) == 1) {
n += state->write(state, "\\u00", 4);
n += state->write(state, &hex_digits[(ch >> 4) % 0xf], 1);
n += state->write(state, &hex_digits[ch % 0xf], 1);
} else {
n += state->write(state, p + i, cl);
i += cl - 1;
}
}
return n;
}
void json_start_object(struct json_state *state)
{
assert(state->depth < JSON_MAX_DEPTH);
/* TODO: assert(state->nested_state[state->depth] != IN_OBJECT); */
if (state->nested_state[state->depth] == IN_ARRAY2 ) {
state->write(state, ", {", 3);
} else {
state->write(state, "{", 1);
}
if (state->nested_state[state->depth] == IN_ARRAY) {
state->nested_state[state->depth] = IN_ARRAY2;
}
if (state->nested_state[state->depth] == IN_OBJECT) {
state->nested_state[state->depth] = IN_OBJECT2;
}
state->nested_state[++state->depth] = IN_OBJECT;
}
void json_end_object(struct json_state *state)
{
assert(state->depth >= 0);
assert(state->nested_state[state->depth] == \
IN_OBJECT || state->nested_state[state->depth] == IN_OBJECT2);
state->write(state, "}", 1);
state->nested_state[state->depth--] = DEFAULT;
}
void json_start_arr(struct json_state *state)
{
assert(state->depth < JSON_MAX_DEPTH);
if (state->nested_state[state->depth] == IN_ARRAY2) {
state->write(state, ", [", 3);
} else {
state->write(state, "[", 1);
}
if (state->nested_state[state->depth] == IN_ARRAY) {
state->nested_state[state->depth] = IN_ARRAY2;
}
if (state->nested_state[state->depth] == IN_OBJECT) {
state->nested_state[state->depth] = IN_OBJECT2;
}
state->nested_state[++state->depth] = IN_ARRAY;
}
void json_end_arr(struct json_state *state)
{
assert(state->depth >= 0);
assert(state->nested_state[state->depth] == \
IN_ARRAY || state->nested_state[state->depth] == IN_ARRAY2);
state->write(state, "]", 1);
state->nested_state[state->depth--] = DEFAULT;
}
void json_key(struct json_state *state, char * key)
{
assert(state->nested_state[state->depth] == \
IN_OBJECT || state->nested_state[state->depth] == IN_OBJECT2);
if (state->nested_state[state->depth] == IN_OBJECT2) {
state->write(state, ", ", 1);
}
state->write(state, "\"", 1);
json_escape(state, key, strlen(key));
state->write(state, "\":", 2);
}
void json_string(struct json_state *state, char * value)
{
assert(state->nested_state[state->depth] != DEFAULT);
if (state->nested_state[state->depth] == IN_ARRAY2) {
state->write(state, ", ", 2);
}
if (state->nested_state[state->depth] == IN_ARRAY) {
state->nested_state[state->depth] = IN_ARRAY2;
}
if (state->nested_state[state->depth] == IN_OBJECT) {
state->nested_state[state->depth] = IN_OBJECT2;
}
state->write(state, "\"", 1);
json_escape(state, value, strlen(value));
state->write(state, "\"", 1);
}
void json_literal(struct json_state *state, char * format, ...)
{
assert(state->nested_state[state->depth] != DEFAULT);
if (state->nested_state[state->depth] == IN_ARRAY2) {
state->write(state, ", ", 2);
}
if (state->nested_state[state->depth] == IN_ARRAY) {
state->nested_state[state->depth] = IN_ARRAY2;
}
if (state->nested_state[state->depth] == IN_OBJECT) {
state->nested_state[state->depth] = IN_OBJECT2;
}
va_list args;
va_start(args, format);
char buf[JSON_MAX_VALUE];
const int rc = vsprintf(buf, format, args);
const size_t bufsz = rc > 0 ? (size_t) rc : 0;
state->write(state, buf, bufsz);
va_end(args);
}
/* --[ UTF toolkit ]-- */
void ISO_8859_To_Unicode(unsigned char *pIn, unsigned char *pOut)
{
while ( (*pIn) != '\0' ) {
if ( (*pIn) > 0x7f ) {
switch ( (*pIn) ) {
case 0xc0 ... 0xff: /* To UTF-8 Ctrl:C3 */
(*pOut) = (*pIn) ^ 0x40;
break;
case 0xa0 ... 0xbf: /* To UTF-8 Ctrl:C2 */
(*pOut) = (*pIn) ^ 0x60;
break;
default:
(*pOut) = 0x20;
break;
}
} else {
(*pOut) = (*pIn);
}
pIn++;
pOut++;
}
(*pOut) = '\0';
}