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

Last change on this file since 1252 was 1252, checked in by Sam Hocevar, 14 years ago
  • Fixed buffer overflow in replace().
  • Property svn:keywords set to Id
File size: 18.0 KB
Line 
1/*
2 *  cacamoo
3 *  Copyright (c) 2006 Jean-Yves Lamoureux <jylam@lnxscene.org>
4 *                All Rights Reserved
5 *
6 *  $Id: main.c 1252 2006-10-28 09:00:58Z sam $
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
327    /* Try direct name */
328    snprintf(filepath, 1023, "%s", cacamoo_file);
329    fp = fopen(filepath, "r");
330    if(fp == NULL)
331    {
332        /* Try direct file + .cow */
333        snprintf(filepath, 1023, "%s.cow", cacamoo_file);
334        fp = fopen(filepath, "r");
335        if(fp == NULL)
336        {
337            /* Try with complete directory */
338            snprintf(filepath, 1023, "%s/%s.cow", cacamoo_dir, cacamoo_file);
339            fp = fopen(filepath, "r");
340            if(fp == NULL)
341            {
342                printf("Can't open %s\n", filepath);
343                perror("fopen");
344                return NULL;
345            }
346        }
347    }
348
349
350    fseek(fp, 0, SEEK_END);
351    s = ftell(fp);
352    fseek(fp, 0, SEEK_SET);
353
354
355    temp = malloc(s+1*sizeof(char));
356    if(temp == NULL) {
357        printf("Not enough memory.\n");
358        return NULL;
359    }
360
361    if(fread(temp, 1, s, fp)!=s)
362    {
363        printf("Can't read %s\n", filepath);
364        perror("fread");
365        return NULL;
366    }
367
368
369    temp = remove_comments(temp);
370    temp = remove_slashes(temp);
371
372
373    /* AHAHAH, THAT'S A COOL PERL INTERPRETER ! */
374    temp = replace(temp, " = <<\"EOC\";", "");
375    temp = replace(temp, " = <<EOC;"    , "");
376    temp = replace(temp, " = <<EOC"     , "");
377    temp = replace(temp, " = << EOC"    , "");
378    temp = replace(temp, "EOC"          , "");
379    temp = replace(temp, "$eyes"        , cacamoo_use_eyes);
380    temp = replace(temp, "${eyes}"      , cacamoo_use_eyes);
381    temp = replace(temp, "$tongue"      , cacamoo_use_tongue);
382    temp = replace(temp, "${tongue}"    , cacamoo_use_tongue);
383    temp = replace(temp, "$thoughts"    , cacamoo_thoughts);
384    temp = replace(temp, "${thoughts}"  , cacamoo_thoughts);
385    temp = replace(temp, "$the_cow"     , (const char*)string);
386    temp = replace(temp, "${the_cow}"   , (const char*)string);
387    *size = strlen(temp)+1;
388
389    fclose(fp);
390    return temp;
391}
392char *remove_slashes(char *str)
393{
394    int i=0, size, r=0;
395
396    if(str == NULL) return NULL;
397
398    size = strlen(str);
399
400    for(i=0; i<size-r; i++)
401    {
402        if(str[i]== '\\')
403        {
404            memmove(&str[i], &str[i+1], strlen(str) - i);
405            str = realloc(str, (size-r)+1);
406            r++;
407        }
408    }
409    return str;
410}
411
412
413char *remove_comments(char *str)
414{
415    int size = 0, added=0;
416    int i=0, j;
417
418    if(str == NULL) return NULL;
419
420    size = strlen(str) + 1;
421
422    while(i < size)
423    {
424        if(str[i] == '#') {
425            for(j = i; j < size; j++)
426
427                if((str[j] == '\n') || (str[j] == '\r') || (str[j] == 0))
428                    goto hop; // just because I like goto's, break sucks
429        hop:
430            j++;
431            added += (i-j);
432            memmove(&str[i], &str[j], size-j);
433            i = j - added;
434            size -= added;
435            str = realloc(str, size);
436            str[size-1] = 0;
437            i = 0;
438        }
439        else
440            i++;
441    }
442    return str;
443}
444
445char *replace(char *s1, char *oldpiece, const char *newpiece)
446{
447    unsigned int oldlen = strlen(oldpiece), newlen = strlen(newpiece);
448    unsigned int i1 = 0, i2 = 0;
449    char *s2 = oldlen < newlen ? NULL : s1;
450
451    for(;;)
452    {
453        char *found = strstr(s1 + i1, oldpiece);
454        unsigned int tocopy;
455
456        if(!found)
457        {
458            tocopy = strlen(s1 + i1);
459            if(oldlen < newlen)
460                s2 = realloc(s2, i2 + tocopy + 1);
461            memmove(s2 + i2, s1 + i1, tocopy + 1);
462            if(oldlen < newlen)
463                free(s1);
464            return s2;
465        }
466
467        tocopy = found - (s1 + i1);
468        if(oldlen < newlen)
469            s2 = realloc(s2, i2 + tocopy + newlen);
470        memmove(s2 + i2, s1 + i1, tocopy);
471        memcpy(s2 + tocopy, newpiece, newlen);
472        i1 += tocopy + oldlen;
473        i2 += tocopy + newlen;
474    }
475}
476
477
478static void version(void)
479{
480    printf("cacamoo Copyright 2006 Jean-Yves Lamoureux %s\n", VERSION);
481    printf("Internet: <jylam@lnscene.org> Version: 0, date: 30 Sep 2006\n");
482    printf("\n");
483}
484
485#if defined(HAVE_GETOPT_H)
486static void usage(void)
487{
488    printf("Usage: cacamoo [ -vh ] [ -d cowsdirectory ]\n");
489    printf("               [ -f cowfile ] [ -w outputwidth ]\n");
490    printf("               [-bdgpstwy]    [ message ]\n");
491#   ifdef HAVE_GETOPT_LONG
492    printf("  -f, --file <cowfile>     select the cow\n");
493    printf("  -d, --directory <dir>    specify cows directory\n");
494    printf("  -W, --width <width>      set output width\n");
495    printf("  -n  --no-wrap            do not wrap string\n");
496    printf("  -O, --think              think\n");
497    printf("  -h                       display this help and exit\n");
498    printf("  -v, --version            output version information and exit\n");
499#   else
500    printf("  -f <cowfile>     select the cow\n");
501    printf("  -d <dir>         specify cows directory\n");
502    printf("  -W <width>       set output width\n");
503    printf("  -n  --no-wrap            do not wrap string\n");
504    printf("  -O, --think      think\n");
505    printf("  -h               display this help and exit\n");
506    printf("  -v               output version information and exit\n");
507#   endif
508}
509#endif
510
511
512/* AHAHAHAHA please make no comment about this, I was in hurry \o/ */
513
514char *wrap_string(char *buffer, unsigned int width, unsigned int *max_width, int no_wrap)
515{
516    unsigned int i = 0, j =0, o = 0, last_space = 0, line = 0, offset  = 0;
517    unsigned int size = strlen(buffer) + 1, t = 0, rew = 0;
518    char *ret = NULL;
519    ret = malloc(2);
520    *max_width = 0;
521
522    /* Wrap string itself */
523    if(size > width && !no_wrap)
524    {
525        while(i<size-1)
526        {
527            if(buffer[i] == ' ')
528            {
529                last_space = i;
530                rew = o;
531            }
532            if(offset == width)
533            {
534                ret = realloc(ret, o+2);
535                if(rew)
536                    o = rew;
537                ret[o++] = '\n';
538
539                if(last_space) {
540
541                    if(width - (i - last_space) >= *max_width)
542                        *max_width = width - (i - last_space);
543
544                    i = last_space + 1;
545                    last_space = 0;
546                    rew = 0;
547
548                } else {
549                    if(width>= *max_width)
550                        *max_width = width;
551                }
552
553
554                offset = 0;
555                line ++;
556            }
557            ret = realloc(ret, o+2);
558            ret[o++] = buffer[i];
559
560            i++;
561            offset++;
562        }
563        if(offset>= *max_width)
564            *max_width = offset;
565        if(!(*max_width))
566            *max_width = size-1;
567
568        ret[o] = 0;
569        line++;
570    }
571    else
572    {
573        *max_width = strlen(buffer);
574        if(ret)
575            free(ret);
576        ret = buffer;
577        line = 1;
578    }
579
580    /* String is wrapped, put spaces after each line */
581    if(line != 1)
582    {
583        char *scaled = malloc(((*max_width+1) * line) + 1);
584        int curx = 0;
585
586        memset(scaled, ' ', (*max_width * line));
587        o = 0;
588        for(i = 0; i < strlen(ret); i ++)
589        {
590            if(ret[i] != '\n')
591            {
592                scaled[o] = ret[i];
593                curx++;
594            }
595            else
596            {
597                for(j=o;j<o+(*max_width - curx);j++)
598                {
599                    scaled[j] = ' ';
600
601                }
602                o += ((*max_width) - curx) -1;
603                curx = 0;
604            }
605            o++;
606        }
607        for(i = o; i <o+(*max_width - curx); i ++)
608        {
609            scaled[i] = ' ';
610        }
611
612        scaled[o+i] = 0;
613        if(ret)
614            free(ret);
615        ret = scaled;
616
617
618        /* Put balloon */
619        o = 0;
620        scaled = malloc((*max_width+5) * (line+2));
621
622        scaled[t++] = ' ';
623        for(i = 0; i < *max_width+2; i++)
624            scaled[t++] = '_';
625        scaled[t++] = ' ';
626        scaled[t++] = '\n';
627
628
629        for(j = 0; j < line ; j++)
630        {
631            if(think)
632            {
633                scaled[t++] = '(';
634            }
635            else
636            {
637                if(j == 0)
638                    scaled[t++] = '/';
639                else if (j == line -1)
640                    scaled[t++] = '\\';
641                else
642                    scaled[t++] = '|';
643            }
644            scaled[t++] = ' ';
645
646            for(i = 0; i < *max_width; i++)
647            {
648                scaled[t++] = ret[o++];
649            }
650            scaled[t++] = ' ';
651            if(think)
652            {
653                scaled[t++] = ')';
654            }
655            else
656            {
657                if(j == 0)
658                    scaled[t++] = '\\';
659                else if (j == line -1)
660                    scaled[t++] = '/';
661                else
662                    scaled[t++] = '|';
663            }
664            scaled[t++] = '\n';
665        }
666
667        scaled[t++] = ' ';
668        for(i = 0; i < *max_width+2; i++)
669            scaled[t++] = '-';
670        scaled[t++] = ' ';
671        scaled[t] = 0;
672
673        free(ret);
674        ret = NULL;
675        ret = scaled;
676    }
677    else
678    {
679        /* Put ballon */
680        char *scaled = malloc((size+4) * 3);
681        t = 0;
682        *max_width = size -1 ;
683        o = 0;
684        scaled[t++] = ' ';
685        for(i = 0; i < *max_width+2; i++)
686            scaled[t++] = '_';
687        scaled[t++] = ' ';
688        scaled[t++] = '\n';
689        if(think)
690            scaled[t++] = '(';
691        else
692            scaled[t++] = '<';
693        scaled[t++] = ' ';
694        for(i = 0; i < *max_width; i++)
695        {
696            scaled[t++] = ret[o++];
697        }
698        scaled[t++] = ' ';
699        if(think)
700            scaled[t++] = ')';
701        else
702            scaled[t++] = '>';
703
704        scaled[t++] = '\n';
705        scaled[t++] = ' ';
706        for(i = 0; i < *max_width+2; i++)
707            scaled[t++] = '-';
708
709        free(ret);
710        ret = NULL;
711        ret = scaled;
712    }
713
714
715    return ret;
716}
717
Note: See TracBrowser for help on using the repository browser.