719 lines
18 KiB

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