source: cacamoo/trunk/src/main.c @ 1453

Last change on this file since 1453 was 1453, checked in by Sam Hocevar, 14 years ago
  • Add a no warranty clause to the code.
  • Property svn:keywords set to Id
File size: 18.1 KB
Line 
1/*
2 *  cacamoo
3 *  Copyright (c) 2006 Jean-Yves Lamoureux <jylam@lnxscene.org>
4 *                All Rights Reserved
5 *
6 *  $Id: main.c 1453 2006-12-11 15:51:55Z sam $
7 *
8 *  This program is free software. It commes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15
16#include "config.h"
17
18#if defined(HAVE_INTTYPES_H)
19#   include <inttypes.h>
20#endif
21#if defined(HAVE_GETOPT_H)
22#   include <getopt.h>
23#endif
24#if defined(HAVE_SYS_IOCTL_H) && defined(TIOCGWINSZ)
25#   include <sys/ioctl.h>
26#endif
27
28#include "cacamoo.h"
29#include <cucul.h>
30
31char const *cacamoo_export = "utf8";
32char const *cacamoo_file = "default";
33char const *cacamoo_dir = "/usr/share/cowsay/cows";
34
35/* Default glyphs */
36char *cacamoo_eyes = "oo";
37char *cacamoo_tongue = "  ";
38char *cacamoo_thoughts = "\\";
39char *cacamoo_think = "o";
40char *cacamoo_borg = "==";
41char *cacamoo_tired = "--";
42char *cacamoo_dead = "xx";
43char *cacamoo_greedy = "$$";
44char *cacamoo_parano = "@@";
45char *cacamoo_stoned = "**";
46char *cacamoo_youth = "..";
47char *cacamoo_wired = "OO";
48char cacamoo_use_eyes[3] = {' ',' ',0};
49char cacamoo_use_tongue[3] = {' ',' ',0};
50
51/* String we have to display */
52char *string = NULL;
53/* Wrapped and balloonified */
54char *wrapped = NULL;
55
56
57/* Width */
58unsigned int term_width = 40-1; /* Default for cowsay */
59
60/* Think ? */
61unsigned char think = 0;
62
63/* Unicode */
64unsigned char unicode = 0;
65
66
67int main (int argc, char **argv)
68{
69    int i, length;
70    char *buffer = NULL;
71    unsigned int buffer_size = 0;
72    unsigned int new_width = 0;
73    char *initial = NULL;
74    unsigned int no_wrap = 0;
75    cucul_buffer_t* input_buffer;
76    cucul_buffer_t* output_buffer;
77    cucul_canvas_t* canvas;
78    int buf_size;
79    char *buf_data;
80
81    if ((strstr(argv[0], "cacathink")) != NULL) {
82        think = 1;
83    }
84
85#if defined(HAVE_GETOPT_H)
86    for(;;)
87    {
88#   ifdef HAVE_GETOPT_LONG
89#       define MOREINFO "Try `%s --help' for more information.\n"
90        int option_index = 0;
91        static struct option long_options[] =
92            {
93                /* Long option, needs arg, flag, short option */
94                { "file", 1, NULL, 'f' },
95                { "directory", 1, NULL, 'D' },
96                { "width", 1, NULL, 'W' },
97                { "no-wrap", 1, NULL, 'n' },
98                { "eyes", 1, NULL, 'e' },
99                { "tongue", 1, NULL, 'T' },
100                { "borg", 1, NULL, 'b' },
101                { "tired", 1, NULL, 't' },
102                { "dead", 1, NULL, 'd' },
103                { "greedy", 1, NULL, 'g' },
104                { "parano", 1, NULL, 'p' },
105                { "stoned", 1, NULL, 's' },
106                { "youth", 1, NULL, 'y' },
107                { "wired", 1, NULL, 'w' },
108                { "think", 1, NULL, 'O' },
109                { "unicode", 1, NULL, 'u' },
110                { "version", 0, NULL, 'v' },
111                { "list-files", 0, NULL, 'l' },
112                { "help", 0, NULL, 'h' },
113                { NULL, 0, NULL, 0 }
114            };
115
116
117
118        int c = getopt_long(argc, argv, "D:f:W:e:T:hvObtdgpsylwnu",
119                            long_options, &option_index);
120#   else
121#       define MOREINFO "Try `%s -h' for more information.\n"
122        int c = getopt(argc, argv, "D:f:W:hvObtdgpsylwnu");
123#   endif
124        if(c == -1)
125            break;
126
127        switch(c)
128        {
129        case 'h': /* --help */
130            usage();
131            return 0;
132        case 'v': /* --version */
133            version();
134            return 0;
135        case 'f': /* --file*/
136            cacamoo_file = optarg;
137            break;
138        case 'D': /* --directory */
139            cacamoo_dir = optarg;
140            break;
141        case 'e': /* --eyes*/
142            cacamoo_eyes = optarg;
143            break;
144        case 'b': /* --borg*/
145            cacamoo_eyes = cacamoo_borg;
146            break;
147        case 't': /* --tired*/
148            cacamoo_eyes = cacamoo_tired;
149            break;
150        case 'd': /* --dead*/
151            cacamoo_eyes = cacamoo_dead;
152            break;
153        case 'g': /* --greedy*/
154            cacamoo_eyes = cacamoo_greedy;
155            break;
156        case 'p': /* --parano*/
157            cacamoo_eyes = cacamoo_parano;
158            break;
159        case 's': /* --stoned*/
160            cacamoo_eyes = cacamoo_stoned;
161            break;
162        case 'y': /* --youth*/
163            cacamoo_eyes = cacamoo_youth;
164            break;
165        case 'w': /* --wired*/
166            cacamoo_eyes = cacamoo_wired;
167            break;
168        case 'T': /* --tongue */
169            cacamoo_tongue = optarg;
170            break;
171        case 'O': /* --thoughts */
172            think = 1;
173            break;
174        case 'W': /* --width */
175            term_width = strtol(optarg, NULL, 10);
176            if(term_width && (term_width != 1)) term_width--;
177            break;
178        case 'l': /* --list-files */
179            list_files(".");
180            list_files(cacamoo_dir);
181            return 0;
182            break;
183        case 'u': /* --unicode */
184            unicode = 1;
185            break;
186        case 'n': /* --no-wrap */
187            no_wrap = 1;
188            break;
189        case '?':
190            printf(MOREINFO, argv[0]);
191            return 1;
192        default:
193            printf("%s: invalid option -- %c\n", argv[0], c);
194            printf(MOREINFO, argv[0]);
195            return 1;
196        }
197    }
198#else
199#   define MOREINFO "Usage: %s message...\n"
200    int optind = 1;
201#endif
202
203    if(think)
204        cacamoo_thoughts = cacamoo_think;
205
206    /* Load rest of commandline */
207    for(i = optind, length = 0; i < argc; i++)
208    {
209        unsigned int k, guessed_len, real_len = 0;
210
211        guessed_len = strlen(argv[i]);
212
213        if(i > optind)
214            string[length++] = ' ';
215
216        string = realloc(string, (length + guessed_len + 1));
217
218        for(k = 0, real_len = 0; k < guessed_len; real_len++)
219        {
220            string[length + real_len] = *(argv[i]+k);
221            k ++;
222        }
223        length += real_len;
224    }
225
226    if(string == NULL) {
227        usage();
228        return -1;
229    }
230
231    string[length] = 0;
232
233
234    /* Eyes and tongue are 2 characters wide */
235    memcpy(cacamoo_use_eyes, cacamoo_eyes, strlen(cacamoo_eyes)>2?2:strlen(cacamoo_eyes));
236    memcpy(cacamoo_use_tongue, cacamoo_tongue, strlen(cacamoo_tongue)>2?2:strlen(cacamoo_tongue));
237
238    initial = malloc(strlen(string)+1);
239    memcpy(initial, string, strlen(string)+1);
240    free(string);
241
242    wrapped = wrap_string(initial, term_width, &new_width, no_wrap);
243    string = wrapped;
244
245    buffer = make_caca_from_file(&buffer_size);
246    if(buffer == NULL)
247    {
248        if(string)
249            free(string);
250        return -1;
251    }
252
253
254    /* Import our buffer as an ansi (color) one */
255    input_buffer = cucul_load_memory(buffer, buffer_size-1);
256    if(input_buffer == NULL)
257    {
258        printf("Can't load file in libcucul !\n");
259        return -1;
260    }
261    canvas = cucul_import_canvas (input_buffer, unicode?"utf8":"ansi");
262    if(canvas == NULL)
263    {
264        printf("Can't load file in libcucul !\n");
265        return -1;
266    }
267    /* Export given canvas to format we want */
268    output_buffer = cucul_export_canvas(canvas, "utf8");
269    if(output_buffer == NULL)
270    {
271        printf("Can't export file to text !\n");
272        return -1;
273    }
274
275    buf_size = cucul_get_buffer_size(output_buffer);
276    buf_data = cucul_get_buffer_data(output_buffer);
277
278    for(i = 0; i < buf_size; i++)
279        printf("%c", buf_data[i]);
280
281    if(string)
282        free(string);
283    if(buffer)
284        free(buffer);
285
286    cucul_free_buffer(input_buffer);
287    cucul_free_buffer(output_buffer);
288    cucul_free_canvas(canvas);
289
290
291    return 0;
292}
293
294
295void list_files(const char *directory)
296{
297    struct dirent * dp;
298    int count = 0;
299    DIR *dir = opendir(directory);
300
301
302    for (dp = readdir(dir); dp != NULL; dp = readdir(dir))
303    {
304        if(!strncmp(&dp->d_name[strlen(dp->d_name)-4], ".cow", 4))
305        {
306            char name[256];
307            memcpy(name, dp->d_name, strlen(dp->d_name)-4);
308            name[strlen(dp->d_name)-4] = 0;
309            printf("%s ", name);
310            count++;
311            if(!(count%6))
312                printf("\n");
313        }
314    }
315    closedir(dir);
316    if(count)
317        printf("\n");
318    return;
319}
320
321char * make_caca_from_file(unsigned int *size)
322{
323    FILE *fp = NULL;
324    char filepath[1024];
325    unsigned int s = 0;
326    char *temp = NULL;
327
328    /* Try direct name */
329    snprintf(filepath, 1023, "%s", cacamoo_file);
330    fp = fopen(filepath, "r");
331    if(fp == NULL)
332    {
333        /* Try direct file + .cow */
334        snprintf(filepath, 1023, "%s.cow", cacamoo_file);
335        fp = fopen(filepath, "r");
336        if(fp == NULL)
337        {
338            /* Try with complete directory */
339            snprintf(filepath, 1023, "%s/%s.cow", cacamoo_dir, cacamoo_file);
340            fp = fopen(filepath, "r");
341            if(fp == NULL)
342            {
343                printf("Can't open %s\n", filepath);
344                perror("fopen");
345                return NULL;
346            }
347        }
348    }
349
350
351    fseek(fp, 0, SEEK_END);
352    s = ftell(fp);
353    fseek(fp, 0, SEEK_SET);
354
355
356    temp = malloc(s+1*sizeof(char));
357    if(temp == NULL) {
358        printf("Not enough memory.\n");
359        return NULL;
360    }
361
362    if(fread(temp, 1, s, fp)!=s)
363    {
364        printf("Can't read %s\n", filepath);
365        perror("fread");
366        return NULL;
367    }
368    temp[s] = '\0';
369
370    temp = remove_comments(temp);
371    temp = remove_slashes(temp);
372
373
374    /* AHAHAH, THAT'S A COOL PERL INTERPRETER ! */
375    temp = replace(temp, " = <<\"EOC\";", "");
376    temp = replace(temp, " = <<EOC;"    , "");
377    temp = replace(temp, " = <<EOC"     , "");
378    temp = replace(temp, " = << EOC"    , "");
379    temp = replace(temp, "EOC"          , "");
380    temp = replace(temp, "$eyes"        , cacamoo_use_eyes);
381    temp = replace(temp, "${eyes}"      , cacamoo_use_eyes);
382    temp = replace(temp, "$tongue"      , cacamoo_use_tongue);
383    temp = replace(temp, "${tongue}"    , cacamoo_use_tongue);
384    temp = replace(temp, "$thoughts"    , cacamoo_thoughts);
385    temp = replace(temp, "${thoughts}"  , cacamoo_thoughts);
386    temp = replace(temp, "$the_cow"     , (const char*)string);
387    temp = replace(temp, "${the_cow}"   , (const char*)string);
388    *size = strlen(temp)+1;
389
390    fclose(fp);
391    return temp;
392}
393char *remove_slashes(char *str)
394{
395    int i=0, size, r=0;
396
397    if(str == NULL) return NULL;
398
399    size = strlen(str);
400
401    for(i=0; i<size-r; i++)
402    {
403        if(str[i]== '\\')
404        {
405            memmove(&str[i], &str[i+1], strlen(str) - i);
406            str = realloc(str, (size-r)+1);
407            r++;
408        }
409    }
410    return str;
411}
412
413
414char *remove_comments(char *str)
415{
416    int size = 0, added=0;
417    int i=0, j;
418
419    if(str == NULL) return NULL;
420
421    size = strlen(str) + 1;
422
423    while(i < size)
424    {
425        if(str[i] == '#') {
426            for(j = i; j < size; j++)
427
428                if((str[j] == '\n') || (str[j] == '\r') || (str[j] == 0))
429                    goto hop; // just because I like goto's, break sucks
430        hop:
431            j++;
432            added += (i-j);
433            memmove(&str[i], &str[j], size-j);
434            i = j - added;
435            size -= added;
436            str = realloc(str, size);
437            str[size-1] = 0;
438            i = 0;
439        }
440        else
441            i++;
442    }
443    return str;
444}
445
446char *replace(char *s1, char *oldpiece, const char *newpiece)
447{
448    unsigned int oldlen = strlen(oldpiece), newlen = strlen(newpiece);
449    unsigned int i1 = 0, i2 = 0;
450    char *s2 = oldlen < newlen ? NULL : s1;
451
452    for(;;)
453    {
454        char *found = strstr(s1 + i1, oldpiece);
455        unsigned int tocopy;
456
457        if(!found)
458        {
459            tocopy = strlen(s1 + i1);
460            if(oldlen < newlen)
461                s2 = realloc(s2, i2 + tocopy + 1);
462            memcpy(s2 + i2, s1 + i1, tocopy + 1);
463            if(oldlen < newlen)
464                free(s1);
465            return s2;
466        }
467
468        tocopy = found - (s1 + i1);
469        if(oldlen < newlen)
470            s2 = realloc(s2, i2 + tocopy + newlen);
471        memmove(s2 + i2, s1 + i1, tocopy);
472        memcpy(s2 + i2 + tocopy, newpiece, newlen);
473        i1 += tocopy + oldlen;
474        i2 += tocopy + newlen;
475    }
476}
477
478
479static void version(void)
480{
481    printf("cacamoo Copyright 2006 Jean-Yves Lamoureux %s\n", VERSION);
482    printf("Internet: <jylam@lnscene.org> Version: 0, date: 30 Sep 2006\n");
483    printf("\n");
484}
485
486#if defined(HAVE_GETOPT_H)
487static void usage(void)
488{
489    printf("Usage: cacamoo [ -vh ] [ -d cowsdirectory ]\n");
490    printf("               [ -f cowfile ] [ -w outputwidth ]\n");
491    printf("               [-bdgpstwy]    [ message ]\n");
492#   ifdef HAVE_GETOPT_LONG
493    printf("  -f, --file <cowfile>     select the cow\n");
494    printf("  -d, --directory <dir>    specify cows directory\n");
495    printf("  -W, --width <width>      set output width\n");
496    printf("  -n  --no-wrap            do not wrap string\n");
497    printf("  -O, --think              think\n");
498    printf("  -h                       display this help and exit\n");
499    printf("  -v, --version            output version information and exit\n");
500#   else
501    printf("  -f <cowfile>     select the cow\n");
502    printf("  -d <dir>         specify cows directory\n");
503    printf("  -W <width>       set output width\n");
504    printf("  -n  --no-wrap            do not wrap string\n");
505    printf("  -O, --think      think\n");
506    printf("  -h               display this help and exit\n");
507    printf("  -v               output version information and exit\n");
508#   endif
509}
510#endif
511
512
513/* AHAHAHAHA please make no comment about this, I was in hurry \o/ */
514
515char *wrap_string(char *buffer, unsigned int width, unsigned int *max_width, int no_wrap)
516{
517    unsigned int i = 0, j =0, o = 0, last_space = 0, line = 0, offset  = 0;
518    unsigned int size = strlen(buffer) + 1, t = 0, rew = 0;
519    char *ret = NULL;
520    ret = malloc(2);
521    *max_width = 0;
522
523    /* Wrap string itself */
524    if(size > width && !no_wrap)
525    {
526        while(i<size-1)
527        {
528            if(buffer[i] == ' ')
529            {
530                last_space = i;
531                rew = o;
532            }
533            if(offset == width)
534            {
535                ret = realloc(ret, o+2);
536                if(rew)
537                    o = rew;
538                ret[o++] = '\n';
539
540                if(last_space) {
541
542                    if(width - (i - last_space) >= *max_width)
543                        *max_width = width - (i - last_space);
544
545                    i = last_space + 1;
546                    last_space = 0;
547                    rew = 0;
548
549                } else {
550                    if(width>= *max_width)
551                        *max_width = width;
552                }
553
554
555                offset = 0;
556                line ++;
557            }
558            ret = realloc(ret, o+2);
559            ret[o++] = buffer[i];
560
561            i++;
562            offset++;
563        }
564        if(offset>= *max_width)
565            *max_width = offset;
566        if(!(*max_width))
567            *max_width = size-1;
568
569        ret[o] = 0;
570        line++;
571    }
572    else
573    {
574        *max_width = strlen(buffer);
575        if(ret)
576            free(ret);
577        ret = buffer;
578        line = 1;
579    }
580
581    /* String is wrapped, put spaces after each line */
582    if(line != 1)
583    {
584        char *scaled = malloc(((*max_width+1) * line) + 1);
585        int curx = 0;
586
587        memset(scaled, ' ', (*max_width * line));
588        o = 0;
589        for(i = 0; i < strlen(ret); i ++)
590        {
591            if(ret[i] != '\n')
592            {
593                scaled[o] = ret[i];
594                curx++;
595            }
596            else
597            {
598                for(j=o;j<o+(*max_width - curx);j++)
599                {
600                    scaled[j] = ' ';
601
602                }
603                o += ((*max_width) - curx) -1;
604                curx = 0;
605            }
606            o++;
607        }
608        for(i = o; i <o+(*max_width - curx); i ++)
609        {
610            scaled[i] = ' ';
611        }
612
613        scaled[o+i] = 0;
614        if(ret)
615            free(ret);
616        ret = scaled;
617
618
619        /* Put balloon */
620        o = 0;
621        scaled = malloc((*max_width+5) * (line+2));
622
623        scaled[t++] = ' ';
624        for(i = 0; i < *max_width+2; i++)
625            scaled[t++] = '_';
626        scaled[t++] = ' ';
627        scaled[t++] = '\n';
628
629
630        for(j = 0; j < line ; j++)
631        {
632            if(think)
633            {
634                scaled[t++] = '(';
635            }
636            else
637            {
638                if(j == 0)
639                    scaled[t++] = '/';
640                else if (j == line -1)
641                    scaled[t++] = '\\';
642                else
643                    scaled[t++] = '|';
644            }
645            scaled[t++] = ' ';
646
647            for(i = 0; i < *max_width; i++)
648            {
649                scaled[t++] = ret[o++];
650            }
651            scaled[t++] = ' ';
652            if(think)
653            {
654                scaled[t++] = ')';
655            }
656            else
657            {
658                if(j == 0)
659                    scaled[t++] = '\\';
660                else if (j == line -1)
661                    scaled[t++] = '/';
662                else
663                    scaled[t++] = '|';
664            }
665            scaled[t++] = '\n';
666        }
667
668        scaled[t++] = ' ';
669        for(i = 0; i < *max_width+2; i++)
670            scaled[t++] = '-';
671        scaled[t++] = ' ';
672        scaled[t] = 0;
673
674        free(ret);
675        ret = NULL;
676        ret = scaled;
677    }
678    else
679    {
680        /* Put ballon */
681        char *scaled = malloc((size+4) * 3);
682        t = 0;
683        *max_width = size -1 ;
684        o = 0;
685        scaled[t++] = ' ';
686        for(i = 0; i < *max_width+2; i++)
687            scaled[t++] = '_';
688        scaled[t++] = ' ';
689        scaled[t++] = '\n';
690        if(think)
691            scaled[t++] = '(';
692        else
693            scaled[t++] = '<';
694        scaled[t++] = ' ';
695        for(i = 0; i < *max_width; i++)
696        {
697            scaled[t++] = ret[o++];
698        }
699        scaled[t++] = ' ';
700        if(think)
701            scaled[t++] = ')';
702        else
703            scaled[t++] = '>';
704
705        scaled[t++] = '\n';
706        scaled[t++] = ' ';
707        for(i = 0; i < *max_width+2; i++)
708            scaled[t++] = '-';
709        scaled[t] = '\0';
710
711        free(ret);
712        ret = NULL;
713        ret = scaled;
714    }
715
716
717    return ret;
718}
719
Note: See TracBrowser for help on using the repository browser.