option parsing buffer overflow vulnerability
[aNetHack.git] / src / write.c
blobb73bfb10bc6ace2fad6d9559cd17b20eb467afdd
1 /* NetHack 3.6 write.c $NHDT-Date: 1450261366 2015/12/16 10:22:46 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.17 $ */
2 /* NetHack may be freely redistributed. See license for details. */
4 #include "hack.h"
6 STATIC_DCL int FDECL(cost, (struct obj *));
7 STATIC_DCL boolean FDECL(label_known, (int, struct obj *));
8 STATIC_DCL char *FDECL(new_book_description, (int, char *));
11 * returns basecost of a scroll or a spellbook
13 STATIC_OVL int
14 cost(otmp)
15 register struct obj *otmp;
17 if (otmp->oclass == SPBOOK_CLASS)
18 return (10 * objects[otmp->otyp].oc_level);
20 switch (otmp->otyp) {
21 #ifdef MAIL
22 case SCR_MAIL:
23 return 2;
24 #endif
25 case SCR_LIGHT:
26 case SCR_GOLD_DETECTION:
27 case SCR_FOOD_DETECTION:
28 case SCR_MAGIC_MAPPING:
29 case SCR_AMNESIA:
30 case SCR_FIRE:
31 case SCR_EARTH:
32 return 8;
33 case SCR_DESTROY_ARMOR:
34 case SCR_CREATE_MONSTER:
35 case SCR_PUNISHMENT:
36 return 10;
37 case SCR_CONFUSE_MONSTER:
38 return 12;
39 case SCR_IDENTIFY:
40 return 14;
41 case SCR_ENCHANT_ARMOR:
42 case SCR_REMOVE_CURSE:
43 case SCR_ENCHANT_WEAPON:
44 case SCR_CHARGING:
45 return 16;
46 case SCR_SCARE_MONSTER:
47 case SCR_STINKING_CLOUD:
48 case SCR_TAMING:
49 case SCR_TELEPORTATION:
50 return 20;
51 case SCR_GENOCIDE:
52 return 30;
53 case SCR_BLANK_PAPER:
54 default:
55 impossible("You can't write such a weird scroll!");
57 return 1000;
60 /* decide whether the hero knowns a particular scroll's label;
61 unfortunately, we can't track things that haven't been added to
62 the discoveries list and aren't present in current inventory,
63 so some scrolls with ought to yield True will end up False */
64 STATIC_OVL boolean
65 label_known(scrolltype, objlist)
66 int scrolltype;
67 struct obj *objlist;
69 struct obj *otmp;
71 /* only scrolls */
72 if (objects[scrolltype].oc_class != SCROLL_CLASS)
73 return FALSE;
74 /* type known implies full discovery; otherwise,
75 user-assigned name implies partial discovery */
76 if (objects[scrolltype].oc_name_known || objects[scrolltype].oc_uname)
77 return TRUE;
78 /* check inventory, including carried containers with known contents */
79 for (otmp = objlist; otmp; otmp = otmp->nobj) {
80 if (otmp->otyp == scrolltype && otmp->dknown)
81 return TRUE;
82 if (Has_contents(otmp) && otmp->cknown
83 && label_known(scrolltype, otmp->cobj))
84 return TRUE;
86 /* not found */
87 return FALSE;
90 static NEARDATA const char write_on[] = { SCROLL_CLASS, SPBOOK_CLASS, 0 };
92 /* write -- applying a magic marker */
93 int
94 dowrite(pen)
95 register struct obj *pen;
97 register struct obj *paper;
98 char namebuf[BUFSZ], *nm, *bp;
99 register struct obj *new_obj;
100 int basecost, actualcost;
101 int curseval;
102 char qbuf[QBUFSZ];
103 int first, last, i, deferred, deferralchance;
104 boolean by_descr = FALSE;
105 const char *typeword;
107 if (nohands(youmonst.data)) {
108 You("need hands to be able to write!");
109 return 0;
110 } else if (Glib) {
111 pline("%s from your %s.", Tobjnam(pen, "slip"),
112 makeplural(body_part(FINGER)));
113 dropx(pen);
114 return 1;
117 /* get paper to write on */
118 paper = getobj(write_on, "write on");
119 if (!paper)
120 return 0;
121 /* can't write on a novel (unless/until it's been converted into a blank
122 spellbook), but we want messages saying so to avoid "spellbook" */
123 typeword = (paper->otyp == SPE_NOVEL)
124 ? "book"
125 : (paper->oclass == SPBOOK_CLASS)
126 ? "spellbook"
127 : "scroll";
128 if (Blind) {
129 if (!paper->dknown) {
130 You("don't know if that %s is blank or not.", typeword);
131 return 0;
132 } else if (paper->oclass == SPBOOK_CLASS) {
133 /* can't write a magic book while blind */
134 pline("%s can't create braille text.",
135 upstart(ysimple_name(pen)));
136 return 0;
139 paper->dknown = 1;
140 if (paper->otyp != SCR_BLANK_PAPER && paper->otyp != SPE_BLANK_PAPER) {
141 pline("That %s is not blank!", typeword);
142 exercise(A_WIS, FALSE);
143 return 1;
146 /* what to write */
147 Sprintf(qbuf, "What type of %s do you want to write?", typeword);
148 getlin(qbuf, namebuf);
149 (void) mungspaces(namebuf); /* remove any excess whitespace */
150 if (namebuf[0] == '\033' || !namebuf[0])
151 return 1;
152 nm = namebuf;
153 if (!strncmpi(nm, "scroll ", 7))
154 nm += 7;
155 else if (!strncmpi(nm, "spellbook ", 10))
156 nm += 10;
157 if (!strncmpi(nm, "of ", 3))
158 nm += 3;
160 if ((bp = strstri(nm, " armour")) != 0) {
161 (void) strncpy(bp, " armor ", 7); /* won't add '\0' */
162 (void) mungspaces(bp + 1); /* remove the extra space */
165 deferred = 0; /* not any scroll or book */
166 deferralchance = 0; /* incremented for each oc_uname match */
167 first = bases[(int) paper->oclass];
168 last = bases[(int) paper->oclass + 1] - 1;
169 for (i = first; i <= last; i++) {
170 /* extra shufflable descr not representing a real object */
171 if (!OBJ_NAME(objects[i]))
172 continue;
174 if (!strcmpi(OBJ_NAME(objects[i]), nm))
175 goto found;
176 if (!strcmpi(OBJ_DESCR(objects[i]), nm)) {
177 by_descr = TRUE;
178 goto found;
180 /* user-assigned name might match real name of a later
181 entry, so we don't simply use first match with it;
182 also, player might assign same name multiple times
183 and if so, we choose one of those matches randomly */
184 if (objects[i].oc_uname && !strcmpi(objects[i].oc_uname, nm)
186 * First match: chance incremented to 1,
187 * !rn2(1) is 1, we remember i;
188 * second match: chance incremented to 2,
189 * !rn2(2) has 1/2 chance to replace i;
190 * third match: chance incremented to 3,
191 * !rn2(3) has 1/3 chance to replace i
192 * and 2/3 chance to keep previous 50:50
193 * choice; so on for higher match counts.
195 && !rn2(++deferralchance))
196 deferred = i;
198 /* writing by user-assigned name is same as by description:
199 fails for books, works for scrolls (having an assigned
200 type name guarantees presence on discoveries list) */
201 if (deferred) {
202 i = deferred;
203 by_descr = TRUE;
204 goto found;
207 There("is no such %s!", typeword);
208 return 1;
209 found:
211 if (i == SCR_BLANK_PAPER || i == SPE_BLANK_PAPER) {
212 You_cant("write that!");
213 pline("It's obscene!");
214 return 1;
215 } else if (i == SPE_BOOK_OF_THE_DEAD) {
216 pline("No mere dungeon adventurer could write that.");
217 return 1;
218 } else if (by_descr && paper->oclass == SPBOOK_CLASS
219 && !objects[i].oc_name_known) {
220 /* can't write unknown spellbooks by description */
221 pline("Unfortunately you don't have enough information to go on.");
222 return 1;
225 /* KMH, conduct */
226 u.uconduct.literate++;
228 new_obj = mksobj(i, FALSE, FALSE);
229 new_obj->bknown = (paper->bknown && pen->bknown);
231 /* shk imposes a flat rate per use, not based on actual charges used */
232 check_unpaid(pen);
234 /* see if there's enough ink */
235 basecost = cost(new_obj);
236 if (pen->spe < basecost / 2) {
237 Your("marker is too dry to write that!");
238 obfree(new_obj, (struct obj *) 0);
239 return 1;
242 /* we're really going to write now, so calculate cost
244 actualcost = rn1(basecost / 2, basecost / 2);
245 curseval = bcsign(pen) + bcsign(paper);
246 exercise(A_WIS, TRUE);
247 /* dry out marker */
248 if (pen->spe < actualcost) {
249 pen->spe = 0;
250 Your("marker dries out!");
251 /* scrolls disappear, spellbooks don't */
252 if (paper->oclass == SPBOOK_CLASS) {
253 pline_The("spellbook is left unfinished and your writing fades.");
254 update_inventory(); /* pen charges */
255 } else {
256 pline_The("scroll is now useless and disappears!");
257 useup(paper);
259 obfree(new_obj, (struct obj *) 0);
260 return 1;
262 pen->spe -= actualcost;
265 * Writing by name requires that the hero knows the scroll or
266 * book type. One has previously been read (and its effect
267 * was evident) or been ID'd via scroll/spell/throne and it
268 * will be on the discoveries list.
269 * (Previous versions allowed scrolls and books to be written
270 * by type name if they were on the discoveries list via being
271 * given a user-assigned name, even though doing the latter
272 * doesn't--and shouldn't--make the actual type become known.)
274 * Writing by description requires that the hero knows the
275 * description (a scroll's label, that is, since books by_descr
276 * are rejected above). BUG: We can only do this for known
277 * scrolls and for the case where the player has assigned a
278 * name to put it onto the discoveries list; we lack a way to
279 * track other scrolls which have been seen closely enough to
280 * read the label without then being ID'd or named. The only
281 * exception is for currently carried inventory, where we can
282 * check for one [with its dknown bit set] of the same type.
284 * Normal requirements can be overridden if hero is Lucky.
287 /* if known, then either by-name or by-descr works */
288 if (!objects[new_obj->otyp].oc_name_known
289 /* else if named, then only by-descr works */
290 && !(by_descr && label_known(new_obj->otyp, invent))
291 /* and Luck might override after both checks have failed */
292 && rnl(Role_if(PM_WIZARD) ? 5 : 15)) {
293 You("%s to write that.", by_descr ? "fail" : "don't know how");
294 /* scrolls disappear, spellbooks don't */
295 if (paper->oclass == SPBOOK_CLASS) {
296 You(
297 "write in your best handwriting: \"My Diary\", but it quickly fades.");
298 update_inventory(); /* pen charges */
299 } else {
300 if (by_descr) {
301 Strcpy(namebuf, OBJ_DESCR(objects[new_obj->otyp]));
302 wipeout_text(namebuf, (6 + MAXULEV - u.ulevel) / 6, 0);
303 } else
304 Sprintf(namebuf, "%s was here!", plname);
305 You("write \"%s\" and the scroll disappears.", namebuf);
306 useup(paper);
308 obfree(new_obj, (struct obj *) 0);
309 return 1;
311 /* can write scrolls when blind, but requires luck too;
312 attempts to write books when blind are caught above */
313 if (Blind && rnl(3)) {
314 /* writing while blind usually fails regardless of
315 whether the target scroll is known; even if we
316 have passed the write-an-unknown scroll test
317 above we can still fail this one, so it's doubly
318 hard to write an unknown scroll while blind */
319 You("fail to write the scroll correctly and it disappears.");
320 useup(paper);
321 obfree(new_obj, (struct obj *) 0);
322 return 1;
325 /* useup old scroll / spellbook */
326 useup(paper);
328 /* success */
329 if (new_obj->oclass == SPBOOK_CLASS) {
330 /* acknowledge the change in the object's description... */
331 pline_The("spellbook warps strangely, then turns %s.",
332 new_book_description(new_obj->otyp, namebuf));
334 new_obj->blessed = (curseval > 0);
335 new_obj->cursed = (curseval < 0);
336 #ifdef MAIL
337 if (new_obj->otyp == SCR_MAIL)
338 /* 0: delivered in-game via external event (or randomly for fake mail);
339 1: from bones or wishing; 2: written with marker */
340 new_obj->spe = 2;
341 #endif
342 /* unlike alchemy, for example, a successful result yields the
343 specifically chosen item so hero recognizes it even if blind;
344 the exception is for being lucky writing an undiscovered scroll,
345 where the label associated with the type-name isn't known yet */
346 new_obj->dknown = label_known(new_obj->otyp, invent) ? 1 : 0;
348 new_obj = hold_another_object(new_obj, "Oops! %s out of your grasp!",
349 The(aobjnam(new_obj, "slip")),
350 (const char *) 0);
351 nhUse(new_obj); /* try to avoid complaint about dead assignment */
352 return 1;
355 /* most book descriptions refer to cover appearance, so we can issue a
356 message for converting a plain book into one of those with something
357 like "the spellbook turns red" or "the spellbook turns ragged";
358 but some descriptions refer to composition and "the book turns vellum"
359 looks funny, so we want to insert "into " prior to such descriptions;
360 even that's rather iffy, indicating that such descriptions probably
361 ought to be eliminated (especially "cloth"!) */
362 STATIC_OVL char *
363 new_book_description(booktype, outbuf)
364 int booktype;
365 char *outbuf;
367 /* subset of description strings from objects.c; if it grows
368 much, we may need to add a new flag field to objects[] instead */
369 static const char *const compositions[] = {
370 "parchment",
371 "vellum",
372 "cloth",
373 #if 0
374 "canvas", "hardcover", /* not used */
375 "papyrus", /* not applicable--can't be produced via writing */
376 #endif /*0*/
379 const char *descr, *const *comp_p;
381 descr = OBJ_DESCR(objects[booktype]);
382 for (comp_p = compositions; *comp_p; ++comp_p)
383 if (!strcmpi(descr, *comp_p))
384 break;
386 Sprintf(outbuf, "%s%s", *comp_p ? "into " : "", descr);
387 return outbuf;
390 /*write.c*/