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 */ 42 static 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 */ 49 int 50 next(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; 108 hitit: 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 */ 121 int 122 save(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 */ 132 int 133 copycmd(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 */ 144 int 145 save1(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 */ 200 int 201 swrite(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 */ 216 char * 217 snarf(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 */ 250 int 251 deletecmd(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 */ 262 int 263 deltype(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 */ 288 int 289 delm(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 */ 324 int 325 undeletecmd(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 */ 344 int 345 retfield(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 */ 356 int 357 igfield(void *v) 358 { 359 char **list = v; 360 361 return(ignore1(list, ignore, "ignored")); 362 } 363 364 int 365 saveretfield(void *v) 366 { 367 char **list = v; 368 369 return(ignore1(list, saveignore + 1, "retained")); 370 } 371 372 int 373 saveigfield(void *v) 374 { 375 char **list = v; 376 377 return(ignore1(list, saveignore, "ignored")); 378 } 379 380 int 381 ignore1(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 */ 411 int 412 igshow(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 */ 437 static int 438 igcomp(const void *l, const void *r) 439 { 440 441 return(strcmp(*(char **)l, *(char **)r)); 442 } 443