1 | /*
2 | * $Log: ml_short.c,v $
3 | * Revision 1.2 2002/04/06 15:03:58 miller
4 | * Added space after "province" and allow "prov" abbreviation
5 | *
6 | * Revision 1.1 2000/11/14 14:27:37 miller
7 | * Initial revision
8 | *
9 | * Revision 1.1 1998/02/28 17:49:42 david
10 | * Initial revision
11 | *
12 | * Revision 1.3 1997/03/16 06:53:13 rpaar
13 | *
14 | * Revision 1.2 1996/11/05 23:11:36 rpaar
15 | * USIT changes to fix minor bugs
16 | */
17 |
18 | /* ml_short. -- process incoming shorthand mail
19 | * Copyright 1987, Lowe.
20 | *
21 | * Diplomacy is a trademark of the Avalon Hill Game Company, Baltimore,
22 | * Maryland, all rights reserved; used with permission.
23 | *
24 | * Redistribution and use in source and binary forms are permitted
25 | * provided that it is for non-profit purposes, that this and the
26 | * above notices are preserved and that due credit is given to Mr.
27 | * Lowe.
28 | */
29 |
30 | #include <stdio.h>
31 | #include <stdlib.h>
32 | #include <string.h>
33 | #include <time.h>
34 |
35 | #include "dip.h"
36 | #include "mail.h"
37 | #include "functions.h"
38 | #include "conf.h"
39 | #include "porder.h"
40 |
41 | #define SH_PROPOSE 1
42 | #define SH_ACCEPT 2
43 | #define SH_REJECT 3
44 | #define SH_INFORM 4
45 | #define SH_DRAW 5
46 | #define SH_PEACE 6
47 | #define SH_ALLY 7
48 | #define SH_TARGET 8
49 | #define SH_DMZ 9
50 | #define SH_NOT 10
51 | #define SH_PROVINCE 11
52 | #define SH_ORDER 15
53 |
54 | /* And these do not appear in 'searchable' list */
55 | #define SH_BLANK 12
56 | #define SH_PHASE 13
57 | #define SH_POWER 14
58 | #define SH_PROV_LIST 16
59 | #define SH_ERROR 17
60 | #define SH_NOPOWERS 18
61 | #define SH_NOPROV 19
62 | #define SH_SEPARATOR 20
63 | #define SH_MASTER 21
64 | #define SH_OBSERVER 22
65 |
66 | static char *sh_prelim[] =
67 | {"",
68 | "proposes","propose",
69 | "accepts", "accept",
70 | "rejects", "reject",
71 | "informs", "inform",
72 | "draw",
73 | "peace",
74 | "alliance","ally",
75 | "targets","target",
76 | "dmz",
77 | "not", "!",
78 | "provinces","province", "prov",
79 | "orders", "order",
80 | ","
81 | };
82 | static int sh_pvalue[] =
83 | {0,
84 | SH_PROPOSE, SH_PROPOSE,
85 | SH_ACCEPT, SH_ACCEPT,
86 | SH_REJECT, SH_REJECT,
87 | SH_INFORM, SH_INFORM,
88 | SH_DRAW,
89 | SH_PEACE,
90 | SH_ALLY, SH_ALLY,
91 | SH_TARGET, SH_TARGET,
92 | SH_DMZ,
93 | SH_NOT, SH_NOT,
94 | SH_PROVINCE, SH_PROVINCE, SH_PROVINCE,
95 | SH_ORDER, SH_ORDER,
96 | SH_SEPARATOR
97 | };
98 |
99 |
100 | #define NO_POWERS 0
101 | #define CANBE_POWERS 1
102 | #define MUSTBE_POWERS 2
103 | #define NO_PROVINCE 0
104 | #define CANBE_PROVINCE 0
105 | #define MUSTBE_PROVINCE 2
106 |
107 | int ProcessShSegment(char **s, char *rrret_text, int power_list, int prov_list);
108 | /*
109 | * This function shoud be replaced by the phase function in phase.c
110 | */
111 |
112 | int CheckShSegmentPhase(char *s, char *rret_text)
113 | {
114 | char poss_phase[7];
115 | rret_text[0] = '\0';
116 | strncpy(poss_phase, s, 6);
117 | poss_phase[6] = '\0';
118 | if (!isalnum(*s)) {
119 | while (!isalnum(*s) && *s != '\0' ) s++; /* Skip spaces */
120 | }
121 | if (GetOneWord(poss_phase) == 0 ) return SH_BLANK;
122 |
123 | if (strlen(s) < 6 ) return SH_ERROR;
124 | /* Only accept annnl */
125 | switch (toupper(s[0]))
126 | {
127 | case 'F':
128 | case 'S':
129 | rret_text[0]=toupper(s[0]);
130 | break;
131 | default:
132 | return SH_ERROR;
133 | }
134 |
135 | if (!isdigit(s[1]) || !isdigit(s[2]) || !isdigit(s[3]) || !isdigit(s[4]) )
136 | {
137 | return SH_ERROR;
138 | }
139 | rret_text[1] = s[1];
140 | rret_text[2] = s[2];
141 | rret_text[3] = s[3];
142 | rret_text[4] = s[4];
143 | switch (toupper(s[5]))
144 | {
145 | case 'M':
146 | case 'R':
147 | rret_text[5] = toupper(s[5]);
148 | break;
149 | case 'B':
150 | /* Check if fall, as only fall has a build phase */
151 | if (rret_text[0] == 'F' ) {
152 | rret_text[5] = toupper(s[5]);
153 | break;
154 | }
155 | /* Intentional fall-through */
156 |
157 | default:
158 | rret_text[5] = '\0';
159 | return SH_ERROR;
160 | }
161 |
162 | rret_text[6] = '\0'; /* Null-terminate it */
163 |
164 | return SH_PHASE;
165 | }
166 |
167 | #define NOIFS 0
168 |
169 | int mail_shorthand(char *text)
170 | {
171 | char sh_line[1024];
172 | int ret;
173 | int last_ret = SH_BLANK;
174 | char ret_text[1024];
175 | int com_type = 0; /* =1 for propose, accept, reject */
176 | int com_class = 0; /* =1 for draw, peace, DMZ, prov, order */
177 | int com_dmz = 0; /* Number of times DMZ encountered */
178 | int com_not = 0; /* Number of times NOT encountered */
179 | int prov_list = 0;
180 | int first_entry = 0;
181 | char *text1;
182 | char phase_text[7];
183 | char phase_type;
184 |
185 | phase_text[0]='\0';
186 | text1 = text;
187 |
188 | sh_line[0] = '\0';
189 | ret_text[0] = '\0';
190 | /* firstly, strip away leading blanks */
191 | despace(text);
192 | if (*text1 == '\0') {
193 | /* blank line, ignore */
194 | *text = '\0';
195 | return 0;
196 | }
197 | if (!dipent.pr_valid) {
198 | /* pr[] must be filled to run this code */
199 | po_init();
200 | gamein();
201 | dipent.pr_valid++;
202 | }
203 |
204 | while (!com_type) {
205 | ret = ProcessShSegment(&text1, ret_text, CANBE_POWERS, NO_PROVINCE);
206 | switch (ret)
207 | {
208 | case SH_INFORM:
209 | case SH_POWER:
210 | {
211 | strcat(sh_line, ret_text);
212 | break;
213 | }
214 | case SH_PROPOSE:
215 | case SH_ACCEPT:
216 | case SH_REJECT:
217 | {
218 | if (last_ret == SH_INFORM) {
219 | strcat(sh_line, "\nError: Inform must be followed by a powerlist.");
220 | strcpy(text,sh_line);
221 | strcat(text,"\n");
222 | return 1;
223 | }
224 | strcat(sh_line,ret_text);
225 | com_type++;
226 | break;
227 | }
228 | case SH_BLANK:
229 | {
230 | if (first_entry == 0) {
231 | /* blank line, return and ignore */
232 | *text = 0;
233 | return 0;
234 | }
235 | strcat(sh_line, "\nError: Line ended before finding Propose/Accept/Reject.");
236 | strcpy(text,sh_line);
237 | strcat(text,"\n");
238 | return 1;
239 | break;
240 | }
241 | case SH_MASTER:
242 | {
243 | strcat(sh_line, "\nError: Master is not a valid power in shorthand power list.");
244 | strcpy(text,sh_line);
245 | strcat(text,"\n");
246 | return 1;
247 | }
248 | default:
249 | strcat(sh_line,"\nError: Expected Propose/Accept/Reject not ");
250 | strcat(sh_line, ret_text);
251 | strcat(sh_line,".\n");
252 | strcpy(text,sh_line);
253 | strcat(text,"\n");
254 | return 1; /* Error return */
255 | }
256 | first_entry++;
257 | last_ret = ret;
258 |
259 | };
260 |
261 | /* OK, now look for a power/power list */
262 | ret = ProcessShSegment(&text1,ret_text, MUSTBE_POWERS, NO_PROVINCE);
263 | switch (ret)
264 | {
265 | case SH_POWER:
266 | {
267 | strcat(sh_line, ret_text);
268 | break;
269 | }
270 | case SH_BLANK:
271 | {
272 | strcat(sh_line, "\nError: Line ended before finding a power.");
273 | strcpy(text,sh_line);
274 | strcat(text,"\n");
275 | return 1;
276 | break;
277 | }
278 | case SH_MASTER:
279 | {
280 | strcat(sh_line, "\nError: Master is not a valid power in powerlist.");
281 | strcpy(text,sh_line);
282 | strcat(text,"\n");
283 | return 1;
284 | }
285 | case SH_OBSERVER:
286 | {
287 | strcat(sh_line, "\nError: Observer is not allowed here as a power.");
288 | strcpy(text,sh_line);
289 | strcat(text,"\n");
290 | return 1;
291 | }
292 |
293 | default:
294 | strcat(sh_line,"\nError: Expected Power not ");
295 | strcat(sh_line, ret_text);
296 | strcat(sh_line,".\n");
297 | strcpy(text,sh_line);
298 | strcat(text,"\n");
299 | return 1; /* Error return */
300 | }
301 | /* Right, now see which command class it is */
302 | while (!com_class) {
303 | ret = ProcessShSegment(&text1, ret_text, NO_POWERS, CANBE_PROVINCE);
304 | switch (ret)
305 | {
306 | case SH_DRAW:
307 | case SH_PEACE:
308 | {
309 | /* These commands end here */
310 | strcat(sh_line, ret_text);
311 | if (CheckShSegmentPhase(text1, phase_text) == SH_PHASE ) {
312 | text1 += 6; /* Skip the found phase */
313 | phase_type = phase_text[5];
314 | strcat(sh_line," ");
315 | strcat(sh_line, phase_text);
316 | strcat(sh_line," ");
317 | }
318 | strcpy(text,sh_line);
319 | strcat(text,"\n");
320 | return 0;
321 | }
322 | case SH_ALLY:
323 | strcat(sh_line, ret_text);
324 | if (CheckShSegmentPhase(text1, phase_text) == SH_PHASE ) {
325 | text1 += 6; /* Skip the found phase */
326 | phase_type = phase_text[5];
327 | strcat(sh_line, phase_text);
328 | strcat(sh_line," ");
329 | }
330 | ret = ProcessShSegment(&text1, ret_text, NO_POWERS, NO_PROVINCE);
331 | switch (ret)
332 | {
333 | case SH_BLANK:
334 | strcpy(text,sh_line);
335 | strcat(text,"\n");
336 | return 0;
337 | break;
338 |
339 | case SH_TARGET:
340 | strcat(sh_line, ret_text);
341 | ret = ProcessShSegment(&text1, ret_text, MUSTBE_POWERS, NO_PROVINCE);
342 | switch (ret)
343 | {
344 | case SH_POWER:
345 | strcat(sh_line, ret_text);
346 | strcpy(text,sh_line);
347 | strcat(text,"\n");
348 | return 0;
349 | break;
350 | case SH_BLANK:
351 | strcat(sh_line,"\nError: Expected powerlist.");
352 | strcpy(text,sh_line);
353 | strcat(text,"\n");
354 | return 1;
355 | break;
356 | case SH_MASTER:
357 | {
358 | strcat(sh_line, "\nError: Master is not a valid power in powerlist.");
359 | strcpy(text,sh_line);
360 | strcat(text,"\n");
361 | return 1;
362 | }
363 | case SH_OBSERVER:
364 | {
365 | strcat(sh_line, "\nError: Observer is not allowed here as a power.");
366 | strcpy(text,sh_line);
367 | strcat(text,"\n");
368 | return 1;
369 | }
370 | default:
371 | strcat(sh_line,"\nError: Expected powerlist not ");
372 | strcat(sh_line,ret_text);
373 | strcat(sh_line, ret_text);
374 | strcpy(text,sh_line);
375 | strcat(text,"\n");
376 | return 1;
377 | }
378 | default:
379 | {
380 | strcat(sh_line,"\nError: Not expected ");
381 | strcat(sh_line,ret_text);
382 | strcat(sh_line, ret_text);
383 | strcpy(text,sh_line);
384 | strcat(text,"\n");
385 | return 1;
386 | }
387 | }
388 | break; /* out of courtesy */
389 | case SH_DMZ:
390 | {
391 | /* Duplicate instances of DMZ are treated as an error */
392 | if (com_dmz != 0) {
393 | strcpy(sh_line,"\n:Error - Duplicate DMZ specified.");
394 | strcpy(text,sh_line);
395 | strcat(text,"\n");
396 | return 1;
397 | }
398 | /* else */
399 | com_class++;
400 | com_dmz++;
401 | prov_list++;
402 | strcat(sh_line, ret_text);
403 | if (CheckShSegmentPhase(text1, phase_text) == SH_PHASE ) {
404 | text1 += 6; /* Skip the found phase */
405 | phase_type = phase_text[5];
406 | strcat(sh_line, phase_text);
407 | strcat(sh_line," ");
408 | }
409 | break;
410 |
411 | }
412 | case SH_ORDER:
413 | {
414 | strcat(sh_line,ret_text);
415 | /* See if there is a phase first, to know how to parse the order */
416 | if (CheckShSegmentPhase(text1, phase_text) == SH_PHASE ) {
417 | text1 += 6; /* Skip the found phase */
418 | phase_type = phase_text[5];
419 | strcat(sh_line, phase_text);
420 | strcat(sh_line," ");
421 | } else {
422 | phase_type = dipent.phase[5]; /* Use current phase */
423 | phase_text[0]='\0';
424 | }
425 | switch (phase_type) {
426 | case 'B':
427 | if (build_syntaxcheck(text1,NOIFS, &sh_line[strlen(sh_line)]) == 0) {
428 | } else {
429 | strcpy(ret_text, "Invalid order:");
430 | strcat(ret_text,text1);
431 | strcat(sh_line,ret_text);
432 | strcpy(text,sh_line);
433 | strcat(text,"\n");
434 | return 1;
435 |
436 | }
437 |
438 | break;
439 | case 'R':
440 | if (retreat_syntaxcheck(text1,NOIFS, &sh_line[strlen(sh_line)]) == 0) {
441 | } else {
442 | strcpy(ret_text, "Invalid order:");
443 | strcat(ret_text,text1);
444 | strcat(sh_line,ret_text);
445 | strcpy(text,sh_line);
446 | strcat(text,"\n");
447 | return 1;
448 |
449 | }
450 |
451 | break;
452 |
453 | case 'M':
454 | if (move_syntaxcheck(text1,NOIFS, &sh_line[strlen(sh_line)]) == 0) {
455 | } else {
456 | strcpy(ret_text, "Invalid order:");
457 | strcat(ret_text,text1);
458 | strcat(sh_line,ret_text);
459 | strcpy(text,sh_line);
460 | strcat(text,"\n");
461 | return 1;
462 |
463 | }
464 |
465 | break;
466 | default:
467 | strcat(sh_line,
468 | "\nError: bad phase type\n");
469 | strcat(sh_line,ret_text);
470 | strcpy(text,sh_line);
471 | strcat(text,"\n");
472 | return 1;
473 | }
474 | /* It was decoded as an order so leave! */
475 | strcpy(text,sh_line);
476 | strcat(text,"\n");
477 | return 0;
478 | }
479 |
480 | case SH_PROVINCE:
481 | {
482 | com_class++;
483 | com_dmz++;
484 | prov_list++;
485 | strcat(sh_line, ret_text);
486 | if (CheckShSegmentPhase(text1, phase_text) == SH_PHASE ) {
487 | text1 += 6; /* Skip the found phase */
488 | phase_type = phase_text[5];
489 | strcat(sh_line, phase_text);
490 | strcat(sh_line," ");
491 | }
492 | break;
493 | }
494 | case SH_NOT:
495 | {
496 | /* Duplicate instances of NOT are treated as an error */
497 | if (com_not != 0) {
498 | strcpy(sh_line,"\n:Error - Duplicate NOT specified.");
499 | strcpy(text,sh_line);
500 | strcat(text,"\n");
501 | return 1;
502 | }
503 | /* else */
504 | com_not++;
505 | strcat(sh_line, ret_text);
506 | break;
507 | }
508 | case SH_BLANK:
509 | {
510 | strcat(sh_line, "\n:Error - Command class expected.");
511 | strcpy(text,sh_line);
512 | strcat(text,"\n");
513 | return 1;
514 | }
515 | default:
516 | strcat(sh_line,"\nError: Expected command not ");
517 | strcat(sh_line, ret_text);
518 | strcat(sh_line,".");
519 | strcpy(text,sh_line);
520 | strcat(text,"\n");
521 | return 1; /* Error return */
522 | }
523 | };
524 | /* OK, at this point, we're expecting a list of provinces only */
525 |
526 | while (prov_list != 0 && prov_list < 4) {
527 | ret = ProcessShSegment(&text1, ret_text, NO_POWERS, MUSTBE_PROVINCE);
528 | switch (ret)
529 | {
530 | case SH_PROV_LIST:
531 | {
532 | if (prov_list != 1) strcat(sh_line, ",");
533 | strcat(sh_line, ret_text);
534 | prov_list++;
535 | if (prov_list >= 4) {
536 | /* Got the maximum number of provinces */
537 | strcpy(text,sh_line);
538 | strcat(text,"\n");
539 | return 0;
540 | }
541 | break;
542 | }
543 | case SH_BLANK:
544 | {
545 | prov_list = 0;
546 | strcpy(text,sh_line);
547 | strcat(text,"\n");
548 | return 0; /* That's the end! */
549 | }
550 | case SH_SEPARATOR:
551 | {
552 | break; /* ignore separators */
553 | }
554 | default:
555 | prov_list = 0;
556 | strcat(sh_line,"\nError: Expected province not ");
557 | strcat(sh_line, ret_text);
558 | strcat(sh_line,".");
559 | strcpy(text,sh_line);
560 | strcat(text,"\n");
561 | return 1; /* Error return */
562 | }
563 | };
564 |
565 | /* Should never get here! */
566 | strcpy(text,"**Unhandled shorthand line!**\n");
567 | return 1;
568 | }
569 | /* Prossess Shorthand single word
570 | * Parameters are:
571 | * s : input text, from which first word is taken
572 | * rrret_text: returned string
573 | * power_list: set to 0 if a power is not possible in this context
574 | * prov_list: set to 0 if a province is not possible in this context
575 | * orderable: set to 0 if an order is not possible in this context
576 | */
577 |
578 | int ProcessShSegment(char **s, char *rrret_text, int power_list, int prov_list)
579 | {
580 | static char power_text[100];
581 | static char in_text[150];
582 | char *t = power_text;
583 | char part_list[MAX_POWERS+1];
584 | int count;
585 |
586 | int p1,c1; /* dummies for get_prov only */
587 |
588 | int i;
589 | int sh_error = 0;
590 | *rrret_text='\0'; /* Initialise output text */
591 |
592 |
593 | /* OK, first lets extract first word from 's' as it could be a power */
594 | strcpy(power_text, *s);
595 | if (GetOneWord(power_text) == 0) return SH_BLANK; /* nothing there! */
596 | strcpy(in_text, *s); /* Remember text! */
597 |
598 | if (power_list != NO_POWERS) {
599 | /* OK, it could be a power, let's check! */
600 | if (parse_powers(&t, part_list, dipent.np, &count,0) == 0 ) {
601 | /*list_powers(rrret_text, power_text); */
602 | i = 0;
603 | while (power_text[i]!= 0) {
604 | power_text[i] = toupper(power_text[i]);
605 | i++;
606 | }
607 | strcpy(rrret_text, power_text);
608 | strcat(rrret_text," ");
609 | while ( isspace(**s)== 0 && **s != '\0') (*s)++; /* skip until spaces start */
610 | while ( isspace(**s)!= 0 && **s != '\0') (*s)++; /* skip until spaces end */
611 | /* The power 'M' is not allowed */
612 | if (strchr(power_text, (int) 'M' ) != NULL ) return SH_MASTER;
613 | /* The power 'O' is not allowed in the last powerlist */
614 | if (strchr(power_text, (int) 'O') != NULL && power_list == MUSTBE_POWERS) return SH_OBSERVER;
615 | return SH_POWER;
616 | }
617 | if (power_list == MUSTBE_POWERS) return SH_NOPOWERS;
618 | }
619 |
620 | if (prov_list != NO_PROVINCE) {
621 | /* OK, it could also be a province, let's check */
622 | t = get_prov(*s, &p1, &c1);
623 | if (p1 != 0) /* indicates a province found */ {
624 | *s = t; /* resume AFTER match position */
625 | strcpy(rrret_text," ");
626 | strcat(rrret_text, pr[p1].name);
627 | return SH_PROV_LIST;
628 | }
629 | if (power_list == MUSTBE_PROVINCE) return SH_NOPROV;
630 | }
631 | *s = lookfor(*s, sh_prelim, nentry(sh_prelim), &i);
632 |
633 | sh_error = sh_pvalue[i];
634 |
635 | switch (sh_pvalue[i]) {
636 |
637 | case SH_PROPOSE:
638 | strcpy(rrret_text,"proposes ");
639 | break;
640 |
641 | case SH_ACCEPT:
642 | strcpy(rrret_text,"accepts ");
643 | break;
644 |
645 | case SH_REJECT:
646 | strcpy(rrret_text,"rejects ");
647 | break;
648 |
649 | case SH_INFORM:
650 | strcpy(rrret_text,"informs ");
651 | break;
652 |
653 | case SH_DRAW:
654 | strcpy(rrret_text,"draw");
655 | break;
656 |
657 | case SH_PEACE:
658 | strcpy(rrret_text,"peace");
659 | break;
660 |
661 | case SH_ALLY:
662 | strcpy(rrret_text,"alliance ");
663 | break;
664 |
665 | case SH_TARGET:
666 | strcpy(rrret_text,"target ");
667 | break;
668 |
669 | case SH_DMZ:
670 | strcpy(rrret_text, "DMZ ");
671 | break;
672 |
673 | case SH_NOT:
674 | strcpy(rrret_text, "NOT ");
675 | break;
676 |
677 | case SH_PROVINCE:
678 | strcpy(rrret_text, "province ");
679 | break;
680 |
681 | case SH_SEPARATOR:
682 | strcpy(rrret_text,", ");
683 | break;
684 | case SH_ORDER:
685 | strcpy(rrret_text,"order ");
686 | break;
687 |
688 | default:
689 | GetOneWord(in_text);
690 | sprintf(rrret_text,"'%s'", in_text);
691 | sh_error = SH_ERROR;
692 | }
693 |
694 | return sh_error;
695 | }
696 |