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

Last change on this file since 1251 was 1251, checked in by Jean-Yves Lamoureux, 14 years ago
  • Fixed buffer overflow in replace function (still need to be fixed in a more clever way)
  • Property svn:keywords set to Id
File size: 19.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 1251 2006-10-28 08:22:58Z 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    cucul_free_buffer(input_buffer);
286    cucul_free_buffer(output_buffer);
287    cucul_free_canvas(canvas);
288
289
290    return 0;
291}
292
293
294void list_files(const char *directory)
295{
296    struct dirent * dp;
297    int count = 0;
298    DIR *dir = opendir(directory);
299
300
301    for (dp = readdir(dir); dp != NULL; dp = readdir(dir))
302    {
303        if(!strncmp(&dp->d_name[strlen(dp->d_name)-4], ".cow", 4))
304        {
305            char name[256];
306            memcpy(name, dp->d_name, strlen(dp->d_name)-4);
307            name[strlen(dp->d_name)-4] = 0;
308            printf("%s ", name);
309            count++;
310            if(!(count%6))
311                printf("\n");
312        }
313    }
314    closedir(dir);
315    if(count)
316        printf("\n");
317    return;
318}
319
320char * make_caca_from_file(unsigned int *size)
321{
322    FILE *fp = NULL;
323    char filepath[1024];
324    unsigned int s = 0;
325    char *temp = NULL;
326    char *temp2 = 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
369
370    temp = remove_comments(temp);
371    temp = remove_slashes(temp);
372
373
374    /* AHAHAH, THAT'S A COOL PERL INTERPRETER ! */
375    temp2 = replace(temp, " = <<\"EOC\";", "");
376    if(temp!=temp2 && temp2 !=NULL)
377    {
378        free(temp);
379        temp = temp2;
380    }
381    temp2 = replace(temp, " = <<EOC;"    , "");
382    if(temp!=temp2 && temp2 !=NULL)
383    {
384        free(temp);
385        temp = temp2;
386    }
387    temp2 = replace(temp, " = <<EOC"     , "");
388    if(temp!=temp2 && temp2 !=NULL)
389    {
390        free(temp);
391        temp = temp2;
392    }
393    temp2 = replace(temp, " = << EOC"    , "");
394    if(temp!=temp2 && temp2 !=NULL)
395    {
396        free(temp);
397        temp = temp2;
398    }
399    temp2 = replace(temp, "EOC"          , "");
400    if(temp!=temp2 && temp2 !=NULL)
401    {
402        free(temp);
403        temp = temp2;
404    }
405    temp2 = replace(temp, "$eyes"        , cacamoo_use_eyes);
406    if(temp!=temp2 && temp2 !=NULL)
407    {
408        free(temp);
409        temp = temp2;
410    }
411    temp2 = replace(temp, "${eyes}"      , cacamoo_use_eyes);
412    if(temp!=temp2 && temp2 !=NULL)
413    {
414        free(temp);
415        temp = temp2;
416    }
417    temp2 = replace(temp, "$tongue"      , cacamoo_use_tongue);
418    if(temp!=temp2 && temp2 !=NULL)
419    {
420        free(temp);
421        temp = temp2;
422    }
423    temp2 = replace(temp, "${tongue}"    , cacamoo_use_tongue);
424    if(temp!=temp2 && temp2 !=NULL)
425    {
426        free(temp);
427        temp = temp2;
428    }
429    temp2 = replace(temp, "$thoughts"    , cacamoo_thoughts);
430    if(temp!=temp2 && temp2 !=NULL)
431    {
432        free(temp);
433        temp = temp2;
434    }
435    temp2 = replace(temp, "${thoughts}"  , cacamoo_thoughts);
436    if(temp!=temp2 && temp2 !=NULL)
437    {
438        free(temp);
439        temp = temp2;
440    }
441    temp2 = replace(temp, "$the_cow"     , (const char*)string);
442    if(temp!=temp2 && temp2 !=NULL)
443    {
444        free(temp);
445        temp = temp2;
446    }
447    temp2 = replace(temp, "${the_cow}"   , (const char*)string);
448    if(temp!=temp2 && temp2 !=NULL)
449    {
450        free(temp);
451        temp = temp2;
452    }
453    *size = strlen(temp)+1;
454
455
456    fclose(fp);
457    return temp;
458}
459char *remove_slashes(char *str)
460{
461    int i=0, size, r=0;
462
463    if(str == NULL) return NULL;
464
465    size = strlen(str);
466
467    for(i=0; i<size-r; i++)
468    {
469        if(str[i]== '\\')
470        {
471            memmove(&str[i], &str[i+1], strlen(str) - i);
472            str = realloc(str, (size-r)+1);
473            r++;
474        }
475    }
476    return str;
477}
478
479
480char *remove_comments(char *str)
481{
482    int size = 0, added=0;
483    int i=0, j;
484
485    if(str == NULL) return NULL;
486
487    size = strlen(str) + 1;
488
489    while(i < size)
490    {
491        if(str[i] == '#') {
492            for(j = i; j < size; j++)
493
494                if((str[j] == '\n') || (str[j] == '\r') || (str[j] == 0))
495                    goto hop; // just because I like goto's, break sucks
496        hop:
497            j++;
498            added += (i-j);
499            memmove(&str[i], &str[j], size-j);
500            i = j - added;
501            size -= added;
502            str = realloc(str, size);
503            str[size-1] = 0;
504            i = 0;
505        }
506        else
507            i++;
508    }
509    return str;
510}
511
512char *replace(char *str, char *oldpiece, const char *newpiece)
513{
514    int str_index, newstr_index, oldpiece_index, end,
515        new_len, old_len, cpy_len;
516    char *c = NULL;
517    char *newstr = NULL;
518    char *orig = str;
519
520    if(oldpiece==NULL || newpiece==NULL)
521        return NULL;
522
523    if ((c = (char *) strstr(str, oldpiece)) == NULL)
524        return str;
525
526
527    newstr = malloc(8192); // FIXME
528
529    if(newstr == NULL)
530    {
531        return str;
532    }
533
534    new_len        = strlen(newpiece);
535    old_len        = strlen(oldpiece);
536    end            = strlen(str)   - old_len;
537    oldpiece_index = c - str;
538
539    newstr_index = 0;
540    str_index = 0;
541    while(str_index <= end && c != NULL)
542    {
543        /* Copy characters from the left of matched pattern occurence */
544        cpy_len = oldpiece_index-str_index;
545        strncpy(newstr+newstr_index, str+str_index, cpy_len);
546        newstr_index += cpy_len;
547        str_index    += cpy_len;
548
549        /* Copy replacement characters instead of matched pattern */
550        strcpy(newstr+newstr_index, newpiece);
551        newstr_index += new_len;
552        str_index    += old_len;
553
554        /* Check for another pattern match */
555        if((c = (char *) strstr(str+str_index, oldpiece)) != NULL)
556            oldpiece_index = c - str;
557    }
558    /* Copy remaining characters from the right of last matched pattern */
559    strcpy(newstr+newstr_index, str+str_index);
560
561    str = orig;
562    return newstr;
563}
564
565
566static void version(void)
567{
568    printf("cacamoo Copyright 2006 Jean-Yves Lamoureux %s\n", VERSION);
569    printf("Internet: <jylam@lnscene.org> Version: 0, date: 30 Sep 2006\n");
570    printf("\n");
571}
572
573#if defined(HAVE_GETOPT_H)
574static void usage(void)
575{
576    printf("Usage: cacamoo [ -vh ] [ -d cowsdirectory ]\n");
577    printf("               [ -f cowfile ] [ -w outputwidth ]\n");
578    printf("               [-bdgpstwy]    [ message ]\n");
579#   ifdef HAVE_GETOPT_LONG
580    printf("  -f, --file <cowfile>     select the cow\n");
581    printf("  -d, --directory <dir>    specify cows directory\n");
582    printf("  -W, --width <width>      set output width\n");
583    printf("  -n  --no-wrap            do not wrap string\n");
584    printf("  -O, --think              think\n");
585    printf("  -h                       display this help and exit\n");
586    printf("  -v, --version            output version information and exit\n");
587#   else
588    printf("  -f <cowfile>     select the cow\n");
589    printf("  -d <dir>         specify cows directory\n");
590    printf("  -W <width>       set output width\n");
591    printf("  -n  --no-wrap            do not wrap string\n");
592    printf("  -O, --think      think\n");
593    printf("  -h               display this help and exit\n");
594    printf("  -v               output version information and exit\n");
595#   endif
596}
597#endif
598
599
600/* AHAHAHAHA please make no comment about this, I was in hurry \o/ */
601
602char *wrap_string(char *buffer, unsigned int width, unsigned int *max_width, int no_wrap)
603{
604    unsigned int i = 0, j =0, o = 0, last_space = 0, line = 0, offset  = 0;
605    unsigned int size = strlen(buffer) + 1, t = 0, rew = 0;
606    char *ret = NULL;
607    ret = malloc(2);
608    *max_width = 0;
609
610    /* Wrap string itself */
611    if(size > width && !no_wrap)
612    {
613        while(i<size-1)
614        {
615            if(buffer[i] == ' ')
616            {
617                last_space = i;
618                rew = o;
619            }
620            if(offset == width)
621            {
622                ret = realloc(ret, o+2);
623                if(rew)
624                    o = rew;
625                ret[o++] = '\n';
626
627                if(last_space) {
628
629                    if(width - (i - last_space) >= *max_width)
630                        *max_width = width - (i - last_space);
631
632                    i = last_space + 1;
633                    last_space = 0;
634                    rew = 0;
635
636                } else {
637                    if(width>= *max_width)
638                        *max_width = width;
639                }
640
641
642                offset = 0;
643                line ++;
644            }
645            ret = realloc(ret, o+2);
646            ret[o++] = buffer[i];
647
648            i++;
649            offset++;
650        }
651        if(offset>= *max_width)
652            *max_width = offset;
653        if(!(*max_width))
654            *max_width = size-1;
655
656        ret[o] = 0;
657        line++;
658    }
659    else
660    {
661        *max_width = strlen(buffer);
662        if(ret)
663            free(ret);
664        ret = buffer;
665        line = 1;
666    }
667
668    /* String is wrapped, put spaces after each line */
669    if(line != 1)
670    {
671        char *scaled = malloc(((*max_width+1) * line) + 1);
672        int curx = 0;
673
674        memset(scaled, ' ', (*max_width * line));
675        o = 0;
676        for(i = 0; i < strlen(ret); i ++)
677        {
678            if(ret[i] != '\n')
679            {
680                scaled[o] = ret[i];
681                curx++;
682            }
683            else
684            {
685                for(j=o;j<o+(*max_width - curx);j++)
686                {
687                    scaled[j] = ' ';
688
689                }
690                o += ((*max_width) - curx) -1;
691                curx = 0;
692            }
693            o++;
694        }
695        for(i = o; i <o+(*max_width - curx); i ++)
696        {
697            scaled[i] = ' ';
698        }
699
700        scaled[o+i] = 0;
701        if(ret)
702            free(ret);
703        ret = scaled;
704
705
706        /* Put balloon */
707        o = 0;
708        scaled = malloc((*max_width+5) * (line+2));
709
710        scaled[t++] = ' ';
711        for(i = 0; i < *max_width+2; i++)
712            scaled[t++] = '_';
713        scaled[t++] = ' ';
714        scaled[t++] = '\n';
715
716
717        for(j = 0; j < line ; j++)
718        {
719            if(think)
720            {
721                scaled[t++] = '(';
722            }
723            else
724            {
725                if(j == 0)
726                    scaled[t++] = '/';
727                else if (j == line -1)
728                    scaled[t++] = '\\';
729                else
730                    scaled[t++] = '|';
731            }
732            scaled[t++] = ' ';
733
734            for(i = 0; i < *max_width; i++)
735            {
736                scaled[t++] = ret[o++];
737            }
738            scaled[t++] = ' ';
739            if(think)
740            {
741                scaled[t++] = ')';
742            }
743            else
744            {
745                if(j == 0)
746                    scaled[t++] = '\\';
747                else if (j == line -1)
748                    scaled[t++] = '/';
749                else
750                    scaled[t++] = '|';
751            }
752            scaled[t++] = '\n';
753        }
754
755        scaled[t++] = ' ';
756        for(i = 0; i < *max_width+2; i++)
757            scaled[t++] = '-';
758        scaled[t++] = ' ';
759        scaled[t] = 0;
760
761        free(ret);
762        ret = NULL;
763        ret = scaled;
764    }
765    else
766    {
767        /* Put ballon */
768        char *scaled = malloc((size+4) * 3);
769        t = 0;
770        *max_width = size -1 ;
771        o = 0;
772        scaled[t++] = ' ';
773        for(i = 0; i < *max_width+2; i++)
774            scaled[t++] = '_';
775        scaled[t++] = ' ';
776        scaled[t++] = '\n';
777        if(think)
778            scaled[t++] = '(';
779        else
780            scaled[t++] = '<';
781        scaled[t++] = ' ';
782        for(i = 0; i < *max_width; i++)
783        {
784            scaled[t++] = ret[o++];
785        }
786        scaled[t++] = ' ';
787        if(think)
788            scaled[t++] = ')';
789        else
790            scaled[t++] = '>';
791
792        scaled[t++] = '\n';
793        scaled[t++] = ' ';
794        for(i = 0; i < *max_width+2; i++)
795            scaled[t++] = '-';
796
797        free(ret);
798        ret = NULL;
799        ret = scaled;
800    }
801
802
803    return ret;
804}
805
Note: See TracBrowser for help on using the repository browser.