You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

394 lines
8.3 KiB

#ifndef COSMOPOLITAN
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#endif
#ifdef COSMOPOLITAN
#include "cosmopolitan.h"
#endif
int replace_tabs(char **rtext);
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');
replace_tabs(&text);
//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 = 1;
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;
}
// 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) {
// normal break
*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");
}
int replace_tabs(char **rtext) {
char *text = *rtext;
size_t len = strlen(text);
char *tab = strstr(text, "\t");
int numtabs = 0;
while (tab != NULL) {
numtabs++;
tab = strstr(tab, "\t");
}
if (numtabs == 0) {
return 0;
}
len += 3 * numtabs;
char * result = malloc(len);
int resulti = 0;
for (int i = 0; i < len; i++) {
if (text[i] == '\t') {
for (int j = 0; j < 4; j++) {
result[resulti++] = ' ';
}
} else {
result[resulti++] = text[i];
}
}
free(text);
*rtext = result;
return numtabs;
};
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);
}