您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 

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