1 /* $NetBSD: hack.objnam.c,v 1.11 2011/08/07 06:03:45 dholland Exp $ */ 2 3 /* 4 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica, 5 * Amsterdam 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are 10 * met: 11 * 12 * - Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * - Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * - Neither the name of the Stichting Centrum voor Wiskunde en 20 * Informatica, nor the names of its contributors may be used to endorse or 21 * promote products derived from this software without specific prior 22 * written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 25 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 27 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 28 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 /* 38 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org> 39 * All rights reserved. 40 * 41 * Redistribution and use in source and binary forms, with or without 42 * modification, are permitted provided that the following conditions 43 * are met: 44 * 1. Redistributions of source code must retain the above copyright 45 * notice, this list of conditions and the following disclaimer. 46 * 2. Redistributions in binary form must reproduce the above copyright 47 * notice, this list of conditions and the following disclaimer in the 48 * documentation and/or other materials provided with the distribution. 49 * 3. The name of the author may not be used to endorse or promote products 50 * derived from this software without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 53 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 54 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 55 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 56 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 57 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 58 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 59 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 60 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 61 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 62 */ 63 64 #include <sys/cdefs.h> 65 #ifndef lint 66 __RCSID("$NetBSD: hack.objnam.c,v 1.11 2011/08/07 06:03:45 dholland Exp $"); 67 #endif /* not lint */ 68 69 #include <stdlib.h> 70 #include "hack.h" 71 #include "extern.h" 72 #define Snprintf (void) snprintf 73 #define Strcat (void) strcat 74 #define Strcpy (void) strcpy 75 #define PREFIX 15 76 77 static char *strprepend(char *, char *); 78 static char *sitoa(int); 79 80 static char * 81 strprepend(char *s, char *pref) 82 { 83 int i = strlen(pref); 84 if (i > PREFIX) { 85 pline("WARNING: prefix too short."); 86 return (s); 87 } 88 s -= i; 89 (void) strncpy(s, pref, i); /* do not copy trailing 0 */ 90 return (s); 91 } 92 93 static char * 94 sitoa(int a) 95 { 96 static char buf[13]; 97 Snprintf(buf, sizeof(buf), (a < 0) ? "%d" : "+%d", a); 98 return (buf); 99 } 100 101 char * 102 typename(int otyp) 103 { 104 static char buf[BUFSZ]; 105 size_t bufpos; 106 struct objclass *ocl = &objects[otyp]; 107 const char *an = ocl->oc_name; 108 const char *dn = ocl->oc_descr; 109 char *un = ocl->oc_uname; 110 int nn = ocl->oc_name_known; 111 switch (ocl->oc_olet) { 112 case POTION_SYM: 113 Strcpy(buf, "potion"); 114 break; 115 case SCROLL_SYM: 116 Strcpy(buf, "scroll"); 117 break; 118 case WAND_SYM: 119 Strcpy(buf, "wand"); 120 break; 121 case RING_SYM: 122 Strcpy(buf, "ring"); 123 break; 124 default: 125 if (nn) { 126 Strcpy(buf, an); 127 if (otyp >= TURQUOISE && otyp <= JADE) 128 Strcat(buf, " stone"); 129 if (un) { 130 bufpos = strlen(buf); 131 Snprintf(buf+bufpos, sizeof(buf)-bufpos, 132 " called %s", un); 133 } 134 if (dn) { 135 bufpos = strlen(buf); 136 Snprintf(buf+bufpos, sizeof(buf)-bufpos, 137 " (%s)", dn); 138 } 139 } else { 140 strlcpy(buf, dn ? dn : an, sizeof(buf)); 141 if (ocl->oc_olet == GEM_SYM) { 142 strlcat(buf, " gem", sizeof(buf)); 143 } 144 if (un) { 145 bufpos = strlen(buf); 146 Snprintf(buf+bufpos, sizeof(buf)-bufpos, 147 " called %s", un); 148 } 149 } 150 return (buf); 151 } 152 /* here for ring/scroll/potion/wand */ 153 if (nn) { 154 bufpos = strlen(buf); 155 Snprintf(buf+bufpos, sizeof(buf)-bufpos, " of %s", an); 156 } 157 if (un) { 158 bufpos = strlen(buf); 159 Snprintf(buf+bufpos, sizeof(buf)-bufpos, " called %s", un); 160 } 161 if (dn) { 162 bufpos = strlen(buf); 163 Snprintf(buf+bufpos, sizeof(buf)-bufpos, " (%s)", dn); 164 } 165 return (buf); 166 } 167 168 char * 169 xname(struct obj *obj) 170 { 171 static char bufr[BUFSZ]; 172 /* caution: doname() and aobjnam() below "know" these sizes */ 173 char *buf = &(bufr[PREFIX]); /* leave room for "17 -3 " */ 174 size_t bufmax = sizeof(bufr) - PREFIX; 175 int nn = objects[obj->otyp].oc_name_known; 176 const char *an = objects[obj->otyp].oc_name; 177 const char *dn = objects[obj->otyp].oc_descr; 178 char *un = objects[obj->otyp].oc_uname; 179 int pl = (obj->quan != 1); 180 181 if (!obj->dknown && !Blind) 182 obj->dknown = 1;/* %% doesnt belong here */ 183 switch (obj->olet) { 184 case AMULET_SYM: 185 Strcpy(buf, (obj->spe < 0 && obj->known) 186 ? "cheap plastic imitation of the " : ""); 187 Strcat(buf, "Amulet of Yendor"); 188 break; 189 case TOOL_SYM: 190 if (!nn) { 191 strlcpy(buf, dn, bufmax); 192 break; 193 } 194 strlcpy(buf, an, bufmax); 195 break; 196 case FOOD_SYM: 197 if (obj->otyp == DEAD_HOMUNCULUS && pl) { 198 pl = 0; 199 Strcpy(buf, "dead homunculi"); 200 break; 201 } 202 /* fungis ? */ 203 /* FALLTHROUGH */ 204 case WEAPON_SYM: 205 if (obj->otyp == WORM_TOOTH && pl) { 206 pl = 0; 207 Strcpy(buf, "worm teeth"); 208 break; 209 } 210 if (obj->otyp == CRYSKNIFE && pl) { 211 pl = 0; 212 Strcpy(buf, "crysknives"); 213 break; 214 } 215 /* FALLTHROUGH */ 216 case ARMOR_SYM: 217 case CHAIN_SYM: 218 case ROCK_SYM: 219 strlcpy(buf, an, bufmax); 220 break; 221 case BALL_SYM: 222 Snprintf(buf, bufmax, "%sheavy iron ball", 223 (obj->owt > objects[obj->otyp].oc_weight) ? "very " : ""); 224 break; 225 case POTION_SYM: 226 if (nn || un || !obj->dknown) { 227 Strcpy(buf, "potion"); 228 if (pl) { 229 pl = 0; 230 Strcat(buf, "s"); 231 } 232 if (!obj->dknown) 233 break; 234 if (un) { 235 Strcat(buf, " called "); 236 strlcat(buf, un, bufmax); 237 } else { 238 Strcat(buf, " of "); 239 strlcat(buf, an, bufmax); 240 } 241 } else { 242 strlcpy(buf, dn, bufmax); 243 strlcat(buf, " potion", bufmax); 244 } 245 break; 246 case SCROLL_SYM: 247 Strcpy(buf, "scroll"); 248 if (pl) { 249 pl = 0; 250 Strcat(buf, "s"); 251 } 252 if (!obj->dknown) 253 break; 254 if (nn) { 255 Strcat(buf, " of "); 256 strlcat(buf, an, bufmax); 257 } else if (un) { 258 Strcat(buf, " called "); 259 strlcat(buf, un, bufmax); 260 } else { 261 Strcat(buf, " labeled "); 262 strlcat(buf, dn, bufmax); 263 } 264 break; 265 case WAND_SYM: 266 if (!obj->dknown) 267 Snprintf(buf, bufmax, "wand"); 268 else if (nn) 269 Snprintf(buf, bufmax, "wand of %s", an); 270 else if (un) 271 Snprintf(buf, bufmax, "wand called %s", un); 272 else 273 Snprintf(buf, bufmax, "%s wand", dn); 274 break; 275 case RING_SYM: 276 if (!obj->dknown) 277 Snprintf(buf, bufmax, "ring"); 278 else if (nn) 279 Snprintf(buf, bufmax, "ring of %s", an); 280 else if (un) 281 Snprintf(buf, bufmax, "ring called %s", un); 282 else 283 Snprintf(buf, bufmax, "%s ring", dn); 284 break; 285 case GEM_SYM: 286 if (!obj->dknown) { 287 Strcpy(buf, "gem"); 288 break; 289 } 290 if (!nn) { 291 Snprintf(buf, bufmax, "%s gem", dn); 292 break; 293 } 294 strlcpy(buf, an, bufmax); 295 if (obj->otyp >= TURQUOISE && obj->otyp <= JADE) 296 strlcat(buf, " stone", bufmax); 297 break; 298 default: 299 Snprintf(buf, bufmax, "glorkum %c (0%o) %u %d", 300 obj->olet, obj->olet, obj->otyp, obj->spe); 301 } 302 if (pl) { 303 char *p; 304 305 for (p = buf; *p; p++) { 306 if (!strncmp(" of ", p, 4)) { 307 /* pieces of, cloves of, lumps of */ 308 int c1, c2 = 's'; 309 310 do { 311 c1 = c2; 312 c2 = *p; 313 *p++ = c1; 314 } while (c1); 315 goto nopl; 316 } 317 } 318 p = eos(buf) - 1; 319 if (*p == 's' || *p == 'z' || *p == 'x' || 320 (*p == 'h' && p[-1] == 's')) { 321 /* boxes */ 322 strlcat(buf, "es", bufmax); 323 } else if (*p == 'y' && !strchr(vowels, p[-1])) { 324 /* rubies, zruties */ 325 *p = '\0'; 326 strlcat(buf, "ies", bufmax); 327 } else { 328 strlcat(buf, "s", bufmax); 329 } 330 } 331 nopl: 332 if (obj->onamelth) { 333 strlcat(buf, " named ", bufmax); 334 strlcat(buf, ONAME(obj), bufmax); 335 } 336 return (buf); 337 } 338 339 char * 340 doname(struct obj *obj) 341 { 342 char prefix[PREFIX]; 343 char *bp = xname(obj); 344 size_t bppos, bpmax; 345 346 /* XXX do this better somehow w/o knowing internals of xname() */ 347 bpmax = BUFSZ - PREFIX; 348 349 if (obj->quan != 1) 350 Snprintf(prefix, sizeof(prefix), "%u ", obj->quan); 351 else 352 Strcpy(prefix, "a "); 353 switch (obj->olet) { 354 case AMULET_SYM: 355 if (strncmp(bp, "cheap ", 6)) 356 Strcpy(prefix, "the "); 357 break; 358 case ARMOR_SYM: 359 if (obj->owornmask & W_ARMOR) 360 strlcat(bp, " (being worn)", bpmax); 361 /* FALLTHROUGH */ 362 case WEAPON_SYM: 363 if (obj->known) { 364 strlcat(prefix, sitoa(obj->spe), sizeof(prefix)); 365 strlcat(prefix, " ", sizeof(prefix)); 366 } 367 break; 368 case WAND_SYM: 369 if (obj->known) { 370 bppos = strlen(bp); 371 Snprintf(bp+bppos, bpmax-bppos, " (%d)", obj->spe); 372 } 373 break; 374 case RING_SYM: 375 if (obj->owornmask & W_RINGR) 376 strlcat(bp, " (on right hand)", bpmax); 377 if (obj->owornmask & W_RINGL) 378 strlcat(bp, " (on left hand)", bpmax); 379 if (obj->known && (objects[obj->otyp].bits & SPEC)) { 380 strlcat(prefix, sitoa(obj->spe), sizeof(prefix)); 381 strlcat(prefix, " ", sizeof(prefix)); 382 } 383 break; 384 } 385 if (obj->owornmask & W_WEP) 386 strlcat(bp, " (weapon in hand)", bpmax); 387 if (obj->unpaid) 388 strlcat(bp, " (unpaid)", bpmax); 389 if (!strcmp(prefix, "a ") && strchr(vowels, *bp)) 390 Strcpy(prefix, "an "); 391 bp = strprepend(bp, prefix); 392 return (bp); 393 } 394 395 /* used only in hack.fight.c (thitu) */ 396 void 397 setan(const char *str, char *buf, size_t bufmax) 398 { 399 if (strchr(vowels, *str)) 400 Snprintf(buf, bufmax, "an %s", str); 401 else 402 Snprintf(buf, bufmax, "a %s", str); 403 } 404 405 char * 406 aobjnam(struct obj *otmp, const char *verb) 407 { 408 char *bp = xname(otmp); 409 char prefix[PREFIX]; 410 size_t bpmax; 411 412 /* XXX do this better somehow w/o knowing internals of xname() */ 413 bpmax = BUFSZ - PREFIX; 414 415 if (otmp->quan != 1) { 416 Snprintf(prefix, sizeof(prefix), "%u ", otmp->quan); 417 bp = strprepend(bp, prefix); 418 } 419 if (verb) { 420 /* verb is given in plural (i.e., without trailing s) */ 421 strlcat(bp, " ", bpmax); 422 if (otmp->quan != 1) 423 strlcat(bp, verb, bpmax); 424 else if (!strcmp(verb, "are")) 425 strlcat(bp, "is", bpmax); 426 else { 427 strlcat(bp, verb, bpmax); 428 strlcat(bp, "s", bpmax); 429 } 430 } 431 return (bp); 432 } 433 434 char * 435 Doname(struct obj *obj) 436 { 437 char *s = doname(obj); 438 439 if ('a' <= *s && *s <= 'z') 440 *s -= ('a' - 'A'); 441 return (s); 442 } 443 444 static const char *const wrp[] = {"wand", "ring", "potion", "scroll", "gem"}; 445 static const char wrpsym[] = {WAND_SYM, RING_SYM, POTION_SYM, SCROLL_SYM, GEM_SYM}; 446 447 struct obj * 448 readobjnam(char *bp) 449 { 450 char *p; 451 unsigned ii; 452 int i; 453 int cnt, spe, spesgn, typ, heavy; 454 char let; 455 char *un, *dn, *an; 456 /* int the = 0; char *oname = 0; */ 457 cnt = spe = spesgn = typ = heavy = 0; 458 let = 0; 459 an = dn = un = 0; 460 for (p = bp; *p; p++) 461 if ('A' <= *p && *p <= 'Z') 462 *p += 'a' - 'A'; 463 if (!strncmp(bp, "the ", 4)) { 464 /* the = 1; */ 465 bp += 4; 466 } else if (!strncmp(bp, "an ", 3)) { 467 cnt = 1; 468 bp += 3; 469 } else if (!strncmp(bp, "a ", 2)) { 470 cnt = 1; 471 bp += 2; 472 } 473 if (!cnt && digit(*bp)) { 474 cnt = atoi(bp); 475 while (digit(*bp)) 476 bp++; 477 while (*bp == ' ') 478 bp++; 479 } 480 if (!cnt) 481 cnt = 1; /* %% what with "gems" etc. ? */ 482 483 if (*bp == '+' || *bp == '-') { 484 spesgn = (*bp++ == '+') ? 1 : -1; 485 spe = atoi(bp); 486 while (digit(*bp)) 487 bp++; 488 while (*bp == ' ') 489 bp++; 490 } else { 491 p = strrchr(bp, '('); 492 if (p) { 493 if (p > bp && p[-1] == ' ') 494 p[-1] = 0; 495 else 496 *p = 0; 497 p++; 498 spe = atoi(p); 499 while (digit(*p)) 500 p++; 501 if (strcmp(p, ")")) 502 spe = 0; 503 else 504 spesgn = 1; 505 } 506 } 507 /* 508 * now we have the actual name, as delivered by xname, say green 509 * potions called whisky scrolls labeled "QWERTY" egg dead zruties 510 * fortune cookies very heavy iron ball named hoei wand of wishing 511 * elven cloak 512 */ 513 for (p = bp; *p; p++) 514 if (!strncmp(p, " named ", 7)) { 515 *p = 0; 516 /* oname = p+7; */ 517 } 518 for (p = bp; *p; p++) 519 if (!strncmp(p, " called ", 8)) { 520 *p = 0; 521 un = p + 8; 522 } 523 for (p = bp; *p; p++) 524 if (!strncmp(p, " labeled ", 9)) { 525 *p = 0; 526 dn = p + 9; 527 } 528 /* first change to singular if necessary */ 529 if (cnt != 1) { 530 /* find "cloves of garlic", "worthless pieces of blue glass" */ 531 for (p = bp; *p; p++) 532 if (!strncmp(p, "s of ", 5)) { 533 while ((*p = p[1]) != '\0') 534 p++; 535 goto sing; 536 } 537 /* remove -s or -es (boxes) or -ies (rubies, zruties) */ 538 p = eos(bp); 539 if (p[-1] == 's') { 540 if (p[-2] == 'e') { 541 if (p[-3] == 'i') { 542 if (!strcmp(p - 7, "cookies")) 543 goto mins; 544 Strcpy(p - 3, "y"); 545 goto sing; 546 } 547 /* note: cloves / knives from clove / knife */ 548 if (!strcmp(p - 6, "knives")) { 549 Strcpy(p - 3, "fe"); 550 goto sing; 551 } 552 /* note: nurses, axes but boxes */ 553 if (!strcmp(p - 5, "boxes")) { 554 p[-2] = 0; 555 goto sing; 556 } 557 } 558 mins: 559 p[-1] = 0; 560 } else { 561 if (!strcmp(p - 9, "homunculi")) { 562 Strcpy(p - 1, "us"); /* !! makes string 563 * longer */ 564 goto sing; 565 } 566 if (!strcmp(p - 5, "teeth")) { 567 Strcpy(p - 5, "tooth"); 568 goto sing; 569 } 570 /* here we cannot find the plural suffix */ 571 } 572 } 573 sing: 574 if (!strcmp(bp, "amulet of yendor")) { 575 typ = AMULET_OF_YENDOR; 576 goto typfnd; 577 } 578 p = eos(bp); 579 if (!strcmp(p - 5, " mail")) { /* Note: ring mail is not a ring ! */ 580 let = ARMOR_SYM; 581 an = bp; 582 goto srch; 583 } 584 for (ii = 0; ii < sizeof(wrpsym); ii++) { 585 int j = strlen(wrp[ii]); 586 if (!strncmp(bp, wrp[ii], j)) { 587 let = wrpsym[ii]; 588 bp += j; 589 if (!strncmp(bp, " of ", 4)) 590 an = bp + 4; 591 /* else if(*bp) ?? */ 592 goto srch; 593 } 594 if (!strcmp(p - j, wrp[ii])) { 595 let = wrpsym[ii]; 596 p -= j; 597 *p = 0; 598 if (p[-1] == ' ') 599 p[-1] = 0; 600 dn = bp; 601 goto srch; 602 } 603 } 604 if (!strcmp(p - 6, " stone")) { 605 p[-6] = 0; 606 let = GEM_SYM; 607 an = bp; 608 goto srch; 609 } 610 if (!strcmp(bp, "very heavy iron ball")) { 611 heavy = 1; 612 typ = HEAVY_IRON_BALL; 613 goto typfnd; 614 } 615 an = bp; 616 srch: 617 if (!an && !dn && !un) 618 goto any; 619 i = 1; 620 if (let) 621 i = bases[letindex(let)]; 622 while (i <= NROFOBJECTS && (!let || objects[i].oc_olet == let)) { 623 const char *zn = objects[i].oc_name; 624 625 if (!zn) 626 goto nxti; 627 if (an && strcmp(an, zn)) 628 goto nxti; 629 if (dn && (!(zn = objects[i].oc_descr) || strcmp(dn, zn))) 630 goto nxti; 631 if (un && (!(zn = objects[i].oc_uname) || strcmp(un, zn))) 632 goto nxti; 633 typ = i; 634 goto typfnd; 635 nxti: 636 i++; 637 } 638 any: 639 if (!let) 640 let = wrpsym[rn2(sizeof(wrpsym))]; 641 typ = probtype(let); 642 typfnd: 643 { 644 struct obj *otmp; 645 let = objects[typ].oc_olet; 646 otmp = mksobj(typ); 647 if (heavy) 648 otmp->owt += 15; 649 if (cnt > 0 && strchr("%?!*)", let) && 650 (cnt < 4 || (let == WEAPON_SYM && typ <= ROCK && cnt < 20))) 651 otmp->quan = cnt; 652 653 if (spe > 3 && spe > otmp->spe) 654 spe = 0; 655 else if (let == WAND_SYM) 656 spe = otmp->spe; 657 if (spe == 3 && u.uluck < 0) 658 spesgn = -1; 659 if (let != WAND_SYM && spesgn == -1) 660 spe = -spe; 661 if (let == BALL_SYM) 662 spe = 0; 663 else if (let == AMULET_SYM) 664 spe = -1; 665 else if (typ == WAN_WISHING && rn2(10)) 666 spe = (rn2(10) ? -1 : 0); 667 otmp->spe = spe; 668 669 if (spesgn == -1) 670 otmp->cursed = 1; 671 672 return (otmp); 673 } 674 } 675