jcs's openbsd hax
openbsd
at jcs 442 lines 9.1 kB view raw
1/* $OpenBSD: cmd2.c,v 1.22 2015/10/16 17:56:07 mmcc Exp $ */ 2/* $NetBSD: cmd2.c,v 1.7 1997/05/17 19:55:10 pk Exp $ */ 3 4/* 5 * Copyright (c) 1980, 1993 6 * The Regents of the University of California. 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 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#include "rcv.h" 34#include <sys/wait.h> 35#include "extern.h" 36 37/* 38 * Mail -- a mail program 39 * 40 * More user commands. 41 */ 42static int igcomp(const void *, const void *); 43 44/* 45 * If any arguments were given, go to the next applicable argument 46 * following dot, otherwise, go to the next applicable message. 47 * If given as first command with no arguments, print first message. 48 */ 49int 50next(void *v) 51{ 52 struct message *mp; 53 int *msgvec = v; 54 int *ip, *ip2, list[2], mdot; 55 56 if (*msgvec != 0) { 57 /* 58 * If some messages were supplied, find the 59 * first applicable one following dot using 60 * wrap around. 61 */ 62 mdot = dot - &message[0] + 1; 63 64 /* 65 * Find the first message in the supplied 66 * message list which follows dot. 67 */ 68 for (ip = msgvec; *ip != 0; ip++) 69 if (*ip > mdot) 70 break; 71 if (*ip == 0) 72 ip = msgvec; 73 ip2 = ip; 74 do { 75 mp = &message[*ip2 - 1]; 76 if ((mp->m_flag & MDELETED) == 0) { 77 dot = mp; 78 goto hitit; 79 } 80 if (*ip2 != 0) 81 ip2++; 82 if (*ip2 == 0) 83 ip2 = msgvec; 84 } while (ip2 != ip); 85 puts("No messages applicable"); 86 return(1); 87 } 88 89 /* 90 * If this is the first command, select message 1. 91 * Note that this must exist for us to get here at all. 92 */ 93 if (!sawcom) 94 goto hitit; 95 96 /* 97 * Just find the next good message after dot, no 98 * wraparound. 99 */ 100 for (mp = dot+1; mp < &message[msgCount]; mp++) 101 if ((mp->m_flag & (MDELETED|MSAVED)) == 0) 102 break; 103 if (mp >= &message[msgCount]) { 104 puts("At EOF"); 105 return(0); 106 } 107 dot = mp; 108hitit: 109 /* 110 * Print dot. 111 */ 112 list[0] = dot - &message[0] + 1; 113 list[1] = 0; 114 return(type(list)); 115} 116 117/* 118 * Save a message in a file. Mark the message as saved 119 * so we can discard when the user quits. 120 */ 121int 122save(void *v) 123{ 124 char *str = v; 125 126 return(save1(str, 1, "save", saveignore)); 127} 128 129/* 130 * Copy a message to a file without affected its saved-ness 131 */ 132int 133copycmd(void *v) 134{ 135 char *str = v; 136 137 return(save1(str, 0, "copy", saveignore)); 138} 139 140/* 141 * Save/copy the indicated messages at the end of the passed file name. 142 * If mark is true, mark the message "saved." 143 */ 144int 145save1(char *str, int mark, char *cmd, struct ignoretab *ignore) 146{ 147 struct message *mp; 148 char *file, *disp; 149 int f, *msgvec, *ip; 150 FILE *obuf; 151 152 msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec)); 153 if ((file = snarf(str, &f)) == NULL) 154 return(1); 155 if (!f) { 156 *msgvec = first(0, MMNORM); 157 if (*msgvec == 0) { 158 printf("No messages to %s.\n", cmd); 159 return(1); 160 } 161 msgvec[1] = 0; 162 } 163 if (f && getmsglist(str, msgvec, 0) < 0) 164 return(1); 165 if ((file = expand(file)) == NULL) 166 return(1); 167 printf("\"%s\" ", file); 168 fflush(stdout); 169 if (access(file, F_OK) >= 0) 170 disp = "[Appended]"; 171 else 172 disp = "[New file]"; 173 if ((obuf = Fopen(file, "a")) == NULL) { 174 warn(NULL); 175 return(1); 176 } 177 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 178 mp = &message[*ip - 1]; 179 touch(mp); 180 if (sendmessage(mp, obuf, ignore, NULL) < 0) { 181 warn("%s", file); 182 (void)Fclose(obuf); 183 return(1); 184 } 185 if (mark) 186 mp->m_flag |= MSAVED; 187 } 188 fflush(obuf); 189 if (ferror(obuf)) 190 warn("%s", file); 191 (void)Fclose(obuf); 192 printf("%s\n", disp); 193 return(0); 194} 195 196/* 197 * Write the indicated messages at the end of the passed 198 * file name, minus header and trailing blank line. 199 */ 200int 201swrite(void *v) 202{ 203 char *str = v; 204 205 return(save1(str, 1, "write", ignoreall)); 206} 207 208/* 209 * Snarf the file from the end of the command line and 210 * return a pointer to it. If there is no file attached, 211 * just return NULL. Put a null in front of the file 212 * name so that the message list processing won't see it, 213 * unless the file name is the only thing on the line, in 214 * which case, return 0 in the reference flag variable. 215 */ 216char * 217snarf(char *linebuf, int *flag) 218{ 219 char *cp; 220 221 *flag = 1; 222 cp = strlen(linebuf) + linebuf - 1; 223 224 /* 225 * Strip away trailing blanks. 226 */ 227 while (cp > linebuf && isspace((unsigned char)*cp)) 228 cp--; 229 *++cp = 0; 230 231 /* 232 * Now search for the beginning of the file name. 233 */ 234 while (cp > linebuf && !isspace((unsigned char)*cp)) 235 cp--; 236 if (*cp == '\0') { 237 puts("No file specified."); 238 return(NULL); 239 } 240 if (isspace((unsigned char)*cp)) 241 *cp++ = 0; 242 else 243 *flag = 0; 244 return(cp); 245} 246 247/* 248 * Delete messages. 249 */ 250int 251deletecmd(void *v) 252{ 253 int *msgvec = v; 254 255 delm(msgvec); 256 return(0); 257} 258 259/* 260 * Delete messages, then type the new dot. 261 */ 262int 263deltype(void *v) 264{ 265 int *msgvec = v; 266 int list[2]; 267 int lastdot; 268 269 lastdot = dot - &message[0] + 1; 270 if (delm(msgvec) >= 0) { 271 list[0] = dot - &message[0] + 1; 272 if (list[0] > lastdot) { 273 touch(dot); 274 list[1] = 0; 275 return(type(list)); 276 } 277 puts("At EOF"); 278 } else 279 puts("No more messages"); 280 return(0); 281} 282 283/* 284 * Delete the indicated messages. 285 * Set dot to some nice place afterwards. 286 * Internal interface. 287 */ 288int 289delm(int *msgvec) 290{ 291 struct message *mp; 292 int *ip, last; 293 294 last = 0; 295 for (ip = msgvec; *ip != 0; ip++) { 296 mp = &message[*ip - 1]; 297 touch(mp); 298 mp->m_flag |= MDELETED|MTOUCH; 299 mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX); 300 last = *ip; 301 } 302 if (last != 0) { 303 dot = &message[last-1]; 304 last = first(0, MDELETED); 305 if (last != 0) { 306 dot = &message[last-1]; 307 return(0); 308 } 309 else { 310 dot = &message[0]; 311 return(-1); 312 } 313 } 314 315 /* 316 * Following can't happen 317 */ 318 return(-1); 319} 320 321/* 322 * Undelete the indicated messages. 323 */ 324int 325undeletecmd(void *v) 326{ 327 int *msgvec = v; 328 int *ip; 329 struct message *mp; 330 331 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 332 mp = &message[*ip - 1]; 333 touch(mp); 334 dot = mp; 335 mp->m_flag &= ~MDELETED; 336 } 337 return(0); 338} 339 340/* 341 * Add the given header fields to the retained list. 342 * If no arguments, print the current list of retained fields. 343 */ 344int 345retfield(void *v) 346{ 347 char **list = v; 348 349 return(ignore1(list, ignore + 1, "retained")); 350} 351 352/* 353 * Add the given header fields to the ignored list. 354 * If no arguments, print the current list of ignored fields. 355 */ 356int 357igfield(void *v) 358{ 359 char **list = v; 360 361 return(ignore1(list, ignore, "ignored")); 362} 363 364int 365saveretfield(void *v) 366{ 367 char **list = v; 368 369 return(ignore1(list, saveignore + 1, "retained")); 370} 371 372int 373saveigfield(void *v) 374{ 375 char **list = v; 376 377 return(ignore1(list, saveignore, "ignored")); 378} 379 380int 381ignore1(char **list, struct ignoretab *tab, char *which) 382{ 383 char field[LINESIZE]; 384 char **ap; 385 struct ignore *igp; 386 int h; 387 388 if (*list == NULL) 389 return(igshow(tab, which)); 390 for (ap = list; *ap != 0; ap++) { 391 istrlcpy(field, *ap, sizeof(field)); 392 if (member(field, tab)) 393 continue; 394 h = hash(field); 395 igp = calloc(1, sizeof(struct ignore)); 396 if (igp == NULL) 397 err(1, "calloc"); 398 igp->i_field = strdup(field); 399 if (igp->i_field == NULL) 400 err(1, "strdup"); 401 igp->i_link = tab->i_head[h]; 402 tab->i_head[h] = igp; 403 tab->i_count++; 404 } 405 return(0); 406} 407 408/* 409 * Print out all currently retained fields. 410 */ 411int 412igshow(struct ignoretab *tab, char *which) 413{ 414 int h; 415 struct ignore *igp; 416 char **ap, **ring; 417 418 if (tab->i_count == 0) { 419 printf("No fields currently being %s.\n", which); 420 return(0); 421 } 422 ring = (char **)salloc((tab->i_count + 1) * sizeof(char *)); 423 ap = ring; 424 for (h = 0; h < HSHSIZE; h++) 425 for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link) 426 *ap++ = igp->i_field; 427 *ap = 0; 428 qsort(ring, tab->i_count, sizeof(char *), igcomp); 429 for (ap = ring; *ap != 0; ap++) 430 puts(*ap); 431 return(0); 432} 433 434/* 435 * Compare two names for sorting ignored field list. 436 */ 437static int 438igcomp(const void *l, const void *r) 439{ 440 441 return(strcmp(*(char **)l, *(char **)r)); 442}