Files
ferrum-old/structured_data.c
2025-04-28 12:35:52 +02:00

479 lines
16 KiB
C

/**
* structured_data.c
* Implementation of structured data operations
*/
#include "structured_data.h"
#include "builtins.h" // For set_color and reset_color functions
#include <strings.h>
/**
* Create a new table with the given headers
*/
TableData* create_table(char **headers, int header_count) {
TableData *table = (TableData*)malloc(sizeof(TableData));
if (!table) {
fprintf(stderr, "lsh: allocation error in create_table\n");
return NULL;
}
// Copy headers
table->headers = (char**)malloc(header_count * sizeof(char*));
if (!table->headers) {
fprintf(stderr, "lsh: allocation error in create_table (headers)\n");
free(table);
return NULL;
}
for (int i = 0; i < header_count; i++) {
table->headers[i] = strdup(headers[i]);
}
table->header_count = header_count;
table->row_count = 0;
table->row_capacity = 10; // Initial capacity for 10 rows
// Allocate memory for rows
table->rows = (DataValue**)malloc(table->row_capacity * sizeof(DataValue*));
if (!table->rows) {
fprintf(stderr, "lsh: allocation error in create_table (rows)\n");
for (int i = 0; i < header_count; i++) {
free(table->headers[i]);
}
free(table->headers);
free(table);
return NULL;
}
return table;
}
/**
* Add a row to a table
*/
void add_table_row(TableData *table, DataValue *row) {
if (!table || !row) return;
// Resize if needed
if (table->row_count >= table->row_capacity) {
table->row_capacity *= 2;
table->rows = (DataValue**)realloc(table->rows, table->row_capacity * sizeof(DataValue*));
if (!table->rows) {
fprintf(stderr, "lsh: allocation error in add_table_row\n");
return;
}
}
// Add the row
table->rows[table->row_count] = row;
table->row_count++;
}
/**
* Free all memory associated with a table
*/
void free_table(TableData *table) {
if (!table) return;
// Free headers
for (int i = 0; i < table->header_count; i++) {
free(table->headers[i]);
}
free(table->headers);
// Free rows
for (int i = 0; i < table->row_count; i++) {
for (int j = 0; j < table->header_count; j++) {
free_data_value(&table->rows[i][j]);
}
free(table->rows[i]);
}
free(table->rows);
// Free table structure
free(table);
}
/**
* Create a copy of a DataValue
*/
DataValue copy_data_value(const DataValue *src) {
DataValue dest;
dest.type = src->type;
dest.is_highlighted = src->is_highlighted; // Make sure we preserve the highlighting flag
switch (src->type) {
case TYPE_STRING:
case TYPE_SIZE:
dest.value.str_val = strdup(src->value.str_val);
break;
case TYPE_INT:
dest.value.int_val = src->value.int_val;
break;
case TYPE_FLOAT:
dest.value.float_val = src->value.float_val;
break;
}
return dest;
}
/**
* Free a DataValue
*/
void free_data_value(DataValue *value) {
if (!value) return;
if ((value->type == TYPE_STRING || value->type == TYPE_SIZE) && value->value.str_val) {
free(value->value.str_val);
value->value.str_val = NULL;
}
}
/**
* Parse human-readable sizes (e.g., "10kb", "2.5MB") into bytes
*/
long parse_size(const char *size_str) {
char *unit;
double size = strtod(size_str, &unit);
// Skip whitespace between number and unit
while (*unit && isspace(*unit)) unit++;
// Handle case insensitive units
if (strcasecmp(unit, "kb") == 0 || strcasecmp(unit, "k") == 0) {
return (long)(size * 1024);
} else if (strcasecmp(unit, "mb") == 0 || strcasecmp(unit, "m") == 0) {
return (long)(size * 1024 * 1024);
} else if (strcasecmp(unit, "gb") == 0 || strcasecmp(unit, "g") == 0) {
return (long)(size * 1024 * 1024 * 1024);
} else if (strcasecmp(unit, "b") == 0 || *unit == '\0') {
return (long)size;
}
// Try to handle cases like "2.5 KB" with a space
char num_str[32];
char unit_str[8];
if (sscanf(size_str, "%31s %7s", num_str, unit_str) == 2) {
double num = atof(num_str);
if (strcasecmp(unit_str, "kb") == 0 || strcasecmp(unit_str, "k") == 0) {
return (long)(num * 1024);
} else if (strcasecmp(unit_str, "mb") == 0 || strcasecmp(unit_str, "m") == 0) {
return (long)(num * 1024 * 1024);
} else if (strcasecmp(unit_str, "gb") == 0 || strcasecmp(unit_str, "g") == 0) {
return (long)(num * 1024 * 1024 * 1024);
} else if (strcasecmp(unit_str, "b") == 0) {
return (long)num;
}
}
return (long)size; // Default to just the number if no unit matches
}
/**
* Extract size in bytes from a formatted size string (e.g., "10.5 KB")
*/
long extract_size_bytes(const char *size_str) {
double size_val = 0;
char unit[8] = "";
// Try the format with float + space + unit (e.g., "10.5 MB")
if (sscanf(size_str, "%lf %7s", &size_val, unit) == 2) {
if (strcasecmp(unit, "B") == 0) {
return (long)size_val;
} else if (strcasecmp(unit, "KB") == 0) {
return (long)(size_val * 1024);
} else if (strcasecmp(unit, "MB") == 0) {
return (long)(size_val * 1024 * 1024);
} else if (strcasecmp(unit, "GB") == 0) {
return (long)(size_val * 1024 * 1024 * 1024);
}
}
// Fallback to direct parsing if the format doesn't match
return parse_size(size_str);
}
/**
* Filter a table based on a condition
*/
TableData* filter_table(TableData *input, char *field, char *op, char *value) {
if (!input || !field || !op || !value) {
return NULL;
}
// Create a new table with the same headers
TableData *result = create_table(input->headers, input->header_count);
if (!result) {
return NULL;
}
// Find field index
int field_idx = -1;
for (int i = 0; i < input->header_count; i++) {
if (strcasecmp(input->headers[i], field) == 0) {
field_idx = i;
break;
}
}
if (field_idx == -1) {
fprintf(stderr, "filter_table: unknown field '%s'\n", field);
free_table(result);
return NULL;
}
// Special handling for size field - parse human-readable sizes
// Modified to handle both "size" and "Memory" columns
int is_size_field = (strcasecmp(field, "size") == 0 || strcasecmp(field, "Memory") == 0);
long value_size = is_size_field ? parse_size(value) : 0;
// Filter rows based on condition
for (int i = 0; i < input->row_count; i++) {
int include_row = 0;
// Handle based on data type
if (input->rows[i][field_idx].type == TYPE_STRING ||
input->rows[i][field_idx].type == TYPE_SIZE) {
char *row_value = input->rows[i][field_idx].value.str_val;
// Special handling for size field with values like "10.5 KB"
if (is_size_field) {
long row_size = extract_size_bytes(row_value);
// Compare sizes
if (strcmp(op, ">") == 0) {
include_row = (row_size > value_size);
} else if (strcmp(op, "<") == 0) {
include_row = (row_size < value_size);
} else if (strcmp(op, ">=") == 0) {
include_row = (row_size >= value_size);
} else if (strcmp(op, "<=") == 0) {
include_row = (row_size <= value_size);
} else if (strcmp(op, "==") == 0) {
include_row = (row_size == value_size);
}
} else {
// String comparison
int cmp = strcasecmp(row_value, value);
if (strcmp(op, ">") == 0) {
include_row = (cmp > 0);
} else if (strcmp(op, "<") == 0) {
include_row = (cmp < 0);
} else if (strcmp(op, "==") == 0) {
include_row = (cmp == 0);
} else if (strcmp(op, ">=") == 0) {
include_row = (cmp >= 0);
} else if (strcmp(op, "<=") == 0) {
include_row = (cmp <= 0);
}
}
} else if (input->rows[i][field_idx].type == TYPE_INT) {
int row_value = input->rows[i][field_idx].value.int_val;
int val = atoi(value);
if (strcmp(op, ">") == 0) {
include_row = (row_value > val);
} else if (strcmp(op, "<") == 0) {
include_row = (row_value < val);
} else if (strcmp(op, ">=") == 0) {
include_row = (row_value >= val);
} else if (strcmp(op, "<=") == 0) {
include_row = (row_value <= val);
} else if (strcmp(op, "==") == 0) {
include_row = (row_value == val);
}
} else if (input->rows[i][field_idx].type == TYPE_FLOAT) {
float row_value = input->rows[i][field_idx].value.float_val;
float val = (float)atof(value);
if (strcmp(op, ">") == 0) {
include_row = (row_value > val);
} else if (strcmp(op, "<") == 0) {
include_row = (row_value < val);
} else if (strcmp(op, ">=") == 0) {
include_row = (row_value >= val);
} else if (strcmp(op, "<=") == 0) {
include_row = (row_value <= val);
} else if (strcmp(op, "==") == 0) {
include_row = (row_value == val);
}
}
if (include_row) {
// Create a copy of the row for the filtered table
DataValue *row_copy = (DataValue*)malloc(input->header_count * sizeof(DataValue));
if (!row_copy) {
fprintf(stderr, "lsh: allocation error in filter_table\n");
free_table(result);
return NULL;
}
for (int j = 0; j < input->header_count; j++) {
row_copy[j] = copy_data_value(&input->rows[i][j]);
}
add_table_row(result, row_copy);
}
}
return result;
}
/**
* Print a table to the console with nice formatting using Unicode box characters
* Linux version using ANSI escape sequences for colors
*/
void print_table(TableData *table) {
if (!table || table->row_count == 0) {
printf("(empty table)\n");
return;
}
// Calculate column widths
int *col_widths = (int*)malloc(table->header_count * sizeof(int));
if (!col_widths) {
fprintf(stderr, "lsh: allocation error in print_table\n");
return;
}
// Initialize with header widths
for (int i = 0; i < table->header_count; i++) {
col_widths[i] = strlen(table->headers[i]);
}
// Check cell widths
for (int i = 0; i < table->row_count; i++) {
for (int j = 0; j < table->header_count; j++) {
if (table->rows[i][j].type == TYPE_STRING || table->rows[i][j].type == TYPE_SIZE) {
int len = strlen(table->rows[i][j].value.str_val);
if (len > col_widths[j]) {
col_widths[j] = len;
}
} else if (table->rows[i][j].type == TYPE_INT) {
char buf[32];
int len = snprintf(buf, sizeof(buf), "%d", table->rows[i][j].value.int_val);
if (len > col_widths[j]) {
col_widths[j] = len;
}
} else if (table->rows[i][j].type == TYPE_FLOAT) {
char buf[32];
int len = snprintf(buf, sizeof(buf), "%.2f", table->rows[i][j].value.float_val);
if (len > col_widths[j]) {
col_widths[j] = len;
}
}
}
}
// Add padding to column widths
for (int i = 0; i < table->header_count; i++) {
col_widths[i] += 4; // 2 spaces on each side
}
// Calculate total table width
int totalWidth = 1; // Start with 1 for the left border
for (int i = 0; i < table->header_count; i++) {
totalWidth += col_widths[i] + 1; // Add column width and right border
}
// Create a buffer big enough for one line of the table
char *lineBuffer = (char*)malloc(totalWidth * 4 + 1); // Unicode chars can be up to 4 bytes
if (!lineBuffer) {
fprintf(stderr, "lsh: allocation error in print_table\n");
free(col_widths);
return;
}
// Begin output with a newline
printf("\n");
// ---- Print top border ----
lineBuffer[0] = '\0';
strcat(lineBuffer, "┌"); // Top-left corner
for (int i = 0; i < table->header_count; i++) {
for (int j = 0; j < col_widths[i]; j++) {
strcat(lineBuffer, "─"); // Horizontal line
}
if (i < table->header_count - 1) {
strcat(lineBuffer, "┬"); // Top T-junction
}
}
strcat(lineBuffer, "┐"); // Top-right corner
printf("%s\n", lineBuffer);
// ---- Print header row ----
lineBuffer[0] = '\0';
strcat(lineBuffer, "│"); // Vertical line
for (int i = 0; i < table->header_count; i++) {
char cellBuffer[256];
snprintf(cellBuffer, sizeof(cellBuffer), " %-*s ", col_widths[i] - 2, table->headers[i]);
strcat(lineBuffer, cellBuffer);
strcat(lineBuffer, "│"); // Vertical line
}
printf("%s\n", lineBuffer);
// ---- Print header/data separator ----
lineBuffer[0] = '\0';
strcat(lineBuffer, "├"); // Left T-junction
for (int i = 0; i < table->header_count; i++) {
for (int j = 0; j < col_widths[i]; j++) {
strcat(lineBuffer, "─"); // Horizontal line
}
if (i < table->header_count - 1) {
strcat(lineBuffer, "┼"); // Cross junction
}
}
strcat(lineBuffer, "┤"); // Right T-junction
printf("%s\n", lineBuffer);
// Print data rows
for (int i = 0; i < table->row_count; i++) {
lineBuffer[0] = '\0';
strcat(lineBuffer, "│"); // Left border
for (int j = 0; j < table->header_count; j++) {
// Format cell content based on data type
char cellBuffer[256];
if (table->rows[i][j].type == TYPE_STRING || table->rows[i][j].type == TYPE_SIZE) {
snprintf(cellBuffer, sizeof(cellBuffer), " %-*s ", col_widths[j] - 2, table->rows[i][j].value.str_val);
} else if (table->rows[i][j].type == TYPE_INT) {
snprintf(cellBuffer, sizeof(cellBuffer), " %-*d ", col_widths[j] - 2, table->rows[i][j].value.int_val);
} else if (table->rows[i][j].type == TYPE_FLOAT) {
snprintf(cellBuffer, sizeof(cellBuffer), " %-*.2f ", col_widths[j] - 2, table->rows[i][j].value.float_val);
}
// Apply color if the cell is highlighted
if (table->rows[i][j].is_highlighted) {
strcat(lineBuffer, ANSI_COLOR_GREEN); // Green text for highlighted cells
strcat(lineBuffer, cellBuffer);
strcat(lineBuffer, ANSI_COLOR_RESET); // Reset color
} else {
strcat(lineBuffer, cellBuffer);
}
strcat(lineBuffer, "│"); // Right border of cell
}
printf("%s\n", lineBuffer);
}
// ---- Print bottom border ----
lineBuffer[0] = '\0';
strcat(lineBuffer, "└"); // Bottom-left corner
for (int i = 0; i < table->header_count; i++) {
for (int j = 0; j < col_widths[i]; j++) {
strcat(lineBuffer, "─"); // Horizontal line
}
if (i < table->header_count - 1) {
strcat(lineBuffer, "┴"); // Bottom T-junction
}
}
strcat(lineBuffer, "┘"); // Bottom-right corner
printf("%s\n\n", lineBuffer);
// Clean up
free(lineBuffer);
free(col_widths);
}