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

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