diff --git a/TODO b/TODO index e69de29..6f2cffd 100644 --- a/TODO +++ b/TODO @@ -0,0 +1,3 @@ +Handle COWPATH env var +Support color (through ANSI) +Support unicode \ No newline at end of file diff --git a/configure.ac b/configure.ac index 8f82977..1887f37 100644 --- a/configure.ac +++ b/configure.ac @@ -25,6 +25,15 @@ if test "$build" != "$host" -a "${PKG_CONFIG_LIBDIR}" = ""; then export PKG_CONFIG_LIBDIR=/dev/null fi +AC_CHECK_HEADERS(getopt.h) + +AC_CHECK_FUNCS(getopt_long, + [AC_DEFINE(HAVE_GETOPT_LONG, 1, Define to 1 if you have the `getopt_long' function.)], + [AC_CHECK_LIB(gnugetopt, getopt_long, + [AC_DEFINE(HAVE_GETOPT_LONG, 1, Define to 1 if you have the `getopt_long' function.) + GETOPT_LIBS="${GETOPT_LIBS} -lgnugetopt"])]) +AC_SUBST(GETOPT_LIBS) + CACA="no" PKG_CHECK_MODULES(cucul, cucul, [CACA="yes"], diff --git a/data/Makefile.am b/data/Makefile.am index d539a6e..fc44b4d 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -1 +1 @@ -EXTRA_DIST = +EXTRA_DIST = caca.cow diff --git a/data/caca.cow b/data/caca.cow new file mode 100644 index 0000000..3b86017 --- /dev/null +++ b/data/caca.cow @@ -0,0 +1,13 @@ +# A nice caca cow ! +# (c) 2006 Jean-Yves Lamoureux +# So much work must be copyrighted, as MOZILLA(tm)(c)(r) team told me. + +$the_cow + $thoughts + $thoughts + $thoughts + / + ( ) + ( $eyes ) + (______) + $tongue \ No newline at end of file diff --git a/src/Makefile.am b/src/Makefile.am index 3caf4e9..14e75b9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ bin_PROGRAMS = cacamoo -cacamoo_SOURCES = main.c +cacamoo_SOURCES = main.c cacamoo.h cacamoo_CFLAGS = `pkg-config --cflags cucul` cacamoo_LDFLAGS = `pkg-config --libs cucul` diff --git a/src/cacamoo.h b/src/cacamoo.h new file mode 100644 index 0000000..cbab7bc --- /dev/null +++ b/src/cacamoo.h @@ -0,0 +1,35 @@ +/* + * cacamoo + * Copyright (c) 2006 Jean-Yves Lamoureux + * 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. + */ + +#ifndef _CACASAY_H_ +#define _CACASAY_H_ + +#include +#include +#include +#include + + +static void version(void); +char *construct_balloon(char *buffer, unsigned int termwidth); +char *wrap_string(char *buffer, unsigned int width, unsigned int *max_width, int no_wrap); +char *make_caca_from_file(unsigned int *size); +char *replace(char *string, char *oldpiece, const char *newpiece); +char *remove_slashes(char *str); +char *remove_comments(char *str); + +#if defined(HAVE_GETOPT_H) +static void usage(void); +#endif + +#endif /* _CACASAY_H_ */ diff --git a/src/main.c b/src/main.c index c05d13e..7faad85 100644 --- a/src/main.c +++ b/src/main.c @@ -1,27 +1,751 @@ /* - * $Id$ + * cacamoo + * Copyright (c) 2006 Jean-Yves Lamoureux + * All Rights Reserved * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * $Id$ * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * 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 +#endif +#if defined(HAVE_GETOPT_H) +# include +#endif +#if defined(HAVE_SYS_IOCTL_H) && defined(TIOCGWINSZ) +# include +#endif + +#include "cacamoo.h" #include +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; + + 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' }, + { "version", 0, NULL, 'v' }, + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + + + int c = getopt_long(argc, argv, "D:f:W:e:T:hvObtdgpsywn", + long_options, &option_index); +# else +# define MOREINFO "Try `%s -h' for more information.\n" + int c = getopt(argc, argv, "D:f:W:hvObtdgpsywn"); +# 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 'n': /* --width */ + 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); + canvas = cucul_import_canvas (input_buffer, "ansi"); + /* Export given canvas to format we want */ + output_buffer = cucul_export_canvas(canvas, "irc"); + + 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); + return 0; } + +char * make_caca_from_file(unsigned int *size) +{ + FILE *fp = NULL; + char filepath[1024]; + unsigned int s = 0; + char *temp = NULL; + char *temp2 = 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 = remove_comments(temp); + temp = remove_slashes(temp); + + + /* AHAHAH, THAT'S A COOL PERL INTERPRETER ! */ + temp2 = replace(temp, " = <<\"EOC\";", ""); + if(temp2 != temp) + { + free(temp); + temp = temp2; + } + temp2 = replace(temp, " = < 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 select the cow\n"); + printf(" -d, --directory 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, --version output version information and exit\n"); +# else + printf(" -f select the cow\n"); + printf(" -d specify cows directory\n"); + printf(" -W 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= *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