PageRenderTime 58ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/terps/alan3/output.c

https://github.com/c4rlo/gargoyle-fedora
C | 484 lines | 365 code | 67 blank | 52 comment | 89 complexity | a1d45dc8018c0da52158ec234294cbcc MD5 | raw file
  1. #include "output.h"
  2. /* IMPORTS */
  3. #include "options.h"
  4. #include "memory.h"
  5. #include "word.h"
  6. #include "lists.h"
  7. #include "term.h"
  8. #include "syserr.h"
  9. #include "dictionary.h"
  10. #include "current.h"
  11. #include "msg.h"
  12. #include "readline.h"
  13. #include "instance.h"
  14. #ifdef HAVE_GLK
  15. #include "glkio.h"
  16. #endif
  17. /* PUBLIC DATA */
  18. bool anyOutput = FALSE;
  19. bool capitalize = FALSE;
  20. bool needSpace = FALSE;
  21. bool skipSpace = FALSE;
  22. /* Screen formatting info */
  23. int col, lin;
  24. int pageLength, pageWidth;
  25. /* Logfile */
  26. #ifdef HAVE_GLK
  27. strid_t logFile;
  28. #else
  29. FILE *logFile;
  30. #endif
  31. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
  32. #if defined(HAVE_GLK) || defined(RUNNING_UNITTESTS)
  33. /*----------------------------------------------------------------------*/
  34. static int updateColumn(int currentColumn, char *string) {
  35. char *newlinePosition = strrchr(string, '\n');
  36. if (newlinePosition != NULL)
  37. return &string[strlen(string)] - newlinePosition;
  38. else
  39. return currentColumn + strlen(string);
  40. }
  41. #endif
  42. /*======================================================================*/
  43. void setSubHeaderStyle(void) {
  44. #ifdef HAVE_GLK
  45. glk_set_style(style_Subheader);
  46. #endif
  47. }
  48. /*======================================================================*/
  49. void setNormalStyle(void) {
  50. #ifdef HAVE_GLK
  51. glk_set_style(style_Normal);
  52. #endif
  53. }
  54. /*======================================================================*/
  55. void newline(void)
  56. {
  57. #ifndef HAVE_GLK
  58. char buf[256];
  59. if (!regressionTestOption && lin == pageLength - 1) {
  60. printAndLog("\n");
  61. needSpace = FALSE;
  62. printMessage(M_MORE);
  63. statusline();
  64. fflush(stdout);
  65. fgets(buf, 256, stdin);
  66. getPageSize();
  67. lin = 0;
  68. } else
  69. printAndLog("\n");
  70. lin++;
  71. #else
  72. printAndLog("\n");
  73. #endif
  74. col = 1;
  75. needSpace = FALSE;
  76. }
  77. /*======================================================================*/
  78. void para(void)
  79. {
  80. /* Make a new paragraph, i.e one empty line (one or two newlines). */
  81. #ifdef HAVE_GLK
  82. if (glk_gestalt(gestalt_Graphics, 0) == 1)
  83. glk_window_flow_break(glkMainWin);
  84. #endif
  85. if (col != 1)
  86. newline();
  87. newline();
  88. capitalize = TRUE;
  89. }
  90. /*======================================================================*/
  91. void clear(void)
  92. {
  93. #ifdef HAVE_GLK
  94. glk_window_clear(glkMainWin);
  95. #else
  96. #ifdef HAVE_ANSI
  97. if (!statusLineOption) return;
  98. printf("\x1b[2J");
  99. printf("\x1b[%d;1H", pageLength);
  100. #endif
  101. #endif
  102. }
  103. /*----------------------------------------------------------------------*/
  104. static void capitalizeFirst(char *str) {
  105. int i = 0;
  106. /* Skip over space... */
  107. while (i < strlen(str) && isSpace(str[i])) i++;
  108. if (i < strlen(str)) {
  109. str[i] = toUpper(str[i]);
  110. capitalize = FALSE;
  111. }
  112. }
  113. /*======================================================================*/
  114. void printAndLog(char string[])
  115. {
  116. #ifdef HAVE_GLK
  117. static int column = 0;
  118. unsigned char *stringCopy;
  119. unsigned char *stringPart;
  120. #endif
  121. printf("%s", string);
  122. if (!onStatusLine && transcriptOption) {
  123. #ifdef HAVE_GLK
  124. // TODO Is this assuming only 70-char wide windows for GLK?
  125. if (strlen(string) > 70-column) {
  126. stringCopy = strdup(string); /* Make sure we can write NULLs */
  127. stringPart = stringCopy;
  128. while (strlen(stringPart) > 70-column) {
  129. int p;
  130. for (p = 70-column; p>0 && !isspace(stringPart[p]); p--);
  131. stringPart[p] = '\0';
  132. glk_put_string_stream(logFile, stringPart);
  133. glk_put_char_stream(logFile, '\n');
  134. column = 0;
  135. stringPart = &stringPart[p+1];
  136. }
  137. glk_put_string_stream(logFile, stringPart);
  138. column = updateColumn(column, stringPart);
  139. free(stringCopy);
  140. } else {
  141. glk_put_string_stream(logFile, string);
  142. column = updateColumn(column, string);
  143. }
  144. #else
  145. fprintf(logFile, "%s", string);
  146. #endif
  147. }
  148. }
  149. /*----------------------------------------------------------------------*/
  150. static void justify(char str[])
  151. {
  152. if (capitalize)
  153. capitalizeFirst(str);
  154. #ifdef HAVE_GLK
  155. printAndLog(str);
  156. #else
  157. int i;
  158. char ch;
  159. if (col >= pageWidth && !skipSpace)
  160. newline();
  161. while (strlen(str) > pageWidth - col) {
  162. i = pageWidth - col - 1;
  163. while (!isSpace(str[i]) && i > 0) /* First find wrap point */
  164. i--;
  165. if (i == 0 && col == 1) /* If it doesn't fit at all */
  166. /* Wrap immediately after this word */
  167. while (!isSpace(str[i]) && str[i] != '\0')
  168. i++;
  169. if (i > 0) { /* If it fits ... */
  170. ch = str[i]; /* Save space or NULL */
  171. str[i] = '\0'; /* Terminate string */
  172. printAndLog(str); /* and print it */
  173. skipSpace = FALSE; /* If skipping, now we're done */
  174. str[i] = ch; /* Restore character */
  175. /* Skip white after printed portion */
  176. for (str = &str[i]; isSpace(str[0]) && str[0] != '\0'; str++);
  177. }
  178. newline(); /* Then start a new line */
  179. while(isSpace(str[0])) str++; /* Skip any leading space on next part */
  180. }
  181. printAndLog(str); /* Print tail */
  182. #endif
  183. col = col + strlen(str); /* Update column */
  184. }
  185. /*----------------------------------------------------------------------*/
  186. static void space(void)
  187. {
  188. if (skipSpace)
  189. skipSpace = FALSE;
  190. else {
  191. if (needSpace) {
  192. printAndLog(" ");
  193. col++;
  194. }
  195. }
  196. needSpace = FALSE;
  197. }
  198. /*----------------------------------------------------------------------*/
  199. static void sayPlayerWordsForParameter(int p) {
  200. int i;
  201. for (i = globalParameters[p].firstWord; i <= globalParameters[p].lastWord; i++) {
  202. justify((char *)pointerTo(dictionary[playerWords[i].code].string));
  203. if (i < globalParameters[p].lastWord)
  204. justify(" ");
  205. }
  206. }
  207. /*----------------------------------------------------------------------*/
  208. static void sayParameter(int p, int form)
  209. {
  210. int i;
  211. for (i = 0; i <= p; i++)
  212. if (isEndOfArray(&globalParameters[i]))
  213. syserr("Nonexistent parameter referenced.");
  214. #ifdef ALWAYS_SAY_PARAMETERS_USING_PLAYER_WORDS
  215. if (params[p].firstWord != EOF) /* Any words he used? */
  216. /* Yes, so use them... */
  217. sayPlayerWordsForParameter(p);
  218. else
  219. sayForm(params[p].code, form);
  220. #else
  221. if (globalParameters[p].useWords) {
  222. /* Ambiguous instance referenced, so use the words he used */
  223. sayPlayerWordsForParameter(p);
  224. } else
  225. sayForm(globalParameters[p].instance, form);
  226. #endif
  227. }
  228. /*----------------------------------------------------------------------
  229. Print an expanded symbolic reference.
  230. N = newline
  231. I = indent on a new line
  232. P = new paragraph
  233. L = current location name
  234. O = current object -> first parameter!
  235. <n> = n:th parameter
  236. +<n> = definite form of n:th parameter
  237. 0<n> = indefinite form of n:th parameter
  238. !<n> = pronoun for the n:th parameter
  239. V = current verb
  240. A = current actor
  241. T = tabulation
  242. $ = no space needed after this, and don't capitalize
  243. */
  244. static char *printSymbol(char str[]) /* IN - The string starting with '$' */
  245. {
  246. int advance = 2;
  247. if (*str == '\0') printAndLog("$");
  248. else switch (toLower(str[1])) {
  249. case 'n':
  250. newline();
  251. needSpace = FALSE;
  252. break;
  253. case 'i':
  254. newline();
  255. printAndLog(" ");
  256. col = 5;
  257. needSpace = FALSE;
  258. break;
  259. case 'o':
  260. space();
  261. sayParameter(0, 0);
  262. needSpace = TRUE; /* We did print something non-white */
  263. break;
  264. case '+':
  265. case '0':
  266. case '-':
  267. case '!':
  268. space();
  269. if (isdigit((int)str[2])) {
  270. int form;
  271. switch (str[1]) {
  272. case '+': form = SAY_DEFINITE; break;
  273. case '0': form = SAY_INDEFINITE; break;
  274. case '-': form = SAY_NEGATIVE; break;
  275. case '!': form = SAY_PRONOUN; break;
  276. default: form = SAY_SIMPLE; break;
  277. }
  278. sayParameter(str[2]-'1', form);
  279. needSpace = TRUE;
  280. }
  281. advance = 3;
  282. break;
  283. case '1':
  284. case '2':
  285. case '3':
  286. case '4':
  287. case '5':
  288. case '6':
  289. case '7':
  290. case '8':
  291. case '9':
  292. space();
  293. sayParameter(str[1]-'1', SAY_SIMPLE);
  294. needSpace = TRUE; /* We did print something non-white */
  295. break;
  296. case 'l':
  297. space();
  298. say(current.location);
  299. needSpace = TRUE; /* We did print something non-white */
  300. break;
  301. case 'a':
  302. space();
  303. say(current.actor);
  304. needSpace = TRUE; /* We did print something non-white */
  305. break;
  306. case 'v':
  307. space();
  308. justify((char *)pointerTo(dictionary[verbWord].string));
  309. needSpace = TRUE; /* We did print something non-white */
  310. break;
  311. case 'p':
  312. para();
  313. needSpace = FALSE;
  314. break;
  315. case 't': {
  316. int i;
  317. int spaces = 4-(col-1)%4;
  318. for (i = 0; i<spaces; i++) printAndLog(" ");
  319. col = col + spaces;
  320. needSpace = FALSE;
  321. break;
  322. }
  323. case '$':
  324. skipSpace = TRUE;
  325. capitalize = FALSE;
  326. break;
  327. default:
  328. advance = 1;
  329. printAndLog("$");
  330. break;
  331. }
  332. return &str[advance];
  333. }
  334. /*----------------------------------------------------------------------*/
  335. static bool inhibitSpace(char *str) {
  336. return str[0] == '$' && str[1] == '$';
  337. }
  338. /*----------------------------------------------------------------------*/
  339. static bool isSpaceEquivalent(char str[]) {
  340. if (str[0] == ' ')
  341. return TRUE;
  342. else
  343. return strncmp(str, "$p", 2) == 0
  344. || strncmp(str, "$n", 2) == 0
  345. || strncmp(str, "$i", 2) == 0
  346. || strncmp(str, "$t", 2) == 0;
  347. }
  348. /*----------------------------------------------------------------------*/
  349. static bool punctuationNext(char *str) {
  350. char *punctuation = strchr(".,!?", str[0]);
  351. bool end = str[1] == '\0';
  352. bool space = isSpaceEquivalent(&str[1]);
  353. return (punctuation != NULL && (end || space));
  354. }
  355. /*----------------------------------------------------------------------*/
  356. static char lastCharOf(char *str) {
  357. return str[strlen(str)-1];
  358. }
  359. /*======================================================================*/
  360. void output(char original[])
  361. {
  362. char ch;
  363. char *str, *copy;
  364. char *symptr;
  365. copy = strdup(original);
  366. str = copy;
  367. if (inhibitSpace(str) || punctuationNext(str))
  368. needSpace = FALSE;
  369. else
  370. space(); /* Output space if needed (& not inhibited) */
  371. /* Output string up to symbol and handle the symbol */
  372. while ((symptr = strchr(str, '$')) != (char *) NULL) {
  373. ch = *symptr; /* Terminate before symbol */
  374. *symptr = '\0';
  375. if (strlen(str) > 0) {
  376. skipSpace = FALSE; /* Only let skipSpace through if it is
  377. last in the string */
  378. if (lastCharOf(str) == ' ') {
  379. str[strlen(str)-1] = '\0'; /* Truncate space character */
  380. justify(str); /* Output part before '$' */
  381. needSpace = TRUE;
  382. } else {
  383. justify(str); /* Output part before '$' */
  384. needSpace = FALSE;
  385. }
  386. }
  387. *symptr = ch; /* restore '$' */
  388. str = printSymbol(symptr); /* Print the symbolic reference and advance */
  389. }
  390. if (str[0] != 0) {
  391. justify(str); /* Output trailing part */
  392. skipSpace = FALSE;
  393. if (lastCharOf(str) != ' ')
  394. needSpace = TRUE;
  395. }
  396. if (needSpace)
  397. capitalize = strchr("!?.", str[strlen(str)-1]) != 0;
  398. anyOutput = TRUE;
  399. free(copy);
  400. }
  401. /*======================================================================*/
  402. bool confirm(MsgKind msgno)
  403. {
  404. char buf[80];
  405. /* This is a bit of a hack since we really want to compare the input,
  406. it could be affirmative, but for now any input is NOT! */
  407. printMessage(msgno);
  408. #ifdef USE_READLINE
  409. if (!readline(buf)) return TRUE;
  410. #else
  411. if (gets(buf) == NULL) return TRUE;
  412. #endif
  413. col = 1;
  414. return (buf[0] == '\0');
  415. }