1: /* rn -- new readnews program 2: * 3: * From: lwall@sdcrdcf.UUCP (Larry Wall) 4: * Organization: System Development Corporation, Santa Monica 5: * 6: * begun: 01/14/83 7: * 1.0: 04/08/83 8: * 2.0: 09/01/83 9: */ 10: 11: static char rnid[] = "@(#)$Header: rn.c,v 4.3.1.4 85/09/10 11:05:13 lwall Exp $"; 12: 13: /* $Log: rn.c,v $ 14: * Revision 4.3.1.4 85/09/10 11:05:13 lwall 15: * Improved %m in in_char(). 16: * 17: * Revision 4.3.1.3 85/05/16 16:47:10 lwall 18: * Catchup confirmation didn't grok -t. 19: * 20: * Revision 4.3.1.2 85/05/13 09:34:53 lwall 21: * Fixed default after do_newsgroup() returns from Q command. 22: * 23: * Revision 4.3.1.1 85/05/10 11:38:08 lwall 24: * Branch for patches. 25: * 26: * Revision 4.3 85/05/01 11:47:56 lwall 27: * Baseline for release with 4.3bsd. 28: * 29: */ 30: 31: #include "INTERN.h" 32: #include "common.h" 33: #include "rn.h" 34: #include "EXTERN.h" 35: #include "rcstuff.h" 36: #include "term.h" 37: #include "final.h" 38: #include "ngdata.h" 39: #include "util.h" 40: #include "only.h" 41: #include "ngsrch.h" 42: #include "help.h" 43: #include "last.h" 44: #include "init.h" 45: #include "intrp.h" 46: #include "rcln.h" 47: #include "sw.h" 48: #include "addng.h" 49: #include "ng.h" 50: #include "INTERN.h" 51: 52: void 53: rn_init() 54: { 55: ; 56: } 57: 58: void 59: main(argc,argv) 60: int argc; 61: char *argv[]; 62: { 63: bool foundany = initialize(argc,argv); 64: register char *s; 65: bool oh_for_the_good_old_days = FALSE; 66: 67: if (maxngtodo) 68: starthere = 0; 69: else if (!foundany) { /* nothing to do? */ 70: #ifdef VERBOSE 71: if (verbose) 72: fputs("\ 73: No unread news in subscribed-to newsgroups. To subscribe to a new\n\ 74: newsgroup use the g<newsgroup> command.\n\ 75: ",stdout) FLUSH; 76: #endif 77: starthere = nextrcline; 78: } 79: 80: /* loop through all unread news */ 81: 82: { 83: char promptbuf[80]; 84: bool special = FALSE; /* temporarily allow newsgroup */ 85: /* with no unread news? */ 86: bool retry; /* cycle back to top of list? */ 87: NG_NUM recent_ng = 0; 88: 89: current_ng = 0; 90: do { 91: retry = FALSE; 92: if (findlast) { 93: findlast = FALSE; 94: starthere = 0; 95: if (*lastngname) { 96: if ((ng = find_ng(lastngname)) == nextrcline) 97: ng = 0; 98: else { 99: set_ngname(lastngname); 100: set_toread(ng); 101: if (toread[ng] <= TR_NONE) 102: ng = 0; 103: } 104: } 105: } 106: else { 107: ng = starthere; 108: starthere = 0; 109: } 110: while (ng <= nextrcline) { /* for each newsgroup */ 111: mode = 'n'; 112: if (ng >= nextrcline) { /* after the last newsgroup? */ 113: ng = nextrcline; /* force it to 1 after */ 114: #ifdef ONLY 115: if (maxngtodo) { 116: if (retry) 117: #ifdef VERBOSE 118: IF(verbose) 119: printf("\nRestriction %s%s still in effect.\n", 120: ngtodo[0], 121: maxngtodo > 1 ? ", etc." : nullstr) FLUSH; 122: ELSE 123: #endif 124: #ifdef TERSE 125: fputs("\n(\"Only\" mode.)\n",stdout) FLUSH; 126: #endif 127: else { 128: #ifdef VERBOSE 129: IF(verbose) 130: fputs("\nNo articles under restriction.", 131: stdout) FLUSH; 132: ELSE 133: #endif 134: #ifdef TERSE 135: fputs("\nNo \"only\" articles.",stdout) FLUSH; 136: #endif 137: end_only(); /* release the restriction */ 138: retry = TRUE; 139: } 140: } 141: #endif 142: dfltcmd = (retry ? "npq" : "qnp"); 143: #ifdef VERBOSE 144: IF(verbose) 145: sprintf(promptbuf, 146: "\n******** End of newsgroups--what next? [%s] ", 147: dfltcmd); 148: ELSE 149: #endif 150: #ifdef TERSE 151: sprintf(promptbuf, 152: "\n**** End--next? [%s] ", dfltcmd); 153: #endif 154: } 155: else { 156: bool shoe_fits; /* newsgroup matches restriction? */ 157: 158: if (toread[ng] >= TR_NONE) { /* recalc toread? */ 159: set_ngname(rcline[ng]); 160: if (shoe_fits = (special || inlist(ngname))) 161: set_toread(ng); 162: if (paranoid) { 163: recent_ng = current_ng; 164: current_ng = ng; 165: cleanup_rc(); 166: /* this may move newsgroups around */ 167: ng = current_ng; 168: set_ngname(rcline[ng]); 169: } 170: } 171: if (toread[ng] < (maxngtodo||special ? TR_NONE : TR_ONE) || !shoe_fits) { 172: /* unwanted newsgroup? */ 173: ng++; /* then skip it */ 174: continue; 175: } 176: dfltcmd = "ynq"; 177: #ifdef VERBOSE 178: IF(verbose) 179: sprintf(promptbuf, 180: "\n******** %3ld unread article%c in %s--read now? [%s] ", 181: (long)toread[ng], (toread[ng]==TR_ONE ? ' ' : 's'), 182: ngname, dfltcmd); /* format prompt string */ 183: ELSE 184: #endif 185: #ifdef TERSE 186: sprintf(promptbuf, 187: "\n**** %3ld in %s--read? [%s] ", 188: (long)toread[ng], 189: ngname,dfltcmd); /* format prompt string */ 190: #endif 191: } 192: special = FALSE; /* go back to normal mode */ 193: if (ng != current_ng) { 194: recent_ng = current_ng; 195: /* remember previous newsgroup */ 196: current_ng = ng; /* remember current newsgroup */ 197: } 198: reask_newsgroup: 199: unflush_output(); /* disable any ^O in effect */ 200: fputs(promptbuf,stdout) FLUSH;/* print prompt */ 201: fflush(stdout); 202: reinp_newsgroup: 203: eat_typeahead(); 204: getcmd(buf); 205: if (errno || *buf == '\f') { 206: putchar('\n') FLUSH; /* if return from stop signal */ 207: goto reask_newsgroup; /* give them a prompt again */ 208: } 209: setdef(buf,dfltcmd); 210: #ifdef VERIFY 211: printcmd(); 212: #endif 213: switch (*buf) { 214: case 'p': /* find previous unread newsgroup */ 215: do { 216: if (ng <= 0) 217: break; 218: ng--; 219: if (toread[ng] == TR_NONE) 220: set_toread(ng); 221: } while (toread[ng] <= TR_NONE); 222: break; 223: case 'P': /* goto previous newsgroup */ 224: do { 225: if (ng <= 0) 226: break; 227: ng--; 228: } while (toread[ng] < TR_NONE); 229: special = TRUE; /* don't skip it if toread==0 */ 230: break; 231: case '-': 232: ng = recent_ng; /* recall previous newsgroup */ 233: special = TRUE; /* don't skip it if toread==0 */ 234: break; 235: case 'q': case 'Q': case 'x': /* quit? */ 236: oh_for_the_good_old_days = (*buf == 'x'); 237: putchar('\n') FLUSH; 238: ng = nextrcline+1; /* satisfy */ 239: retry = FALSE; /* loop conditions */ 240: break; 241: case '^': 242: putchar('\n') FLUSH; 243: ng = 0; 244: break; 245: case 'n': case '+': /* find next unread newsgroup */ 246: if (ng == nextrcline) { 247: putchar('\n') FLUSH; 248: retry = TRUE; 249: } 250: else if (toread[ng] > TR_NONE) 251: retry = TRUE; 252: ng++; 253: break; 254: case 'N': /* goto next newsgroup */ 255: ng++; 256: special = TRUE; /* and don't skip it if toread==0 */ 257: break; 258: case '1': /* goto 1st newsgroup */ 259: ng = 0; 260: special = TRUE; /* and don't skip it if toread==0 */ 261: break; 262: case '$': 263: ng = nextrcline; /* goto last newsgroup */ 264: retry = TRUE; 265: break; 266: case 'L': 267: list_newsgroups(); 268: goto reask_newsgroup; 269: case '/': case '?': /* scan for newsgroup pattern */ 270: #ifdef NGSEARCH 271: switch (ng_search(buf,TRUE)) { 272: case NGS_ABORT: 273: goto reinp_newsgroup; 274: case NGS_INTR: 275: #ifdef VERBOSE 276: IF(verbose) 277: fputs("\n(Interrupted)\n",stdout) FLUSH; 278: ELSE 279: #endif 280: #ifdef TERSE 281: fputs("\n(Intr)\n",stdout) FLUSH; 282: #endif 283: ng = current_ng; 284: goto reask_newsgroup; 285: case NGS_FOUND: 286: special = TRUE; /* don't skip it if toread==0 */ 287: break; 288: case NGS_NOTFOUND: 289: #ifdef VERBOSE 290: IF(verbose) 291: fputs("\n\nNot found--use g to add newsgroups\n", 292: stdout) FLUSH; 293: ELSE 294: #endif 295: #ifdef TERSE 296: fputs("\n\nNot found\n",stdout) FLUSH; 297: #endif 298: goto reask_newsgroup; 299: } 300: #else 301: notincl("/"); 302: #endif 303: break; 304: case 'm': 305: #ifndef RELOCATE 306: notincl("m"); 307: break; 308: #endif 309: case 'g': /* goto named newsgroup */ 310: if (!finish_command(FALSE)) 311: /* if they didn't finish command */ 312: goto reinp_newsgroup; /* go try something else */ 313: for (s = buf+1; *s == ' '; s++); 314: /* skip leading spaces */ 315: if (!*s) 316: strcpy(s,ngname); 317: #ifdef RELOCATE 318: if (!get_ng(s,*buf=='m')) /* try to find newsgroup */ 319: #else 320: if (!get_ng(s,FALSE)) /* try to find newsgroup */ 321: #endif 322: ng = current_ng;/* if not found, go nowhere */ 323: special = TRUE; /* don't skip it if toread==0 */ 324: break; 325: #ifdef DEBUGGING 326: case 'D': 327: printf("\nTries: %d Hits: %d\n", 328: softtries,softtries-softmisses) FLUSH; 329: goto reask_newsgroup; 330: #endif 331: case '!': /* shell escape */ 332: if (escapade()) /* do command */ 333: goto reinp_newsgroup; 334: /* if rubbed out, re input */ 335: goto reask_newsgroup; 336: case Ctl('k'): /* edit global KILL file */ 337: edit_kfile(); 338: goto reask_newsgroup; 339: case 'c': /* catch up */ 340: #ifdef CATCHUP 341: reask_catchup: 342: #ifdef VERBOSE 343: IF(verbose) 344: in_char("\nDo you really want to mark everything as read? [yn] ", 'C'); 345: ELSE 346: #endif 347: #ifdef TERSE 348: in_char("\nReally? [ynh] ", 'C'); 349: #endif 350: putchar('\n') FLUSH; 351: setdef(buf,"y"); 352: if (*buf == 'h') { 353: #ifdef VERBOSE 354: printf("Type y or SP to mark all articles as read.\n"); 355: printf("Type n to leave articles marked as they are.\n"); 356: #else 357: printf("y or SP to mark all read.\n"); 358: printf("n to forget it.\n"); 359: #endif 360: goto reask_catchup; 361: } 362: else if (*buf!=' ' && *buf!='y' && *buf!='n' && *buf!='q') { 363: printf(hforhelp); 364: settle_down(); 365: goto reask_catchup; 366: } else if ( (*buf == ' ' || *buf == 'y') && ng<nextrcline ) 367: catch_up(ng); 368: else 369: retry = TRUE; 370: ng++; 371: #else 372: notincl("c"); 373: #endif 374: break; 375: case 'u': /* unsubscribe */ 376: if (ng < nextrcline && toread[ng] >= TR_NONE) { 377: /* unsubscribable? */ 378: printf(unsubto,rcline[ng]) FLUSH; 379: rcchar[ng] = NEGCHAR; 380: /* unsubscribe to (from?) it */ 381: toread[ng] = TR_UNSUB; 382: /* and make line invisible */ 383: ng++; /* do an automatic 'n' */ 384: } 385: break; 386: case 'h': { /* help */ 387: int cmd; 388: 389: if ((cmd = help_ng()) > 0) 390: pushchar(cmd); 391: goto reask_newsgroup; 392: } 393: case 'a': 394: #ifndef FINDNEWNG 395: notincl("a"); 396: goto reask_newsgroup; 397: #else 398: /* FALL THROUGH */ 399: #endif 400: case 'o': 401: #ifdef ONLY 402: { 403: #ifdef FINDNEWNG 404: bool doscan = (*buf == 'a'); 405: #endif 406: 407: if (!finish_command(TRUE)) /* get rest of command */ 408: goto reinp_newsgroup; /* if rubbed out, try something else */ 409: end_only(); 410: if (buf[1]) { 411: bool minusd = instr(buf+1,"-d") != Nullch; 412: 413: sw_list(buf+1); 414: if (minusd) 415: cwd_check(); 416: putchar('\n') FLUSH; 417: #ifdef FINDNEWNG 418: if (doscan && maxngtodo) 419: scanactive(); 420: #endif 421: } 422: ng = 0; /* simulate ^ */ 423: retry = FALSE; 424: break; 425: } 426: #else 427: notincl("o"); 428: goto reask_newsgroup; 429: #endif 430: case '&': 431: if (switcheroo()) /* get rest of command */ 432: goto reinp_newsgroup; /* if rubbed out, try something else */ 433: goto reask_newsgroup; 434: case 'l': { /* list other newsgroups */ 435: if (!finish_command(TRUE)) /* get rest of command */ 436: goto reinp_newsgroup; /* if rubbed out, try something else */ 437: for (s = buf+1; *s == ' '; s++); 438: /* skip leading spaces */ 439: sprintf(cmd_buf,"%s '%s'",filexp(NEWSGROUPS),s); 440: resetty(); 441: if (doshell(sh,cmd_buf)) 442: #ifdef VERBOSE 443: IF(verbose) 444: fputs(" (Error from newsgroups program)\n", 445: stdout) FLUSH; 446: ELSE 447: #endif 448: #ifdef TERSE 449: fputs("(Error)\n",stdout) FLUSH; 450: #endif 451: noecho(); 452: crmode(); 453: goto reask_newsgroup; 454: } 455: case '.': case '=': 456: case 'y': case 'Y': /* do normal thing */ 457: if (ng >= nextrcline) { 458: fputs("\nNot on a newsgroup.",stdout) FLUSH; 459: goto reask_newsgroup; 460: } 461: if (*buf == '=') 462: s = savestr("="); 463: else if (*buf == '.') { /* start command? */ 464: if (!finish_command(FALSE)) /* get rest of command */ 465: goto reinp_newsgroup; 466: s = savestr(buf+1); 467: /* do_newsgroup will free it */ 468: } 469: else 470: s = Nullch; 471: if (toread[ng]) 472: retry = TRUE; 473: switch (do_newsgroup(s)) { 474: case NG_ERROR: 475: case NG_NORM: 476: ng++; 477: break; 478: case NG_ASK: 479: dfltcmd = "ynq"; 480: goto reask_newsgroup; 481: case NG_MINUS: 482: ng = recent_ng; /* recall previous newsgroup */ 483: special = TRUE; /* don't skip it if toread==0 */ 484: break; 485: } 486: break; 487: #ifdef STRICTCR 488: case '\n': 489: fputs(badcr,stdout) FLUSH; 490: goto reask_newsgroup; 491: #endif 492: case 'v': 493: printf("\n%s\n",rnid) FLUSH; 494: goto reask_newsgroup; 495: default: 496: printf("\n%s",hforhelp) FLUSH; 497: settle_down(); 498: goto reask_newsgroup; 499: } 500: } 501: } while (retry); 502: } 503: 504: /* now write .newsrc back out */ 505: 506: write_rc(); 507: 508: if (oh_for_the_good_old_days) 509: get_old_rc(); 510: 511: finalize(0); /* and exit */ 512: } 513: 514: /* set current newsgroup */ 515: 516: void 517: set_ngname(what) 518: char *what; 519: { 520: int len = strlen(what)+1; 521: 522: growstr(&ngname,&ngnlen,len); 523: strcpy(ngname,what); 524: growstr(&ngdir,&ngdlen,len); 525: strcpy(ngdir,getngdir(ngname)); 526: } 527: 528: static char *myngdir; 529: static int ngdirlen = 0; 530: 531: char * 532: getngdir(ngnam) 533: char *ngnam; 534: { 535: register char *s; 536: 537: growstr(&myngdir,&ngdirlen,strlen(ngnam)+1); 538: strcpy(myngdir,ngnam); 539: for (s = myngdir; *s; s++) 540: if (*s == '.') 541: *s = '/'; 542: return myngdir; 543: }