See the previous iteration: A trivial command line utility for trimming whitespace from lines in C
Note: see the next iteration at A trivial command line utility for trimming whitespace from lines in C - follow-up 2
Now my code looks like this:
#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define HELP_MESSAGE "Usage: trim [-h] [-v] [FILE1, [FILE2, [...]]]\n" \ " -h Print the help message and exit.\n" \ " -v Print the version message and exit.\n" \ " If no files specified, reads from standard input.\n" #define VERSION_MESSAGE "trim 1.61\n" \ "By Rodion \"rodde\" Efremov 07.04.2015 Helsinki\n" #define HELP_FLAG "-h" #define VERSION_FLAG "-v" #define NEWLINE_CHAR '\n' /******************************************************************************* * This routine removes all leading and trailing whitespace from a string, * * doing that in-place. ( * ********************************************************************************/ static char* trim_inplace(char* start) { char* end; for (end = &start[strlen(start) - 1]; isspace(*end); --end) { *end = '\0'; } while (isspace(*start)) { ++start; } return start; } /******************************************************************************* * Implements a (singly) linked list node holding a single character. * *******************************************************************************/ typedef struct char_node_t { char c; struct char_node_t* p_next; } char_node_t; /******************************************************************************* * Extracts a string from a linked list of characters, and frees the memory of * * the list holding the characters. * *******************************************************************************/ static char* build_string(char_node_t* p_head, size_t length) { char* string = (char*) malloc(sizeof(char) * length); size_t i = 0; char_node_t* p_node; while (p_head) { string[i++] = p_head->c; p_node = p_head; p_head = p_head->p_next; free(p_node); } string[i] = '\0'; return string; } /******************************************************************************* * Gets a line from file of any feasible length (fitting into the RAM). * * Returns NULL if file is exhausted. * *******************************************************************************/ static char* my_getline(FILE* file) { char_node_t* p_head = NULL; char_node_t* p_tail = NULL; char_node_t* p_tmp; size_t string_length = 0; char current_character; if (feof(file)) { return NULL; } for(;;) { current_character = fgetc(file); if (current_character == NEWLINE_CHAR || current_character == EOF) { if (string_length == 0) { // Otherwise a zero-length string will leak and produce a // superfluous empty line after the end of a file. return NULL; } // +1 for the NULL terminator. return build_string(p_head, string_length + 1); } if (string_length == 0) { // Initialize list. p_head = p_tail = (char_node_t*) malloc(sizeof(char_node_t)); p_head->c = current_character; p_head->p_next = p_tail->p_next = NULL; } else { // Append one character node to the list. p_tmp = (char_node_t*) malloc(sizeof(char_node_t)); p_tmp->c = current_character; p_tmp->p_next = NULL; p_tail->p_next = p_tmp; p_tail = p_tmp; } ++string_length; } } /******************************************************************************* * Processes a file. * *******************************************************************************/ static void process_file(FILE* file) { char* line; while ((line = my_getline(file))) { puts(trim_inplace(line)); free(line); } fclose(file); } /******************************************************************************* * Prints the help message and exits. * *******************************************************************************/ static void print_help() { printf(HELP_MESSAGE); exit(EXIT_SUCCESS); } /******************************************************************************* * Prints the version string. * *******************************************************************************/ static void print_version() { printf(VERSION_MESSAGE); exit(EXIT_SUCCESS); } /******************************************************************************* * Checks the flags. * *******************************************************************************/ static void check_flags(int argc, char** argv) { size_t i; for (i = 1; i < argc; ++i) { if (strcmp(argv[i], HELP_FLAG) == 0) { print_help(); } else if (strcmp(argv[i], VERSION_FLAG) == 0) { print_version(); } } } /******************************************************************************* * The entry point for a trivial line trimmer. * *******************************************************************************/ int main(int argc, char** argv) { size_t i; FILE* file; check_flags(argc, argv); if (argc < 2) { process_file(stdin); return EXIT_SUCCESS; } for (i = 1; i < argc; ++i) { file = fopen(argv[i], "r"); if (!file) { perror("Error opening a file"); return (EXIT_FAILURE); } process_file(file); } } I have fixed the following issues:
- File handles are closed as soon as possible.
trim_inplacesimplified.- No superfluous
consts. - The program should handle lines of arbitrary length.
- No
return EXIT_SUCCESSat the end ofmain.
Is there anything else to improve?