PageRenderTime 71ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 0ms

/Parser/gameinfo.c

https://github.com/rpj/ugo
C | 719 lines | 539 code | 104 blank | 76 comment | 165 complexity | 4756bedd3090455d0b879a17913a2cb6 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /**************************************************************************
  2. *** Project: SGF Syntax Checker & Converter
  3. *** File: gameinfo.c
  4. ***
  5. *** Copyright (C) 1996-2004 by Arno Hollosi
  6. *** (see 'main.c' for more copyright information)
  7. ***
  8. **************************************************************************/
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <ctype.h>
  13. #include "all.h"
  14. #include "protos.h"
  15. /**************************************************************************
  16. *** Function: Get_Fraction
  17. *** Checks for written out fractions and small numbers
  18. *** Parameters: val ... pointer to string
  19. *** Returns: 0 .. nothing found / fraction value (*4)
  20. **************************************************************************/
  21. int Get_Fraction(char *val)
  22. {
  23. int frac = 0;
  24. char *t;
  25. if((t = strstr(val, "1/2"))) frac = 2;
  26. else if((t = strstr(val, "3/4"))) frac = 3;
  27. else if((t = strstr(val, "1/4"))) frac = 1;
  28. else if((t = strstr(val, "2/4"))) frac = 2;
  29. if(t)
  30. strcpy(t, " "); /* remove fraction */
  31. if(strstr(val, "half")) frac = 2;
  32. if(strstr(val, "one")) frac += 4;
  33. if(strstr(val, "two")) frac += 8;
  34. if(strstr(val, "three")) frac += 12;
  35. if(strstr(val, "four")) frac += 16;
  36. if(strstr(val, "five")) frac += 20;
  37. return(frac);
  38. }
  39. /**************************************************************************
  40. *** Function: Parse_Komi
  41. *** Checks komi value and corrects it if possible
  42. *** Parameters: val ... pointer to KM string
  43. *** Returns: -1/0/1/2: corrected error / error / ok / corrected
  44. **************************************************************************/
  45. int Parse_Komi(char *val, U_SHORT dummy)
  46. {
  47. int frac, ret;
  48. double points = 0.0;
  49. frac = Get_Fraction(val);
  50. if((frac == 4) && strstr(val, "none"))
  51. frac = -1;
  52. ret = Parse_Float(val, 0);
  53. if(frac)
  54. {
  55. if(frac > 0)
  56. {
  57. points = frac / 4.0;
  58. if(ret)
  59. points += atof(val);
  60. }
  61. sprintf(val, "%f", points);
  62. Parse_Float(val, 0); /* remove trailing '0' */
  63. ret = -1;
  64. }
  65. return(ret);
  66. }
  67. /**************************************************************************
  68. *** Function: Parse_Time
  69. *** Checks time value and corrects it if possible
  70. *** Parameters: val ... pointer to TM string
  71. *** Returns: -1/0/1/2: corrected error / error / ok / corrected
  72. **************************************************************************/
  73. int Parse_Time(char *val, U_SHORT dummy)
  74. {
  75. int ret = 1, hour = 0, min = 0;
  76. double time;
  77. char *s;
  78. if(Kill_Chars(val, C_ISSPACE, NULL))
  79. ret = -1;
  80. /* ":/;+" indicate that there's byo-yomi time given too */
  81. /* &val[1] because of possible leading '+' */
  82. if(Test_Chars(&val[1], C_inSET, ":/;+"))
  83. return(0);
  84. if(Test_Chars(val, C_ISALPHA, NULL))
  85. {
  86. ret = -1;
  87. s = val + strlen(val) - 1;
  88. if(strstr(val, "hr")) hour = 3600;
  89. if(strstr(val, "hour")) hour = 3600;
  90. if(*s == 'h') hour = 3600;
  91. if(strstr(val, "min")) min = 60;
  92. if(*s == 'm') min = 60;
  93. if(hour && min) return(0); /* can't handle both */
  94. if(!hour) hour = min;
  95. if(Parse_Float(val, 0))
  96. {
  97. time = atof(val) * hour;
  98. sprintf(val, "%f", time);
  99. Parse_Float(val, 0); /* remove trailing '0' */
  100. }
  101. else
  102. return(0);
  103. }
  104. else
  105. switch(Parse_Float(val, 0))
  106. {
  107. case 0: return(0);
  108. case -1: return(-1);
  109. case 2: if(ret == 1)
  110. return(2);
  111. }
  112. return(ret);
  113. }
  114. /**************************************************************************
  115. *** Function: Parse_Result
  116. *** Checks result value and corrects it if possible
  117. *** Parameters: val ... pointer to RE string
  118. *** Returns: -1/0/1/2: corrected error / error / ok / corrected
  119. **************************************************************************/
  120. int Parse_Result(char *val, U_SHORT dummy)
  121. {
  122. char *s, *d;
  123. int err = 1, charpoints, type = 0;
  124. double points = 0.0;
  125. if(Kill_Chars(val, C_ISSPACE, NULL))
  126. err = -1;
  127. switch(val[0])
  128. {
  129. case '0':
  130. case '?': if(strlen(val) > 1)
  131. {
  132. err = -1;
  133. val[1] = 0;
  134. }
  135. break;
  136. case 'j':
  137. case 'J': if(strnccmp(val, "jigo", 4))
  138. return(0);
  139. case 'd': err = -1;
  140. val[0] = 'D';
  141. case 'D': if(!strcmp(val, "Draw"))
  142. break;
  143. err = -1;
  144. strcpy(val, "0"); /* use shortcut for draw */
  145. break;
  146. case 'v': err = -1;
  147. val[0] = 'V';
  148. case 'V': if(!strcmp(val, "Void"))
  149. break;
  150. err = -1;
  151. strcpy(val, "Void");
  152. break;
  153. case 'z':
  154. case 'Z': if(strnccmp(val, "zwart", 5))
  155. return(0);
  156. else
  157. val[0] = 'B';
  158. case 'b':
  159. case 'w': err = -1;
  160. val[0] = toupper(val[0]);
  161. case 'B':
  162. case 'W': charpoints = Get_Fraction(val);
  163. if(val[1] != '+') /* some text between 'B/W' and '+' */
  164. {
  165. s = SkipText(val, NULL, '+', 0);
  166. if(s)
  167. {
  168. err = -1;
  169. d = &val[1];
  170. while((*d++ = *s++)); /* copy must be left2right */
  171. }
  172. else /* no '+' at all */
  173. {
  174. if(strstr(val, "resign")) type |= 1;
  175. if(strstr(val, "Resign")) type |= 1;
  176. if(strstr(val, "opgave")) type |= 1;
  177. if(strstr(val, "win")) type |= 2;
  178. if(strstr(val, "won")) type |= 2;
  179. if(strstr(val, "lose")) type |= 4;
  180. if(strstr(val, "loose")) type |= 4;
  181. if(strstr(val, "lost")) type |= 4;
  182. if(strstr(val, "with")) type |= 8;
  183. if(strstr(val, "by")) type |= 8;
  184. if(strstr(val, "point")) type |= 16;
  185. if(strstr(val, "punt")) type |= 16;
  186. /* type 64: for search for double numbers */
  187. if((!(type & 7) && !charpoints) ||
  188. ((type & 2) && (type& 4))) /* win and lose? */
  189. {
  190. return(0);
  191. }
  192. if(type & 1) /* resignation */
  193. {
  194. if(!(type & 2))
  195. type |= 4;
  196. strcpy(&val[1], "+R");
  197. }
  198. else
  199. if((type & 24) || charpoints) /* point win */
  200. {
  201. err = Parse_Float(&val[1], TYPE_GINFO);
  202. if(!err && !charpoints) /* no points found */
  203. {
  204. if(type & 16)
  205. return(0); /* info would be lost */
  206. else
  207. strcpy(&val[1], "+");
  208. }
  209. else
  210. {
  211. if(err)
  212. points = atof(&val[1]);
  213. points += (charpoints / 4.0);
  214. sprintf(&val[1], "+%f", points);
  215. Parse_Float(&val[2], TYPE_GINFO);
  216. }
  217. }
  218. else /* just win or lose */
  219. strcpy(&val[1], "+");
  220. if((type & 4))
  221. {
  222. if(val[0] == 'B') val[0] = 'W';
  223. else val[0] = 'B';
  224. }
  225. err = -1;
  226. break;
  227. }
  228. }
  229. if(val[2]) /* text after the '+' */
  230. {
  231. if(!strcmp(&val[2], "Resign"))
  232. break;
  233. if(!strcmp(&val[2], "Time"))
  234. break;
  235. if(!strcmp(&val[2], "Forfeit"))
  236. break;
  237. switch(val[2]) /* looking for shortcuts */
  238. { /* or win by points */
  239. case 'r':
  240. case 't':
  241. case 'f': err = -1;
  242. val[2] = toupper(val[2]);
  243. case 'R':
  244. case 'T':
  245. case 'F': if(strlen(val) > 3)
  246. {
  247. err = -1;
  248. val[3] = 0;
  249. }
  250. break;
  251. default: switch(Parse_Float(&val[2], TYPE_GINFO))
  252. {
  253. case 0: err = 0; break;
  254. case -1: err = -1; break;
  255. case 1: break;
  256. case 2: if(err == 1)
  257. err = 2;
  258. break;
  259. }
  260. if(charpoints)
  261. {
  262. err = -1;
  263. points = atof(&val[2]) + (charpoints/4.0);
  264. sprintf(&val[2], "%f", points);
  265. Parse_Float(&val[2], TYPE_GINFO);
  266. }
  267. else
  268. if(!err)
  269. {
  270. val[2] = 0; /* win without reason */
  271. err = -1;
  272. }
  273. break;
  274. }
  275. }
  276. break;
  277. default: err = 0;
  278. break;
  279. }
  280. return(err);
  281. }
  282. /**************************************************************************
  283. *** Function: Correct_Date
  284. *** Tries to fix date value
  285. *** Parameters: value ... pointer to date string
  286. *** Returns: -1/0: corrected error / error
  287. **************************************************************************/
  288. int Correct_Date(char *value)
  289. {
  290. int year = -1, month = -1, day = -1, day2 = -1;
  291. int i, charmonth = FALSE;
  292. long n;
  293. char *s;
  294. const char months[26][4] = { "Jan", "jan", "Feb", "feb", "Mar", "mar",
  295. "Apr", "apr", "May", "may", "Jun", "jun",
  296. "Jul", "jul", "Aug", "aug", "Sep", "sep",
  297. "Oct", "oct", "Nov", "nov", "Dec", "dec",
  298. "Okt", "okt" };
  299. Kill_Chars(value, C_inSET, "\n");
  300. for(i = 0; i < 26; i++)
  301. {
  302. s = strstr(value, months[i]);
  303. if(s)
  304. {
  305. if(charmonth) /* found TWO month names */
  306. return(0);
  307. else
  308. {
  309. month = i/2 + 1;
  310. charmonth = TRUE;
  311. }
  312. }
  313. }
  314. if(month == 13) month = 10; /* correct 'okt' value */
  315. s = value;
  316. while(*s)
  317. {
  318. if(isdigit(*s))
  319. {
  320. n = strtol(s, &s, 10);
  321. if(n > 31)
  322. if(year < 0) year = n;
  323. else return(0); /* two values >31 */
  324. else
  325. if(n > 12 || charmonth)
  326. if(day < 0) day = n;
  327. else
  328. if(day2 < 0) day2 = n;
  329. else return(0); /* more than two days found */
  330. else
  331. if(month < 0) month = n;
  332. else return(0); /* can't tell if MM or DD */
  333. }
  334. else
  335. s++;
  336. }
  337. if(year < 0 || year > 9999) /* year is missing or false */
  338. return(0);
  339. else
  340. if(year < 100) /* only two digits? -> 20th century */
  341. year += 1900;
  342. if(day > 0 && month > 0)
  343. {
  344. if(day2 > 0)
  345. sprintf(value, "%04d-%02d-%02d,%02d", year, month, day, day2);
  346. else
  347. sprintf(value, "%04d-%02d-%02d", year, month, day);
  348. }
  349. else
  350. if(month > 0)
  351. sprintf(value, "%04d-%02d", year, month);
  352. else
  353. sprintf(value, "%04d", year);
  354. return(-1);
  355. }
  356. /**************************************************************************
  357. *** Function: Parse_Date
  358. *** Checks date value, corrects easy errors (space etc.)
  359. *** Tough cases are passed on to CorrectDate
  360. *** Parameters: value ... pointer to date string
  361. *** Returns: -1/0/1: corrected error / error / ok
  362. **************************************************************************/
  363. int Parse_Date(char *value, U_SHORT dummy)
  364. {
  365. int ret = 1, allowed, type, hasgoty, turn, oldtype;
  366. char *c, *d;
  367. long num;
  368. /* type: 0 YYYY
  369. 1 YYYY-MM
  370. 2 YYYY-MM-DD
  371. 3 MM-DD
  372. 4 MM
  373. 5 DD
  374. allowed: bitmask of type
  375. */
  376. /* bad chars? -> pass on to CorrectDate */
  377. if(Test_Chars(value, C_NOTinSET, "0123456789-,"))
  378. return(Correct_Date(value));
  379. c = d = value;
  380. while(*c) /* remove spaces, and unnecessary '-', ',' */
  381. {
  382. if(isspace(*c)) /* keep spaces in between numbers */
  383. {
  384. if(ret) ret = -1;
  385. if((d != value) && isdigit(*(d-1)) && isdigit(*(c+1)))
  386. {
  387. *d++ = *c++; /* space between two numbers */
  388. ret = 0;
  389. }
  390. else
  391. c++;
  392. }
  393. else
  394. if(*c == '-') /* remove all '-' not in between two numbers */
  395. {
  396. if((d != value) && isdigit(*(d-1)) && (isdigit(*(c+1)) || isspace(*(c+1))))
  397. *d++ = *c++;
  398. else
  399. {
  400. if(ret) ret = -1;
  401. c++;
  402. }
  403. }
  404. else
  405. if(*c == ',') /* remove all ',' not preceeded by a number */
  406. {
  407. if((d != value) && isdigit(*(d-1)) && *(c+1))
  408. *d++ = *c++;
  409. else
  410. {
  411. if(ret) ret = -1;
  412. c++;
  413. }
  414. }
  415. else
  416. *d++ = *c++;
  417. }
  418. *d = 0;
  419. c = value;
  420. allowed = 0x07;
  421. oldtype = type = hasgoty = 0;
  422. turn = 1;
  423. while(ret && *c) /* parse the nasty bastard */
  424. {
  425. d = c;
  426. num = strtol(c, &c, 10);
  427. if((num < 1) || (num > 9999) || (((c-d) != 2) && ((c-d) != 4)) ||
  428. (turn == 1 && ((c-d) == 2) && !(allowed & 0x30)) ||
  429. (turn != 1 && ((c-d) == 4)))
  430. {
  431. /* illegal number or != 2 or 4 digits
  432. or start number isn't year when required
  433. or digits inside date (MM,DD part) */
  434. ret = 0;
  435. break;
  436. }
  437. if((c-d) == 4) /* date has year */
  438. hasgoty = TRUE;
  439. switch(*c)
  440. {
  441. case '-': if(((c-d) == 2) && num > 31)
  442. ret = 0;
  443. c++; /* loop inc */
  444. turn++;
  445. if(turn == 4)
  446. ret = 0;
  447. break;
  448. case ',': c++; /* loop inc */
  449. case 0: switch(turn) /* date has got which type? */
  450. {
  451. case 1: if(hasgoty) type = 0;
  452. else
  453. if(oldtype == 1 || oldtype == 4)
  454. type = 4;
  455. else
  456. type = 5;
  457. break;
  458. case 2: if(hasgoty) type = 1;
  459. else type = 3;
  460. break;
  461. case 3: type = 2;
  462. break;
  463. }
  464. if(!(allowed & (1 << type)))
  465. {
  466. ret = 0; /* is current type allowed? */
  467. break;
  468. }
  469. switch(type) /* set new allow mask */
  470. {
  471. case 0: allowed = 0x07; break;
  472. case 1: allowed = 0x1f; break;
  473. case 2: allowed = 0x2f; break;
  474. case 3: allowed = 0x2f; break;
  475. case 4: allowed = 0x1f; break;
  476. case 5: allowed = 0x2f; break;
  477. }
  478. turn = 1; /* new date to parse */
  479. hasgoty = FALSE;
  480. oldtype = type;
  481. break;
  482. }
  483. }
  484. if(!ret) /* date has got tough errors -> pass on to CorrectDate */
  485. ret = Correct_Date(value);
  486. return(ret);
  487. }
  488. /**************************************************************************
  489. *** Function: Check_GameInfo
  490. *** Checks RE,DT,TM, KM value
  491. *** Parameters: p ... pointer to property containing the value
  492. *** v ... pointer to property value
  493. *** Returns: TRUE for success / FALSE if value has to be deleted
  494. **************************************************************************/
  495. int Check_GameInfo(struct Property *p, struct PropValue *v)
  496. {
  497. char *val;
  498. int res, size;
  499. int (*parse)(char *, U_SHORT);
  500. if(!Check_Text(p, v)) /* parse text (converts spaces) */
  501. return(FALSE);
  502. switch(p->id)
  503. {
  504. case TKN_RE: parse = Parse_Result; break;
  505. case TKN_DT: parse = Parse_Date; break;
  506. case TKN_TM: parse = Parse_Time; break;
  507. case TKN_KM: parse = Parse_Komi; break;
  508. default: return(TRUE);
  509. }
  510. size = (strlen(v->value) > 25) ? (strlen(v->value) + 2) : 27;
  511. /* correct funcions may use up to 25 bytes */
  512. SaveMalloc(char *, val, size, "result value buffer");
  513. strcpy(val, v->value);
  514. res = (*parse)(val, 0);
  515. if(option_interactive)
  516. {
  517. if(res < 1)
  518. if(!PromptGameInfo(p, v, parse))
  519. {
  520. free(val);
  521. return(FALSE);
  522. }
  523. }
  524. else
  525. {
  526. switch(res)
  527. {
  528. case 0: PrintError(E4_FAULTY_GC, v->buffer, p->idstr, "(NOT CORRECTED!)");
  529. break;
  530. case -1: PrintError(E4_BAD_VALUE_CORRECTED, v->buffer, p->idstr, val);
  531. free(v->value);
  532. v->value = val;
  533. return(TRUE);
  534. }
  535. }
  536. if(res == 2)
  537. strcpy(v->value, val);
  538. free(val);
  539. return(TRUE);
  540. }
  541. /**************************************************************************
  542. *** Function: PromptGameInfo
  543. *** If interactive mode: prompts for game-info value
  544. *** else just print error message
  545. *** Parameters: p ... property
  546. *** v ... faulty value (part of p)
  547. *** Returns: TRUE / FALSE if property should be deleted
  548. **************************************************************************/
  549. int PromptGameInfo(struct Property *p, struct PropValue *v,
  550. int (*Parse_Value)(char *, U_SHORT))
  551. {
  552. char *newgi, *oldgi, inp[2001];
  553. long size;
  554. int ret;
  555. if(!option_interactive)
  556. {
  557. PrintError(E4_FAULTY_GC, v->buffer, p->idstr, "(NOT CORRECTED!)");
  558. return(TRUE);
  559. }
  560. oldgi = SkipText(v->buffer, NULL, ']', 0);
  561. size = oldgi - v->buffer;
  562. if(size < 25) /* CorrectDate may use up to 15 chars */
  563. size = 25;
  564. SaveMalloc(char *, newgi, size+2, "game info value buffer");
  565. CopyValue(newgi, v->buffer+1, oldgi - v->buffer-1, FALSE);
  566. SaveMalloc(char *, oldgi, strlen(newgi)+2, "game info value buffer");
  567. strcpy(oldgi, newgi);
  568. PrintError(E4_FAULTY_GC, v->buffer, p->idstr, "");
  569. while(TRUE)
  570. {
  571. ret = (*Parse_Value)(newgi, 0);
  572. if(ret) printf("--> Use [%s] (enter), delete (d) or type in new value? ", newgi);
  573. else printf("--> Keep faulty value (enter), delete (d) or type in new value? ");
  574. fgets(inp, 2000, stdin);
  575. if(strlen(inp))
  576. inp[strlen(inp)-1] = 0; /* delete last char, it is a newline */
  577. if(!strnccmp(inp, "d", 0)) /* delete */
  578. {
  579. free(newgi);
  580. free(oldgi);
  581. return(FALSE);
  582. }
  583. if(strlen(inp)) /* edit */
  584. {
  585. ret = (*Parse_Value)(inp, 0);
  586. if(ret == 1)
  587. {
  588. free(v->value);
  589. SaveMalloc(char *, v->value, strlen(inp)+4, "game info value buffer");
  590. strcpy(v->value, inp);
  591. break;
  592. }
  593. else
  594. {
  595. puts("--! Error in input string !--");
  596. if(ret == -1)
  597. {
  598. size = (strlen(inp) > 25) ? strlen(inp) : 25;
  599. free(newgi);
  600. SaveMalloc(char *, newgi, size+2, "game info value buffer");
  601. strcpy(newgi, inp);
  602. }
  603. }
  604. }
  605. else /* return */
  606. {
  607. if(ret) strcpy(v->value, newgi);
  608. else strcpy(v->value, oldgi);
  609. break;
  610. }
  611. }
  612. free(newgi);
  613. free(oldgi);
  614. return(TRUE);
  615. }