mirror of
https://github.com/NationalSecurityAgency/maat.git
synced 2026-11-08 17:39:33 -10:00
359 lines
8.8 KiB
C
359 lines
8.8 KiB
C
/*
|
|
* Copyright 2023 United States Government
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
|
|
#include <util/csv.h>
|
|
#include <util/util.h>
|
|
|
|
/**
|
|
* Read a specific line from a CSV file. This line is
|
|
* identified using a key value, which is stored in
|
|
* a specific column (zero indexed). Gives the full
|
|
* line in the out_buf.
|
|
*
|
|
* Returns 0 on success, 1 if the key is not found in
|
|
* the file, and -1 otherwise.
|
|
*/
|
|
int read_line_csv(const char *filename, const char *key,
|
|
int key_col, size_t max_line_len, char **out_buf)
|
|
{
|
|
int i = 0;
|
|
int ret = 1;
|
|
char *tmp = NULL;
|
|
char *tok;
|
|
char *out;
|
|
char *csv_line;
|
|
FILE *fp;
|
|
|
|
if (out_buf == NULL) {
|
|
dlog(1, "Given null argument for out parameter\n");
|
|
return -1;
|
|
}
|
|
|
|
if (max_line_len == 0) {
|
|
dlog(1, "Given 0 size for the length of the line from the CSV file\n");
|
|
return -1;
|
|
}
|
|
|
|
if (SIZE_MAX > INT_MAX && max_line_len > INT_MAX) {
|
|
dlog(0, "Given size %zu which would overflow int buffer of max value %d\n",
|
|
max_line_len, INT_MAX);
|
|
return -1;
|
|
}
|
|
|
|
csv_line = calloc(max_line_len, 1);
|
|
if (csv_line == NULL) {
|
|
dlog(0,
|
|
"Unable to allocate buffer to store csv from file\n");
|
|
return -1;
|
|
}
|
|
|
|
fp = fopen(filename, "r");
|
|
if (fp == NULL) {
|
|
dlog(1, "Unable to open CSV file: %s\n", filename);
|
|
free(csv_line);
|
|
return -1;
|
|
}
|
|
|
|
while(1) {
|
|
/* The previous bounds checking should ensure max_line_len can be
|
|
* represented as an int */
|
|
out = fgets(csv_line, (int) max_line_len, fp);
|
|
if (out == NULL) {
|
|
ret = -1;
|
|
break;
|
|
} else {
|
|
tmp = calloc(strlen(csv_line) + 1, 1);
|
|
if(tmp == NULL) {
|
|
dlog(0, "Unable to allocate return buffer\n");
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
strcpy(tmp, csv_line);
|
|
|
|
tok = strtok(csv_line, ",");
|
|
while(tok != NULL) {
|
|
if(i == key_col && strcmp(key, tok) == 0) {
|
|
*out_buf = tmp;
|
|
ret = 0;
|
|
goto cleanup;
|
|
} else if (i > key_col) {
|
|
break;
|
|
}
|
|
|
|
i += 1;
|
|
tok = strtok(NULL, ",");
|
|
}
|
|
|
|
free(tmp);
|
|
i = 0;
|
|
memset(csv_line, 0, max_line_len);
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
fclose(fp);
|
|
free(csv_line);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Read a specific value from a column in a line from a
|
|
* CSV file. The value given back is stored in the column
|
|
* identified in val_col (zero based). Gives the desired
|
|
* value in the out_buf.
|
|
*
|
|
* Returns 0 on success, 1 if the column does not exist
|
|
* in the line, and -1 otherwise.
|
|
*/
|
|
int get_col_from_csv_line(const char *str, int val_col,
|
|
size_t max_line_len, char **out_buf)
|
|
{
|
|
int i = 0;
|
|
char *cpy;
|
|
char *tmp;
|
|
char *tok;
|
|
|
|
if(out_buf == NULL) {
|
|
dlog(1, "Given null string return parameter\n");
|
|
return -1;
|
|
}
|
|
|
|
if (max_line_len == 0) {
|
|
dlog(1, "Given 0 size for the length of the line from the CSV file\n");
|
|
return -1;
|
|
}
|
|
|
|
cpy = calloc(max_line_len, 1);
|
|
if(cpy == NULL) {
|
|
dlog(0,
|
|
"Unable to allocate buffer to hold CSV line info\n");
|
|
return -1;
|
|
}
|
|
|
|
strncpy(cpy, str, max_line_len - 1);
|
|
|
|
tok = strtok(cpy, ",");
|
|
while(tok != NULL) {
|
|
if(i == val_col) {
|
|
tmp = calloc(strlen(tok) + 1, 1);
|
|
if(tmp == NULL) {
|
|
dlog(0, "Unable to allocate buffer for token\n");
|
|
free(cpy);
|
|
return -1;
|
|
}
|
|
|
|
memcpy(tmp, tok, strlen(tok));
|
|
|
|
*out_buf = tmp;
|
|
free(cpy);
|
|
return 0;
|
|
}
|
|
|
|
i += 1;
|
|
tok = strtok(NULL, ",");
|
|
}
|
|
|
|
free(cpy);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Read a specific value from a CSV file. The line the
|
|
* value is pulled from is identified using a key value,
|
|
* which is stored in a specific column (zero indexed).
|
|
* The value given back is stored in the column identified
|
|
* in val_col (zero based). Gives the full line in the out_buf.
|
|
*
|
|
* Returns 0 on success, 1 if the key is not found in
|
|
* the file, 2 if the val_col is not in the CSV, and
|
|
* -1 otherwise.
|
|
*/
|
|
int read_val_csv(const char *filename, const char *key,
|
|
int key_col, int val_col, size_t max_line_len,
|
|
char **out_buf)
|
|
{
|
|
int ret;
|
|
char *csv_line = NULL;
|
|
char *tmp = NULL;
|
|
|
|
if (out_buf == NULL) {
|
|
dlog(1, "Given NULL out buffer\n");
|
|
return -1;
|
|
}
|
|
|
|
ret = read_line_csv(filename, key, key_col, max_line_len,
|
|
&csv_line);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
ret = get_col_from_csv_line(csv_line, val_col, max_line_len,
|
|
&tmp);
|
|
free(csv_line);
|
|
if (ret == 0) {
|
|
*out_buf = tmp;
|
|
} else if (ret == 1) {
|
|
ret = 2;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int append_line_to_file(const char *filename, const char *line)
|
|
{
|
|
int ret;
|
|
FILE *fp;
|
|
|
|
fp = fopen(filename, "a+");
|
|
if (fp == NULL) {
|
|
dlog(1, "Unable to open the file %s for appending\n", filename);
|
|
return -1;
|
|
}
|
|
|
|
ret = fprintf(fp, "%s\n", line);
|
|
fclose(fp);
|
|
if (ret < 0) {
|
|
dlog(1, "Unable to write to file %s\n", filename);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Append a set of values given to this function into a CSV
|
|
* file. This function will not check that the number of columns
|
|
* is correct, so the caller should ensure that the correct
|
|
* number of columns are being written to.
|
|
*
|
|
* Returns 0 on a successful write and -1 otherwise.
|
|
*/
|
|
int append_toks_to_csv(const char *filename, size_t num_strings, ...)
|
|
{
|
|
int ret;
|
|
size_t len;
|
|
size_t i;
|
|
size_t j;
|
|
char *str;
|
|
char **strs;
|
|
va_list args;
|
|
|
|
strs = calloc(num_strings, sizeof(char *));
|
|
if (strs == NULL) {
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
va_start(args, num_strings);
|
|
|
|
for(i = 0; i < num_strings; i++) {
|
|
/* The copying is likely not required, but va_* functions are tricky
|
|
and I do NOT want to be the victim of weird portability issues */
|
|
str = va_arg(args, char *);
|
|
|
|
if (str == NULL) {
|
|
ret = -1;
|
|
goto inner_alloc_err;
|
|
}
|
|
|
|
len = strlen(str);
|
|
strs[i] = calloc(len + 1, sizeof(char));
|
|
|
|
if (strs[i] == NULL) {
|
|
ret = -1;
|
|
goto inner_alloc_err;
|
|
}
|
|
|
|
memcpy(strs[i], str, len);
|
|
}
|
|
|
|
va_end(args);
|
|
|
|
/*
|
|
* This delegation reduces code duplication at the expense of speed.
|
|
* I think this is a worthwhile tradeoff
|
|
*/
|
|
ret = append_tok_list_to_csv(filename, num_strings,
|
|
(const char **)strs);
|
|
|
|
inner_alloc_err:
|
|
for(j = 0; j < i; j++) {
|
|
free(strs[j]);
|
|
}
|
|
|
|
free(strs);
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Append a set of values given to this function into a CSV
|
|
* file. This function will not check that the number of columns
|
|
* is correct, so the caller should ensure that the correct
|
|
* number of columns are being written to.
|
|
*
|
|
* Returns 0 on success and -1 otherwise.
|
|
*/
|
|
int append_tok_list_to_csv(const char *filename, size_t num_strings,
|
|
const char *strs[])
|
|
{
|
|
size_t i;
|
|
int ret;
|
|
size_t size = 0;
|
|
char *tmp;
|
|
const char *str;
|
|
char *csv_line = NULL;
|
|
|
|
for(i = 0; i < num_strings; i++) {
|
|
str = strs[i];
|
|
|
|
// Detect overflow in the size
|
|
if(size > (size + strlen(str))) {
|
|
dlog(1, "Full length of the combination of the strings would cause an overflow in realloc\n");
|
|
free(csv_line);
|
|
return -1;
|
|
}
|
|
|
|
tmp = realloc(csv_line, size + strlen(str) + 1);
|
|
if(tmp == NULL) {
|
|
dlog(0, "Unable to allocate a new buffer for the csv line\n");
|
|
free(csv_line);
|
|
return -1;
|
|
}
|
|
csv_line = tmp;
|
|
|
|
memcpy(csv_line + size, str, strlen(str));
|
|
size += strlen(str);
|
|
|
|
if(i + 1 == num_strings) {
|
|
csv_line[size] = '\0';
|
|
} else {
|
|
csv_line[size] = ',';
|
|
}
|
|
size += 1;
|
|
}
|
|
|
|
ret = append_line_to_file(filename, csv_line);
|
|
free(csv_line);
|
|
|
|
return ret;
|
|
}
|