Skip to main content
Code Review

Return to Question

replaced http://codereview.stackexchange.com/ with https://codereview.stackexchange.com/
Source Link
Added a link to the next iteration.
Source Link
coderodde
  • 31.8k
  • 15
  • 77
  • 202
Source Link
coderodde
  • 31.8k
  • 15
  • 77
  • 202

A trivial command line utility for trimming whitespace from lines in C - follow-up

See the previous iteration: A trivial command line utility for trimming whitespace from lines in C

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_inplace simplified.
  • No superfluous consts.
  • The program should handle lines of arbitrary length.
  • No return EXIT_SUCCESS at the end of main.

Is there anything else to improve?

lang-c

AltStyle によって変換されたページ (->オリジナル) /