/* * cacamoo * Copyright (c) 2006 Jean-Yves Lamoureux <jylam@lnxscene.org> * All Rights Reserved * * $Id$ * * This program is free software; you can redistribute it and/or * modify it under the terms of the Do What The Fuck You Want To * Public License, Version 2, as published by Sam Hocevar. See * http://sam.zoy.org/wtfpl/COPYING for more details. */ #include "config.h" #if defined(HAVE_INTTYPES_H) # include <inttypes.h> #endif #if defined(HAVE_GETOPT_H) # include <getopt.h> #endif #if defined(HAVE_SYS_IOCTL_H) && defined(TIOCGWINSZ) # include <sys/ioctl.h> #endif #include "cacamoo.h" #include <cucul.h> char const *cacamoo_export = "utf8"; char const *cacamoo_file = "default"; char const *cacamoo_dir = "/usr/share/cowsay/cows"; /* Default glyphs */ char *cacamoo_eyes = "oo"; char *cacamoo_tongue = " "; char *cacamoo_thoughts = "\\"; char *cacamoo_think = "o"; char *cacamoo_borg = "=="; char *cacamoo_tired = "--"; char *cacamoo_dead = "xx"; char *cacamoo_greedy = "$$"; char *cacamoo_parano = "@@"; char *cacamoo_stoned = "**"; char *cacamoo_youth = ".."; char *cacamoo_wired = "OO"; char cacamoo_use_eyes[3] = {' ',' ',0}; char cacamoo_use_tongue[3] = {' ',' ',0}; /* String we have to display */ char *string = NULL; /* Wrapped and balloonified */ char *wrapped = NULL; /* Width */ unsigned int term_width = 40-1; /* Default for cowsay */ /* Think ? */ unsigned char think = 0; /* Unicode */ unsigned char unicode = 0; int main (int argc, char **argv) { int i, length; char *buffer = NULL; unsigned int buffer_size = 0; unsigned int new_width = 0; char *initial = NULL; unsigned int no_wrap = 0; cucul_buffer_t* input_buffer; cucul_buffer_t* output_buffer; cucul_canvas_t* canvas; int buf_size; char *buf_data; if ((strstr(argv[0], "cacathink")) != NULL) { think = 1; } #if defined(HAVE_GETOPT_H) for(;;) { # ifdef HAVE_GETOPT_LONG # define MOREINFO "Try `%s --help' for more information.\n" int option_index = 0; static struct option long_options[] = { /* Long option, needs arg, flag, short option */ { "file", 1, NULL, 'f' }, { "directory", 1, NULL, 'D' }, { "width", 1, NULL, 'W' }, { "no-wrap", 1, NULL, 'n' }, { "eyes", 1, NULL, 'e' }, { "tongue", 1, NULL, 'T' }, { "borg", 1, NULL, 'b' }, { "tired", 1, NULL, 't' }, { "dead", 1, NULL, 'd' }, { "greedy", 1, NULL, 'g' }, { "parano", 1, NULL, 'p' }, { "stoned", 1, NULL, 's' }, { "youth", 1, NULL, 'y' }, { "wired", 1, NULL, 'w' }, { "think", 1, NULL, 'O' }, { "unicode", 1, NULL, 'u' }, { "version", 0, NULL, 'v' }, { "list-files", 0, NULL, 'l' }, { "help", 0, NULL, 'h' }, { NULL, 0, NULL, 0 } }; int c = getopt_long(argc, argv, "D:f:W:e:T:hvObtdgpsylwnu", long_options, &option_index); # else # define MOREINFO "Try `%s -h' for more information.\n" int c = getopt(argc, argv, "D:f:W:hvObtdgpsylwnu"); # endif if(c == -1) break; switch(c) { case 'h': /* --help */ usage(); return 0; case 'v': /* --version */ version(); return 0; case 'f': /* --file*/ cacamoo_file = optarg; break; case 'D': /* --directory */ cacamoo_dir = optarg; break; case 'e': /* --eyes*/ cacamoo_eyes = optarg; break; case 'b': /* --borg*/ cacamoo_eyes = cacamoo_borg; break; case 't': /* --tired*/ cacamoo_eyes = cacamoo_tired; break; case 'd': /* --dead*/ cacamoo_eyes = cacamoo_dead; break; case 'g': /* --greedy*/ cacamoo_eyes = cacamoo_greedy; break; case 'p': /* --parano*/ cacamoo_eyes = cacamoo_parano; break; case 's': /* --stoned*/ cacamoo_eyes = cacamoo_stoned; break; case 'y': /* --youth*/ cacamoo_eyes = cacamoo_youth; break; case 'w': /* --wired*/ cacamoo_eyes = cacamoo_wired; break; case 'T': /* --tongue */ cacamoo_tongue = optarg; break; case 'O': /* --thoughts */ think = 1; break; case 'W': /* --width */ term_width = strtol(optarg, NULL, 10); if(term_width && (term_width != 1)) term_width--; break; case 'l': /* --list-files */ list_files("."); list_files(cacamoo_dir); return 0; break; case 'u': /* --unicode */ unicode = 1; break; case 'n': /* --no-wrap */ no_wrap = 1; break; case '?': printf(MOREINFO, argv[0]); return 1; default: printf("%s: invalid option -- %c\n", argv[0], c); printf(MOREINFO, argv[0]); return 1; } } #else # define MOREINFO "Usage: %s message...\n" int optind = 1; #endif if(think) cacamoo_thoughts = cacamoo_think; /* Load rest of commandline */ for(i = optind, length = 0; i < argc; i++) { unsigned int k, guessed_len, real_len = 0; guessed_len = strlen(argv[i]); if(i > optind) string[length++] = ' '; string = realloc(string, (length + guessed_len + 1)); for(k = 0, real_len = 0; k < guessed_len; real_len++) { string[length + real_len] = *(argv[i]+k); k ++; } length += real_len; } if(string == NULL) { usage(); return -1; } string[length] = 0; /* Eyes and tongue are 2 characters wide */ memcpy(cacamoo_use_eyes, cacamoo_eyes, strlen(cacamoo_eyes)>2?2:strlen(cacamoo_eyes)); memcpy(cacamoo_use_tongue, cacamoo_tongue, strlen(cacamoo_tongue)>2?2:strlen(cacamoo_tongue)); initial = malloc(strlen(string)+1); memcpy(initial, string, strlen(string)+1); free(string); wrapped = wrap_string(initial, term_width, &new_width, no_wrap); string = wrapped; buffer = make_caca_from_file(&buffer_size); if(buffer == NULL) { if(string) free(string); return -1; } /* Import our buffer as an ansi (color) one */ input_buffer = cucul_load_memory(buffer, buffer_size-1); if(input_buffer == NULL) { printf("Can't load file in libcucul !\n"); return -1; } canvas = cucul_import_canvas (input_buffer, unicode?"utf8":"ansi"); if(canvas == NULL) { printf("Can't load file in libcucul !\n"); return -1; } /* Export given canvas to format we want */ output_buffer = cucul_export_canvas(canvas, "ansi"); if(output_buffer == NULL) { printf("Can't export file to text !\n"); return -1; } buf_size = cucul_get_buffer_size(output_buffer); buf_data = cucul_get_buffer_data(output_buffer); for(i = 0; i < buf_size; i++) printf("%c", buf_data[i]); if(string) free(string); if(buffer) free(buffer); cucul_free_buffer(input_buffer); cucul_free_buffer(output_buffer); cucul_free_canvas(canvas); return 0; } void list_files(const char *directory) { struct dirent * dp; int count = 0; DIR *dir = opendir(directory); for (dp = readdir(dir); dp != NULL; dp = readdir(dir)) { if(!strncmp(&dp->d_name[strlen(dp->d_name)-4], ".cow", 4)) { char name[256]; memcpy(name, dp->d_name, strlen(dp->d_name)-4); name[strlen(dp->d_name)-4] = 0; printf("%s ", name); count++; if(!(count%6)) printf("\n"); } } closedir(dir); if(count) printf("\n"); return; } char * make_caca_from_file(unsigned int *size) { FILE *fp = NULL; char filepath[1024]; unsigned int s = 0; char *temp = NULL; /* Try direct name */ snprintf(filepath, 1023, "%s", cacamoo_file); fp = fopen(filepath, "r"); if(fp == NULL) { /* Try direct file + .cow */ snprintf(filepath, 1023, "%s.cow", cacamoo_file); fp = fopen(filepath, "r"); if(fp == NULL) { /* Try with complete directory */ snprintf(filepath, 1023, "%s/%s.cow", cacamoo_dir, cacamoo_file); fp = fopen(filepath, "r"); if(fp == NULL) { printf("Can't open %s\n", filepath); perror("fopen"); return NULL; } } } fseek(fp, 0, SEEK_END); s = ftell(fp); fseek(fp, 0, SEEK_SET); temp = malloc(s+1*sizeof(char)); if(temp == NULL) { printf("Not enough memory.\n"); return NULL; } if(fread(temp, 1, s, fp)!=s) { printf("Can't read %s\n", filepath); perror("fread"); return NULL; } temp[s] = '\0'; temp = remove_comments(temp); temp = remove_slashes(temp); /* AHAHAH, THAT'S A COOL PERL INTERPRETER ! */ temp = replace(temp, " = <<\"EOC\";", ""); temp = replace(temp, " = <<EOC;" , ""); temp = replace(temp, " = <<EOC" , ""); temp = replace(temp, " = << EOC" , ""); temp = replace(temp, "EOC" , ""); temp = replace(temp, "$eyes" , cacamoo_use_eyes); temp = replace(temp, "${eyes}" , cacamoo_use_eyes); temp = replace(temp, "$tongue" , cacamoo_use_tongue); temp = replace(temp, "${tongue}" , cacamoo_use_tongue); temp = replace(temp, "$thoughts" , cacamoo_thoughts); temp = replace(temp, "${thoughts}" , cacamoo_thoughts); temp = replace(temp, "$the_cow" , (const char*)string); temp = replace(temp, "${the_cow}" , (const char*)string); *size = strlen(temp)+1; fclose(fp); return temp; } char *remove_slashes(char *str) { int i=0, size, r=0; if(str == NULL) return NULL; size = strlen(str); for(i=0; i<size-r; i++) { if(str[i]== '\\') { memmove(&str[i], &str[i+1], strlen(str) - i); str = realloc(str, (size-r)+1); r++; } } return str; } char *remove_comments(char *str) { int size = 0, added=0; int i=0, j; if(str == NULL) return NULL; size = strlen(str) + 1; while(i < size) { if(str[i] == '#') { for(j = i; j < size; j++) if((str[j] == '\n') || (str[j] == '\r') || (str[j] == 0)) goto hop; // just because I like goto's, break sucks hop: j++; added += (i-j); memmove(&str[i], &str[j], size-j); i = j - added; size -= added; str = realloc(str, size); str[size-1] = 0; i = 0; } else i++; } return str; } char *replace(char *s1, char *oldpiece, const char *newpiece) { unsigned int oldlen = strlen(oldpiece), newlen = strlen(newpiece); unsigned int i1 = 0, i2 = 0; char *s2 = oldlen < newlen ? NULL : s1; for(;;) { char *found = strstr(s1 + i1, oldpiece); unsigned int tocopy; if(!found) { tocopy = strlen(s1 + i1); if(oldlen < newlen) s2 = realloc(s2, i2 + tocopy + 1); memcpy(s2 + i2, s1 + i1, tocopy + 1); if(oldlen < newlen) free(s1); return s2; } tocopy = found - (s1 + i1); if(oldlen < newlen) s2 = realloc(s2, i2 + tocopy + newlen); memmove(s2 + i2, s1 + i1, tocopy); memcpy(s2 + i2 + tocopy, newpiece, newlen); i1 += tocopy + oldlen; i2 += tocopy + newlen; } } static void version(void) { printf("cacamoo Copyright 2006 Jean-Yves Lamoureux %s\n", VERSION); printf("Internet: <jylam@lnscene.org> Version: 0, date: 30 Sep 2006\n"); printf("\n"); } #if defined(HAVE_GETOPT_H) static void usage(void) { printf("Usage: cacamoo [ -vh ] [ -d cowsdirectory ]\n"); printf(" [ -f cowfile ] [ -w outputwidth ]\n"); printf(" [-bdgpstwy] [ message ]\n"); # ifdef HAVE_GETOPT_LONG printf(" -f, --file <cowfile> select the cow\n"); printf(" -d, --directory <dir> specify cows directory\n"); printf(" -W, --width <width> set output width\n"); printf(" -n --no-wrap do not wrap string\n"); printf(" -O, --think think\n"); printf(" -h display this help and exit\n"); printf(" -v, --version output version information and exit\n"); # else printf(" -f <cowfile> select the cow\n"); printf(" -d <dir> specify cows directory\n"); printf(" -W <width> set output width\n"); printf(" -n --no-wrap do not wrap string\n"); printf(" -O, --think think\n"); printf(" -h display this help and exit\n"); printf(" -v output version information and exit\n"); # endif } #endif /* AHAHAHAHA please make no comment about this, I was in hurry \o/ */ char *wrap_string(char *buffer, unsigned int width, unsigned int *max_width, int no_wrap) { unsigned int i = 0, j =0, o = 0, last_space = 0, line = 0, offset = 0; unsigned int size = strlen(buffer) + 1, t = 0, rew = 0; char *ret = NULL; ret = malloc(2); *max_width = 0; /* Wrap string itself */ if(size > width && !no_wrap) { while(i<size-1) { if(buffer[i] == ' ') { last_space = i; rew = o; } if(offset == width) { ret = realloc(ret, o+2); if(rew) o = rew; ret[o++] = '\n'; if(last_space) { if(width - (i - last_space) >= *max_width) *max_width = width - (i - last_space); i = last_space + 1; last_space = 0; rew = 0; } else { if(width>= *max_width) *max_width = width; } offset = 0; line ++; } ret = realloc(ret, o+2); ret[o++] = buffer[i]; i++; offset++; } if(offset>= *max_width) *max_width = offset; if(!(*max_width)) *max_width = size-1; ret[o] = 0; line++; } else { *max_width = strlen(buffer); if(ret) free(ret); ret = buffer; line = 1; } /* String is wrapped, put spaces after each line */ if(line != 1) { char *scaled = malloc(((*max_width+1) * line) + 1); int curx = 0; memset(scaled, ' ', (*max_width * line)); o = 0; for(i = 0; i < strlen(ret); i ++) { if(ret[i] != '\n') { scaled[o] = ret[i]; curx++; } else { for(j=o;j<o+(*max_width - curx);j++) { scaled[j] = ' '; } o += ((*max_width) - curx) -1; curx = 0; } o++; } for(i = o; i <o+(*max_width - curx); i ++) { scaled[i] = ' '; } scaled[o+i] = 0; if(ret) free(ret); ret = scaled; /* Put balloon */ o = 0; scaled = malloc((*max_width+5) * (line+2)); scaled[t++] = ' '; for(i = 0; i < *max_width+2; i++) scaled[t++] = '_'; scaled[t++] = ' '; scaled[t++] = '\n'; for(j = 0; j < line ; j++) { if(think) { scaled[t++] = '('; } else { if(j == 0) scaled[t++] = '/'; else if (j == line -1) scaled[t++] = '\\'; else scaled[t++] = '|'; } scaled[t++] = ' '; for(i = 0; i < *max_width; i++) { scaled[t++] = ret[o++]; } scaled[t++] = ' '; if(think) { scaled[t++] = ')'; } else { if(j == 0) scaled[t++] = '\\'; else if (j == line -1) scaled[t++] = '/'; else scaled[t++] = '|'; } scaled[t++] = '\n'; } scaled[t++] = ' '; for(i = 0; i < *max_width+2; i++) scaled[t++] = '-'; scaled[t++] = ' '; scaled[t] = 0; free(ret); ret = NULL; ret = scaled; } else { /* Put ballon */ char *scaled = malloc((size+4) * 3); t = 0; *max_width = size -1 ; o = 0; scaled[t++] = ' '; for(i = 0; i < *max_width+2; i++) scaled[t++] = '_'; scaled[t++] = ' '; scaled[t++] = '\n'; if(think) scaled[t++] = '('; else scaled[t++] = '<'; scaled[t++] = ' '; for(i = 0; i < *max_width; i++) { scaled[t++] = ret[o++]; } scaled[t++] = ' '; if(think) scaled[t++] = ')'; else scaled[t++] = '>'; scaled[t++] = '\n'; scaled[t++] = ' '; for(i = 0; i < *max_width+2; i++) scaled[t++] = '-'; scaled[t] = '\0'; free(ret); ret = NULL; ret = scaled; } return ret; }