Created
January 8, 2012 13:03
-
-
Save jrosskopf/1578291 to your computer and use it in GitHub Desktop.
12_reverse2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <errno.h> | |
#include <stdbool.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#define EOL '\n' | |
#define DEFAULT_LINE_BUFFER_LENGTH 1024 | |
#define DEFAULT_LINE_BUFFER_INCREMENT DEFAULT_LINE_BUFFER_LENGTH | |
#define ARG_IS_NOT_SEARCH_WORD "-x" | |
#define ARG_SEARCH_WORD "-s" | |
#define ARG_LINE_MODE "-l" | |
#define ARG_CHAR_MODE "-c" | |
#define ARG_FILE_STDIN "-" | |
typedef struct _arg_file { | |
struct _arg_file *next; | |
struct _arg_file *prev; | |
char *name; | |
FILE *fhandle; | |
} arg_file; | |
typedef struct _arg_context { | |
bool is_not_search_word; | |
bool is_char_mode; | |
bool char_mode_explicitly_set; | |
bool is_line_mode; | |
char *search_word; | |
arg_file *first_file; | |
} arg_context; | |
void usage(const char *message) { | |
fprintf(stderr, "Syntax: \n"); | |
fprintf(stderr, "reverse [–c] [–l] [–s WORD [–x]] [FILES...]\n"); | |
if (message != NULL) | |
fprintf(stderr, "Hinweis: %s\n", message); | |
} | |
arg_context *create_default_arg_ctx() { | |
arg_context *ret = (arg_context *)calloc(1, sizeof(struct _arg_context)); | |
ret->is_char_mode = true; | |
return ret; | |
} | |
void cleanup_arg_file(arg_file *file) { | |
if (file->fhandle != NULL && strcmp(file->name, ARG_FILE_STDIN) > 0) | |
fclose(file->fhandle); | |
if (file->name != NULL) | |
free(file->name); | |
free(file); | |
} | |
void cleanup_and_exit(arg_context *ctx, int exit_code) { | |
if (ctx->search_word != NULL) | |
free(ctx->search_word); | |
arg_file *current_file = ctx->first_file; | |
arg_file *next_file = NULL; | |
while(current_file != NULL) { | |
next_file = current_file->next; | |
cleanup_arg_file(current_file); | |
current_file = next_file; | |
} | |
free(ctx); | |
exit(exit_code); | |
} | |
arg_file *create_file_arg_for_filename(arg_context *ctx, char *filename) { | |
arg_file *new_file = calloc(1, sizeof(arg_file)); | |
FILE *fhandle; | |
if (strcmp(filename, ARG_FILE_STDIN) == 0) { | |
fhandle = stdin; | |
} | |
else { | |
fhandle = fopen(filename, "r"); | |
if (fhandle == NULL) { | |
char error_msg[DEFAULT_LINE_BUFFER_LENGTH]; | |
sprintf(error_msg, "%s: %s", filename, strerror(errno)); | |
usage(error_msg); | |
free(filename); | |
cleanup_and_exit(ctx, 2); | |
} | |
} | |
new_file->name = filename; | |
new_file->fhandle = fhandle; | |
return new_file; | |
} | |
bool append_file_to_ctx(arg_context *ctx, arg_file *new_file) { | |
if (ctx->first_file == NULL) { | |
ctx->first_file = new_file; | |
return true; | |
} | |
arg_file *current_file = NULL; | |
for(current_file = ctx->first_file; current_file->next != NULL; current_file = current_file->next) { | |
if (strcmp(current_file->name, new_file->name) == 0) | |
return false; | |
} | |
current_file->next = new_file; | |
new_file->prev = current_file; | |
return true; | |
} | |
void create_file_arg_and_append_to_ctx(arg_context *ctx, char *filename) { | |
arg_file *new_file = create_file_arg_for_filename(ctx, filename); | |
if (append_file_to_ctx(ctx, new_file) == false) { | |
cleanup_arg_file(new_file); | |
} | |
} | |
size_t consume_is_not_search_word_arg(arg_context *ctx, char **argv) { | |
char *current_arg = *argv; | |
if (strstr(current_arg, ARG_IS_NOT_SEARCH_WORD) == NULL) | |
return 0; | |
ctx->is_not_search_word = true; | |
return 1; | |
} | |
size_t consume_search_word_arg(arg_context *ctx, char **argv) { | |
char *current_arg = *argv; | |
if (strstr(current_arg, ARG_SEARCH_WORD) == NULL) | |
return 0; | |
// -cSUCHWORT | |
if (strlen(current_arg) > strlen(ARG_SEARCH_WORD)) { | |
ctx->search_word = strdup((current_arg + strlen(ARG_SEARCH_WORD))); | |
return 1; | |
} | |
// -c SUCHWORT | |
char *next_arg = *(++argv); | |
if (next_arg == NULL || next_arg[0] == '-') { | |
usage("Nach -c muss ein SUCHWORT folgen"); | |
cleanup_and_exit(ctx, 1); | |
} | |
ctx->search_word = strdup(next_arg); | |
return 2; | |
} | |
size_t consume_line_mode_arg(arg_context *ctx, char **argv) { | |
char *current_arg = *argv; | |
if (strstr(current_arg, ARG_LINE_MODE) == NULL) | |
return 0; | |
ctx->is_line_mode = true; | |
if (ctx->char_mode_explicitly_set == false) | |
ctx->is_char_mode = false; | |
return 1; | |
} | |
size_t consume_char_mode_arg(arg_context *ctx, char **argv) { | |
char *current_arg = *argv; | |
if (strstr(current_arg, ARG_CHAR_MODE) == NULL) | |
return 0; | |
ctx->is_char_mode = true; | |
ctx->char_mode_explicitly_set = true; | |
return 1; | |
} | |
size_t consume_file_arg(arg_context *ctx, char **argv) { | |
char *current_arg = *argv; | |
create_file_arg_and_append_to_ctx(ctx, strdup(current_arg)); | |
return 1; | |
} | |
size_t consume_next_argument(arg_context *ctx, char **argv) { | |
size_t consume_count = 0; | |
if ((consume_count = consume_is_not_search_word_arg(ctx, argv)) > 0) | |
return consume_count; | |
if ((consume_count = consume_search_word_arg(ctx, argv)) > 0) | |
return consume_count; | |
if ((consume_count = consume_line_mode_arg(ctx, argv)) > 0) | |
return consume_count; | |
if ((consume_count = consume_char_mode_arg(ctx, argv)) > 0) | |
return consume_count; | |
consume_count = consume_file_arg(ctx, argv); | |
return consume_count; | |
} | |
void create_default_file_arg_if_neccessary(arg_context *ctx) { | |
if (ctx->first_file == NULL) | |
create_file_arg_and_append_to_ctx(ctx, strdup(ARG_FILE_STDIN)); | |
} | |
char *strdup_reverse(char *orig_word) { | |
char *new_word = strdup(orig_word); | |
size_t n = (unsigned int)strlen(orig_word); | |
for (size_t i = 0; i < n; i++) | |
new_word[n - i - 1] = orig_word[i]; | |
return new_word; | |
} | |
char *read_next_line(arg_file *file) { | |
char *line = NULL; | |
char c = EOF; | |
size_t idx = 0; | |
for(size_t r = 1; (c = fgetc(file->fhandle)) != EOF; r--) { | |
if (r <= 1) { | |
line = (char *)realloc(line, idx + 1 + DEFAULT_LINE_BUFFER_INCREMENT); | |
r = DEFAULT_LINE_BUFFER_INCREMENT; | |
} | |
if (c == EOL) | |
break; | |
line[idx++] = c; | |
} | |
if (line != NULL) | |
line[idx] = '\0'; | |
return line; | |
} | |
char *read_previous_line(arg_file *file) { | |
char *line = NULL; | |
size_t idx = 0; | |
char c = EOL; | |
size_t r = 1; | |
for(r = 1; fseek(file->fhandle, -2L, SEEK_CUR) >= 0; r--) { | |
if (r <= 1) { | |
line = (char *)realloc(line, idx + 1 + DEFAULT_LINE_BUFFER_INCREMENT); | |
r = DEFAULT_LINE_BUFFER_INCREMENT; | |
} | |
c = fgetc(file->fhandle); | |
if (c == EOF || c == EOL) | |
break; | |
line[idx++] = c; | |
} | |
if (line == NULL) | |
return NULL; | |
line[idx] = '\0'; | |
char *reversed_line = strdup_reverse(line); | |
free(line); | |
return reversed_line; | |
} | |
void print_reversed_line(char *line) { | |
char *delimiter = " ,.;!"; | |
for(char *orig_token = strtok(line, delimiter); orig_token != NULL; orig_token = strtok(NULL, delimiter)) { | |
char *new_token = strdup_reverse(orig_token); | |
printf("%s ", new_token); | |
free(new_token); | |
} | |
printf("\n"); | |
} | |
void cat_single_line(arg_context *ctx, char *line) { | |
// Kein Suchbegriff und Character Mode. | |
if (ctx->search_word == NULL && ctx->is_char_mode == true) { | |
print_reversed_line(line); | |
return; | |
} | |
// Kein Suchbegriff und nicht Character Mode. | |
if (ctx->search_word == NULL && ctx->is_char_mode == false) { | |
printf("%s\n", line); | |
return; | |
} | |
bool search_word_found = (strstr(line, ctx->search_word)) != NULL ? true : false; | |
// Zeile normal ausgeben, wenn Suchwort gefunden und mit -x negiert | |
// oder Suchwort nicht gefunden | |
if ( (search_word_found == true && ctx->is_not_search_word == true) | |
||(search_word_found == false && ctx->is_not_search_word == false)) | |
{ | |
printf("%s\n", line); | |
} | |
else { | |
print_reversed_line(line); | |
} | |
} | |
void cat_single_file(arg_context *ctx, arg_file *file) { | |
printf("%s >>>\n", file->name); | |
char *line = NULL; | |
while ((line = read_next_line(file)) != NULL) { | |
cat_single_line(ctx, line); | |
free(line); | |
} | |
printf("<<< %s\n", file->name); | |
} | |
void seek_file_to_end(arg_context *ctx, arg_file *file) { | |
if (file->fhandle == stdin) { | |
usage("Kann stdin nicht rückwärts lesen!"); | |
cleanup_and_exit(ctx, 3); | |
} | |
if (fseek(file->fhandle, 0, SEEK_END) < 0) { | |
char error_msg[DEFAULT_LINE_BUFFER_LENGTH]; | |
sprintf(error_msg, "%s: %s", file->name, strerror(errno)); | |
usage(error_msg); | |
cleanup_and_exit(ctx, 4); | |
} | |
} | |
void cat_single_file_reverse(arg_context *ctx, arg_file *file) { | |
printf("%s >>>\n", file->name); | |
char *line = NULL; | |
while ((line = read_previous_line(file)) != NULL) { | |
cat_single_line(ctx, line); | |
free(line); | |
} | |
printf("<<< %s\n", file->name); | |
} | |
void cat_files_from_arg_context(arg_context *ctx) { | |
for(arg_file *current_file = ctx->first_file; current_file != NULL; current_file = current_file->next) { | |
if (ctx->is_line_mode) { | |
seek_file_to_end(ctx, current_file); | |
cat_single_file_reverse(ctx, current_file); | |
} | |
else { | |
cat_single_file(ctx, current_file); | |
} | |
} | |
} | |
int main(int argc, char *argv[]) { | |
arg_context *ctx = create_default_arg_ctx(); | |
argv = (argv + 1); | |
while(*argv != NULL) { | |
size_t consume_count = consume_next_argument(ctx, argv); | |
argv += consume_count; | |
} | |
create_default_file_arg_if_neccessary(ctx); | |
cat_files_from_arg_context(ctx); | |
cleanup_and_exit(ctx, 0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment