Files
FERRUM/src/filters.c

512 lines
14 KiB
C

// this is another test
/**
* filters.c
* Implementation of filter commands (where, sort-by, etc.)
*/
#include "filters.h"
#include <string.h>
#include <strings.h> // For strcasecmp and strncasecmp
/**
* Filter a table based on a condition (e.g., where size > 10kb)
*/
TableData *lsh_where(TableData *input, char **args) {
if (!input || !args || !args[0]) {
fprintf(stderr, "lsh: where: missing arguments\n");
fprintf(stderr, "Usage: ... | where FIELD OPERATOR VALUE\n");
fprintf(stderr, " e.g.: ls | where size > 10kb\n");
return NULL;
}
// Parse filter condition (field, operator, value)
char *field = args[0];
char *op = NULL;
char *value = NULL;
// Find operator and value
if (args[1] != NULL) {
if (strcmp(args[1], ">") == 0 || strcmp(args[1], "<") == 0 ||
strcmp(args[1], "==") == 0 || strcmp(args[1], ">=") == 0 ||
strcmp(args[1], "<=") == 0) {
op = args[1];
value = args[2];
}
}
if (!op || !value) {
fprintf(stderr, "lsh: where: invalid filter condition\n");
fprintf(stderr, "Usage: ... | where FIELD OPERATOR VALUE\n");
fprintf(stderr, " e.g.: ls | where size > 10kb\n");
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, "lsh: where: unknown field '%s'\n", field);
fprintf(stderr, "Available fields: ");
for (int i = 0; i < input->header_count; i++) {
fprintf(stderr, "%s%s", i > 0 ? ", " : "", input->headers[i]);
}
fprintf(stderr, "\n");
return NULL;
}
// Create new filtered table
return filter_table(input, field, op, value);
}
/**
* Sort a table based on a column (e.g., sort-by size desc)
*/
TableData *lsh_sort_by(TableData *input, char **args) {
if (!input || !args || !args[0]) {
fprintf(stderr, "lsh: sort-by: missing arguments\n");
fprintf(stderr, "Usage: ... | sort-by FIELD [asc|desc]\n");
fprintf(stderr, " e.g.: ls | sort-by size desc\n");
return NULL;
}
// Parse sort field and direction
char *field = args[0];
int descending = 0;
// Check if direction is specified
if (args[1] != NULL) {
if (strcasecmp(args[1], "desc") == 0 ||
strcasecmp(args[1], "descending") == 0) {
descending = 1;
}
}
// 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, "lsh: sort-by: unknown field '%s'\n", field);
fprintf(stderr, "Available fields: ");
for (int i = 0; i < input->header_count; i++) {
fprintf(stderr, "%s%s", i > 0 ? ", " : "", input->headers[i]);
}
fprintf(stderr, "\n");
return NULL;
}
// Create a copy of the table
TableData *result = create_table(input->headers, input->header_count);
if (!result) {
return NULL;
}
// Copy all rows to the new table
for (int i = 0; i < input->row_count; i++) {
DataValue *row_copy =
(DataValue *)malloc(input->header_count * sizeof(DataValue));
if (!row_copy) {
fprintf(stderr, "lsh: allocation error in sort_by\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);
}
// Now sort the rows based on the specified column
// Bubble sort for simplicity (not efficient for large datasets)
for (int i = 0; i < result->row_count - 1; i++) {
for (int j = 0; j < result->row_count - i - 1; j++) {
int compare_result = 0;
// Compare based on data type
if (result->rows[j][field_idx].type == TYPE_STRING ||
result->rows[j][field_idx].type == TYPE_SIZE) {
// Handle special case for sizes (KB, MB, etc.)
if (strcasecmp(result->headers[field_idx], "Size") == 0 ||
strcasecmp(result->headers[field_idx], "Memory") == 0) {
long size1 =
extract_size_bytes(result->rows[j][field_idx].value.str_val);
long size2 =
extract_size_bytes(result->rows[j + 1][field_idx].value.str_val);
compare_result = (size1 > size2) ? 1 : (size1 < size2) ? -1 : 0;
} else {
// Regular string comparison
compare_result =
strcasecmp(result->rows[j][field_idx].value.str_val,
result->rows[j + 1][field_idx].value.str_val);
}
} else if (result->rows[j][field_idx].type == TYPE_INT) {
int val1 = result->rows[j][field_idx].value.int_val;
int val2 = result->rows[j + 1][field_idx].value.int_val;
compare_result = (val1 > val2) ? 1 : (val1 < val2) ? -1 : 0;
} else if (result->rows[j][field_idx].type == TYPE_FLOAT) {
float val1 = result->rows[j][field_idx].value.float_val;
float val2 = result->rows[j + 1][field_idx].value.float_val;
compare_result = (val1 > val2) ? 1 : (val1 < val2) ? -1 : 0;
}
// If descending, invert comparison result
if (descending) {
compare_result = -compare_result;
}
// Swap if needed
if (compare_result > 0) {
DataValue *temp = result->rows[j];
result->rows[j] = result->rows[j + 1];
result->rows[j + 1] = temp;
}
}
}
return result;
}
/**
* Select specific columns from a table
* Supports both space-separated and comma-separated field names
*/
TableData *lsh_select(TableData *input, char **args) {
if (!input || !args || !args[0]) {
fprintf(stderr, "lsh: select: missing arguments\n");
fprintf(stderr, "Usage: ... | select FIELD1 FIELD2 ...\n");
fprintf(stderr, " e.g.: ls | select Name Size\n");
return NULL;
}
// Count how many fields we have
int num_fields = 0;
int arg_index = 0;
// First pass: count fields
while (args[arg_index] != NULL) {
// Check if this argument contains commas
char *p = args[arg_index];
int commas_in_arg = 0;
while (*p) {
if (*p == ',') {
commas_in_arg++;
}
p++;
}
if (commas_in_arg > 0) {
// Add number of comma-separated fields (commas + 1)
num_fields += commas_in_arg + 1;
} else {
// Just one field in this argument
num_fields++;
}
arg_index++;
}
// Allocate array for field indices
int *field_indices = (int *)malloc(num_fields * sizeof(int));
if (!field_indices) {
fprintf(stderr, "lsh: allocation error in select\n");
return NULL;
}
// Second pass: process field names
int field_count = 0;
arg_index = 0;
while (args[arg_index] != NULL && field_count < num_fields) {
// Make a copy since we'll modify it with strtok
char *arg_copy = strdup(args[arg_index]);
if (!arg_copy) {
fprintf(stderr, "lsh: allocation error in select\n");
free(field_indices);
return NULL;
}
// Check if this argument contains commas
if (strchr(arg_copy, ',')) {
// Handle comma-separated fields
char *field_token;
char *saveptr;
field_token = strtok_r(arg_copy, ",", &saveptr);
while (field_token && field_count < num_fields) {
// Trim whitespace
while (isspace(*field_token))
field_token++;
char *end = field_token + strlen(field_token) - 1;
while (end > field_token && isspace(*end))
end--;
*(end + 1) = '\0';
// Find field index
int found = 0;
for (int i = 0; i < input->header_count; i++) {
if (strcasecmp(input->headers[i], field_token) == 0) {
field_indices[field_count++] = i;
found = 1;
break;
}
}
if (!found) {
fprintf(stderr, "lsh: select: unknown field '%s'\n", field_token);
free(field_indices);
free(arg_copy);
return NULL;
}
field_token = strtok_r(NULL, ",", &saveptr);
}
} else {
// This is a single field name (no commas)
char *field = arg_copy;
// Trim whitespace
while (isspace(*field))
field++;
char *end = field + strlen(field) - 1;
while (end > field && isspace(*end))
end--;
*(end + 1) = '\0';
// Find field index
int found = 0;
for (int i = 0; i < input->header_count; i++) {
if (strcasecmp(input->headers[i], field) == 0) {
field_indices[field_count++] = i;
found = 1;
break;
}
}
if (!found) {
fprintf(stderr, "lsh: select: unknown field '%s'\n", field);
free(arg_copy);
free(field_indices);
return NULL;
}
}
free(arg_copy);
arg_index++;
}
// Create new headers for result table
char **new_headers = (char **)malloc(field_count * sizeof(char *));
if (!new_headers) {
fprintf(stderr, "lsh: allocation error in select\n");
free(field_indices);
return NULL;
}
for (int i = 0; i < field_count; i++) {
new_headers[i] = strdup(input->headers[field_indices[i]]);
}
// Create result table with new headers
TableData *result = create_table(new_headers, field_count);
// Free temporary header array
for (int i = 0; i < field_count; i++) {
free(new_headers[i]);
}
free(new_headers);
if (!result) {
free(field_indices);
return NULL;
}
// Copy rows with only the selected columns
for (int i = 0; i < input->row_count; i++) {
DataValue *row = (DataValue *)malloc(field_count * sizeof(DataValue));
if (!row) {
fprintf(stderr, "lsh: allocation error in select\n");
free_table(result);
free(field_indices);
return NULL;
}
for (int j = 0; j < field_count; j++) {
row[j] = copy_data_value(&input->rows[i][field_indices[j]]);
}
add_table_row(result, row);
}
free(field_indices);
return result;
}
/**
* Case-insensitive substring search (strcasestr equivalent for Windows)
*/
char *my_strcasestr(const char *haystack, const char *needle) {
if (!haystack || !needle)
return NULL;
size_t needle_len = strlen(needle);
if (needle_len == 0)
return (char *)haystack;
size_t haystack_len = strlen(haystack);
if (haystack_len < needle_len)
return NULL;
for (size_t i = 0; i <= haystack_len - needle_len; i++) {
if (strncasecmp(haystack + i, needle, needle_len) == 0) {
return (char *)(haystack + i);
}
}
return NULL;
}
/**
* Filter rows where a column contains a substring
*/
TableData *lsh_contains(TableData *input, char **args) {
if (!input || !args || !args[0] || !args[1]) {
fprintf(stderr, "lsh: contains: missing arguments\n");
fprintf(stderr, "Usage: ... | contains FIELD VALUE\n");
fprintf(stderr, " e.g.: ls | contains Name .exe\n");
return NULL;
}
// Parse arguments
char *field = args[0];
char *value = args[1];
// 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, "lsh: contains: unknown field '%s'\n", field);
fprintf(stderr, "Available fields: ");
for (int i = 0; i < input->header_count; i++) {
fprintf(stderr, "%s%s", i > 0 ? ", " : "", input->headers[i]);
}
fprintf(stderr, "\n");
return NULL;
}
// Create result table with same headers
TableData *result = create_table(input->headers, input->header_count);
if (!result) {
return NULL;
}
// Filter rows based on whether the specified column contains the substring
for (int i = 0; i < input->row_count; i++) {
int include_row = 0;
// Only string fields can be checked with contains
if (input->rows[i][field_idx].type == TYPE_STRING ||
input->rows[i][field_idx].type == TYPE_SIZE) {
char *cell_value = input->rows[i][field_idx].value.str_val;
// Case-insensitive substring check
char *found = my_strcasestr(cell_value, value);
include_row = (found != NULL);
}
if (include_row) {
// Create a copy of the row for the result table
DataValue *row_copy =
(DataValue *)malloc(input->header_count * sizeof(DataValue));
if (!row_copy) {
fprintf(stderr, "lsh: allocation error in contains\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;
}
/**
* Limit the number of rows displayed
*/
TableData *lsh_limit(TableData *input, char **args) {
if (!input || !args || !args[0]) {
fprintf(stderr, "lsh: limit: missing arguments\n");
fprintf(stderr, "Usage: ... | limit N\n");
fprintf(stderr, " e.g.: ls | sort-by Size desc | limit 5\n");
return NULL;
}
// Parse limit
int limit = atoi(args[0]);
if (limit <= 0) {
fprintf(stderr,
"lsh: limit: invalid limit '%s', must be a positive number\n",
args[0]);
return NULL;
}
// Create result table with same headers
TableData *result = create_table(input->headers, input->header_count);
if (!result) {
return NULL;
}
// Copy only the specified number of rows
int rows_to_copy = (limit < input->row_count) ? limit : input->row_count;
for (int i = 0; i < rows_to_copy; i++) {
DataValue *row_copy =
(DataValue *)malloc(input->header_count * sizeof(DataValue));
if (!row_copy) {
fprintf(stderr, "lsh: allocation error in limit\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;
}
// Define the filter arrays here
char *filter_str[] = {"where", "sort-by", "select", "contains", "limit"};
TableData *(*filter_func[])(TableData *, char **) = {
&lsh_where, &lsh_sort_by, &lsh_select, &lsh_contains, &lsh_limit};
int filter_count = sizeof(filter_str) / sizeof(char *);