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

Last change on this file since 2979 was 2979, checked in by Sam Hocevar, 12 years ago

Port cacamoo to the unified libcaca 0.99.beta15 API.

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