home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume28 / yapp / part04 / rfp.c < prev    next >
C/C++ Source or Header  |  1994-05-29  |  24KB  |  833 lines

  1. /* RFP.C */
  2. static    char sccsid[] = "@(#)rfp.c 1.3 94/01/20 (c)1993 thalerd";
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <sys/types.h>
  6. #include <sys/time.h>
  7. #include "config.h"
  8. #include "struct.h"
  9. #include "globals.h"
  10. #include "rfp.h"
  11. #include "item.h"
  12. #include "macro.h"
  13. #include "driver.h"
  14. #include "lib.h"
  15. #include "sum.h"
  16. #include "xalloc.h"
  17. #include "arch.h"
  18. #include "system.h"
  19. #include "files.h"
  20. #include "range.h"
  21. #include "sep.h"
  22. #include "news.h"
  23. #include "stats.h" /* for get_config */
  24.  
  25. extern FILE *ext_fp;
  26. extern short ext_first,ext_last;
  27.  
  28. /* Commands available in RFP mode */
  29. static dispatch_t rfp_cmd[]={
  30.    "cen_sor",    censor,
  31.    "scr_ibble",  censor,
  32.    "e_nter",     enter,
  33.    "pr_eserve",  preserve,
  34.    "po_stpone",  preserve,
  35.    "hide",       preserve,
  36.    "p_ass",      preserve,
  37.    "n_ew",       preserve,
  38.    "wait",       preserve,
  39.    "tree",       tree,
  40.    "*freeze",    freeze,
  41.    "*thaw",      freeze,
  42.    "*forget",    freeze,
  43.    "*retire",    freeze,
  44.    "*unretire",  freeze,
  45.    "r_espond",   rfp_respond,
  46.    "ps_eudonym", rfp_respond,
  47.    "*rem_ember", remember,
  48.    "*unfor_get", remember,
  49.    "*kill",      do_kill,
  50.    "*f_ind",     do_find,
  51.    "*lo_cate",   do_find,
  52. /* "*fix_seen",  fixseen,  this by itself doesn't work */
  53.    "reply",      reply,
  54.    0, 0
  55. };
  56.  
  57. /******************************************************************************/
  58. /* DISPATCH CONTROL TO APPROPRIATE RFP MODE FUNCTION                          */
  59. /******************************************************************************/
  60. char                        /* RETURNS: 0 on abort, 1 else */
  61. rfp_cmd_dispatch(argc,argv) /* ARGUMENTS:                  */
  62. int    argc;                /*    Number of arguments      */
  63. char **argv;                /*    Argument list            */
  64. {
  65.    short a,b,c;
  66.    int i,j;
  67.  
  68.    for (i=0; rfp_cmd[i].name; i++) {
  69.       if (match(argv[0],rfp_cmd[i].name))
  70.          return rfp_cmd[i].func(argc,argv);
  71.       else if (rfp_cmd[i].name[0]=='*' && match(argv[0],rfp_cmd[i].name+1)) {
  72.          char buff[MAX_LINE_LENGTH];
  73.  
  74.          mode = M_OK; 
  75.          st_glob.r_first = st_glob.r_last+1;
  76.          sprintf(buff,"%s %d",compress(rfp_cmd[i].name+1),st_glob.i_current);
  77.          for (j=1; j<argc; j++) {
  78.             strcat(buff," ");
  79.             strcat(buff,argv[j]);
  80.          }
  81.          return command(buff,0);
  82.       }
  83.    }
  84.  
  85.    /* Command dispatch */
  86.    if (match(argv[0],"h_eader")) {
  87.       show_header();
  88.    } else if (match(argv[0],"text"))     { /* re-read from 0 */
  89.       st_glob.r_first = 0;
  90.       st_glob.r_last  = MAX_RESPONSES;
  91.       show_range();
  92.    } else if (match(argv[0],"a_gain"))   { /* re-read same range */
  93.       sepinit(IS_ITEM|IS_ALL);
  94.       show_header();
  95.       st_glob.r_first = ext_first;
  96.       st_glob.r_last  = ext_last;
  97.       if (st_glob.r_first>0)
  98.          show_nsep(ext_fp); /* nsep between header and responses */
  99.       show_range();
  100.    } else if (match(argv[0],"^")
  101.     ||        match(argv[0],"fi_rst"))   {
  102.       st_glob.r_first = 1;
  103.       st_glob.r_last  = MAX_RESPONSES;
  104.       show_range();
  105.    } else if (match(argv[0],".")
  106.     ||        match(argv[0],"th_is")
  107.     ||        match(argv[0],"cu_rrent")) {
  108.       st_glob.r_first = st_glob.r_current;
  109.       st_glob.r_last  = MAX_RESPONSES;
  110.       show_range();
  111.    } else if (match(argv[0],"$")
  112.     ||        match(argv[0],"l_ast"))  {
  113.       st_glob.r_first = st_glob.r_max;
  114.       st_glob.r_last  = MAX_RESPONSES;
  115.       show_range();
  116.    } else if (match(argv[0],"up")
  117.     ||        match(argv[0],"par_ent")) {
  118.       short a;
  119.  
  120.       if ((a=parent(st_glob.r_current)) < 0)
  121.          printf("No previous response\n");
  122.       else {
  123.          st_glob.r_first = a;
  124.          st_glob.r_last  = a;
  125.          show_range();
  126.       }
  127.    } else if (match(argv[0],"chi_ldren")
  128.      ||       match(argv[0],"do_wn")) {
  129.       short a;
  130.  
  131.       /* Find 1st child */
  132.       if ((a = child(st_glob.r_current)) < 0)
  133.          printf("No children\n");
  134.       else {
  135.          st_glob.r_first = a;
  136.          st_glob.r_last  = a;
  137.          show_range();
  138.       }
  139.    } else if (match(argv[0],"sib_ling")
  140.     ||        match(argv[0],"ri_ght")) {
  141.       short a;
  142.  
  143.       /* Find next sibling */
  144.       if ((a = sibling(st_glob.r_current)) < 0)
  145.          printf("No more siblings\n");
  146.       else {
  147.          st_glob.r_first = a;
  148.          st_glob.r_last  = a;
  149.          show_range();
  150.       }
  151.    } else if (match(argv[0],"sync_hronous"))   { mode = M_OK; /* KKK */ }
  152.    else   if (match(argv[0],"async_hronous"))  { mode = M_OK; /* KKK */ }
  153.    else if (match(argv[0],"si_nce"))         {   
  154.       time_t t;
  155.       short i;
  156.  
  157.       i=0;
  158.       t=since(argc,argv,&i);
  159.       for (i=st_glob.r_max; i>=0; i--) {
  160.          if (!re[i].date) get_resp(ext_fp,&(re[i]),(short)GR_HEADER,i);
  161.          if (re[i].date<t) break;
  162.       }
  163.       st_glob.r_first = i+1;
  164.       st_glob.r_last  = MAX_RESPONSES;
  165.       show_range();
  166.    } else if (match(argv[0],"onl_y"))          { 
  167.       if (argc>2) {
  168.          printf("Bad parameters near \"%s\"\n",argv[2]);
  169.          return 2;
  170.       } else if (argc>1 && sscanf(argv[1],"%hd",&a)==1) {
  171.          st_glob.r_first = a;
  172.          st_glob.r_last  = a;
  173.          show_range();
  174.       } else
  175.          wputs("You must specify a comment number\n");
  176.    } else if (argc && sscanf(argv[0],"%hd-%hd",&a,&b)==2) {
  177.       if (b<a) { c=a; a=b; b=c; }
  178.       if (a<0) {
  179.          printf("Response #%d is too small\n",a);
  180.       } else if (b>st_glob.r_max) {
  181.          printf("Response #%d is too big (last %d)\n",a,st_glob.r_max);
  182.       } else {
  183.          st_glob.r_first = a;
  184.          st_glob.r_last  = b;
  185.          show_range();
  186.       }
  187.    } else if (argc && (sscanf(argv[0],"%hd",&a)==1
  188.     || !strcmp(argv[0],"-") || !strcmp(argv[0],"+"))) {
  189.       if (!strcmp(argv[0],"-")) a = -1; 
  190.       if (!strcmp(argv[0],"+")) a =  1;
  191.       if (argv[0][0]=='+' || argv[0][0]=='-')
  192.          a += st_glob.r_current;
  193.       if (a<0) {
  194.          printf("Response #%d is too small\n",a);
  195.       } else if (a>st_glob.r_max) {
  196.          printf("Response #%d is too big (last %d)\n",a,st_glob.r_max);
  197.       } else {
  198.          st_glob.r_first = a;
  199.          st_glob.r_last  = MAX_RESPONSES;
  200.          show_range();
  201.       }
  202.    } else {
  203.       a=misc_cmd_dispatch(argc,argv);
  204.       if (!a) preserve(argc,argv);
  205.       return a;
  206.    }
  207.    return 1;
  208. }
  209.  
  210. /******************************************************************************/
  211. /* ADD A NEW RESPONSE                                                         */
  212. /******************************************************************************/
  213. int
  214. add_response(this,text,idx,sum,part,stt,art,mid,uid,login,fullname,resp)
  215. sumentry_t  *this; /*   New item summary */
  216. char       **text; /*   New item text    */
  217. short        idx;
  218. sumentry_t  *sum;
  219. partentry_t *part;
  220. status_t    *stt;
  221. long         art;
  222. char        *mid;
  223. uid_t        uid;
  224. char        *login;
  225. char        *fullname;
  226. short        resp;
  227. {
  228.    short item,line,nr;
  229.    char  buff2[MAX_LINE_LENGTH];
  230.    FILE *fp;
  231.  
  232.    item = stt->i_current;
  233.    nr = sum[ item-1 ].nr;
  234.    sprintf(buff2, "%s/_%d", conflist[idx].location, item);
  235.  
  236.    /* Begin critical section */
  237.    if (fp=mopen(buff2, O_A|O_NOCREATE)) {
  238.       short n;
  239.  
  240.       /* was: update before open, in case of a link - why? (was wrong) */
  241.       if (!art)
  242.          refresh_sum(item,idx,sum,part,stt);
  243.  
  244.       n = sum[item-1].nr - nr;
  245.       if (n>1) {
  246.          printf("Warning: %d comments slipped in ahead of yours at %d-%d!\n",
  247.           n,nr,sum[item-1].nr-1);
  248.       } else if (n==1) {
  249.          printf("Warning: a comment slipped in ahead of yours at %d!\n",
  250.           sum[item-1].nr-1);
  251.       } else if (!(flags & O_STAY))
  252.          stt->r_last = -1; 
  253.  
  254.       re[sum[item-1].nr].offset   = ftell(fp);
  255.       fprintf(fp,",R%04X\n,U%d,%s\n,A%s\n,D%lX\n",
  256.          RF_NORMAL,uid,login,fullname,(art)? this->last : time((time_t *)0));
  257.       if (resp)
  258.          fprintf(fp,",P%d\n",resp-1);
  259.       fprintf(fp,",T\n");
  260.       re[sum[item-1].nr].parent   = resp;
  261.       re[sum[item-1].nr].textoff  = ftell(fp);
  262.       re[sum[item-1].nr].numchars = -1;
  263.       if (art) {
  264.          fprintf(fp,",N%06ld\n",art);
  265.          fprintf(fp,",M%s\n",mid);
  266.       } else {
  267.          for (line=0; line<xsizeof(text); line++) {
  268.             if (text[line][0]==',') fprintf(fp,",,%s\n",text[line]);
  269.             else                    fprintf(fp,"%s\n",  text[line]);
  270.          }
  271.       }
  272.       if (fprintf(fp,",E\n")>=0) {
  273.  
  274.          /* Update seen */
  275.          stt->r_current = stt->r_max = sum[item-1].nr;
  276.    /*    if (!(flags & O_METOO) && stt->r_lastseen==sum[item-1].nr)  */
  277.          time(&(part[item-1].last));
  278.          if (!(flags & O_METOO)) {
  279.             part[item-1].nr    = sum[item-1].nr;
  280.             stt->r_lastseen = stt->r_current+1;
  281.          }
  282.  
  283.          sum[item-1].last  = time((time_t *)0);
  284.          sum[item-1].nr++;
  285.          save_sum(sum,(short)(item-1),idx,stt);
  286.  
  287.       } else 
  288.      error("writing response","");
  289.       mclose(fp);
  290.  
  291.    } 
  292.    /* End critical section */
  293.  
  294.    xfree(text);
  295. }
  296.  
  297. /******************************************************************************/
  298. /* ENTER A RESPONSE INTO THE CURRENT ITEM                                     */
  299. /******************************************************************************/
  300. void                /* RETURNS: (nothing)              */
  301. do_respond(ps,resp) /* ARGUMENTS:                      */
  302. int   ps;           /*    Use a pseudo?                */
  303. short resp;         /*    Response to prev. response # */
  304. {
  305.    short         nr;
  306.    char          buff[MAX_LINE_LENGTH],
  307.                       pseudo[MAX_LINE_LENGTH],
  308.                     **file;
  309.    unsigned char omode;
  310.    FILE         *fp;
  311.     char        **config;
  312.  
  313.    /* Check for valid permissions and arguments */
  314.    if (st_glob.c_status & CS_NORESPONSE) {
  315.       printf("You only have read access.\n");
  316.       return;
  317.    }
  318.  
  319.    if (sum[st_glob.i_current-1].flags & IF_FROZEN) {
  320.       wputs("Item is frozen!\n");
  321.       return;
  322.    }
  323.    nr = sum[ st_glob.i_current-1 ].nr;
  324.    if (resp>nr) {
  325.       printf("Highest response # is %d\n",nr-1);
  326.       return;
  327.    }
  328.    
  329.    /* Get pseudo */
  330.    if (ps) {
  331.       printf("What's your handle? ");
  332.       if (!ngets(buff, st_glob.inp)) 
  333.      return;
  334.       if (strlen(buff)) 
  335.      strcpy(pseudo,buff);
  336.       else {
  337.          wputs("Response aborted!  Returning to current item.\n");
  338.          return;
  339.       }
  340.    } else strcpy(pseudo,st_glob.fullname);
  341.    
  342.    if (nr >= MAX_RESPONSES) {
  343.       wputs("Too many responses on this item!\n");
  344.       return;
  345.    }
  346.    
  347. #ifdef NEWS
  348.    if (st_glob.c_security & CT_NEWS) {
  349.       char rnh[MAX_LINE_LENGTH];
  350.  
  351.       if (!resp) resp=nr;
  352.  
  353.         if (!(config = get_config(confidx)))
  354.            return;
  355.       sprintf(buff,"%s/%s/%d",NEWSDIR,dot2slash(config[CF_NEWSGROUP]),
  356.        st_glob.i_current);
  357.  
  358.       if (resp>0) {
  359.          if ((fp=mopen(buff,O_R))==NULL) return;
  360.          get_resp(fp,&(re[resp-1]),GR_ALL,resp-1);
  361.          mclose(fp);
  362.       }
  363.  
  364.       make_rnhead(re,resp);
  365.       if (resp>0)
  366.          xfree(re[resp-1].text);
  367.       sprintf(rnh,"%s/.rnhead",home);
  368.       sprintf(buff,"Pnews -h %s",rnh);
  369.       unix_cmd(buff);
  370.       rm(rnh,SL_USER);
  371.       return;
  372.    } else 
  373. #endif
  374.     if (text_loop(1,1)) { /* success */
  375.       omode = mode;
  376.       mode = M_OK;
  377.       if (!(file = grab_file(work,"cf.buffer",0)))
  378.          wputs("can't open cfbuffer\n");
  379.       else if (!xsizeof(file)) {
  380.          wputs("No text in buffer!\n");
  381.          xfree(file);
  382.       } else {
  383.          add_response(&(sum[st_glob.i_current-1]),file,confidx,sum,part,
  384.       &st_glob,0, NULL,uid,login,pseudo,resp);
  385.       }
  386.  
  387.       if (flags & O_STAY)
  388.          mode = omode;
  389.    } else
  390.       wputs("Response aborted!  Returning to current item.\n");
  391.    
  392.    /* Delete text buffer */
  393.    sprintf(buff,"%s/cf.buffer",work);
  394.    rm(buff,SL_USER);
  395. }
  396.  
  397. /******************************************************************************/
  398. /* ADD A RESPONSE TO THE CURRENT ITEM                                         */
  399. /******************************************************************************/
  400. int                /* RETURNS: (nothing)          */
  401. respond(argc,argv) /* ARGUMENTS:                  */
  402. int    argc;       /*    Number of arguments      */
  403. char **argv;       /*    Argument list            */
  404. {
  405.    char buff[MAX_LINE_LENGTH];
  406.    char act[MAX_ITEMS];
  407.    short j,fl;
  408.  
  409.    /* Check for valid permissions and arguments */
  410.    if (st_glob.c_status & CS_NORESPONSE) {
  411.       printf("You only have read access.\n");
  412.       return 1;
  413.    }
  414.  
  415.    rangeinit(&st_glob,act);
  416.    refresh_sum(0,confidx,sum,part,&st_glob);
  417.    st_glob.r_first = -1;
  418.  
  419.    fl = 0;
  420.    if (argc<2) {
  421.       printf("Error, no item specified! (try HELP RANGE)\n");
  422.    } else { /* Process args */
  423.       range(argc,argv,&fl,act,sum,&st_glob,0);
  424.    }
  425.  
  426.    /* Process items */
  427.    for (j=st_glob.i_first; j<=st_glob.i_last && !(status & S_INT); j++) {
  428.       if (!act[j-1] || !sum[j-1].flags) continue;
  429.  
  430. #ifdef NEWS
  431.       if (st_glob.c_security & CT_NEWS) {
  432.             char **config;
  433.  
  434.          if (!(config = get_config(confidx)))
  435.               return 1;
  436.          sprintf(buff,"%s/%s/%d",NEWSDIR,dot2slash(config[CF_NEWSGROUP]),
  437.       j);
  438.       } else
  439. #endif
  440.          sprintf(buff,"%s/_%d",conflist[confidx].location,j);
  441.       st_glob.i_current=j;
  442.       show_header();
  443.       if (status & S_REDIRECT) spclose(st_glob.outp);
  444.       do_respond(argc>0 && match(argv[0],"ps_eudonym"),st_glob.r_first+1);
  445.    }
  446.    return 1;
  447. }
  448.  
  449. /******************************************************************************/
  450. /* ENTER A RESPONSE IN THE CURRENT ITEM                                       */
  451. /******************************************************************************/
  452. int                    /* RETURNS: (nothing)          */
  453. rfp_respond(argc,argv) /* ARGUMENTS:                  */
  454. int    argc;           /*    Number of arguments      */
  455. char **argv;           /*    Argument list            */
  456. {
  457.    short a= -1;
  458.  
  459.    if (argc>2 || (argc>1 && (sscanf(argv[1],"%hd",&a)<1 || a<0))) {
  460.       printf("Bad parameters near \"%s\"\n",argv[(argc>2)?2:1]);
  461.       return 2;
  462.    } else
  463.       do_respond(argc>0 && match(argv[0],"ps_eudonym"),(short)a+1);
  464.  
  465.    return 1;
  466. }
  467.  
  468. void
  469. dump_reply(sep) 
  470. char *sep;
  471. {
  472.       sepinit(IS_START);
  473.       itemsep(sep,0);
  474.       for (st_glob.l_current=0;
  475.            st_glob.l_current<xsizeof(re[st_glob.r_current].text)
  476.         && !(status & S_INT);
  477.            st_glob.l_current++) {
  478.          sepinit(IS_ITEM);
  479.          itemsep(sep,0);
  480.       }
  481.       sepinit(IS_CFIDX);
  482.       itemsep(sep,0);
  483. }
  484.  
  485. /******************************************************************************/
  486. /* MAIL A REPLY TO THE AUTHOR OF A RESPONSE                                   */
  487. /******************************************************************************/
  488. int              /* RETURNS: (nothing)          */
  489. reply(argc,argv) /* ARGUMENTS:                  */
  490. int    argc;     /*    Number of arguments      */
  491. char **argv;     /*    Argument list            */
  492. {
  493.    char buff[MAX_LINE_LENGTH];
  494.    short i;
  495.    FILE *fp,*pp;
  496.    flag_t ss;
  497.    register int cpid,wpid;
  498.    int statusp;
  499.  
  500.    /* Validate arguments */
  501.    if (argc<2 || sscanf(argv[1],"%hd",&i)<1) {
  502.       printf("You must specify a comment number.\n");
  503.       return 1;
  504.    } else if (argc>2) {
  505.       printf("Bad parameters near \"%s\"\n",argv[2]);
  506.       return 2;
  507.    }
  508.    refresh_sum(0,confidx,sum,part,&st_glob);
  509.    if (i<0 || i>=sum[st_glob.i_current - 1].nr) {
  510.       wputs("Can't go that far! near \"<newline>\"\n");
  511.       return 1;
  512.    }
  513.  
  514.    if (re[i].flags & RF_CENSORED) {
  515.       wputs("Cannot reply to censored response!\n");
  516.       return 1;
  517.    }
  518.    if (re[i].offset < 0) {
  519.       printf("Offset error.\n"); /* should never happen */
  520.       return 1;
  521.    }
  522.  
  523.    /* Get complete text */
  524. #ifdef NEWS
  525.    if (st_glob.c_security & CT_NEWS) {
  526.         char **config;
  527.  
  528.         if (!(config = get_config(confidx)))
  529.             return 1;
  530.       sprintf(buff,"%s/%s/%d",NEWSDIR,dot2slash(config[CF_NEWSGROUP]),st_glob.i_current);
  531.    } else
  532. #endif
  533.       sprintf(buff,"%s/_%d",conflist[confidx].location,st_glob.i_current);
  534.    if ((fp=mopen(buff,O_R))==NULL) return 1;
  535.    get_resp(fp,&(re[i]),GR_ALL,i);
  536.    mclose(fp);
  537.  
  538.    /* Fork & setuid down when creating cf.buffer */
  539.    if (cpid=fork()) { /* parent */
  540.       if (cpid<0) return -1; /* error: couldn't fork */
  541.       while ((wpid = wait(&statusp)) != cpid && wpid != -1);
  542.       /* post = !statusp; */
  543.    } else { /* child */
  544.       if (setuid(getuid())) error("setuid","");
  545.       setgid(getgid());
  546.  
  547.       /* Save to cf.buffer */
  548.       sprintf(buff,"%s/cf.buffer",work);
  549.       if ((fp=mopen(buff,O_W))==NULL) {
  550.          xfree( re[i].text );
  551.          return 1;
  552.       }
  553.  
  554.       pp = st_glob.outp;
  555.       ss = status;
  556.       st_glob.r_current = i;
  557.       st_glob.outp = fp;
  558.       status |= S_REDIRECT;
  559.  
  560.       dump_reply("replysep");
  561.  
  562.       st_glob.outp = pp;
  563.       status     = ss;
  564.  
  565.       dump_reply("replysep");
  566.  
  567.       mclose(fp);
  568.       exit(0);
  569.    }
  570.  
  571.    /* Invoke mailer */
  572.    sprintf(buff,"mail %s",re[i].login);
  573.    command(buff,0);
  574.  
  575.    xfree( re[i].text );
  576.    mode = M_RFP;
  577.    return 1;
  578. }
  579.  
  580. /******************************************************************************/
  581. /* CENSOR A RESPONSE IN THE CURRENT ITEM                                      */
  582. /******************************************************************************/
  583. int               /* RETURNS: (nothing)          */
  584. censor(argc,argv) /* ARGUMENTS:                  */
  585. int    argc;      /*    Number of arguments      */
  586. char **argv;      /*    Argument list            */
  587. {
  588.    char buff[MAX_LINE_LENGTH],over[MAX_LINE_LENGTH];
  589.    short i,typ,j,k;
  590.    FILE *fp;
  591.    char **text; short len;
  592.  
  593.    typ = (match(argv[0],"scr_ibble"))? RF_CENSORED|RF_SCRIBBLED : RF_CENSORED;
  594.  
  595.    /* Validate arguments */
  596.    if (argc<2 || sscanf(argv[1],"%hd",&i)<1) {
  597.       printf("You must specify a comment number.\n");
  598.       return 1;
  599.    } else if (argc>2) {
  600.       printf("Bad parameters near \"%s\"\n",argv[2]);
  601.       return 2;
  602.    }
  603.    refresh_sum(0,confidx,sum,part,&st_glob);
  604.    if (i<0 || i>=sum[st_glob.i_current - 1].nr) {
  605.       wputs("Can't go that far! near \"<newline>\"\n");
  606.       return 1;
  607.    }
  608.  
  609.    /* Check for permission to censor */
  610.    if (!(st_glob.c_status & CS_FW) && uid!=re[i].uid) {
  611.       wputs("You can't do that!\n");
  612.       return 1;
  613.    }
  614.    if (sum[st_glob.i_current-1].flags & IF_FROZEN) {
  615.       wputs("Cannot censor frozen items!\n");
  616.       return 1;
  617.    }
  618.  
  619.    if ((re[i].flags & typ)==typ) return 1; /* already done */
  620.    if (re[i].offset < 0) {
  621.       printf("Offset error.\n"); /* should never happen */
  622.       return 1;
  623.    }
  624.  
  625.    sprintf(buff,"%s/_%d",conflist[confidx].location,st_glob.i_current);
  626.    if ((fp=mopen(buff,O_RPLUS))!=NULL) {
  627.       if (fseek(fp,re[i].offset,0))
  628.          error("fseeking in ",buff);
  629.       fprintf(fp,",R%04d\n",typ);
  630.  
  631.       /* log it and overwrite it, unless it's a news article */
  632. #ifdef NEWS
  633.       if (!re[i].article) {
  634. #endif
  635.          get_resp(fp,&(re[i]),GR_ALL,i);
  636.          fseek(fp,re[i].textoff,0);
  637.          text = re[i].text;
  638.          if (typ & RF_SCRIBBLED) {
  639.             sprintf(over,"%s %s %s ",login,get_date(time((time_t *)0),0),st_glob.fullname);
  640.             len = strlen(over);
  641.             for (j=re[i].numchars; j>76; j-=76) {
  642.                for (k=0; k<75; k++)
  643.                   fputc(over[k%len],fp);
  644.                fputc('\n',fp);
  645.             }
  646.             for (k=0; k<j-1; k++)
  647.                fputc(over[k%len],fp);
  648.             fputc('\n',fp);
  649.          }
  650. #ifdef NEWS
  651.       }
  652. #endif
  653.       mclose(fp);
  654.    }
  655.  
  656.    /* free_sum(sum); unneeded, always SF_FAST */
  657.  
  658.    /* Write to censorlog */
  659.    sprintf(buff,"%s/censored",bbsdir);
  660.    if (fp=mopen(buff,O_A|O_PRIVATE)) {
  661.       fprintf(fp,",C %s item %d resp %d rflg %d %s,%d %s date %s\n",
  662.        conflist[confidx].location, st_glob.i_current, i, typ,
  663.        login, uid, get_date(time((time_t *)0),0), st_glob.fullname);
  664.       fprintf(fp,",R%04X\n,U%d,%s\n,A%s\n,D%lX\n,T\n",
  665.        re[i].flags,re[i].uid,re[i].login,re[i].fullname,re[i].date);
  666.       for (j=0; j<xsizeof(text); j++) 
  667.          fprintf(fp,"%s\n",text[j]);
  668.       fprintf(fp,",E\n");
  669.       mclose(fp);
  670.    }
  671.    xfree( re[i].text );
  672.    re[i].flags=typ;
  673.    return 1;
  674. }
  675.  
  676. short stack[MAX_RESPONSES],top=0;
  677. void
  678. traverse(i)
  679. short i;
  680. {
  681.    short c,l,s;
  682.  
  683.    printf("%s%3d", (top)?"-":" ", i);
  684.    stack[top++]=i;
  685.    c=child(i);
  686.    if (c<0) putchar('\n');
  687.    else     traverse(c);
  688.    
  689.    top--;
  690.    if (!top) return;
  691.  
  692.    c=sibling(i);
  693.    if (c>=0) {
  694.       for (l=1; l<=top; l++) {
  695.          printf("   "); /* printf("(%d)",stack[l]); */
  696.          s=sibling(stack[l]);
  697.          if (s<0) putchar(' ');
  698.          else if (l<top) putchar('|');
  699.          else putchar( (sibling(s)<0)?'\\':'+' );
  700.       }
  701.       traverse(c);
  702.    }
  703. }
  704.  
  705. /******************************************************************************/
  706. /* DISPLAY RESPONSE TREE                                                      */
  707. /******************************************************************************/
  708. int                 /* RETURNS: (nothing)          */
  709. tree(argc,argv) /* ARGUMENTS:                  */
  710. int    argc;        /*    Number of arguments      */
  711. char **argv;        /*    Argument list            */
  712. {
  713.    short i=0;
  714.  
  715.    /* Validate arguments */
  716.    if (argc>2 || (argc>1 && sscanf(argv[1],"%hd",&i)<1)) {
  717.       printf("Bad parameters near \"%s\"\n",argv[2]);
  718.       return 2;
  719.    }
  720.    refresh_sum(0,confidx,sum,part,&st_glob);
  721.    if (i<0 || i>=sum[st_glob.i_current-1].nr) {
  722.       wputs("Can't go that far! near \"<newline>\"\n");
  723.       return 1;
  724.    }
  725.    traverse(i);
  726.    return 1;
  727. }
  728.  
  729. /******************************************************************************/
  730. /* PRESERVE RESPONSES IN THE CURRENT ITEM                                     */
  731. /******************************************************************************/
  732. int                 /* RETURNS: (nothing)          */
  733. preserve(argc,argv) /* ARGUMENTS:                  */
  734. int    argc;        /*    Number of arguments      */
  735. char **argv;        /*    Argument list            */
  736. {
  737.    short i;
  738.     short i_i;
  739.  
  740.    if (match(argv[0],"pr_eserve") || match(argv[0],"po_stpone")
  741.     || match(argv[0],"n_ew") ||      match(argv[0],"wait")) {
  742.       wputs("This item will still be new.\n");
  743.       st_glob.r_last = -2; 
  744.    } else
  745.       st_glob.r_last = -1; /* re-read nothing */
  746.  
  747.    /* Lots of ways to stop, so check inverse */
  748.    i_i = st_glob.i_current - 1;
  749.    if (!match(argv[0],"pr_eserve") && !match(argv[0],"po_stpone")
  750.     && !match(argv[0],"p_ass") && !match(argv[0],"hide")) {
  751.       if (st_glob.opt_flags & OF_REVERSE)
  752.          st_glob.i_current = st_glob.i_first;
  753.       else
  754.         st_glob.i_current = st_glob.i_last;
  755.       wputs("Stopping.\n");
  756.       status |= S_STOP;
  757.    }
  758.  
  759.    if (argc<2) {
  760.       mode = M_OK;
  761.       return 1;
  762.    }
  763.  
  764.    /* Validate arguments */
  765.    if (sscanf(argv[1],"%hd",&i)<1) {
  766.       printf("You must specify a comment number.\n");
  767.       return 1;
  768.    } else if (argc>2) {
  769.       printf("Bad parameters near \"%s\"\n",argv[2]);
  770.       return 2;
  771.    }
  772.    refresh_sum(0,confidx,sum,part,&st_glob);
  773.    if (i<0 || i>=sum[i_i].nr) {
  774.       wputs("Can't go that far! near \"<newline>\"\n");
  775.       return 1;
  776.    }
  777.    
  778.    /* Do it */
  779.    part[i_i].nr = st_glob.r_lastseen = i;
  780.    if (st_glob.r_last == -2)
  781.       st_glob.r_last  =  -1;
  782.    else
  783.       time(&(part[i_i].last));
  784.    mode = M_OK;
  785.    
  786.    return 1;
  787. }
  788.  
  789. short
  790. sibling(r)
  791. short r;
  792. {
  793.       short a,p;
  794.  
  795.       /* Find next sibling */
  796.       p = parent(r);
  797.       a=r+1;
  798.       if (!re[a].date) get_resp(ext_fp,&(re[a]),GR_HEADER,a);
  799.       while (a<=st_glob.r_max && parent(a)!=p) {
  800.          a++;
  801.          if (!re[a].date) get_resp(ext_fp,&(re[a]),GR_HEADER,a);
  802.       }
  803.       if (a>st_glob.r_max) return -1;
  804.       else return a;
  805. }
  806.  
  807. short 
  808. parent(r)
  809. short r;
  810. {
  811.       return (re[r].parent < 1)? r-1 : re[r].parent-1;
  812. }
  813.  
  814. short
  815. child(r)
  816. short r;
  817. {
  818.       short a,p;
  819.  
  820.       /* Find 1st child */
  821.       a = p = r+1;
  822.       if (!re[a].date) get_resp(ext_fp,&(re[a]),GR_HEADER,a);
  823.       if (re[a].parent && re[a].parent!=p) {
  824.          a++;
  825.          if (!re[a].date) get_resp(ext_fp,&(re[a]),GR_HEADER,a);
  826.          while (a<=st_glob.r_max && re[a].parent!=p) {
  827.             a++;
  828.             if (!re[a].date) get_resp(ext_fp,&(re[a]),GR_HEADER,a);
  829.          }
  830.       }
  831.       return (a>st_glob.r_max)? -1 : a;
  832. }
  833.