Upon further use and review I believe that it is better to treat all input sources as a single buffer.
This allows for all of the parameters to work closer to what feels like normal logic.
If I want to get the total lines for all listed files, then I should get that.
If I want to get the total lines for each listed file, then I can call this program once for each file to get that.
I am working on Basic List first but this will be repeated for all of the other FSS read projects as well (likely in a single commit).
One of the downsides of this is that it exposes a current design limitation where the max buffer size is more likely to be reached.
Future work will most likely address this in some manner.
}
}
+ f_file_t file = f_file_t_initialize;
fss_basic_read_depths_t depths = fss_basic_read_depths_t_initialize;
-
f_fss_delimits_t delimits = f_fss_delimits_t_initialize;
- f_array_length_t original_size = data->quantity.total;
-
if (F_status_is_error_not(status)) {
- status = fss_basic_read_main_preprocess_depth(arguments, *data, &depths);
+ status = fss_basic_read_depth_process(arguments, *data, &depths);
if (F_status_is_error(status)) {
- fll_error_print(data->error, F_status_set_fine(status), "fss_basic_read_main_preprocess_depth", F_true);
+ fll_error_print(data->error, F_status_set_fine(status), "fss_basic_read_depth_process", F_true);
}
}
status = F_status_set_error(F_parameter);
}
- if (F_status_is_error_not(status) && data->process_pipe) {
- f_file_t file = f_file_t_initialize;
+ // Provide a range designating where within the buffer a particular file exists, using a statically allocated array. @fixme make this a structure with
+ fss_basic_read_file_t files_array[data->remaining.used + 1];
+ fss_basic_read_files_t files = fss_basic_read_files_t_initialize;
+
+ if (F_status_is_error_not(status)) {
+ files.array = files_array;
+ files.size += data->remaining.used;
+ for (f_array_length_t i = 0; i < files.used; ++i) {
+ f_macro_string_range_t_clear(files.array[i].range);
+ } // for
+ }
+
+ if (F_status_is_error_not(status) && data->process_pipe) {
file.id = f_type_descriptor_input;
+ file.stream = f_type_input;
+
+ files.array[0].name = 0;
+ files.array[0].range.start = 0;
- status = f_file_read(file, &data->buffer);
+ status = f_file_stream_read(file, 1, &data->buffer);
if (F_status_is_error(status)) {
- fll_error_file_print(data->error, F_status_set_fine(status), "f_file_read", F_true, "-", "read", fll_error_file_type_pipe);
+ fll_error_file_print(data->error, F_status_set_fine(status), "f_file_stream_read", F_true, "-", "read", fll_error_file_type_pipe);
}
- else {
- status = fss_basic_read_main_process_file(arguments, data, "-", depths, &delimits);
+ else if (data->buffer.used) {
+ files.array[0].range.stop = data->buffer.used - 1;
+
+ // This standard is newline sensitive, when appending files to the buffer if the file lacks a final newline then this could break the format for files appended thereafter.
+ // Guarantee that a newline exists at the end of the buffer.
+ status = f_string_append_assure(f_string_eol_s, 1, &data->buffer);
if (F_status_is_error(status)) {
- fll_error_file_print(data->error, F_status_set_fine(status), "fss_basic_read_main_process_file", F_true, "-", "read", fll_error_file_type_pipe);
+ fll_error_file_print(data->error, F_status_set_fine(status), "f_string_append_assure", F_true, "-", "read", fll_error_file_type_pipe);
}
}
-
- // Clear buffers before continuing.
- f_macro_fss_contents_t_delete_simple(data->contents);
- f_macro_fss_objects_t_delete_simple(data->objects);
- f_macro_string_dynamic_t_delete_simple(data->buffer);
+ else {
+ files.array[0].range.start = 1;
+ }
}
if (F_status_is_error_not(status) && data->remaining.used > 0) {
for (f_array_length_t i = 0; i < data->remaining.used; i++) {
- f_file_t file = f_file_t_initialize;
- status = f_file_open(arguments.argv[data->remaining.array[i]], 0, &file);
+ files.array[files.used].range.start = data->buffer.used;
+ file.stream = 0;
+ file.id = -1;
- data->quantity.total = original_size;
+ status = f_file_stream_open(arguments.argv[data->remaining.array[i]], 0, &file);
if (F_status_is_error(status)) {
- fll_error_file_print(data->error, F_status_set_fine(status), "f_file_open", F_true, arguments.argv[data->remaining.array[i]], "open", fll_error_file_type_file);
- break;
- }
-
- if (!data->quantity.total) {
- status = f_file_size_by_id(file.id, &data->quantity.total);
-
- if (F_status_is_error(status)) {
- fll_error_file_print(data->error, F_status_set_fine(status), "f_file_size_by_id", F_true, arguments.argv[data->remaining.array[i]], "read", fll_error_file_type_file);
-
- f_file_stream_close(F_true, &file);
- break;
- }
+ fll_error_file_print(data->error, F_status_set_fine(status), "f_file_stream_open", F_true, arguments.argv[data->remaining.array[i]], "open", fll_error_file_type_file);
- // Skip past empty files.
- if (!data->quantity.total) {
- if (data->parameters[fss_basic_read_parameter_total].result == f_console_result_found) {
- fprintf(data->output.stream, "0%c", f_string_eol_s[0]);
- }
-
- f_file_stream_close(F_true, &file);
- continue;
- }
+ f_file_stream_close(F_true, &file);
+ break;
}
- status = f_file_read_until(file, data->quantity.total, &data->buffer);
+ status = f_file_stream_read(file, 1, &data->buffer);
f_file_stream_close(F_true, &file);
if (F_status_is_error(status)) {
- fll_error_file_print(data->error, F_status_set_fine(status), "f_file_read_until", F_true, arguments.argv[data->remaining.array[i]], "read", fll_error_file_type_file);
+ fll_error_file_print(data->error, F_status_set_fine(status), "f_file_stream_read", F_true, arguments.argv[data->remaining.array[i]], "read", fll_error_file_type_file);
+
break;
}
+ else if (data->buffer.used > files.array[files.used].range.start) {
+ files.array[files.used].name = arguments.argv[data->remaining.array[i]];
+ files.array[files.used++].range.stop = data->buffer.used - 1;
- status = fss_basic_read_main_process_file(arguments, data, arguments.argv[data->remaining.array[i]], depths, &delimits);
+ // This standard is newline sensitive, when appending files to the buffer if the file lacks a final newline then this could break the format for files appended thereafter.
+ // Guarantee that a newline exists at the end of the buffer.
+ status = f_string_append_assure(f_string_eol_s, 1, &data->buffer);
- if (F_status_is_error(status)) {
- fll_error_file_print(data->error, F_status_set_fine(status), "fss_basic_read_main_process_file", F_true, arguments.argv[data->remaining.array[i]], "read", fll_error_file_type_file);
- break;
+ if (F_status_is_error(status)) {
+ fll_error_file_print(data->error, F_status_set_fine(status), "f_string_append_assure", F_true, "-", "read", fll_error_file_type_pipe);
+ }
+ }
+ else {
+ files.array[files.used].range.start = 1;
}
-
- // Clear buffers before repeating the loop.
- f_macro_fss_contents_t_delete_simple(data->contents);
- f_macro_fss_objects_t_delete_simple(data->objects);
- f_macro_string_dynamic_t_delete_simple(data->buffer);
} // for
+ }
- if (F_status_is_error(status)) {
- f_macro_fss_contents_t_delete_simple(data->contents);
- f_macro_fss_objects_t_delete_simple(data->objects);
- f_macro_string_dynamic_t_delete_simple(data->buffer);
- }
+ if (F_status_is_error_not(status)) {
+ status = fss_basic_read_process(arguments, files, depths, data, &delimits);
}
+ f_macro_fss_contents_t_delete_simple(data->contents);
+ f_macro_fss_objects_t_delete_simple(data->objects);
+ f_macro_string_dynamic_t_delete_simple(data->buffer);
+
fss_basic_read_macro_depths_t_delete_simple(depths);
f_macro_fss_delimits_t_delete_simple(delimits);
}
f_string_dynamic_t buffer;
f_fss_objects_t objects;
f_fss_contents_t contents;
- f_string_quantity_t quantity;
uint8_t delimit_mode;
f_array_length_t delimit_depth;
f_string_dynamic_t_initialize, \
f_fss_objects_t_initialize, \
f_fss_contents_t_initialize, \
- f_string_quantity_t_initialize, \
fss_basic_read_delimit_mode_all, \
0, \
f_color_context_t_initialize, \
extern "C" {
#endif
-#ifndef _di_fss_basic_read_main_preprocess_depth_
- f_status_t fss_basic_read_main_preprocess_depth(const f_console_arguments_t arguments, const fss_basic_read_data_t data, fss_basic_read_depths_t *depths) {
+#ifndef _di_fss_basic_read_depth_process_
+ f_status_t fss_basic_read_depth_process(const f_console_arguments_t arguments, const fss_basic_read_data_t data, fss_basic_read_depths_t *depths) {
+
f_status_t status = F_none;
{
if (F_status_is_error(status)) {
f_color_print(data.error.to.stream, data.context.set.error, "%sUnable to allocate memory.%c", fll_error_print_error, f_string_eol_s[0]);
+
return status;
}
f_array_length_t position_name = 0;
for (f_array_length_t i = 0; i < depths->used; i++) {
+
depths->array[i].depth = 0;
depths->array[i].index_at = 0;
depths->array[i].index_name = 0;
if (F_status_is_error(status)) {
fll_error_parameter_integer_print(data.error, F_status_set_fine(status), "fl_conversion_string_to_number_unsigned", F_true, fss_basic_read_long_at, arguments.argv[depths->array[i].index_at]);
+
return status;
}
} // for
return F_none;
}
-#endif // _di_fss_basic_read_main_preprocess_depth_
+#endif // _di_fss_basic_read_depth_process_
+
+#ifndef _di_fss_basic_read_file_identify_
+ f_string_t fss_basic_read_file_identify(const f_array_length_t at, const fss_basic_read_files_t files) {
+
+ for (f_array_length_t i = 0; i < files.used; ++i) {
+
+ if (at >= files.array[i].range.start && at <= files.array[i].range.stop) {
+ return files.array[i].name;
+ }
+ } // for
+
+ return "";
+ }
+#endif // _di_fss_basic_read_file_identify_
+
+#ifndef _di_fss_basic_read_load_number_
+ f_status_t fss_basic_read_load_number(const f_console_arguments_t arguments, const fss_basic_read_data_t data, const f_array_length_t parameter, const f_string_t name, f_number_unsigned_t *number) {
+
+ if (data.parameters[parameter].result == f_console_result_additional) {
+ const f_array_length_t index = data.parameters[parameter].values.array[data.parameters[parameter].values.used - 1];
+ const f_string_range_t range = f_macro_string_range_t_initialize(strnlen(arguments.argv[index], f_console_parameter_size));
+
+ const f_status_t status = fl_conversion_string_to_number_unsigned(arguments.argv[index], range, number);
+
+ if (F_status_is_error(status)) {
+ fll_error_parameter_integer_print(data.error, F_status_set_fine(status), "fl_conversion_string_to_number_unsigned", F_true, name, arguments.argv[index]);
+
+ return status;
+ }
+ }
+
+ return F_none;
+ }
+#endif // _di_fss_basic_read_load_number_
+
+#ifndef _di_fss_basic_read_print_object_end_
+ void fss_basic_read_print_object_end(const fss_basic_read_data_t data) {
+
+ if (data.parameters[fss_basic_read_parameter_pipe].result == f_console_result_found) {
+ fprintf(data.output.stream, "%c", fss_basic_read_pipe_content_start);
+ }
+ else {
+ fprintf(data.output.stream, "%c", f_fss_space);
+ }
+ }
+#endif // _di_fss_basic_read_print_object_end_
+
+#ifndef _di_fss_basic_read_print_set_end_
+ void fss_basic_read_print_set_end(const fss_basic_read_data_t data) {
+
+ if (data.parameters[fss_basic_read_parameter_pipe].result == f_console_result_found) {
+ fprintf(data.output.stream, "%c", fss_basic_read_pipe_content_end);
+ }
+ else {
+ fprintf(data.output.stream, "%c", f_fss_eol);
+ }
+ }
+#endif // _di_fss_basic_read_print_set_end_
+
+#ifndef _di_fss_basic_read_process_
+ f_status_t fss_basic_read_process(const f_console_arguments_t arguments, const fss_basic_read_files_t files, const fss_basic_read_depths_t depths, fss_basic_read_data_t *data, f_fss_delimits_t *delimits) {
-#ifndef _di_fss_basic_read_main_process_file_
- f_status_t fss_basic_read_main_process_file(const f_console_arguments_t arguments, fss_basic_read_data_t *data, const f_string_t filename, const fss_basic_read_depths_t depths, f_fss_delimits_t *delimits) {
f_status_t status = F_none;
const f_array_lengths_t except_none = f_array_lengths_t_initialize;
bool delimited = F_true;
+ bool include_empty = F_false;
+ f_number_unsigned_t select = 0;
+ f_number_unsigned_t line = 0;
- // for this standard, delimits would always be applied, except for when delimit_depth is greater than 0.
if (data->delimit_mode == fss_basic_read_delimit_mode_none || (data->delimit_depth && (data->delimit_mode == fss_basic_read_delimit_mode_depth || data->delimit_mode == fss_basic_read_delimit_mode_depth_greater))) {
delimited = F_false;
}
+ if (data->parameters[fss_basic_read_parameter_empty].result == f_console_result_found) {
+ include_empty = F_true;
+ }
+
+ status = fss_basic_read_load_number(arguments, *data, fss_basic_read_parameter_select, fss_basic_read_long_select, &select);
+
+ if (F_status_is_error_not(status)) {
+ status = fss_basic_read_load_number(arguments, *data, fss_basic_read_parameter_line, fss_basic_read_long_line, &line);
+ }
+
+ if (F_status_is_error(status)) {
+ fll_error_print(data->error, F_status_set_fine(status), "fss_basic_read_load_setting_number", F_true);
+
+ return status;
+ }
+
+ if (data->parameters[fss_basic_read_parameter_select].result == f_console_result_additional) {
+
+ // This standard does not support multiple content groups.
+ if (select > 0) {
+ return F_none;
+ }
+ }
+
{
f_string_range_t input = f_macro_string_range_t_initialize(data->buffer.used);
status = fll_fss_basic_read(data->buffer, &input, &data->objects, &data->contents, 0, delimits, 0);
if (F_status_is_error(status)) {
- // @todo: detect and replace fll_error_file_type_file with fll_error_file_type_pipe as appropriate.
- fll_error_file_print(data->error, F_status_set_fine(status), "fll_fss_basic_read", F_true, filename, "process", fll_error_file_type_file);
+ const f_string_t file_name = fss_basic_read_file_identify(input.start, files);
+
+ fll_error_file_print(data->error, F_status_set_fine(status), "fll_fss_basic_read", F_true, file_name ? file_name : "-", "process", fll_error_file_type_file);
}
else if (status == F_data_not_stop || status == F_data_not_eos) {
f_macro_fss_contents_t_delete_simple(data->contents);
if (data->parameters[fss_basic_read_parameter_total].result == f_console_result_found) {
fprintf(data->output.stream, "0%c", f_string_eol_s[0]);
+
return F_none;
}
}
if (F_status_is_error(status)) {
- f_macro_fss_contents_t_delete_simple(data->contents);
- f_macro_fss_objects_t_delete_simple(data->objects);
- f_macro_string_dynamic_t_delete_simple(data->buffer);
-
- return status;
- }
- }
-
- f_number_unsigned_t select = 0;
-
- if (data->parameters[fss_basic_read_parameter_select].result == f_console_result_additional) {
- const f_array_length_t index = data->parameters[fss_basic_read_parameter_select].values.array[data->parameters[fss_basic_read_parameter_select].values.used - 1];
- const f_string_range_t range = f_macro_string_range_t_initialize(strlen(arguments.argv[index]));
-
- status = fl_conversion_string_to_number_unsigned(arguments.argv[index], range, &select);
-
- if (F_status_is_error(status)) {
- fll_error_parameter_integer_print(data->error, F_status_set_fine(status), "fl_conversion_string_to_number_unsigned", F_true, fss_basic_read_long_select, arguments.argv[index]);
- return status;
- }
-
- // This standard does not support multiple content groups.
- if (select > 0) {
- return F_none;
- }
- }
-
- f_array_length_t line = 0;
-
- if (data->parameters[fss_basic_read_parameter_line].result == f_console_result_additional) {
- const f_array_length_t index = data->parameters[fss_basic_read_parameter_line].values.array[data->parameters[fss_basic_read_parameter_line].values.used - 1];
- const f_string_range_t range = f_macro_string_range_t_initialize(strlen(arguments.argv[index]));
-
- status = fl_conversion_string_to_number_unsigned(arguments.argv[index], range, &line);
-
- if (F_status_is_error(status)) {
- fll_error_parameter_integer_print(data->error, F_status_set_fine(status), "fl_conversion_string_to_number_unsigned", F_true, fss_basic_read_long_line, arguments.argv[index]);
return status;
}
}
memset(names, 1, sizeof(bool) * data->objects.used);
}
- bool include_empty = 0;
-
- if (data->parameters[fss_basic_read_parameter_empty].result == f_console_result_found) {
- include_empty = 1;
- }
-
if (data->parameters[fss_basic_read_parameter_object].result == f_console_result_found) {
if (data->parameters[fss_basic_read_parameter_total].result == f_console_result_found) {
if (depths.array[0].index_at > 0) {
return F_none;
}
-#endif // _di_fss_basic_read_main_process_file_
-
-#ifndef _di_fss_basic_read_print_object_end_
- void fss_basic_read_print_object_end(const fss_basic_read_data_t data) {
-
- if (data.parameters[fss_basic_read_parameter_pipe].result == f_console_result_found) {
- fprintf(data.output.stream, "%c", fss_basic_read_pipe_content_start);
- }
- else {
- fprintf(data.output.stream, "%c", f_fss_space);
- }
- }
-#endif // _di_fss_basic_read_print_object_end_
-
-#ifndef _di_fss_basic_read_print_set_end_
- void fss_basic_read_print_set_end(const fss_basic_read_data_t data) {
-
- if (data.parameters[fss_basic_read_parameter_pipe].result == f_console_result_found) {
- fprintf(data.output.stream, "%c", fss_basic_read_pipe_content_end);
- }
- else {
- fprintf(data.output.stream, "%c", f_fss_eol);
- }
- }
-#endif // _di_fss_basic_read_print_set_end_
+#endif // _di_fss_basic_read_process_
#ifdef __cplusplus
} // extern "C"
#endif // _di_fss_basic_read_depths_t_
/**
- * Pre-process the parameters, parsing out and handling the depth and depth related parameters.
+ * A structure for designating where within the buffer a particular file exists, using a statically allocated array.
+ *
+ * name: The name of the file representing the range. Set string to NULL to represent the STDIN pipe.
+ */
+#ifndef _di_fss_basic_read_file_t_
+ typedef struct {
+ f_string_t name;
+ f_string_range_t range;
+ } fss_basic_read_file_t;
+
+ #define fss_basic_read_file_t_initialize \
+ { \
+ f_string_t_initialize, \
+ f_string_range_t_initialize, \
+ }
+#endif // _di_fss_basic_read_file_t_
+
+/**
+ * An array of depth parameters.
+ *
+ * This is intended to be defined and used statically allocated and as such no dynamic allocation or dynamic deallocation methods are provided.
+ *
+ * The STDIN pipe is reserved for index 0 and as such size and used must be initialized to 1.
+ *
+ * array: The array of depths.
+ * size: Total amount of allocated space.
+ * used: Total number of allocated spaces used.
+ */
+#ifndef _di_fss_basic_read_files_t_
+ typedef struct {
+ fss_basic_read_file_t *array;
+
+ f_array_length_t size;
+ f_array_length_t used;
+ } fss_basic_read_files_t;
+
+ #define fss_basic_read_files_t_initialize { 0, 1, 1 }
+#endif // _di_fss_basic_read_files_t_
+
+/**
+ * Process the parameters, parsing out and handling the depth and depth related parameters.
*
* Will handle depth-sensitive parameter conflicts, such as --name being used with --at (which is not allowed).
*
*
* Status codes (with error bit) are returned on any problem.
*/
-#ifndef _di_fss_basic_read_main_preprocess_depth_
- extern f_status_t fss_basic_read_main_preprocess_depth(const f_console_arguments_t arguments, const fss_basic_read_data_t data, fss_basic_read_depths_t *depths) f_attribute_visibility_internal;
-#endif // _di_fss_basic_read_main_preprocess_depth_
+#ifndef _di_fss_basic_read_depth_process_
+ extern f_status_t fss_basic_read_depth_process(const f_console_arguments_t arguments, const fss_basic_read_data_t data, fss_basic_read_depths_t *depths) f_attribute_visibility_internal;
+#endif // _di_fss_basic_read_depth_process_
+
+/**
+ * Get the name of the file the given position represents within the buffer.
+ *
+ * @param at
+ * The position within the buffer.
+ * @param files
+ * The representation of files and their respective ranges within the buffer.
+ *
+ * @return
+ * A string with the name when found.
+ * NULL is returned if the range represents the STDIN pipe.
+ *
+ * On failure to identify, an empty string is returned.
+ */
+#ifndef _di_fss_basic_read_file_identify_
+ extern f_string_t fss_basic_read_file_identify(const f_array_length_t at, const fss_basic_read_files_t files) f_attribute_visibility_internal;
+#endif // _di_fss_basic_read_file_identify_
/**
- * Process a given file.
+ * Load a given number parameter.
*
* @param arguments
* The console arguments passed to the program.
* @param data
* The program specific data.
- * @param file_name
- * The name of the file being processed.
- * @param depths
- * The processed depth parameters.
- * @param delimits
- * An array of delimits detected during processing.
+ * @param parameter
+ * An ID representing the parameter.
+ * @param name
+ * The parameter name to print on error.
+ * @param number
+ * The location to store the loaded number.
*
* @return
* F_none on success.
*
* Status codes (with error bit) are returned on any problem.
- *
- * @see fss_basic_read_main_preprocess_depth()
*/
-#ifndef _di_fss_basic_read_main_process_file_
- extern f_status_t fss_basic_read_main_process_file(const f_console_arguments_t arguments, fss_basic_read_data_t *data, const f_string_t file_name, const fss_basic_read_depths_t depths, f_fss_delimits_t *delimits) f_attribute_visibility_internal;
-#endif // _di_fss_basic_read_main_process_file_
+#ifndef _di_fss_basic_read_load_number_
+ extern f_status_t fss_basic_read_load_number(const f_console_arguments_t arguments, const fss_basic_read_data_t data, const f_array_length_t parameter, const f_string_t name, f_number_unsigned_t *number) f_attribute_visibility_internal;
+#endif // _di_fss_basic_read_load_number_
/**
* Print the end of an object (which is essentially the start of a content).
extern void fss_basic_read_print_set_end(const fss_basic_read_data_t data) f_attribute_visibility_internal;
#endif // _di_fss_basic_read_print_set_end_
+/**
+ * Perform the basic read processing on the buffer.
+ *
+ * @param arguments
+ * The console arguments passed to the program.
+ * @param files
+ * An array representing the ranges in which a given file exists in the buffer.
+ * @param depths
+ * The processed depth parameters.
+ * @param data
+ * The program specific data.
+ * @param delimits
+ * An array of delimits detected during processing.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Status codes (with error bit) are returned on any problem.
+ *
+ * @see fss_basic_read_load_setting()
+ */
+#ifndef _di_fss_basic_read_process_
+ extern f_status_t fss_basic_read_process(const f_console_arguments_t arguments, const fss_basic_read_files_t files, const fss_basic_read_depths_t depths, fss_basic_read_data_t *data, f_fss_delimits_t *delimits) f_attribute_visibility_internal;
+#endif // _di_fss_basic_read_process_
+
#ifdef __cplusplus
} // extern "C"
#endif