alistair
4 years ago
commit
3f197848c4
3 changed files with 376 additions and 0 deletions
@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
|
||||
.PHONY: default |
||||
|
||||
default: main |
||||
|
||||
debug: cowsay.c |
||||
gcc -g cowsay.c -o cowsay
|
||||
|
||||
install: main |
||||
cp cowsay /usr/local/bin/
|
||||
|
||||
main: cowsay.c |
||||
gcc -O3 cowsay.c -o cowsay
|
||||
|
||||
static: cowsay.c |
||||
gcc -O3 -static cowsay.c -o cowsay
|
||||
|
||||
clean: cowsay |
||||
rm cowsay
|
||||
|
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
# c-cowsay |
||||
|
||||
I was bored on an aeroplane so I decided to re-write Tony Monroe's cowsay in C. |
||||
It does not support multiple cow types or changing the features of the cow, but |
||||
after several more hours of lazy printf debugging it does wrap text in an almost- |
||||
identical way. |
@ -0,0 +1,348 @@
@@ -0,0 +1,348 @@
|
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <stdbool.h> |
||||
#include <stdlib.h> |
||||
|
||||
int read_file(FILE* in, char **out) { |
||||
int size_of_buffer = 20; |
||||
char *buffer = calloc(size_of_buffer, sizeof(char)); |
||||
char character; |
||||
int count = 0; |
||||
|
||||
while (true) { |
||||
character = fgetc(in); |
||||
if (character == EOF) { |
||||
buffer[count] = '\0'; |
||||
*out = buffer; |
||||
return 0; |
||||
} |
||||
if (count == (size_of_buffer - 3)) { |
||||
buffer = reallocarray(buffer, (size_of_buffer *= 2), sizeof(char)); |
||||
|
||||
if (!buffer) { |
||||
return 1; |
||||
} |
||||
} |
||||
buffer[count] = character; |
||||
count++; |
||||
} |
||||
} |
||||
|
||||
char *read_argvs(int argc, char**argv) { |
||||
int len = 1; |
||||
char *string = calloc(1, sizeof(char)); |
||||
|
||||
int clen = 0; |
||||
int now = 0; |
||||
for (int i = 1; i < argc; i++) { |
||||
int slen = strlen(argv[i]); |
||||
clen += slen; |
||||
|
||||
if (clen == len) { |
||||
clen += 1; |
||||
} |
||||
|
||||
if (clen >= len - 1) { |
||||
string = reallocarray(string, now + slen + 2, sizeof(char)); |
||||
} |
||||
|
||||
strcpy(&string[now], argv[i]); |
||||
strcpy(&(string[now + slen]), " "); |
||||
|
||||
now = now + slen + 1; |
||||
} |
||||
return string; |
||||
} |
||||
|
||||
// Remove duplicate instances of <del> so that a maximum of 3 consecutive
|
||||
// remain
|
||||
void strip_excess_char(char **text, char del) { |
||||
int len = strlen(*text); |
||||
int count = 0; |
||||
|
||||
do { |
||||
|
||||
char last_char = 'a'; |
||||
char llast_char = 'a'; |
||||
char this_char; |
||||
count = 0; |
||||
|
||||
for (int i = 0; (*text)[i] != '\0'; i++) { |
||||
this_char = (*text)[i]; |
||||
|
||||
if (this_char == del && last_char == del && llast_char == del) { |
||||
count++; |
||||
|
||||
if ((*text)[i+1] == '\0') { |
||||
/*(*text)[i-1] = '\0';*/ |
||||
(*text)[i] = '\0'; |
||||
break; |
||||
} |
||||
|
||||
for (int j = i; (*text)[j] != '\0'; j++) { |
||||
/*(*text)[j-1] = (*text)[j];*/ |
||||
/*(*text)[j] = (*text)[j+1];*/ |
||||
(*text)[j] = (*text)[j+1]; |
||||
} |
||||
|
||||
//*text = reallocarray(*text, --len, sizeof(char));
|
||||
} |
||||
llast_char = last_char; |
||||
last_char = this_char; |
||||
} |
||||
} while (count > 0); |
||||
} |
||||
|
||||
void strip_tailing_whitespace(char *text) { |
||||
int len = strlen(text); |
||||
|
||||
char *match = "\n\t "; |
||||
|
||||
for (int i = len-1; i > 0; i ++) { |
||||
bool m = false; |
||||
for (int j = 0; j < strlen(match); j++) { |
||||
if (text[i] == match[j]) { |
||||
text[i] = '\0'; |
||||
m = true; |
||||
break; |
||||
} |
||||
} |
||||
if (!m) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
char *read_stdin(void) { |
||||
char *text; |
||||
int err = read_file(stdin, &text); |
||||
|
||||
if (err) { |
||||
return NULL; |
||||
} else { |
||||
strip_excess_char(&text, '\n'); |
||||
strip_excess_char(&text, '\t'); |
||||
strip_excess_char(&text, ' '); |
||||
strip_tailing_whitespace(text); |
||||
return text; |
||||
} |
||||
} |
||||
|
||||
char *get_text(int argc, char **argv) { |
||||
if (argc == 1) { |
||||
// stdin mode
|
||||
return read_stdin(); |
||||
} else if (argc > 1) { |
||||
// arg mode
|
||||
return read_argvs(argc, argv);
|
||||
}
|
||||
return NULL; |
||||
} |
||||
|
||||
void insert_char(char **str, char c, int p) { |
||||
int len = strlen(*str); |
||||
|
||||
*str = reallocarray(*str, len + 3, sizeof(char)); |
||||
|
||||
for (int i = len-1; i >= p; i--) { |
||||
(*str)[i+1] = (*str)[i]; |
||||
} |
||||
|
||||
(*str)[p] = c; |
||||
} |
||||
|
||||
// hard-warps string on spaces and long-lines to match width.
|
||||
// @param width: the maximum width of the resulting string.
|
||||
// @param text: The text to be wrapped. It is modified in place.
|
||||
void wrap_text(char *text, int width) { |
||||
|
||||
char *last_break = NULL; |
||||
int current_line = 0; |
||||
int last_break_pos = 0; |
||||
|
||||
for (int i = 0; text[i] != '\0'; i++) { |
||||
|
||||
if (text[i] == '\n') { |
||||
last_break = text + i; |
||||
last_break_pos = current_line; |
||||
current_line = 0; |
||||
} |
||||
|
||||
char *breaks = "\t "; |
||||
|
||||
// set whitespace to be break point
|
||||
if ((text[i] == ' ') && current_line <= width + 2) { |
||||
last_break_pos = current_line; |
||||
last_break = text + i; |
||||
} |
||||
|
||||
// force break on long liness
|
||||
if ((current_line > width + 1) && last_break == NULL) { |
||||
insert_char(&text, '\n', i-1); |
||||
last_break = NULL; |
||||
current_line = 0; |
||||
} else if (current_line > width + 1) { |
||||
*last_break = '\n'; |
||||
last_break = NULL; |
||||
current_line = current_line - last_break_pos; |
||||
}
|
||||
|
||||
current_line++; |
||||
} |
||||
} |
||||
|
||||
void print_bubble(char *text, int width, int line_count) { |
||||
|
||||
printf(" "); |
||||
for (int i = 0; i < width + 3; i ++) { |
||||
printf("_"); |
||||
} |
||||
printf("\n"); |
||||
|
||||
int lc = 0; |
||||
int ll = 0; |
||||
for (int i = 0; text[i] != '\0'; i++) { |
||||
// printf("%d: %p = %c\n", i, text + i, text[i]);
|
||||
if (ll == 0) { |
||||
if (lc == 0) { |
||||
printf("/ "); |
||||
} else if (lc == line_count) { |
||||
printf("\\ "); |
||||
} else { |
||||
printf("| "); |
||||
} |
||||
} |
||||
|
||||
if (text[i] == '\n') { |
||||
|
||||
for (;ll <= width; ll++) { |
||||
printf(" "); |
||||
} |
||||
|
||||
if (lc == 0) { |
||||
printf(" \\\n"); |
||||
} else if (lc == line_count) { |
||||
printf(" /\n"); |
||||
} else { |
||||
printf(" |\n"); |
||||
} |
||||
|
||||
ll = -1; |
||||
lc++; |
||||
} else { |
||||
printf("%c", text[i]); |
||||
} |
||||
ll++; |
||||
} |
||||
|
||||
for (;ll <= width; ll++) { |
||||
printf(" "); |
||||
} |
||||
|
||||
if (lc == 0) { |
||||
printf(" \\\n"); |
||||
} else if (lc == line_count ) { |
||||
printf(" /\n"); |
||||
} else if (lc == line_count ) { |
||||
; |
||||
} else { |
||||
printf(" |\n"); |
||||
} |
||||
|
||||
printf(" "); |
||||
for (int i = 0; i < width + 3; i ++) { |
||||
printf("-"); |
||||
} |
||||
} |
||||
|
||||
void print_single_line_bubble(char *text, int len) { |
||||
printf(" "); |
||||
for (int i = 0; i < len + 1; i++) { |
||||
printf("_"); |
||||
} |
||||
|
||||
printf("\n<"); |
||||
printf(" %s", text); |
||||
printf(">\n"); |
||||
|
||||
printf(" "); |
||||
|
||||
for (int i = 0; i < len + 1; i++) { |
||||
printf("-"); |
||||
} |
||||
printf("\n"); |
||||
|
||||
} |
||||
|
||||
void print_speech(char *text, int width) { |
||||
|
||||
int len = strlen(text); |
||||
|
||||
if (len <= width) { |
||||
print_single_line_bubble(text, len); |
||||
return; |
||||
} |
||||
|
||||
width -= 2; // for margins
|
||||
wrap_text(text, width); |
||||
|
||||
int line_count = 0; |
||||
for (int i = 0; text[i] != '\0'; i++) { |
||||
if (text[i] == '\n') { |
||||
line_count++; |
||||
} |
||||
} |
||||
|
||||
print_bubble(text, width, line_count); |
||||
|
||||
printf("\n");
|
||||
|
||||
} |
||||
|
||||
struct Cow { |
||||
char *eyes; |
||||
char *tongue; |
||||
char *thoughts; |
||||
char *cow; |
||||
}; |
||||
|
||||
void print_cow(struct Cow *cow) { |
||||
char default_cow[] =
|
||||
" \\ ^__^\n" \
|
||||
" \\ (oo)\\_______\n" \
|
||||
" (__)\\ )\\/\\\n" \
|
||||
" ||----w |\n" \
|
||||
" || ||\n" ; |
||||
char default_eyes[] = "oo"; |
||||
char default_thoughts[] = "\\"; |
||||
|
||||
if (!cow->eyes) { |
||||
cow->eyes = default_eyes; |
||||
} |
||||
if (!cow->thoughts) { |
||||
cow->thoughts = default_thoughts; |
||||
} |
||||
if (!cow->cow) { |
||||
cow->cow = default_cow; |
||||
} |
||||
// TODO: write a replace string function to support cowfile-like
|
||||
// substitution of features for $eyes, $tongue, and $thoughts into
|
||||
// default_cow
|
||||
|
||||
printf("%s", default_cow); |
||||
} |
||||
|
||||
int main(int argc, char** argv) { |
||||
|
||||
char *text = get_text(argc, argv); |
||||
|
||||
if (!text) { |
||||
return 1; |
||||
} |
||||
|
||||
print_speech(text, 40); |
||||
|
||||
struct Cow *cow = calloc(1, sizeof(struct Cow)); |
||||
print_cow(cow); |
||||
|
||||
} |
Loading…
Reference in new issue