|
|
|
#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);
|
|
|
|
|
|
|
|
}
|