1 1.45 rin /* $NetBSD: fio.c,v 1.45 2023/08/23 03:49:00 rin Exp $ */ 2 1.5 christos 3 1.1 cgd /* 4 1.4 deraadt * Copyright (c) 1980, 1993 5 1.4 deraadt * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.22 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.10 lukem #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.5 christos #if 0 35 1.6 tls static char sccsid[] = "@(#)fio.c 8.2 (Berkeley) 4/20/95"; 36 1.5 christos #else 37 1.45 rin __RCSID("$NetBSD: fio.c,v 1.45 2023/08/23 03:49:00 rin Exp $"); 38 1.5 christos #endif 39 1.1 cgd #endif /* not lint */ 40 1.1 cgd 41 1.1 cgd #include "rcv.h" 42 1.4 deraadt #include "extern.h" 43 1.28 christos #include "thread.h" 44 1.32 christos #include "sig.h" 45 1.41 christos #include <wordexp.h> 46 1.1 cgd 47 1.1 cgd /* 48 1.1 cgd * Mail -- a mail program 49 1.1 cgd * 50 1.1 cgd * File I/O. 51 1.1 cgd */ 52 1.1 cgd 53 1.28 christos #ifndef THREAD_SUPPORT 54 1.28 christos /************************************************************************/ 55 1.28 christos /* 56 1.28 christos * If we have threading support, these routines live in thread.c. 57 1.28 christos */ 58 1.28 christos static struct message *message; /* message structure array */ 59 1.28 christos static int msgCount; /* Count of messages read in */ 60 1.28 christos 61 1.28 christos PUBLIC struct message * 62 1.28 christos next_message(struct message *mp) 63 1.28 christos { 64 1.28 christos if (mp + 1 < message || mp + 1 >= message + msgCount) 65 1.30 christos return NULL; 66 1.30 christos 67 1.28 christos return mp + 1; 68 1.28 christos } 69 1.28 christos 70 1.28 christos PUBLIC struct message * 71 1.28 christos prev_message(struct message *mp) 72 1.28 christos { 73 1.28 christos if (mp - 1 < message || mp - 1 >= message + msgCount) 74 1.30 christos return NULL; 75 1.30 christos 76 1.28 christos return mp - 1; 77 1.28 christos } 78 1.28 christos 79 1.28 christos PUBLIC struct message * 80 1.28 christos get_message(int msgnum) 81 1.28 christos { 82 1.28 christos if (msgnum < 1 || msgnum > msgCount) 83 1.28 christos return NULL; 84 1.28 christos 85 1.28 christos return message + msgnum - 1; 86 1.28 christos } 87 1.28 christos 88 1.28 christos PUBLIC int 89 1.28 christos get_msgnum(struct message *mp) 90 1.28 christos { 91 1.28 christos if (mp < message || mp >= message + msgCount) 92 1.28 christos return 0; 93 1.28 christos 94 1.28 christos return mp - message + 1; 95 1.28 christos } 96 1.28 christos 97 1.28 christos PUBLIC int 98 1.28 christos get_msgCount(void) 99 1.28 christos { 100 1.28 christos return msgCount; 101 1.28 christos } 102 1.28 christos #endif /* THREAD_SUPPORT */ 103 1.28 christos /************************************************************************/ 104 1.28 christos 105 1.28 christos /* 106 1.28 christos * Initialize a message structure. 107 1.28 christos */ 108 1.28 christos static void 109 1.28 christos message_init(struct message *mp, off_t offset, short flags) 110 1.28 christos { 111 1.28 christos /* use memset so new fields are always zeroed */ 112 1.28 christos (void)memset(mp, 0, sizeof(*mp)); 113 1.28 christos mp->m_flag = flags; 114 1.28 christos mp->m_block = blockof(offset); 115 1.29 dogcow mp->m_offset = blkoffsetof(offset); 116 1.28 christos } 117 1.28 christos 118 1.28 christos /* 119 1.28 christos * Take the data out of the passed ghost file and toss it into 120 1.28 christos * a dynamically allocated message structure. 121 1.28 christos */ 122 1.28 christos static void 123 1.28 christos makemessage(FILE *f, int omsgCount, int nmsgCount) 124 1.28 christos { 125 1.28 christos size_t size; 126 1.28 christos struct message *omessage; /* old message structure array */ 127 1.28 christos struct message *nmessage; 128 1.44 mrg ptrdiff_t off; 129 1.45 rin int need_init; 130 1.28 christos 131 1.28 christos omessage = get_abs_message(1); 132 1.28 christos 133 1.28 christos size = (nmsgCount + 1) * sizeof(*nmessage); 134 1.44 mrg 135 1.44 mrg if (omsgCount == 0 || omessage == NULL) 136 1.44 mrg off = 0; 137 1.44 mrg else 138 1.44 mrg off = dot - omessage; 139 1.45 rin need_init = (omessage == NULL); 140 1.28 christos nmessage = realloc(omessage, size); 141 1.28 christos if (nmessage == NULL) 142 1.34 christos err(EXIT_FAILURE, 143 1.34 christos "Insufficient memory for %d messages", nmsgCount); 144 1.44 mrg dot = nmessage + off; 145 1.28 christos 146 1.45 rin if (off != 0 || need_init != 0) 147 1.45 rin thread_fix_old_links(nmessage, off, omsgCount); 148 1.28 christos 149 1.28 christos #ifndef THREAD_SUPPORT 150 1.28 christos message = nmessage; 151 1.28 christos #endif 152 1.28 christos size -= (omsgCount + 1) * sizeof(*nmessage); 153 1.28 christos (void)fflush(f); 154 1.28 christos (void)lseek(fileno(f), (off_t)sizeof(*nmessage), SEEK_SET); 155 1.28 christos if (read(fileno(f), &nmessage[omsgCount], size) != (ssize_t)size) 156 1.28 christos errx(EXIT_FAILURE, "Message temporary file corrupted"); 157 1.28 christos 158 1.28 christos message_init(&nmessage[nmsgCount], (off_t)0, 0); /* append a dummy */ 159 1.28 christos 160 1.28 christos thread_fix_new_links(nmessage, omsgCount, nmsgCount); 161 1.28 christos 162 1.28 christos (void)Fclose(f); 163 1.28 christos } 164 1.28 christos 165 1.28 christos /* 166 1.28 christos * Append the passed message descriptor onto the temp file. 167 1.28 christos * If the write fails, return 1, else 0 168 1.28 christos */ 169 1.28 christos static int 170 1.28 christos append(struct message *mp, FILE *f) 171 1.28 christos { 172 1.31 christos return fwrite(mp, sizeof(*mp), 1, f) != 1; 173 1.28 christos } 174 1.28 christos 175 1.1 cgd /* 176 1.1 cgd * Set up the input pointers while copying the mail file into /tmp. 177 1.1 cgd */ 178 1.28 christos PUBLIC void 179 1.15 wiz setptr(FILE *ibuf, off_t offset) 180 1.1 cgd { 181 1.25 christos int c; 182 1.25 christos size_t len; 183 1.24 christos char *cp; 184 1.24 christos const char *cp2; 185 1.1 cgd struct message this; 186 1.1 cgd FILE *mestmp; 187 1.1 cgd int maybe, inhead; 188 1.1 cgd char linebuf[LINESIZE]; 189 1.6 tls int omsgCount; 190 1.28 christos #ifdef THREAD_SUPPORT 191 1.28 christos int nmsgCount; 192 1.28 christos #else 193 1.28 christos # define nmsgCount msgCount 194 1.28 christos #endif 195 1.1 cgd 196 1.1 cgd /* Get temporary file. */ 197 1.7 mikel (void)snprintf(linebuf, LINESIZE, "%s/mail.XXXXXX", tmpdir); 198 1.1 cgd if ((c = mkstemp(linebuf)) == -1 || 199 1.43 christos (mestmp = Fdopen(c, "ref+")) == NULL) { 200 1.1 cgd (void)fprintf(stderr, "mail: can't open %s\n", linebuf); 201 1.1 cgd exit(1); 202 1.1 cgd } 203 1.1 cgd (void)unlink(linebuf); 204 1.1 cgd 205 1.28 christos nmsgCount = get_abs_msgCount(); 206 1.6 tls if (offset == 0) { 207 1.28 christos nmsgCount = 0; 208 1.6 tls } else { 209 1.6 tls /* Seek into the file to get to the new messages */ 210 1.25 christos (void)fseeko(ibuf, offset, 0); 211 1.6 tls /* 212 1.6 tls * We need to make "offset" a pointer to the end of 213 1.6 tls * the temp file that has the copy of the mail file. 214 1.6 tls * If any messages have been edited, this will be 215 1.6 tls * different from the offset into the mail file. 216 1.6 tls */ 217 1.28 christos (void)fseek(otf, 0L, SEEK_END); 218 1.6 tls offset = ftell(otf); 219 1.6 tls } 220 1.28 christos omsgCount = nmsgCount; 221 1.1 cgd maybe = 1; 222 1.1 cgd inhead = 0; 223 1.28 christos message_init(&this, (off_t)0, MUSED|MNEW); 224 1.28 christos 225 1.1 cgd for (;;) { 226 1.1 cgd if (fgets(linebuf, LINESIZE, ibuf) == NULL) { 227 1.32 christos if (append(&this, mestmp)) 228 1.32 christos err(EXIT_FAILURE, "temporary file"); 229 1.28 christos makemessage(mestmp, omsgCount, nmsgCount); 230 1.1 cgd return; 231 1.1 cgd } 232 1.16 wiz len = strlen(linebuf); 233 1.9 matt /* 234 1.9 matt * Transforms lines ending in <CR><LF> to just <LF>. 235 1.9 matt * This allows mail to be able to read Eudora mailboxes 236 1.9 matt * that reside on a DOS partition. 237 1.9 matt */ 238 1.30 christos if (len >= 2 && linebuf[len - 1] == '\n' && 239 1.30 christos linebuf[len - 2] == '\r') { 240 1.30 christos linebuf[len - 2] = '\n'; 241 1.16 wiz len--; 242 1.9 matt } 243 1.31 christos (void)fwrite(linebuf, sizeof(*linebuf), len, otf); 244 1.32 christos if (ferror(otf)) 245 1.32 christos err(EXIT_FAILURE, "/tmp"); 246 1.33 christos if (len) 247 1.16 wiz linebuf[len - 1] = 0; 248 1.1 cgd if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 249 1.28 christos nmsgCount++; 250 1.32 christos if (append(&this, mestmp)) 251 1.32 christos err(EXIT_FAILURE, "temporary file"); 252 1.28 christos message_init(&this, offset, MUSED|MNEW); 253 1.1 cgd inhead = 1; 254 1.1 cgd } else if (linebuf[0] == 0) { 255 1.1 cgd inhead = 0; 256 1.1 cgd } else if (inhead) { 257 1.1 cgd for (cp = linebuf, cp2 = "status";; cp++) { 258 1.1 cgd if ((c = *cp2++) == 0) { 259 1.12 christos while (isspace((unsigned char)*cp++)) 260 1.28 christos continue; 261 1.1 cgd if (cp[-1] != ':') 262 1.1 cgd break; 263 1.5 christos while ((c = *cp++) != '\0') 264 1.1 cgd if (c == 'R') 265 1.1 cgd this.m_flag |= MREAD; 266 1.1 cgd else if (c == 'O') 267 1.1 cgd this.m_flag &= ~MNEW; 268 1.1 cgd inhead = 0; 269 1.1 cgd break; 270 1.1 cgd } 271 1.1 cgd if (*cp != c && *cp != toupper(c)) 272 1.1 cgd break; 273 1.1 cgd } 274 1.1 cgd } 275 1.16 wiz offset += len; 276 1.16 wiz this.m_size += len; 277 1.1 cgd this.m_lines++; 278 1.21 ross if (!inhead) { 279 1.21 ross int lines_plus_wraps = 1; 280 1.32 christos int linelen = (int)strlen(linebuf); 281 1.21 ross 282 1.28 christos if (screenwidth && (int)linelen > screenwidth) { 283 1.21 ross lines_plus_wraps = linelen / screenwidth; 284 1.21 ross if (linelen % screenwidth != 0) 285 1.21 ross ++lines_plus_wraps; 286 1.21 ross } 287 1.21 ross this.m_blines += lines_plus_wraps; 288 1.21 ross } 289 1.1 cgd maybe = linebuf[0] == 0; 290 1.1 cgd } 291 1.1 cgd } 292 1.1 cgd 293 1.1 cgd /* 294 1.1 cgd * Drop the passed line onto the passed output buffer. 295 1.1 cgd * If a write error occurs, return -1, else the count of 296 1.8 phil * characters written, including the newline if requested. 297 1.1 cgd */ 298 1.28 christos PUBLIC int 299 1.24 christos putline(FILE *obuf, const char *linebuf, int outlf) 300 1.1 cgd { 301 1.25 christos size_t c; 302 1.1 cgd 303 1.1 cgd c = strlen(linebuf); 304 1.31 christos (void)fwrite(linebuf, sizeof(*linebuf), c, obuf); 305 1.8 phil if (outlf) { 306 1.19 wiz (void)putc('\n', obuf); 307 1.8 phil c++; 308 1.8 phil } 309 1.1 cgd if (ferror(obuf)) 310 1.28 christos return -1; 311 1.32 christos return (int)c; 312 1.1 cgd } 313 1.1 cgd 314 1.1 cgd /* 315 1.1 cgd * Read up a line from the specified input into the line 316 1.1 cgd * buffer. Return the number of characters read. Do not 317 1.1 cgd * include the newline at the end. 318 1.1 cgd */ 319 1.28 christos PUBLIC int 320 1.32 christos readline(FILE *ibuf, char *linebuf, int linesize, int no_restart) 321 1.1 cgd { 322 1.32 christos struct sigaction osa_sigtstp; 323 1.32 christos struct sigaction osa_sigttin; 324 1.32 christos struct sigaction osa_sigttou; 325 1.10 lukem int n; 326 1.1 cgd 327 1.1 cgd clearerr(ibuf); 328 1.32 christos 329 1.32 christos sig_check(); 330 1.32 christos if (no_restart) { 331 1.32 christos (void)sig_setflags(SIGTSTP, 0, &osa_sigtstp); 332 1.32 christos (void)sig_setflags(SIGTTIN, 0, &osa_sigttin); 333 1.32 christos (void)sig_setflags(SIGTTOU, 0, &osa_sigttou); 334 1.32 christos } 335 1.1 cgd if (fgets(linebuf, linesize, ibuf) == NULL) 336 1.32 christos n = -1; 337 1.32 christos else { 338 1.32 christos n = (int)strlen(linebuf); 339 1.32 christos if (n > 0 && linebuf[n - 1] == '\n') 340 1.32 christos linebuf[--n] = '\0'; 341 1.32 christos } 342 1.32 christos if (no_restart) { 343 1.32 christos (void)sigaction(SIGTSTP, &osa_sigtstp, NULL); 344 1.32 christos (void)sigaction(SIGTTIN, &osa_sigttin, NULL); 345 1.32 christos (void)sigaction(SIGTTOU, &osa_sigttou, NULL); 346 1.32 christos } 347 1.32 christos sig_check(); 348 1.1 cgd return n; 349 1.1 cgd } 350 1.1 cgd 351 1.1 cgd /* 352 1.1 cgd * Return a file buffer all ready to read up the 353 1.1 cgd * passed message pointer. 354 1.1 cgd */ 355 1.28 christos PUBLIC FILE * 356 1.23 ross setinput(const struct message *mp) 357 1.1 cgd { 358 1.1 cgd 359 1.25 christos (void)fflush(otf); 360 1.28 christos if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), SEEK_SET) < 0) 361 1.34 christos err(EXIT_FAILURE, "fseek"); 362 1.28 christos return itf; 363 1.1 cgd } 364 1.1 cgd 365 1.1 cgd /* 366 1.1 cgd * Delete a file, but only if the file is a plain file. 367 1.1 cgd */ 368 1.28 christos PUBLIC int 369 1.15 wiz rm(char *name) 370 1.1 cgd { 371 1.1 cgd struct stat sb; 372 1.1 cgd 373 1.1 cgd if (stat(name, &sb) < 0) 374 1.28 christos return -1; 375 1.1 cgd if (!S_ISREG(sb.st_mode)) { 376 1.1 cgd errno = EISDIR; 377 1.28 christos return -1; 378 1.1 cgd } 379 1.28 christos return unlink(name); 380 1.1 cgd } 381 1.1 cgd 382 1.1 cgd /* 383 1.1 cgd * Determine the size of the file possessed by 384 1.1 cgd * the passed buffer. 385 1.1 cgd */ 386 1.28 christos PUBLIC off_t 387 1.15 wiz fsize(FILE *iob) 388 1.1 cgd { 389 1.1 cgd struct stat sbuf; 390 1.1 cgd 391 1.1 cgd if (fstat(fileno(iob), &sbuf) < 0) 392 1.1 cgd return 0; 393 1.1 cgd return sbuf.st_size; 394 1.1 cgd } 395 1.1 cgd 396 1.1 cgd /* 397 1.28 christos * Determine the current folder directory name. 398 1.28 christos */ 399 1.28 christos PUBLIC int 400 1.28 christos getfold(char *name, size_t namesize) 401 1.28 christos { 402 1.37 christos char unres[PATHSIZE], res[PATHSIZE]; 403 1.28 christos char *folder; 404 1.28 christos 405 1.28 christos if ((folder = value(ENAME_FOLDER)) == NULL) 406 1.28 christos return -1; 407 1.40 christos if (*folder != '/') { 408 1.40 christos (void)snprintf(unres, sizeof(unres), "%s/%s", homedir, folder); 409 1.40 christos folder = unres; 410 1.38 christos } 411 1.40 christos if (realpath(folder, res) == NULL) 412 1.40 christos warn("Can't canonicalize folder `%s'", folder); 413 1.28 christos else 414 1.40 christos folder = res; 415 1.40 christos (void)strlcpy(name, folder, namesize); 416 1.28 christos return 0; 417 1.28 christos } 418 1.28 christos 419 1.28 christos /* 420 1.1 cgd * Evaluate the string given as a new mailbox name. 421 1.1 cgd * Supported meta characters: 422 1.1 cgd * % for my system mail box 423 1.1 cgd * %user for user's system mail box 424 1.1 cgd * # for previous file 425 1.1 cgd * & invoker's mbox file 426 1.1 cgd * +file file in folder directory 427 1.1 cgd * any shell meta character 428 1.1 cgd * Return the file name as a dynamic string. 429 1.1 cgd */ 430 1.28 christos PUBLIC const char * 431 1.24 christos expand(const char *name) 432 1.1 cgd { 433 1.1 cgd char xname[PATHSIZE]; 434 1.41 christos char cmdbuf[PATHSIZE]; 435 1.41 christos int e; 436 1.41 christos wordexp_t we; 437 1.41 christos sigset_t nset, oset; 438 1.1 cgd 439 1.1 cgd /* 440 1.1 cgd * The order of evaluation is "%" and "#" expand into constants. 441 1.1 cgd * "&" can expand into "+". "+" can expand into shell meta characters. 442 1.1 cgd * Shell meta characters expand into constants. 443 1.1 cgd * This way, we make no recursive expansion. 444 1.1 cgd */ 445 1.1 cgd switch (*name) { 446 1.1 cgd case '%': 447 1.28 christos findmail(name[1] ? name + 1 : myname, xname, sizeof(xname)); 448 1.1 cgd return savestr(xname); 449 1.1 cgd case '#': 450 1.1 cgd if (name[1] != 0) 451 1.1 cgd break; 452 1.1 cgd if (prevfile[0] == 0) { 453 1.36 christos warnx("No previous file"); 454 1.17 wiz return NULL; 455 1.1 cgd } 456 1.1 cgd return savestr(prevfile); 457 1.1 cgd case '&': 458 1.28 christos if (name[1] == 0 && (name = value(ENAME_MBOX)) == NULL) 459 1.1 cgd name = "~/mbox"; 460 1.1 cgd /* fall through */ 461 1.1 cgd } 462 1.28 christos if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) { 463 1.28 christos (void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1); 464 1.1 cgd name = savestr(xname); 465 1.1 cgd } 466 1.1 cgd /* catch the most common shell meta character */ 467 1.1 cgd if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) { 468 1.28 christos (void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1); 469 1.1 cgd name = savestr(xname); 470 1.1 cgd } 471 1.18 wiz if (strpbrk(name, "~{[*?$`'\"\\") == NULL) 472 1.1 cgd return name; 473 1.41 christos 474 1.41 christos *xname = '\0'; 475 1.41 christos 476 1.41 christos sigemptyset(&nset); 477 1.41 christos sigaddset(&nset, SIGCHLD); 478 1.41 christos sigprocmask(SIG_BLOCK, &nset, &oset); 479 1.41 christos e = wordexp(name, &we, WRDE_NOCMD); 480 1.41 christos sigprocmask(SIG_SETMASK, &oset, NULL); 481 1.41 christos 482 1.41 christos switch (e) { 483 1.41 christos case 0: /* OK */ 484 1.42 christos break; 485 1.41 christos case WRDE_NOSPACE: 486 1.41 christos warnx("Out of memory expanding `%s'", name); 487 1.41 christos return NULL; 488 1.41 christos case WRDE_BADVAL: 489 1.41 christos case WRDE_BADCHAR: 490 1.41 christos case WRDE_SYNTAX: 491 1.41 christos warnx("Syntax error expanding `%s'", name); 492 1.17 wiz return NULL; 493 1.41 christos case WRDE_CMDSUB: 494 1.41 christos warnx("Command substitution not allowed expanding `%s'", 495 1.41 christos name); 496 1.17 wiz return NULL; 497 1.41 christos default: 498 1.41 christos warnx("Unknown expansion error %d expanding `%s'", e, name); 499 1.17 wiz return NULL; 500 1.1 cgd } 501 1.41 christos 502 1.41 christos switch (we.we_wordc) { 503 1.41 christos case 0: 504 1.34 christos warnx("No match for `%s'", name); 505 1.41 christos break; 506 1.41 christos case 1: 507 1.42 christos if (strlen(we.we_wordv[0]) >= PATHSIZE) 508 1.41 christos warnx("Expansion too long for `%s'", name); 509 1.42 christos strlcpy(xname, we.we_wordv[0], PATHSIZE); 510 1.41 christos break; 511 1.41 christos default: 512 1.41 christos warnx("Ambiguous expansion for `%s'", name); 513 1.41 christos break; 514 1.1 cgd } 515 1.41 christos 516 1.41 christos wordfree(&we); 517 1.41 christos if (!*xname) 518 1.17 wiz return NULL; 519 1.41 christos else 520 1.41 christos return savestr(xname); 521 1.1 cgd } 522 1.1 cgd 523 1.1 cgd /* 524 1.1 cgd * Return the name of the dead.letter file. 525 1.1 cgd */ 526 1.28 christos PUBLIC const char * 527 1.15 wiz getdeadletter(void) 528 1.1 cgd { 529 1.24 christos const char *cp; 530 1.1 cgd 531 1.28 christos if ((cp = value(ENAME_DEAD)) == NULL || (cp = expand(cp)) == NULL) 532 1.1 cgd cp = expand("~/dead.letter"); 533 1.1 cgd else if (*cp != '/') { 534 1.1 cgd char buf[PATHSIZE]; 535 1.28 christos (void)snprintf(buf, sizeof(buf), "~/%s", cp); 536 1.1 cgd cp = expand(buf); 537 1.1 cgd } 538 1.1 cgd return cp; 539 1.1 cgd } 540