mystic.cpp 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <unistd.h> // usleep()
  4. #include <fcntl.h>
  5. #include <pty.h>
  6. #include <termios.h>
  7. #include <sys/select.h>
  8. #include <sys/wait.h>
  9. // #include <signal.h> // handle Ctrl-C/SIGINT
  10. #include <strings.h> // strcasecmp
  11. #include <time.h> // usleep(), nanonsleep() ?
  12. #include <ctype.h>
  13. #include <stdlib.h> // random()
  14. #include <regex.h>
  15. /* Log level guideline:
  16. * - ZF_LOG_FATAL - happened something impossible and absolutely unexpected.
  17. * Process can't continue and must be terminated.
  18. * Example: division by zero, unexpected modifications from other thread.
  19. * - ZF_LOG_ERROR - happened something possible, but highly unexpected. The
  20. * process is able to recover and continue execution.
  21. * Example: out of memory (could also be FATAL if not handled properly).
  22. * - ZF_LOG_WARN - happened something that *usually* should not happen and
  23. * significantly changes application behavior for some period of time.
  24. * Example: configuration file not found, auth error.
  25. * - ZF_LOG_INFO - happened significant life cycle event or major state
  26. * transition.
  27. * Example: app started, user logged in.
  28. * - ZF_LOG_DEBUG - minimal set of events that could help to reconstruct the
  29. * execution path. Usually disabled in release builds.
  30. * - ZF_LOG_VERBOSE - all other events. Usually disabled in release builds.
  31. *
  32. * *Ideally*, log file of debugged, well tested, production ready application
  33. * should be empty or very small. Choosing a right log level is as important as
  34. * providing short and self descriptive log message.
  35. */
  36. /*
  37. #define ZF_LOG_VERBOSE 1
  38. #define ZF_LOG_DEBUG 2
  39. #define ZF_LOG_INFO 3
  40. #define ZF_LOG_WARN 4
  41. #define ZF_LOG_ERROR 5
  42. #define ZF_LOG_FATAL 6
  43. */
  44. // LOGGING with file output
  45. #include "zf_log.h"
  46. FILE *g_log_file;
  47. static void file_output_callback(const zf_log_message *msg, void *arg) {
  48. (void)arg;
  49. *msg->p = '\n';
  50. fwrite(msg->buf, msg->p - msg->buf + 1, 1, g_log_file);
  51. fflush(g_log_file);
  52. }
  53. static void file_output_close(void) { fclose(g_log_file); }
  54. static void file_output_open(const char *const log_path) {
  55. g_log_file = fopen(log_path, "a");
  56. if (!g_log_file) {
  57. ZF_LOGW("Failed to open log file %s", log_path);
  58. return;
  59. }
  60. atexit(file_output_close);
  61. zf_log_set_output_v(ZF_LOG_PUT_STD, 0, file_output_callback);
  62. }
  63. /**
  64. * Display a repr of the given string.
  65. *
  66. * This converts most \n\r\v\f\t codes,
  67. * defaults to \xHH (hex value).
  68. */
  69. const char *repr(const char *data) {
  70. static char buffer[4096];
  71. char *cp;
  72. strcpy(buffer, data);
  73. cp = buffer;
  74. while (*cp != 0) {
  75. char c = *cp;
  76. if (isspace(c)) {
  77. if (c == ' ') {
  78. cp++;
  79. continue;
  80. };
  81. /* Ok, it's form-feed ('\f'), newline ('\n'), carriage return ('\r'),
  82. * horizontal tab ('\t'), and vertical tab ('\v') */
  83. memmove(cp + 1, cp, strlen(cp) + 1);
  84. *cp = '\\';
  85. cp++;
  86. switch (c) {
  87. case '\f':
  88. *cp = 'f';
  89. cp++;
  90. break;
  91. case '\n':
  92. *cp = 'n';
  93. cp++;
  94. break;
  95. case '\r':
  96. *cp = 'r';
  97. cp++;
  98. break;
  99. case '\t':
  100. *cp = 't';
  101. cp++;
  102. break;
  103. case '\v':
  104. *cp = 'v';
  105. cp++;
  106. break;
  107. default:
  108. *cp = '?';
  109. cp++;
  110. break;
  111. }
  112. continue;
  113. }
  114. if (isprint(c)) {
  115. cp++;
  116. continue;
  117. };
  118. // Ok, default to \xHH output.
  119. memmove(cp + 3, cp, strlen(cp) + 1);
  120. *cp = '\\';
  121. cp++;
  122. *cp = 'x';
  123. cp++;
  124. char buffer[3];
  125. sprintf(buffer, "%02x", (int)c & 0xff);
  126. *cp = buffer[0];
  127. cp++;
  128. *cp = buffer[1];
  129. cp++;
  130. continue;
  131. }
  132. return buffer;
  133. }
  134. #define CHUNKSIZE 40
  135. void zf_repr_chunk(int LOG_LEVEL, const char *chunk) {
  136. const char *output = repr(chunk);
  137. ZF_LOG_WRITE(LOG_LEVEL, _ZF_LOG_TAG, "r\"%s\"", output);
  138. }
  139. void zf_repr(int LOG_LEVEL, const char *string) {
  140. // Chunks -- 32?
  141. char buffer[CHUNKSIZE + 1];
  142. int pos = 0;
  143. int len = strlen(string);
  144. while (len > CHUNKSIZE) {
  145. strncpy(buffer, string + pos, CHUNKSIZE);
  146. buffer[CHUNKSIZE] = 0;
  147. pos += CHUNKSIZE;
  148. len -= CHUNKSIZE;
  149. zf_repr_chunk(LOG_LEVEL, buffer);
  150. };
  151. if (len > 0) {
  152. strcpy(buffer, string + pos);
  153. zf_repr_chunk(LOG_LEVEL, buffer);
  154. }
  155. }
  156. // Here's my idea for debugging
  157. #define ZF_LOGV_REPR(str) zf_repr(ZF_LOG_VERBOSE, str)
  158. #define ZF_LOGD_REPR(str) zf_repr(ZF_LOG_DEBUG, str)
  159. #define ZF_LOGI_REPR(str) zf_repr(ZF_LOG_INFO, str)
  160. #define ZF_LOGW_REPR(str) zf_repr(ZF_LOG_WARN, str)
  161. #define ZF_LOGE_REPR(str) zf_repr(ZF_LOG_ERROR, str)
  162. #define ZF_LOGF_REPR(str) zf_repr(ZF_LOG_FATAL, str)
  163. // END LOGGING
  164. #include "lastseen.h"
  165. // http://c-faq.com/lib/randrange.html
  166. int randint(int N) { return rand() / (RAND_MAX / N + 1); }
  167. // http://c-faq.com/lib/randrange.html
  168. // numbers in the range [M, N] could be generated with something like
  169. int randrange(int M, int N) {
  170. return M + rand() / (RAND_MAX / (N - M + 1) + 1);
  171. }
  172. /*
  173. * strnstr()
  174. *
  175. * buffer safe version that looks for a string.
  176. */
  177. const char *strnstr(const char *source, int len, const char *needle) {
  178. int pos;
  179. for (pos = 0; pos < len; pos++) {
  180. if (source[pos] == needle[0]) {
  181. if (strncmp(source + pos, needle, strlen(needle)) == 0) {
  182. return source + pos;
  183. }
  184. }
  185. }
  186. return NULL;
  187. }
  188. /*
  189. What is the name of the actual, real Mystic executable
  190. that we'll be executing and mangling?
  191. */
  192. #define TARGET "./mySTIC"
  193. // Size of our input and output buffers.
  194. #define BSIZE 1024
  195. /*
  196. * string_insert()
  197. * Inserts a string into a given position.
  198. * This safely checks to make sure the buffer isn't overrun.
  199. */
  200. int string_insert(char *buffer, int max_length, int pos, const char *insert) {
  201. if (strlen(buffer) + strlen(insert) >= max_length) {
  202. ZF_LOGD("string_insert() failed inserting [%s]", repr(insert));
  203. return 0;
  204. }
  205. memmove(buffer + pos + strlen(insert), buffer + pos,
  206. strlen(buffer + pos) + 1);
  207. // cp + strlen(display), cp, strlen(cp) + 1);
  208. strncpy(buffer + pos, insert, strlen(insert));
  209. // (cp, display, strlen(display));
  210. return 1; // success
  211. }
  212. // Should this be passed around to the functions that use it?
  213. struct render {
  214. int speed;
  215. int effect;
  216. } current_render;
  217. int render_overlimit = 0;
  218. void reset_render(void) {
  219. current_render.speed = 0;
  220. current_render.effect = 0;
  221. render_overlimit = 0;
  222. }
  223. #define TRIGGER "^"
  224. // Max limit we'll sleep before ignoring effects/speed.
  225. #define SLEEP_LIMIT 30
  226. int ms_sleep(unsigned int ms) {
  227. int result = 0;
  228. struct timespec ts = {ms / 1000, (ms % 1000) * 1000000L};
  229. do {
  230. struct timespec ts_sleep = ts;
  231. result = nanosleep(&ts_sleep, &ts);
  232. } while ((-1 == result));
  233. return result;
  234. }
  235. void render_sleep(void) {
  236. if (render_overlimit)
  237. return;
  238. if (current_render.speed) { // * 100 still too slow.
  239. ms_sleep(current_render.speed * 10);
  240. }
  241. }
  242. /*
  243. Terminal tracking
  244. Actually, I believe I only really need to track the color information.
  245. Everything else, I'm not sure I really care about.
  246. */
  247. struct console_details {
  248. int posx, posy;
  249. int savedx, savedy;
  250. char ansi[20]; // current ANSI command being processed.
  251. int in_ansi;
  252. int fgcolor; // 0-7 // not 0-15
  253. int bgcolor; // 0-7
  254. int status; // 0, 1 or 5 (Blink)
  255. } console;
  256. void console_init(struct console_details *cdp) {
  257. cdp->posx = 0;
  258. cdp->posy = 0;
  259. cdp->savedx = 0;
  260. cdp->savedy = 0;
  261. cdp->in_ansi = 0;
  262. cdp->fgcolor = 7;
  263. cdp->bgcolor = 0;
  264. cdp->status = 0;
  265. }
  266. void ansi_color(struct console_details *cdp, int color) {
  267. // ZF_LOGV("ansi_color(%d)", color);
  268. if (color == 0) {
  269. cdp->status = 0;
  270. cdp->fgcolor = 7;
  271. cdp->bgcolor = 0;
  272. return;
  273. }
  274. if (color == 1) {
  275. cdp->status = 1;
  276. return;
  277. }
  278. if (color == 5) {
  279. cdp->status = 5;
  280. return;
  281. }
  282. if ((color >= 30) && (color <= 37)) {
  283. cdp->fgcolor = color - 30;
  284. return;
  285. }
  286. if ((color >= 40) && (color <= 47)) {
  287. cdp->bgcolor = color - 40;
  288. return;
  289. }
  290. ZF_LOGD("ansi_color( %d ) is unknown to me.", color);
  291. }
  292. const char *color_restore(struct console_details *cdp) {
  293. static char buffer[30];
  294. if (cdp->status == 0) {
  295. sprintf(buffer, "\x1b[0;3%d;4%dm", cdp->fgcolor, cdp->bgcolor);
  296. } else {
  297. sprintf(buffer, "\x1b[0;%d;3%d;4%dm", cdp->status, cdp->fgcolor,
  298. cdp->bgcolor);
  299. };
  300. return buffer;
  301. }
  302. void console_ansi(struct console_details *cdp, const char *ansi) {
  303. int understood = 0;
  304. const char *cp = ansi;
  305. const char *last = ansi + strlen(ansi) - 1;
  306. int number, number2;
  307. if (*cp == '\x1b') {
  308. cp++;
  309. // Ok, that's expected.
  310. if (*cp == '[') {
  311. cp++;
  312. // https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
  313. switch (*last) {
  314. case 'A':
  315. // cursor up
  316. if (cp == last) {
  317. number = 1;
  318. } else {
  319. number = atoi(cp);
  320. if (number < 1) {
  321. ZF_LOGD("console_ansi( %s ): number error (%d)", repr(ansi),
  322. number);
  323. number = 1;
  324. }
  325. };
  326. cdp->posy -= number;
  327. if (cdp->posy < 0) {
  328. cdp->posy = 0;
  329. ZF_LOGD(
  330. "console_ansi( %s ): attempt to move above top of screen (%d)",
  331. repr(ansi), number);
  332. }
  333. understood = 1;
  334. return;
  335. case 'B':
  336. // cursor down
  337. if (cp == last) {
  338. number = 1;
  339. } else {
  340. number = atoi(cp);
  341. if (number < 1) {
  342. ZF_LOGD("console_ansi( %s ): number error (%d)", repr(ansi),
  343. number);
  344. number = 1;
  345. }
  346. };
  347. cdp->posy += number;
  348. // check range/"scroll"
  349. understood = 1;
  350. return;
  351. case 'C':
  352. // cursor forward
  353. if (cp == last) {
  354. number = 1;
  355. } else {
  356. number = atoi(cp);
  357. if (number < 1) {
  358. ZF_LOGD("console_ansi( %s ): number error (%d)", repr(ansi),
  359. number);
  360. number = 1;
  361. }
  362. };
  363. cdp->posx += number;
  364. // Well. According to the "spec", the screen limits are hard
  365. // If the cursor is already at the edge of the screen, this has no
  366. // effect. ^ This is *NOT* how any ANSI BBS terminal program acts!
  367. while (cdp->posx > 79) {
  368. cdp->posy++;
  369. // check range/"scroll"
  370. cdp->posx -= 79;
  371. }
  372. understood = 1;
  373. return;
  374. case 'D':
  375. // cursor backwards
  376. if (cp == last) {
  377. number = 1;
  378. } else {
  379. number = atoi(cp);
  380. if (number < 1) {
  381. ZF_LOGD("console_ansi( %s ): number error (%d)", repr(ansi),
  382. number);
  383. number = 0;
  384. }
  385. };
  386. cdp->posx -= number;
  387. // Well. According to the "spec", the screen limits are hard
  388. // If the cursor is already at the edge of the screen, this has no
  389. // effect. ^ This is *NOT* how any ANSI BBS terminal program acts!
  390. while (cdp->posx < 0) {
  391. cdp->posy--;
  392. if (cdp->posy < 0) {
  393. cdp->posy = 0;
  394. }
  395. cdp->posx += 79;
  396. }
  397. understood = 1;
  398. return;
  399. case 'H':
  400. // cursor position
  401. if (*cp == ';') {
  402. // Missing first number
  403. number = 1;
  404. cp++;
  405. if (cp == last) {
  406. // missing 2nd number as well?
  407. number2 = 1;
  408. } else {
  409. number2 = atoi(cp);
  410. }
  411. } else {
  412. // Ok, find the first number
  413. number = atoi(cp);
  414. cp = strchr(cp, ';');
  415. if (cp == NULL) {
  416. // Missing 2nd number
  417. number2 = 1;
  418. } else {
  419. // 2nd number found, maybe.
  420. cp++;
  421. if (cp == last) {
  422. number2 = 1;
  423. } else {
  424. number2 = atoi(cp);
  425. }
  426. }
  427. }
  428. // Our positions start at zero, not one.
  429. cdp->posx = number - 1;
  430. cdp->posy = number2 - 1;
  431. understood = 1;
  432. break;
  433. case 'J':
  434. // clear
  435. if (cp == last) {
  436. number = 0;
  437. } else {
  438. number = atoi(cp);
  439. };
  440. // clears ... part of the screen.
  441. if (number == 2) {
  442. cdp->posx = 0;
  443. cdp->posy = 0;
  444. };
  445. understood = 1;
  446. break;
  447. case 'm':
  448. // color
  449. if (cp == last) {
  450. // nothing given, default to 0.
  451. number = 0;
  452. ansi_color(cdp, number);
  453. } else {
  454. while (cp != last) {
  455. number = atoi(cp);
  456. ansi_color(cdp, number);
  457. cp++;
  458. while ((cp != last) && (isdigit(*cp))) {
  459. cp++;
  460. };
  461. if (cp != last) {
  462. if (*cp == ';') {
  463. cp++;
  464. }
  465. }
  466. }
  467. }
  468. understood = 1;
  469. break;
  470. case 't':
  471. case 'r':
  472. case 'h':
  473. case '!':
  474. // These are ones that I don't care about.
  475. case 'n': // This is terminal detect -- give me cursor position
  476. understood = 1;
  477. break;
  478. default:
  479. // unsure -- possibly not important
  480. ZF_LOGD("console_ansi( %s ): ???", repr(ansi));
  481. understood = 0;
  482. }
  483. }
  484. };
  485. if (!understood) {
  486. ZF_LOGD("console_ansi( %s ): was not understood.", repr(ansi));
  487. }
  488. }
  489. /*
  490. * console_char()
  491. * return whether or not we are still in_ansi
  492. */
  493. int console_char(struct console_details *cdp, char ch) {
  494. char *cp;
  495. if (cdp->in_ansi) {
  496. // Ok, append this char
  497. cp = cdp->ansi + strlen(cdp->ansi);
  498. *cp = ch;
  499. cp++;
  500. *cp = 0;
  501. if (isalpha(ch)) {
  502. // Ok!
  503. console_ansi(cdp, cdp->ansi);
  504. cdp->in_ansi = 0;
  505. cdp->ansi[0] = 0;
  506. return 1;
  507. }
  508. return 1;
  509. } else {
  510. if (ch == '\x1b') {
  511. cp = cdp->ansi;
  512. *cp = ch;
  513. cp++;
  514. *cp = 0;
  515. cdp->in_ansi = 1;
  516. return 1;
  517. }
  518. if (ch == '\r') {
  519. // Carriage return
  520. cdp->posx = 0;
  521. return 0;
  522. }
  523. if (ch == '\n') {
  524. cdp->posy++;
  525. // check range/"scroll"
  526. return 0;
  527. }
  528. if (ch == '\b') {
  529. // Backspace.
  530. if (cdp->posx > 0) {
  531. cdp->posx--;
  532. }
  533. return 0;
  534. }
  535. if (ch == '\f') {
  536. // form feed
  537. // treat as clear screen
  538. cdp->posx = 0;
  539. cdp->posy = 0;
  540. return 0;
  541. }
  542. /*
  543. I don't believe that anything else can possibly be here, other then an
  544. actual printable character. So!
  545. */
  546. cdp->posx++;
  547. if (cdp->posx > 79) {
  548. cdp->posx = 0;
  549. cdp->posy++;
  550. // check range/"scroll"
  551. }
  552. return 0;
  553. }
  554. }
  555. void console_string(struct console_details *cdp, const char *chars) {
  556. int x;
  557. for (x = 0; x < strlen(chars); x++) {
  558. console_char(cdp, chars[x]);
  559. }
  560. }
  561. void console_receive(struct console_details *cdp, const char *chars, int len) {
  562. int x;
  563. for (x = 0; x < len; x++) {
  564. console_char(cdp, chars[x]);
  565. }
  566. }
  567. /*
  568. Well SNAP! Mystic numbers don't remotely match ANSI color codes.
  569. 00 : Sets the current foreground to Black 0;30
  570. 01 : Sets the current foreground to Dark Blue 0;34
  571. 02 : Sets the current foreground to Dark Green 0;32
  572. 03 : Sets the current foreground to Dark Cyan 0;36
  573. 04 : Sets the current foreground to Dark Red 0;31
  574. 05 : Sets the current foreground to Dark Magenta 0;35
  575. 06 : Sets the current foreground to Brown 0;33
  576. 07 : Sets the current foreground to Grey 0;37
  577. 08 : Sets the current foreground to Dark Grey 1;30
  578. 09 : Sets the current foreground to Light Blue 1;34
  579. 10 : Sets the current foreground to Light Green 1;32
  580. 11 : Sets the current foreground to Light Cyan 1;36
  581. 12 : Sets the current foreground to Light Red 1;31
  582. 13 : Sets the current foreground to Light Magenta 1;35
  583. 14 : Sets the current foreground to Yellow 1;33
  584. 15 : Sets the current foreground to White 1;37
  585. 16 : Sets the current background to Black 40
  586. 17 : Sets the current background to Blue 44
  587. 18 : Sets the current background to Green 42
  588. 19 : Sets the current background to Cyan 46
  589. 20 : Sets the current background to Red 41
  590. 21 : Sets the current background to Magenta 45
  591. 22 : Sets the current background to Brown 43
  592. 23 : Sets the current background to Grey 47
  593. 24 : Sets the current background to black with blinking foreground 5;40
  594. 25 : Sets the current background to blue with blinking foreground 5;44
  595. 26 : Sets the current background to green with blinking foreground 5;42
  596. 27 : Sets the current background to cyan with blinking foreground 5;46
  597. 28 : Sets the current background to red with blinking foreground 5;41
  598. 29 : Sets the current background to magenta with blinking foreground 5;45
  599. 30 : Sets the current background to brown with blinking foreground 5;43
  600. 31 : Sets the current background to grey with blinking foreground 5;47
  601. Other things that Mystic does ...
  602. [A## - Move the cursor up ## lines
  603. [B## - Move the cursor down ## lines
  604. [C## - Move the cursor forward (to the right) ## columns
  605. [D## - Move the cursor backwards (to the left) ## columns
  606. [K - Clear from the current cursor position to the end of the line
  607. [L - Move cursor and erase data backwards from current column to column ##
  608. [X## - Move cursor to X coordinate ##
  609. [Y## - Move cursor to Y coordinate ##
  610. BS - Sends 1 destructive backspace sequence (ASCII 8-32-8)
  611. CL - Clears the screen (ANSI 1,1 locate and [2J or ASCII 12)
  612. CR - Send a carrage return and line feed (move to next line)
  613. RA - Restore the saved text attribute color
  614. RS - Restore the saved user's terminal screen
  615. SA - Save the current text attribute color
  616. SS - Save the entire user's terminal screen
  617. */
  618. // Covert MYSTIC color to (Proper) ANSI COLOR.
  619. const int MYSTIC[] = {0, 4, 2, 6, 1, 5, 3, 7};
  620. // ANSI_color = MYSTIC[ odd_mystic_color % 8 ]
  621. void write_color(int fd, int color) {
  622. char buffer[12];
  623. switch (color) {
  624. case 0:
  625. case 1:
  626. case 2:
  627. case 3:
  628. case 4:
  629. case 5:
  630. case 6:
  631. case 7:
  632. sprintf(buffer, "\x1b[0;3%dm", MYSTIC[color]);
  633. break;
  634. case 8:
  635. case 9:
  636. case 10:
  637. case 11:
  638. case 12:
  639. case 13:
  640. case 14:
  641. case 15:
  642. sprintf(buffer, "\x1b[0;1;3%dm", MYSTIC[color - 8]);
  643. break;
  644. case 16:
  645. case 17:
  646. case 18:
  647. case 19:
  648. case 20:
  649. case 21:
  650. case 22:
  651. case 23:
  652. sprintf(buffer, "\x1b[4%dm", MYSTIC[color - 16]);
  653. break;
  654. case 24:
  655. case 25:
  656. case 26:
  657. case 27:
  658. case 28:
  659. case 29:
  660. case 30:
  661. case 31:
  662. sprintf(buffer, "\x1b[5;4%dm", MYSTIC[color - 24]);
  663. break;
  664. default:
  665. buffer[0] = 0;
  666. break;
  667. }
  668. ZF_LOGD("write_color( %d ): %s", color, repr(buffer));
  669. write(fd, buffer, strlen(buffer));
  670. }
  671. int send_file(int fd, char *filename) {
  672. FILE *fp;
  673. char buffer[100];
  674. int read;
  675. fp = fopen(filename, "rb");
  676. if (fp == NULL) {
  677. ZF_LOGD("Failed to open %s", filename);
  678. return 0;
  679. }
  680. while ((read = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
  681. write(fd, buffer, read);
  682. };
  683. fclose(fp);
  684. return 1;
  685. }
  686. void send_goto(int fd, int x, int y) {
  687. char gbuffer[16];
  688. sprintf(gbuffer, "\x1b[%d;%dH", y, x);
  689. write(fd, gbuffer, strlen(gbuffer));
  690. }
  691. int send_file(int fd, int x, int y, char *filename) {
  692. FILE *fp;
  693. char buffer[100];
  694. int read;
  695. fp = fopen(filename, "rb");
  696. if (fp == NULL) {
  697. ZF_LOGD("Failed to open %s", filename);
  698. return 0;
  699. }
  700. send_goto(fd, x, y);
  701. y++;
  702. while ((read = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
  703. char *cp, *last_cp;
  704. buffer[read] = 0;
  705. last_cp = buffer;
  706. while ((cp = strchr(last_cp, '\n')) != NULL) {
  707. *cp = 0;
  708. write(fd, last_cp, strlen(last_cp));
  709. send_goto(fd, x, y);
  710. y++;
  711. last_cp = cp + 1;
  712. };
  713. write(fd, last_cp, strlen(last_cp));
  714. };
  715. fclose(fp);
  716. return 1;
  717. }
  718. /**
  719. * process_trigger( fd, *cp )
  720. *
  721. * This process a command trigger.
  722. * It has seen TRIGGER, and now it is
  723. * processing whatever comes after it.
  724. * It will perform the process, and
  725. * return the char * of whatever is next.
  726. */
  727. const char *process_trigger(int fd, const char *cp) {
  728. char ch;
  729. int i, x, y;
  730. ch = *cp;
  731. cp++;
  732. switch (ch) {
  733. case 'D':
  734. i = 0;
  735. if (isdigit(*cp)) {
  736. i = (*cp) - '0';
  737. cp++;
  738. };
  739. if (isdigit(*cp)) {
  740. i *= 10;
  741. i += (*cp) - '0';
  742. cp++;
  743. };
  744. if ((i > 0) && (i < 80)) {
  745. ZF_LOGI("DEL %02d", i);
  746. for (x = 0; x < i; x++) {
  747. write(fd, "\b \b", 3);
  748. }
  749. };
  750. break;
  751. case 'C': {
  752. i = 0;
  753. if (*cp == 'R') {
  754. cp++;
  755. const char *restore = color_restore(&console);
  756. write(fd, restore, strlen(restore));
  757. break;
  758. }
  759. if (isdigit(*cp)) {
  760. i = (*cp) - '0';
  761. cp++;
  762. };
  763. if (isdigit(*cp)) {
  764. i *= 10;
  765. i += (*cp) - '0';
  766. cp++;
  767. };
  768. write_color(fd, i);
  769. } break;
  770. case 'F':
  771. case 'f': {
  772. int pos = 0;
  773. if (ch == 'f') {
  774. pos = 1;
  775. x = (*cp) - '0';
  776. cp++;
  777. x *= 10;
  778. x += (*cp) - '0';
  779. cp++;
  780. y = (*cp) - '0';
  781. cp++;
  782. y *= 10;
  783. y += (*cp) - '0';
  784. cp++;
  785. ZF_LOGI("file at (%d, %d)", x, y);
  786. }
  787. // Ok, look for filename
  788. char ansifile[32] = "./hh/";
  789. char *ap = ansifile + strlen(ansifile);
  790. while (*cp != '.') {
  791. *ap = *cp;
  792. ap++;
  793. *ap = 0;
  794. cp++;
  795. };
  796. strcat(ansifile, ".ans");
  797. cp++;
  798. ZF_LOGD("FILE [%s]", ansifile);
  799. if (pos) {
  800. send_file(fd, x, y, ansifile);
  801. } else {
  802. send_file(fd, ansifile);
  803. };
  804. } break;
  805. case 'G': {
  806. x = 0;
  807. if (isdigit(*cp)) {
  808. x = (*cp) - '0';
  809. cp++;
  810. };
  811. if (isdigit(*cp)) {
  812. x *= 10;
  813. x += (*cp) - '0';
  814. cp++;
  815. };
  816. y = 0;
  817. if (isdigit(*cp)) {
  818. y = (*cp) - '0';
  819. cp++;
  820. };
  821. if (isdigit(*cp)) {
  822. y *= 10;
  823. y += (*cp) - '0';
  824. cp++;
  825. };
  826. char buffer[20]; // row ; column H
  827. ZF_LOGD("GOTO (%d,%d)", x, y);
  828. sprintf(buffer, "\x1b[%d;%dH", y, x);
  829. write(fd, buffer, strlen(buffer));
  830. } break;
  831. case 'R': {
  832. i = 0;
  833. if (isdigit(*cp)) {
  834. i = (*cp) - '0';
  835. cp++;
  836. };
  837. if ((i > 0) && (i < 10)) {
  838. ZF_LOGI("RENDER %d", i);
  839. current_render.effect = i;
  840. } else {
  841. current_render.effect = 0;
  842. }
  843. } break;
  844. case 'S': {
  845. i = 0;
  846. if (isdigit(*cp)) {
  847. i = (*cp) - '0';
  848. cp++;
  849. };
  850. if ((i > 0) && (i < 10)) {
  851. ZF_LOGI("SPEED %d", i);
  852. current_render.speed = i;
  853. } else {
  854. current_render.speed = 0;
  855. }
  856. } break;
  857. case 'P': {
  858. i = 0;
  859. if (isdigit(*cp)) {
  860. i = (*cp) - '0';
  861. cp++;
  862. };
  863. if ((i > 0) && (i < 10)) {
  864. ZF_LOGI("PAWS %d", i);
  865. // sleep(i);
  866. if (!render_overlimit) {
  867. sleep(i);
  868. };
  869. }
  870. } break;
  871. }
  872. return cp;
  873. }
  874. /**
  875. * render_effect( fd, ch )
  876. *
  877. * Displays the given character with whatever
  878. * rendering effect is currently active.
  879. * (If any).
  880. */
  881. void render_effect(int fd, char ch) {
  882. int effect = current_render.effect;
  883. int l;
  884. char space = ' ';
  885. char bs = '\b';
  886. switch (effect) {
  887. case 1:
  888. // CHAR + SPC + BS
  889. render_sleep();
  890. write(fd, &ch, 1);
  891. render_sleep();
  892. write(fd, &space, 1);
  893. render_sleep();
  894. render_sleep();
  895. write(fd, &bs, 1);
  896. break;
  897. case 2:
  898. // CHAR + 8 spaces + 8 BS
  899. render_sleep();
  900. write(fd, &ch, 1);
  901. for (l = 0; l < 8; l++) {
  902. render_sleep();
  903. write(fd, &space, 1);
  904. }
  905. for (l = 0; l < 8; l++) {
  906. render_sleep();
  907. write(fd, &bs, 1);
  908. }
  909. break;
  910. case 0:
  911. default:
  912. // NORMAL
  913. render_sleep();
  914. write(fd, &ch, 1);
  915. break;
  916. }
  917. }
  918. /**
  919. * render( fd, string_out )
  920. *
  921. * Render an entire string.
  922. * Handles TRIGGER.
  923. * Renders with effects.
  924. */
  925. void render(int fd, const char *string_out, int len) {
  926. const char *cp = string_out;
  927. const char *trigger = cp;
  928. time_t start = time(NULL);
  929. int elapsed;
  930. int over = 0;
  931. reset_render();
  932. // ZF_LOGV("render(%d, %s)", fd, repr(string_out));
  933. ZF_LOGV_MEM(string_out, len, "render(%d, %d bytes):", fd, len);
  934. // Check our time from time to time.
  935. // If we start running long, disable sleeps.
  936. while ((trigger = strnstr(cp, len - (cp - trigger), TRIGGER)) != NULL) {
  937. // There is special things to handle in here.
  938. while (cp != trigger) {
  939. elapsed = time(NULL) - start;
  940. if (elapsed > SLEEP_LIMIT) {
  941. render_overlimit = 1;
  942. current_render.speed = 0;
  943. };
  944. // write(fd, cp, 1 );
  945. render_effect(fd, *cp);
  946. cp++;
  947. };
  948. // ZF_LOGI( "at trigger: (%s)", cp);
  949. cp += strlen(TRIGGER);
  950. // Ok, we're pointing at the trigger -- do something.
  951. cp = process_trigger(fd, cp);
  952. // ZF_LOGI( "after trigger: (%s)", cp);
  953. };
  954. // We still might be under a rendering effect.
  955. while (cp < (string_out + len)) {
  956. elapsed = time(NULL) - start;
  957. if (elapsed > SLEEP_LIMIT) {
  958. render_overlimit = 1;
  959. current_render.speed = 0;
  960. };
  961. // write(fd, cp, 1);
  962. render_effect(fd, *cp);
  963. cp++;
  964. }
  965. }
  966. /*
  967. These are harry "timeout" events.
  968. These happen when we've been sitting around awhile.
  969. */
  970. #ifdef CPP_MADMAN_STL_CODE
  971. const char *random_phrase(const char *words, int len, int last_seen) {
  972. // ooh. a map of char *s to last_seen_events. :P
  973. static map<const char *, array<int>> tracker;
  974. map<const char *, array<int>>::iterator it;
  975. array<int, last_seen> it = tracker.find(words);
  976. if (it == tracker.end()) {
  977. // key does not exist.
  978. array<int, last_seen> last;
  979. for (int i = 0; i < last_seen; i++) {
  980. last[i] = -1;
  981. };
  982. tracker.insert(words, last);
  983. it = tracker.find(words);
  984. };
  985. }
  986. #endif
  987. void harry_idle_event(int fd) {
  988. // Make something happen
  989. char buffer[100];
  990. int r;
  991. // This is no where near finished, BUT!
  992. const char *phrases[] = {"Hahaha", "Snicker, snicker", "Boo!",
  993. "MeOW", "I see U", "Arrooo!",
  994. "Ahh-wooo!", "Aaaooo!"};
  995. const char *cp;
  996. static LastSeen last_seen_harry_event(2);
  997. // Remember the last phrase used,
  998. // and don't repeat (the last two)!
  999. do {
  1000. r = randint((sizeof(phrases) / sizeof(char *)));
  1001. // r = random() % ((sizeof(phrases) / sizeof(char *)) - 1);
  1002. } while (last_seen_harry_event.seen_before(r));
  1003. // ZF_LOGD("%d => %d %d", r, last_seen_harry_event[0],
  1004. // last_seen_harry_event[1]);
  1005. cp = phrases[r];
  1006. int color = random() % 15 + 1;
  1007. /*
  1008. int color = random() % 16;
  1009. if (color == 0) {
  1010. color++;
  1011. } // If it's 0 let's make it 1. // color = (random() % 15) + 1
  1012. */
  1013. sprintf(buffer, "^S2^C%02d%s^P2^CR^D%02d", color, cp, (int)strlen(cp));
  1014. ZF_LOGD("harry_event: render(%d, \"%s\")", fd, buffer);
  1015. render(fd, buffer, strlen(buffer));
  1016. }
  1017. void init_harry() {
  1018. // init_have_seen(last_seen_harry_event, MAX_HARRY_EVENT_DUPS);
  1019. // ZF_LOGD("init => %d %d", last_seen_harry_event[0],
  1020. // last_seen_harry_event[1]);
  1021. console_init(&console);
  1022. }
  1023. /*
  1024. The code to get the username and fullname is useless on telnet
  1025. connections.
  1026. */
  1027. const char *username = NULL;
  1028. const char *fullname = NULL;
  1029. /*
  1030. Pascal String Copy. Copy from pascal string, to C String.
  1031. First char is pascal string length. (Max 255).
  1032. */
  1033. void pcopy(char *pstring, char *str) {
  1034. int len = (int)*pstring;
  1035. strncpy(str, pstring + 1, len);
  1036. str[len] = 0;
  1037. }
  1038. /*
  1039. This only works for those few idiots that use the
  1040. horribly broken SSH crap that Mystic uses.
  1041. */
  1042. int locate_user(const char *alias) {
  1043. FILE *user;
  1044. char buffer[0x600];
  1045. char temp[100];
  1046. user = fopen("data/users.dat", "rb");
  1047. if (user == NULL)
  1048. return 0;
  1049. // Carry on!
  1050. while (fread(buffer, 0x600, 1, user) == 1) {
  1051. pcopy(buffer + 0x6d, temp);
  1052. if (strcasecmp(temp, username) == 0) {
  1053. pcopy(buffer + 0x8c, temp);
  1054. fullname = strdup(temp);
  1055. break;
  1056. }
  1057. /*
  1058. printf("Alias: %s\n", temp);
  1059. pcopy(buffer + 0x8c, temp );
  1060. printf("Full Name: %s\n", temp );
  1061. */
  1062. }
  1063. fclose(user);
  1064. return 1;
  1065. }
  1066. // Buffers are BSIZE + 1, so a buffer that size can strcpy safely.
  1067. regex_t ANSI;
  1068. regex_t WORDS;
  1069. regex_t WORD;
  1070. int init_regex(void) {
  1071. int ret;
  1072. char ansi[] = "\x1b\[[0-9]+(;[0-9]+)*?[a-zA-Z]";
  1073. char words[] = "[a-zA-Z]+( [a-zA-Z]+)+";
  1074. char word[] = "[a-zA-Z]+";
  1075. char errorbuf[100];
  1076. if (ret = regcomp(&ANSI, ansi, REG_EXTENDED | REG_NEWLINE)) {
  1077. regerror(ret, &ANSI, errorbuf, sizeof(errorbuf));
  1078. ZF_LOGW("Regex %s failed to compile: %s", ansi, errorbuf);
  1079. return 0;
  1080. };
  1081. if (ret = regcomp(&WORDS, words, REG_EXTENDED | REG_NEWLINE)) {
  1082. regerror(ret, &WORDS, errorbuf, sizeof(errorbuf));
  1083. ZF_LOGW("Regex %s failed to compile: %s", words, errorbuf);
  1084. return 0;
  1085. };
  1086. if (ret = regcomp(&WORD, word, REG_EXTENDED | REG_NEWLINE)) {
  1087. regerror(ret, &WORD, errorbuf, sizeof(errorbuf));
  1088. ZF_LOGW("Regex %s failed to compile: %s", word, errorbuf);
  1089. return 0;
  1090. };
  1091. return 1;
  1092. }
  1093. int regmatch(regex_t *preg, const char *string, size_t nmatch,
  1094. regmatch_t pmatch[], int eflags) {
  1095. // returns number of matches found. (Max nmatch)
  1096. int matches = 0;
  1097. int offset = 0;
  1098. while (matches < nmatch) {
  1099. int ret = regexec(preg, string + offset, nmatch - matches, pmatch + matches,
  1100. eflags);
  1101. if (!ret) {
  1102. int current = offset;
  1103. offset += pmatch[matches].rm_eo;
  1104. pmatch[matches].rm_so += current;
  1105. pmatch[matches].rm_eo += current;
  1106. matches++;
  1107. } else if (ret == REG_NOMATCH) {
  1108. break;
  1109. } else {
  1110. break;
  1111. }
  1112. }
  1113. return matches;
  1114. }
  1115. #define MAX_MATCH 32
  1116. regmatch_t rxmatch[MAX_MATCH];
  1117. int rx_match(regex_t *regex, const char *buffer) {
  1118. int ret;
  1119. ret = regmatch(regex, buffer, MAX_MATCH, rxmatch, 0);
  1120. if (0) {
  1121. for (int i = 0; i < ret; i++) {
  1122. ZF_LOGI("%d : (%d-%d)", i, rxmatch[i].rm_so, rxmatch[i].rm_eo);
  1123. }
  1124. }
  1125. return ret;
  1126. }
  1127. /*
  1128. Terminal processing section.
  1129. Monitor lines, position, color.
  1130. What screen size do I want to emulate?
  1131. ANSI codes.
  1132. Do I need this??
  1133. */
  1134. /**
  1135. * random_activate()
  1136. *
  1137. * Is a weight (1-10),
  1138. * tests if random number is < weight * 10.
  1139. *
  1140. * So random_activate(9) happens more frequently
  1141. * then random_activate(8) or lower.
  1142. *
  1143. * This probably needs to be fixed.
  1144. * We need a better randint(RANGE) code.
  1145. */
  1146. int random_activate(int w) {
  1147. int r = random() % 100;
  1148. if (r <= (w * 10)) {
  1149. return 1;
  1150. };
  1151. return 0;
  1152. }
  1153. /*
  1154. word_state():
  1155. -1 only lower
  1156. +1 only upper
  1157. 0 mixed
  1158. */
  1159. int word_state(const char *buffer, int len) {
  1160. int p;
  1161. int upper = 0;
  1162. int lower = 0;
  1163. int ret;
  1164. float pct;
  1165. for (p = 0; p < len; p++) {
  1166. char c = buffer[p];
  1167. if (isalpha(c)) {
  1168. if (isupper(c)) {
  1169. upper++;
  1170. };
  1171. if (islower(c)) {
  1172. lower++;
  1173. };
  1174. }
  1175. }
  1176. if (upper == lower) {
  1177. return 0;
  1178. }
  1179. if (upper > lower) {
  1180. ret = 1;
  1181. pct = ((float)lower / (float)upper) * 100.0;
  1182. } else {
  1183. ret = -1;
  1184. pct = ((float)upper / (float)lower) * 100.0;
  1185. }
  1186. // ZF_LOGD("So far %d with %f %%", ret, pct);
  1187. if (pct < 40.0) {
  1188. return ret;
  1189. }
  1190. return 0;
  1191. }
  1192. /*
  1193. Given a buffer and length, mangle away.
  1194. We *REALLY* want this to do something, so better run some tests.
  1195. Or, maybe we don't. This treats words consistently the same way.
  1196. HMM.
  1197. toupper, tolower, flipper
  1198. */
  1199. int word_mangler(char *buffer, int len) {
  1200. int p;
  1201. int count = 0;
  1202. int state;
  1203. state = word_state(buffer, len);
  1204. // ZF_LOGD("word_state(%.*s) %d", len, buffer, state);
  1205. // TODO: Transposer
  1206. for (p = 0; p < len; p++) {
  1207. char c = buffer[p];
  1208. switch (state) {
  1209. case -1:
  1210. // upper
  1211. if (islower(c)) {
  1212. count++;
  1213. buffer[p] = toupper(c);
  1214. }
  1215. break;
  1216. case 1:
  1217. // lower
  1218. if (isupper(c)) {
  1219. count++;
  1220. buffer[p] = tolower(c);
  1221. }
  1222. break;
  1223. case 0:
  1224. // flipper
  1225. if (islower(c)) {
  1226. count++;
  1227. buffer[p] = toupper(c);
  1228. } else {
  1229. if (isupper(c)) {
  1230. count++;
  1231. buffer[p] = tolower(c);
  1232. }
  1233. }
  1234. break;
  1235. }
  1236. }
  1237. return count;
  1238. }
  1239. int buffer_insert(char *buffer, int len, int max_length, int pos,
  1240. const char *insert) {
  1241. if (len + strlen(insert) > max_length) {
  1242. ZF_LOGD("buffer_insert failed [%s]", repr(insert));
  1243. return 0;
  1244. }
  1245. memmove(buffer + pos + strlen(insert), buffer + pos, len - pos);
  1246. strncpy(buffer + pos, insert, strlen(insert));
  1247. return 1;
  1248. }
  1249. /*
  1250. * The buffer that we've been given is much larger now.
  1251. *
  1252. * We can no longer mangle or insert into the given buffer.
  1253. * Why? Because we don't know what is behind it now!
  1254. */
  1255. int mangle(int fd, const char *buffer, int len) {
  1256. int x, i;
  1257. int need_render = 0; // changing word case around doesn't need the render
  1258. int mangled = 0;
  1259. int mangled_chars = 0;
  1260. char play[2048];
  1261. const char *cp;
  1262. memcpy(play, buffer, len);
  1263. // NEVER reference buffer from this point on!
  1264. // Ok, we want to look for words to:
  1265. // MaNGlE , or transpose (both?!)
  1266. // or possibly transpose words
  1267. char work[2048];
  1268. // Use terminal - clean out ANSI
  1269. // ZF_LOGI("mangle:");
  1270. ZF_LOGI_MEM(play, len, "Mangle (%u bytes):", len);
  1271. // strcpy(work, buffer);
  1272. /*
  1273. NOTE: We copy the buffer, so we can clear out ANSI codes, etc.
  1274. Otherwise we might mess some ANSI up in the manglying
  1275. process.
  1276. */
  1277. /*
  1278. (random) Look for ANSI CLS and:
  1279. display random spooky texts around, with delays ... then CLS.
  1280. display ANSI graphic file, with delays ... then CLS
  1281. */
  1282. const char *ANSI_CLS = "\x1b[2J";
  1283. cp = strnstr(play, len, ANSI_CLS); // strstr(buffer, ANSI_CLS);
  1284. if (cp != NULL) {
  1285. static int ANSI_CLS_count = 0; // count the number we've seen
  1286. ZF_LOGI("seen: ANSI_CLS");
  1287. ANSI_CLS_count++;
  1288. // Don't activate on the very first CLS. Too soon, don't screw up the ANSI
  1289. // detection.
  1290. if (ANSI_CLS_count > 1) {
  1291. // Ok, figure out the restore color, just in case
  1292. struct console_details temp_console;
  1293. // Make exact copy of our current console state.
  1294. memcpy(&temp_console, &console, sizeof(console));
  1295. // Play the buffer into the console
  1296. console_receive(&temp_console, play, cp - play);
  1297. char restore_color[30]; // ansi color
  1298. strcpy(restore_color, color_restore(&temp_console));
  1299. if (random_activate(3)) {
  1300. char display[100] = "";
  1301. int needs_cls = 0;
  1302. struct file_need {
  1303. const char *filename;
  1304. int cls;
  1305. int rand_pos;
  1306. } possibles[] = {
  1307. {"goofy_head", 1, 0}, {"bat", 1, 0}, {"panther", 1, 0},
  1308. {"wolf", 1, 0}, {"skull", 0, 1}, {"skull2", 0, 1},
  1309. {"guy", 0, 1}, {"blinkman", 0, 1}, {"ghost", 1, 0}};
  1310. // bat.ans creature.ans goofy_head.ans panther.ans
  1311. // skull.ans ghost.ans
  1312. // blinkman.ans dog.ans guy.ans skull2.ans wolf.ans
  1313. static LastSeen last_files(2);
  1314. int r;
  1315. do {
  1316. r = randint((sizeof(possibles) / sizeof(file_need)));
  1317. } while (last_files.seen_before(r));
  1318. int x, y;
  1319. x = randint(50) + 1;
  1320. y = randint(12) + 1;
  1321. char fgoto[10];
  1322. if (possibles[r].rand_pos) {
  1323. sprintf(fgoto, "^f%02d%02d", x, y);
  1324. } else {
  1325. strcpy(fgoto, "^F");
  1326. }
  1327. // (2); // (sizeof(possibles) / sizeof(file_need)) - 1);
  1328. needs_cls = possibles[r].cls;
  1329. // I get what's happening. Mystic moves cursor to home, CLS, cursor
  1330. // home. When we get here, we're ALWAYS at the top of the screen...
  1331. // Hence our bat isn't displayed at the end of the screen.
  1332. // This is before the actual CLS, so we CLS before displaying our files.
  1333. // I tried a ^P2 before doing this .. but I'd rather have the picture up
  1334. // right away I think.
  1335. sprintf(display, "%s%s%s.^P3%s", needs_cls ? "\x1b[2J" : "", fgoto,
  1336. possibles[r].filename, restore_color);
  1337. ZF_LOGI("mangle(ANSI_CLS): %d file inserted %s", r, repr(display));
  1338. // Move the buffer so there's room for the display string.
  1339. if (buffer_insert(play, len, sizeof(play), cp - play, display)) {
  1340. len += strlen(display);
  1341. // if (string_insert(buffer, 1024, cp - buffer, display)) {
  1342. ZF_LOGI_MEM(play, len, "mangle(ANSI_CLS) (%u bytes):", len);
  1343. // ZF_LOGI("mangle(ANSI_CLS):");
  1344. // ZF_LOGI_REPR(buffer);
  1345. // ZF_LOGI("mangle(ANSI_CLS): [%s]", repr(buffer));
  1346. need_render = 1;
  1347. /*
  1348. Copy the new buffer over, but hide our "render" code
  1349. from the remaining mangler steps.
  1350. */
  1351. memcpy(work, play, len);
  1352. // strcpy(work, buffer);
  1353. i = cp - play;
  1354. // find offset into "buffer"
  1355. // apply to work.
  1356. memset(work + i, ' ', strlen(display));
  1357. } else {
  1358. ZF_LOGD("insert failed [%s].", repr(display));
  1359. }
  1360. } else {
  1361. if (random_activate(4)) {
  1362. int r;
  1363. char display[100] = "";
  1364. const char *phrasing[] = {"^R1Haha^P1ha^P1ha", "Poof!", "Got U",
  1365. "Anyone there?", "^R1Knock, ^P1Knock"};
  1366. static LastSeen last_phrasing(2);
  1367. ZF_LOGI("mangle(ANSI_CLS)");
  1368. // sprintf( display, "^P2...");
  1369. // This string actually screws up ANSI detection (takes too long)
  1370. // strcpy(display, "^P2^S501234567890^P1abcdef^P2g^P3h^P4i^S0^P2");
  1371. // strcpy(display, "^P2^S301234^P15^S0^P2");
  1372. // Add in random text, plus color!
  1373. do {
  1374. r = random() % ((sizeof(phrasing) / sizeof(char *)) - 1);
  1375. } while (last_phrasing.seen_before(r));
  1376. int color = random() % 15 + 1;
  1377. int x = random() % 30 + 1;
  1378. int y = random() % 15 + 1;
  1379. /*
  1380. Don't have it pause there before moving the cursor.
  1381. Move the cursor, get the color changed, THEN pause.
  1382. Then act all crazy.
  1383. NOTE: Make sure if you use any ^R Render effects, turn them off
  1384. before trying to display the restore_color. :P ^R0 Also, make
  1385. sure you re-home the cursor ^G0101 because that's where they are
  1386. expecting the cursor to be! (At least it's how Mystic does it.)
  1387. HOME, CLS, HOME, ... Not sure what others do there. We'll see.
  1388. */
  1389. sprintf(display, "^G%02d%02d^S3^C%02d^P1%s^S0^R0%s^P1^G0101", x, y,
  1390. color, phrasing[r], restore_color);
  1391. // sprintf(display, "^P1^S3^C%02d%s^S0^R0%s^P1", color, phrasing[r],
  1392. // restore_color);
  1393. // ZF_LOGI("mangle(ANSI_CLS): Inserted (%02d) %s", color,
  1394. // phrasing[r]);
  1395. ZF_LOGI_MEM(play, len, "mangle(ANSI_CLS) :");
  1396. // Move the buffer so there's room for the display string.
  1397. if (buffer_insert(play, len, 1024, cp - play, display)) {
  1398. len += strlen(display);
  1399. // if (string_insert(buffer, BSIZE * 4, cp - buffer, display)) {
  1400. ZF_LOGI_MEM(play, len, "mangle(ANSI_CLS) + :");
  1401. // ZF_LOGI("mangle(ANSI_CLS):");
  1402. // ZF_LOGI_REPR(buffer);
  1403. need_render = 1;
  1404. /*
  1405. Copy the new buffer over, but hide our "render" code
  1406. from the remaining mangler steps.
  1407. */
  1408. memcpy(work, play, len);
  1409. // strcpy(work, buffer);
  1410. i = cp - play;
  1411. // find offset into "buffer"
  1412. // apply to work.
  1413. memset(work + i, ' ', strlen(display));
  1414. } else {
  1415. ZF_LOGD("insert failed [%s].", repr(display));
  1416. }
  1417. }
  1418. }
  1419. }
  1420. }
  1421. memcpy(work, play, len);
  1422. // strcpy(work, buffer); // sure.
  1423. const char replace_with = ' ';
  1424. for (x = 0; x < len; x++) {
  1425. int ansi = console_char(&console, play[x]);
  1426. if (ansi) {
  1427. work[x] = replace_with;
  1428. }
  1429. // fixup "work" so it's a valid C string
  1430. if (buffer[x] == 0) {
  1431. work[x] = replace_with;
  1432. }
  1433. }
  1434. // fixup "work" buffer so it's a valid c string
  1435. work[len] = 0;
  1436. ZF_LOGI_MEM(work, len, "Work now:");
  1437. /*
  1438. ZF_LOGD("work now:");
  1439. ZF_LOGD_REPR(work);
  1440. ZF_LOGD("Work Now: [%s]", repr(work));
  1441. */
  1442. #ifdef OLD_WAY
  1443. /* work -- clear out ANSI so we don't mangle ANSI codes. */
  1444. x = rx_match(&ANSI, work);
  1445. if (x > 0) {
  1446. ZF_LOGD("found %d ANSI", x);
  1447. for (i = 0; i < x; i++) {
  1448. memset(work + rxmatch[i].rm_so, replace_with,
  1449. rxmatch[i].rm_eo - rxmatch[i].rm_so);
  1450. };
  1451. ZF_LOGD("Work Now: [%s]", repr(work));
  1452. }
  1453. #endif
  1454. // ZF_LOGI("mangle: %s", repr(work));
  1455. /*
  1456. (random) Locate words (in work), and possibly flip them around.
  1457. Transpose words. Transpose case. Transpose letters.
  1458. */
  1459. x = rx_match(&WORDS, work);
  1460. ZF_LOGD("found %d word groups", x);
  1461. if (x > 0) {
  1462. for (i = 0; i < x; i++) {
  1463. // Yes! Be random!
  1464. if (random_activate(8)) {
  1465. int c = word_mangler(play + rxmatch[i].rm_so,
  1466. rxmatch[i].rm_eo - rxmatch[i].rm_so);
  1467. if (c) {
  1468. mangled++;
  1469. mangled_chars += c;
  1470. }
  1471. }
  1472. }
  1473. }
  1474. /*
  1475. (random) Locate single words, and transpose words.
  1476. Transpose letters.
  1477. */
  1478. /*
  1479. (random) Display up to certain point. Delay.
  1480. Print some characters slowly. Delay.
  1481. */
  1482. if (need_render) {
  1483. ZF_LOGD_MEM(play, len, "Ready to render:");
  1484. // ZF_LOGD("HH %d : (%d) %s", need_render, (int)strlen(buffer), repr(buffer));
  1485. } else {
  1486. if (mangled) {
  1487. ZF_LOGD_MEM(play, len, "Mangled %d words, %d chars:", mangled,
  1488. mangled_chars);
  1489. /* ZF_LOGD("Mangled %d words, %d chars : %s", mangled, mangled_chars,
  1490. repr(buffer)); */
  1491. }
  1492. }
  1493. if (need_render) {
  1494. render(fd, play, len);
  1495. } else {
  1496. write(fd, play, len); // strlen(buffer));
  1497. };
  1498. return need_render && mangled;
  1499. }
  1500. int harry_happens(time_t *last_event, int wakeup) {
  1501. time_t now = time(NULL);
  1502. int elapsed = now - *last_event;
  1503. if (elapsed > wakeup) {
  1504. // Ok! It's been too long since we've done something.
  1505. *last_event = now;
  1506. return 1;
  1507. }
  1508. return 0;
  1509. }
  1510. /*
  1511. TO FIX: Stop using c strings, must use char * buffer + int length.
  1512. MAY CONTAIN NULL VALUES.
  1513. Rework some things here.
  1514. Here's the "plan":
  1515. if buffer is EMPTY:
  1516. time_idle = 1;
  1517. // setup for "random timeout value mess"
  1518. // we're in luck! The last parameter is time interval/timeout. :D
  1519. timeout.tv_sec = 10; // randrange(10-25)
  1520. timeout.tv_usec = 0;
  1521. NOT EMPTY:
  1522. // we're in luck! The last parameter is time interval/timeout. :D
  1523. timeout.tv_sec = 0;
  1524. timeout.tv_usec = 10; // Wild Guess Here? Maybe higher, maybe lower?
  1525. time_idle = 0;
  1526. ON READ:
  1527. read/append to current buffer.
  1528. We can't use nulls -- what if they are using ZModem, there's nulls in the
  1529. file! Look for trailing / the very last "\r\n".
  1530. (I could mangle/chunk it line by line. But I'm not sure I'd need to do
  1531. that.)
  1532. Optional "mangle" buffer up to that very point -- and send up to that point.
  1533. Option #2: Maybe we send everything if program has been running for under
  1534. 20 seconds. This would allow the ANSI detect to not get screwed up by this
  1535. new idea.
  1536. ON TIMEOUT:
  1537. if time_idle:
  1538. Activate funny harry timeout events.
  1539. else:
  1540. Ok, we *STILL* haven't received any more characters into the buffer --
  1541. even after waiting. (Maybe we haven't waited long enough?)
  1542. send the pending information in the buffer and clear it out.
  1543. Maybe this is a prompt, and there won't be a \r\n.
  1544. This allows for cleaner process of "lines" of buffer. We shouldn't break
  1545. in the midDLE OF A WORD. Downside is that we sit on buffer contents a little
  1546. while / some amount of time -- which will add some lag to prompts showing up.
  1547. ZModem:
  1548. start: "rz^M**"...
  1549. 05-12 18:12:15.916 >> rz^M**^XB00000000000000^M<8A>^Q
  1550. 05-12 18:12:15.928 << **\x18B0100000023be50\r\n\x11
  1551. 05-12 18:12:15.928 >> *^XC^D
  1552. 05-12 18:12:15.939 << **\x18B0900000000a87c\r\n\x11
  1553. 05-12 18:12:15.940 >> *^XC
  1554. # Start of PK zipfile.
  1555. 05-12 18:12:15.941 >> PK^C^D^T
  1556. end:
  1557. 05-12 18:26:38.700 << **\x18B0100000023be50\r\n\x11
  1558. 05-12 18:26:38.700 >> **^XB0823a77600344c^M<8A>
  1559. 05-12 18:26:38.711 << **\x18B0800000000022d\r\n
  1560. 05-12 18:26:38.712 >> OO^MESC[0m
  1561. */
  1562. /*
  1563. * rstrnstr() Reverse string find in a string
  1564. *
  1565. * This obeys the len, and handles nulls in buffer.
  1566. * find is a c-string (null terminated)
  1567. */
  1568. int rstrnstr(const char *buffer, int len, const char *find) {
  1569. int flen = strlen(find);
  1570. if (len < flen) {
  1571. // I can't find a string in a buffer smaller then it is!
  1572. return -1;
  1573. }
  1574. int pos = len - flen;
  1575. while (pos > 0) {
  1576. if (buffer[pos] == find[0]) {
  1577. // First chars match, check them all.
  1578. if (strncmp(buffer + pos, find, flen) == 0) {
  1579. return pos;
  1580. }
  1581. }
  1582. pos--;
  1583. }
  1584. return -1;
  1585. }
  1586. int main(int argc, char *argv[]) {
  1587. int master;
  1588. pid_t pid;
  1589. int node = -1;
  1590. file_output_open("horrible_harry.log");
  1591. init_harry();
  1592. srandom(time(NULL));
  1593. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown
  1594. // -Ubugz -PUWISHPASSWORD
  1595. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
  1596. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown
  1597. // -Ubugz -PUWISH
  1598. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
  1599. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
  1600. // ./mystic -TID9 -IP192.168.0.1 -HOSTUnknown -ML0 -SL1 -ST0 -CUnknown
  1601. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown
  1602. // -Ubugz -PDUMBWAYTODOTHIS
  1603. // ./mystic -TID9 -IP192.168.0.1 -HOSTUnknown -ML1 -SL1 -ST2 -CUnknown
  1604. // -Ubugz -PIDONTUSEPASCAL
  1605. // SSH: -ML1 -ST2
  1606. // Telnet: -ML0 -ST0
  1607. // Locate username (if given) in the command line
  1608. // -U<username>
  1609. for (int x = 0; x < argc; x++) {
  1610. if (strncmp("-U", argv[x], 2) == 0) {
  1611. username = argv[x] + 2;
  1612. ZF_LOGI("Username: [%s]", username);
  1613. };
  1614. if (strncmp("-SL", argv[x], 3) == 0) {
  1615. node = argv[x][3] - '0' + 1;
  1616. ZF_LOGI("Node: %d", node);
  1617. }
  1618. }
  1619. if (username != NULL) {
  1620. locate_user(username);
  1621. ZF_LOGD("Username: [%s] A.K.A. [%s]", username, fullname);
  1622. }
  1623. if (!init_regex())
  1624. return 2;
  1625. pid = forkpty(&master, NULL, NULL, NULL);
  1626. // impossible to fork
  1627. if (pid < 0) {
  1628. return 1;
  1629. }
  1630. // child
  1631. else if (pid == 0) {
  1632. char *args[20]; // max 20 args
  1633. int x;
  1634. char new_exec[] = TARGET;
  1635. args[0] = new_exec;
  1636. for (x = 1; x < argc; x++) {
  1637. args[x] = argv[x];
  1638. };
  1639. args[x] = NULL;
  1640. // run Mystic, run!
  1641. execvp(TARGET, args);
  1642. }
  1643. // parent
  1644. else {
  1645. struct termios tios, orig1;
  1646. struct timeval timeout;
  1647. time_t last_event = 0; // time(NULL);
  1648. ZF_LOGD("starting");
  1649. tcgetattr(master, &tios);
  1650. tios.c_lflag &= ~(ECHO | ECHONL | ICANON);
  1651. /*
  1652. tios.c_iflag &= ~(ICRNL | IXON | BRKINT);
  1653. tios.c_iflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
  1654. tios.c_oflag &= ~(OPOST);
  1655. */
  1656. tcsetattr(master, TCSAFLUSH, &tios);
  1657. tcgetattr(1, &orig1);
  1658. tios = orig1;
  1659. tios.c_iflag &= ~(ICRNL | IXON | BRKINT);
  1660. tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
  1661. tios.c_oflag &= ~(OPOST);
  1662. // https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html
  1663. tcsetattr(1, TCSAFLUSH, &tios);
  1664. char buffer[1024];
  1665. int size = 0;
  1666. for (;;) {
  1667. int time_idle;
  1668. // define estruturas para o select, que serve para verificar qual
  1669. // se tornou "pronto pra uso"
  1670. fd_set read_fd;
  1671. fd_set write_fd;
  1672. fd_set except_fd;
  1673. // inicializa as estruturas
  1674. FD_ZERO(&read_fd);
  1675. FD_ZERO(&write_fd);
  1676. FD_ZERO(&except_fd);
  1677. // atribui o descritor master, obtido pelo forkpty, ao read_fd
  1678. FD_SET(master, &read_fd);
  1679. // atribui o stdin ao read_fd
  1680. FD_SET(STDIN_FILENO, &read_fd);
  1681. // o descritor tem que ser unico para o programa, a documentacao
  1682. // recomenda um calculo entre os descritores sendo usados + 1
  1683. /*
  1684. TODO: Figure out how this would work.
  1685. I'm thinking something like timeouts 30-50 seconds?
  1686. And as we get closer, 15-25 seconds.
  1687. */
  1688. if (size == 0) {
  1689. // buffer is empty
  1690. timeout.tv_sec = randrange(10, 20);
  1691. timeout.tv_usec = 0;
  1692. time_idle = 1;
  1693. } else {
  1694. // buffer is not empty
  1695. timeout.tv_sec = 0;
  1696. timeout.tv_usec = 1;
  1697. time_idle = 0;
  1698. }
  1699. if (select(master + 1, &read_fd, &write_fd, &except_fd, &timeout) == 0) {
  1700. // This means timeout!
  1701. if (time_idle) {
  1702. ZF_LOGI("TIMEOUT");
  1703. harry_idle_event(STDOUT_FILENO);
  1704. } else {
  1705. ZF_LOGI_MEM(buffer, size, "TIMEOUT size=%d", size);
  1706. console_receive(&console, buffer, size);
  1707. write(STDOUT_FILENO, buffer, size);
  1708. size = 0;
  1709. // buffer is empty now
  1710. }
  1711. }
  1712. /*
  1713. static char output[(BSIZE * 4) + 1];
  1714. */
  1715. int total;
  1716. // read_fd esta atribuido com read_fd?
  1717. if (FD_ISSET(master, &read_fd)) {
  1718. // leia o que bc esta mandando
  1719. // ZF_LOGD("read (%d) %d bytes", size, BSIZE - size);
  1720. if ((total = read(master, buffer + size, BSIZE - size)) != -1) {
  1721. // Ok, we've read more into the buffer.
  1722. // ZF_LOGD("total = %d", total);
  1723. /*
  1724. // this ... isn't working.
  1725. ZF_LOGD_MEM(buffer, total, "Buffer now:");
  1726. write(STDOUT_FILENO, buffer, total);
  1727. size = 0;
  1728. */
  1729. ZF_LOGD_MEM(buffer + size, total, "Read %d bytes:", total);
  1730. size += total;
  1731. ZF_LOGD_MEM(buffer, size, "Buffer now:");
  1732. int pos = rstrnstr(buffer, size, "\r\n");
  1733. if (pos >= 0) {
  1734. // found something!
  1735. pos += 2;
  1736. ZF_LOGD_MEM(buffer, pos, "mangle buffer %d bytes:", pos);
  1737. mangle(STDOUT_FILENO, buffer, pos);
  1738. memmove(buffer, buffer + pos, size - pos);
  1739. size -= pos;
  1740. } else {
  1741. ZF_LOGD("position of /r/n not found.");
  1742. }
  1743. } else
  1744. break;
  1745. }
  1746. #ifdef OLD_CRUFTY
  1747. if ((total = read(master, &output, BSIZE)) != -1) {
  1748. // e escreva isso na saida padrao
  1749. output[total] = 0;
  1750. // if ( harry_happens( &last_event, 5)) {
  1751. if (1) {
  1752. ZF_LOGI("harry_happens");
  1753. if (mangle(STDOUT_FILENO, output) == 0) {
  1754. // failed, so. Try again.
  1755. last_event = 0;
  1756. }
  1757. } else {
  1758. write(STDOUT_FILENO, &output, total);
  1759. // This is OUTPUT from the BBS
  1760. // ZF_LOGI( ">> %s", repr(output));
  1761. ZF_LOGI(">> %d chars", (int)strlen(output));
  1762. // I think repr is flipping out here. :(
  1763. // ZF_LOGI( ">> %d", (int)strlen(repr(output)));
  1764. }
  1765. } else
  1766. break;
  1767. }
  1768. #endif
  1769. // read_fd esta atribuido com a entrada padrao?
  1770. if (FD_ISSET(STDIN_FILENO, &read_fd)) {
  1771. // leia a entrada padrao
  1772. char input[BSIZE];
  1773. int r = read(STDIN_FILENO, &input, BSIZE);
  1774. input[r] = 0;
  1775. // e escreva no bc
  1776. ZF_LOGI("<< %s", repr(input));
  1777. write(master, &input, r);
  1778. // This is INPUT from the USER
  1779. // ZF_LOGI_MEM( input, strlen(input), "<< ");
  1780. }
  1781. }
  1782. // Restore terminal
  1783. tcsetattr(1, TCSAFLUSH, &orig1);
  1784. ZF_LOGD("exit");
  1785. }
  1786. return 0;
  1787. }