home *** CD-ROM | disk | FTP | other *** search
/ ftp.uv.es / 2014.11.ftp.uv.es.tar / ftp.uv.es / pub / unix / pine4.10.tar.gz / pine4.10.tar / pine4.10 / pico / attach.c next >
C/C++ Source or Header  |  1998-12-15  |  31KB  |  1,258 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: attach.c,v 4.61 1998/12/16 03:24:01 mikes Exp $";
  3. #endif
  4. /*
  5.  * Program:    Routines to support attachments in the Pine composer 
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  *
  19.  * Pine and Pico are registered trademarks of the University of Washington.
  20.  * No commercial use of these trademarks may be made without prior written
  21.  * permission of the University of Washington.
  22.  * 
  23.  * Pine, Pico, and Pilot software and its included text are Copyright
  24.  * 1989-1998 by the University of Washington.
  25.  * 
  26.  * The full text of our legal notices is contained in the file called
  27.  * CPYRIGHT, included with this distribution.
  28.  *
  29.  */
  30. #include "headers.h"
  31. #include <math.h>
  32.  
  33. #ifdef    ATTACHMENTS
  34.  
  35.  
  36. #ifdef    ANSI
  37.     int    ParseAttach(struct hdr_line **,int *,char *,
  38.                int,char *,char *,int,int *);
  39.     PATMT *NewAttach(char *, long, char *);
  40.     void   ZotAttach(struct pico_atmt *);
  41.     int    sinserts(char *, int, char *, int);
  42.     int       AttachUpload(char *, char *);
  43.     int       AttachCancel(char *);
  44. #else
  45.     int    ParseAttach();
  46.     PATMT *NewAttach();
  47.     void   ZotAttach();
  48.     int    sinserts();
  49.     int       AttachUpload();
  50.     int       AttachCancel();
  51. #endif
  52.  
  53. #define    HIBIT_WARN    "Only ASCII characters allowed in attachment comments"
  54.  
  55.  
  56. /*
  57.  * AskAttach - ask for attachment fields and build resulting structure
  58.  *             return pointer to that struct if OK, NULL otherwise
  59.  */
  60. AskAttach(fn, sz, cmnt)
  61. char *fn, *sz, *cmnt;
  62. {
  63.     int        i, status, fbrv, upload = 0;
  64.     off_t   attsz = 0;
  65.     char    bfn[NLINE];
  66.  
  67.     i = 2;
  68.     fn[0] = '\0';
  69.     sz[0] = '\0';
  70.     cmnt[0] = '\0';
  71.  
  72.     while(i){
  73.     if(i == 2){
  74.         EXTRAKEYS menu_attach[4];
  75.         int          n;
  76.  
  77.         menu_attach[n = 0].name  = "^T";
  78.         menu_attach[n].label     = "To Files";
  79.         menu_attach[n].key         = (CTRL|'T');
  80.  
  81.         if(gmode & MDCMPLT){
  82.         menu_attach[++n].name = "TAB";
  83.         menu_attach[n].label  = "Complete";
  84.         menu_attach[n].key    = (CTRL|'I');
  85.         }
  86.  
  87. #if    !defined(DOS) && !defined(MAC)
  88.         if(Pmaster && Pmaster->upload){
  89.         /*
  90.          * The Plan: ^R prompts for uploaded file's name which
  91.          * is passed to the defined upload command when the user
  92.          * hits Return to confirm the name.
  93.          * NOTE: this is different than upload into message
  94.          * text in which case the uploaded name isn't useful so
  95.          * a temp file is ok (be sure to fix the file mode).
  96.          */
  97.         menu_attach[++n].name = "^Y";
  98.         menu_attach[n].key    = (CTRL|'Y');
  99.         menu_attach[n].label  = upload ? "Read File" : "RcvUpload";
  100.         }
  101. #endif
  102.  
  103.         menu_attach[++n].name  = NULL;
  104.         KS_OSDATASET(&menu_attach[0], KS_NONE);
  105.         status = mlreply(upload ? "Name to give uploaded attachment: "
  106.                     : "File to attach: ",
  107.                  fn, NLINE, QNORML, menu_attach);
  108.     }
  109.     else
  110.       status = mlreply("Attachment comment: ", cmnt, NLINE, QNODQT, NULL);
  111.  
  112.     switch(status){
  113.       case HELPCH:
  114.         if(Pmaster){
  115.         VARS_TO_SAVE *saved_state;
  116.  
  117.         saved_state = save_pico_state();
  118.         (*Pmaster->helper)(Pmaster->attach_help, "Attach Help", 1);
  119.         if(saved_state){
  120.             restore_pico_state(saved_state);
  121.             free_pico_state(saved_state);
  122.         }
  123.  
  124.         refresh(FALSE, 1);
  125.         update();
  126.         continue;
  127.         }
  128.         else{
  129.         emlwrite("No Attachment %s help yet!",
  130.              (i == 2) ? "file" : "comment");
  131.         sleep(3);
  132.         }
  133.  
  134.         break;
  135.  
  136.       case (CTRL|'I') :
  137.         if(i == 2){
  138.         char *fname, *p;
  139.         int   l = NLINE;
  140.  
  141.         bfn[0] = '\0';
  142.         if(*fn && (p = strrchr(fn, C_FILESEP))){
  143.             fname = p + 1;
  144.             l -= fname - fn;
  145.             if(p == fn)
  146.               strcpy(bfn, S_FILESEP);
  147. #ifdef    DOS
  148.             else if(fn[0] == C_FILESEP
  149.                 || (isalpha((unsigned char)fn[0])
  150.                 && fn[1] == ':')){
  151. #else
  152.             else if (fn[0] == C_FILESEP || fn[0] == '~') {
  153. #endif
  154.             strncpy(bfn, fn, p - fn);
  155.             bfn[p-fn] = '\0';
  156.             }
  157.             else
  158.               sprintf(bfn, "%s%c%.*s", 
  159.                   (gmode & MDCURDIR)
  160.                 ? "."
  161.                 : ((gmode & MDTREE) || opertree[0])
  162.                     ? opertree : gethomedir(NULL),
  163.                   C_FILESEP, p - fn, fn);
  164.             }
  165.             else{
  166.             fname = fn;
  167.             strcpy(bfn, (gmode & MDCURDIR)
  168.                        ? "."
  169.                        : ((gmode & MDTREE) || opertree[0])
  170.                         ? opertree : gethomedir(NULL));
  171.             }
  172.  
  173.             if(!pico_fncomplete(bfn, fname, l - 1))
  174.               (*term.t_beep)();
  175.         }
  176.         else
  177.           (*term.t_beep)();
  178.  
  179.         break;
  180.  
  181.       case (CTRL|'T'):
  182.         if(i != 2){
  183.         (*term.t_beep)();
  184.         break;
  185.         }
  186.  
  187.         *bfn = '\0';
  188.         if(*fn == '\0' || !isdir(fn, NULL, NULL))
  189.           strcpy(fn, (gmode & MDCURDIR)
  190.                 ? "."
  191.                 : ((gmode & MDTREE) || opertree[0])
  192.                  ? opertree : gethomedir(NULL));
  193.         if((fbrv = FileBrowse(fn, NLINE, bfn, NLINE, sz, FB_READ)) == 1){
  194.           if ((strlen(fn)+strlen(S_FILESEP)+strlen(bfn)) < NLINE){
  195.         strcat(fn, S_FILESEP);
  196.         strcat(fn, bfn);
  197.         if(upload && !AttachUpload(fn, sz)){              
  198.           i = 2;                  /* keep prompting for file */
  199.           sleep(3);                       /* problem, show error! */
  200.         }
  201.         else{
  202.           (void) QuoteAttach(fn);
  203.           i--;                    /* go prompt for comment */
  204.         }
  205.           }
  206.           else{                           /* trouble */
  207.         *fn = '\0';
  208.         AttachCancel(fn);
  209.         refresh(FALSE,1);
  210.         update();
  211.         emlwrite("\007File name too BIG, cannot select!", NULL);
  212.             sleep(3);
  213.           }
  214.         }
  215.         else if (!fbrv)
  216.           *fn = '\0';
  217.         else{
  218.           *fn = '\0';
  219.           AttachCancel(fn);
  220.           refresh(FALSE, 1);
  221.           update();
  222.           emlwrite("\007File name too big, cannot select!", NULL);         
  223.           sleep(3);
  224.         }
  225.  
  226.         /* fall thru to clean up the screen */
  227.  
  228.       case (CTRL|'L'):
  229.         refresh(FALSE, 1);
  230.         update();
  231.         continue;
  232.  
  233. #if    !defined(DOS) && !defined(MAC)
  234.       case (CTRL|'Y'):            /* upload? */
  235.         if(i == 2)
  236.           upload ^= 1;            /* flip mode */
  237.         else
  238.           (*term.t_beep)();
  239.  
  240.         break;
  241. #endif
  242.  
  243.       case ABORT:
  244.         return(AttachCancel((upload && i == 1) ? fn : NULL));
  245.  
  246.       case TRUE:                /* some comment */
  247.       case FALSE:                /* No comment */
  248.         if(i-- == 2){
  249.         if(upload){
  250.             fixpath(fn, NLINE);        /* names relative to ~ */
  251.             status = AttachUpload(fn, sz);
  252.             refresh(FALSE, 1);
  253.             update();
  254.             if(!status){
  255.             i = 2;            /* keep prompting for file */
  256.             sleep(3);        /* problem, show error! */
  257.             }
  258.         }
  259.         else {
  260.             if(*fn == '\"' && fn[strlen(fn)-1] == '\"'){
  261.             int j;
  262.  
  263.             for(j = 0; fn[j] = fn[j+1]; j++)
  264.               ;
  265.  
  266.             fn[j-1] = '\0';
  267.             }
  268.  
  269.             if(fn[0]){
  270.             if((gmode & MDTREE)
  271.                && !compresspath(opertree, fn, NLINE)){
  272.                 emlwrite(
  273.         "Restricted mode allows attachments from %s only: too many ..'s",
  274.                  (gmode&MDSCUR) ? "home directory" : opertree);
  275.                 return(0);
  276.             }
  277.             else{
  278.                 fixpath(fn, NLINE);    /* names relative to ~ */
  279.                 if((gmode&MDTREE) && !in_oper_tree(fn)){
  280.                 emlwrite(
  281.             "\007Restricted mode allows attachments from %s only",
  282.             (gmode&MDSCUR) ? "home directory" : opertree);
  283.                 return(0);
  284.                 }
  285.             }
  286.  
  287.             if((status = fexist(fn, "r", &attsz)) != FIOSUC){
  288.                 fioperr(status, fn); /* file DOESN'T exist! */
  289.                 return(0);
  290.             }
  291.  
  292.             QuoteAttach(fn);
  293.             strcpy(sz, prettysz(attsz));
  294.             }
  295.             else
  296.               return(AttachCancel((upload && i == 1) ? fn : NULL));
  297.         }
  298.         }
  299.         else{
  300.         mlerase();
  301.         return(1);            /* mission accomplished! */
  302.         }
  303.  
  304.         break;
  305.       default:
  306.         break;
  307.     }
  308.     }
  309. }
  310.  
  311.  
  312. /*
  313.  * AttachUpload - Use call back to run the external upload command.
  314.  */
  315. int
  316. AttachUpload(fn, sz)
  317.     char *fn, *sz;
  318. {
  319.     long l;
  320.  
  321.     if(gmode&MDSCUR){
  322.     emlwrite("\007Restricted mode disallows uploaded command", NULL);
  323.     return(0);
  324.     }
  325.  
  326.     if(Pmaster && Pmaster->upload && (*Pmaster->upload)(fn, &l)){
  327.     strcpy(sz, prettysz((off_t)l));
  328.     return(1);
  329.     }
  330.  
  331.     return(0);
  332. }
  333.  
  334.  
  335. /*
  336.  * AttachCancel - 
  337.  */
  338. int
  339. AttachCancel(fn)
  340.     char *fn;
  341. {
  342.     emlwrite("Attach cancelled", NULL);
  343.     if(fn && fn[0])
  344.       unlink(fn);                /* blast uploaded file */
  345.  
  346.     return(0);
  347. }
  348.  
  349.  
  350. extern struct headerentry *headents;
  351.  
  352. /*
  353.  * SyncAttach - given a pointer to a linked list of attachment structures,
  354.  *              return with that structure sync'd with what's displayed.
  355.  *              delete any attachments in list of structs that's not on 
  356.  *              the display, and add any that aren't in list but on display.
  357.  */
  358. SyncAttach()
  359. {
  360.     int offset = 0,                /* the offset to begin       */
  361.         rv = 0,
  362.         ki = 0,                    /* number of known attmnts   */
  363.         bi = 0,                    /* build array index         */
  364.         nbld = 0,                        /* size of build array       */
  365.         na,                    /* old number of attachmnt   */
  366.         status, i, j, n = 0;
  367.     char file[NLINE],                /* buffers to hold it all    */
  368.          size[32],
  369.          comment[1024];
  370.     struct hdr_line *lp;            /* current line in header    */
  371.     struct headerentry *entry;
  372.     PATMT *tp, **knwn = NULL, **bld;
  373.  
  374.     for(entry = headents; entry->name != NULL; entry++) {
  375.       if(entry->is_attach)
  376.     break;
  377.     }
  378.  
  379.     if(Pmaster == NULL)
  380.       return(-1);
  381.  
  382.     for(tp = Pmaster->attachments; tp; tp = tp->next)
  383.       ki++;                            /* Count known attachments */
  384.  
  385.     if(ki){
  386.     if((knwn = (PATMT **)malloc((ki+1) * (sizeof(PATMT *)))) == NULL){
  387.         emlwrite("\007Can't allocate space for %d known attachment array entries", 
  388.              (void *) (ki + 1));
  389.         rv = -1;
  390.         goto exit_early;
  391.     }
  392.     for(i=0, tp = Pmaster->attachments; i < ki; i++, tp = tp->next){
  393.         knwn[i] = tp;            /* fill table of     */
  394.                                             /* known attachments */
  395.     }
  396.     }
  397.  
  398.     
  399.     /*
  400.      * As a quick hack to avoid too many reallocs, check to see if
  401.      * there are more header lines than known attachments.
  402.      */
  403.     for(lp = entry->hd_text; lp ; lp = lp->next)
  404.       nbld++;                            /* count header lines */
  405.     nbld = nbld > ki ? nbld : ki + 1;           
  406.  
  407.     if((bld = (PATMT **)malloc(nbld * (sizeof(PATMT *)))) == NULL){
  408.     emlwrite("\007Can't allocate space for %d build array entries",
  409.          (void *) nbld);
  410.     rv = -1;
  411.     goto exit_early;
  412.     }
  413.  
  414.     lp = entry->hd_text;
  415.     while(lp != NULL){
  416.     char fn[NLINE];
  417.     na = ++n;
  418.  
  419.     if(bi == nbld){                        /* need to grow build array? */
  420.         if((bld = (PATMT **)realloc(bld, ++nbld * sizeof(PATMT *))) == NULL){
  421.         emlwrite("\007Can't resize build array to %d entries ",
  422.              (void *) nbld);
  423.         rv = -1;
  424.         goto exit_early;
  425.         }
  426.     }
  427.     
  428.     if(status = ParseAttach(&lp, &offset, file, NLINE, size, 
  429.                 comment, 1024, &na))
  430.         rv = (rv < 0) ? rv : status ;       /* remember worst case */
  431.  
  432.     if(*file == '\0'){
  433.         if(n != na && na > 0 && na <= ki && (knwn[na-1]->flags & A_FLIT)){
  434.         bld[bi++] = knwn[na-1];
  435.         knwn[na-1] = NULL;
  436.         }
  437.         continue;
  438.     }
  439.  
  440.     if((gmode&MDTREE)
  441.        && file[0] != '['
  442.        && (!in_oper_tree(file)
  443.            || !compresspath(file, fn, NLINE)))
  444.       /* no attachments outside ~ in secure mode! */
  445.       continue;
  446.  
  447.     tp = NULL;
  448.     for(i = 0; i < ki; i++){        /* already know about it? */
  449.         /*
  450.          * this is kind of gruesome. what we want to do is keep track
  451.          * of literal attachment entries because they may not be 
  452.          * actual files we can access or that the user can readily 
  453.          * access.
  454.          */
  455.         if(knwn[i]
  456.            && ((!(knwn[i]->flags&A_FLIT)
  457.             && !strcmp(file, knwn[i]->filename))
  458.            || ((knwn[i]->flags&A_FLIT) && i+1 == na))){
  459.         tp = knwn[i];
  460.         knwn[i] = NULL;            /* forget we know about it */
  461.  
  462.         if(status == -1)        /* ignore garbage! */
  463.           break;
  464.  
  465.         if((tp->flags&A_FLIT) && strcmp(file, tp->filename)){
  466.             rv = 1;
  467.             if((j=strlen(file)) > strlen(tp->filename)){
  468.             if((tp->filename = (char *)realloc(tp->filename,
  469.                             sizeof(char)*(j+1))) == NULL){
  470.                 emlwrite("\007Can't realloc filename space",NULL);
  471.                 rv = -1;
  472.                 goto exit_early;
  473.             }
  474.             }
  475.  
  476.             strcpy(tp->filename, file);
  477.         }
  478.         else if(tp->size && strcmp(tp->size, size)){
  479.             rv = 1;
  480.             if((j=strlen(size)) > strlen(tp->size)){
  481.             if((tp->size=(char *)realloc(tp->size,
  482.                             sizeof(char)*(j+1))) == NULL){
  483.                 emlwrite("\007Can't realloc space for size", NULL);
  484.                 rv = -1;
  485.                 goto exit_early;
  486.             }
  487.             }
  488.  
  489.             strcpy(tp->size, size);
  490.         }
  491.  
  492.         if(strcmp(tp->description, comment)){    /* new comment */
  493.             rv = 1;
  494.             if((j=strlen(comment)) > strlen(tp->description)){
  495.             if((tp->description=(char *)realloc(tp->description,
  496.                         sizeof(char)*(j+1))) == NULL){
  497.                 emlwrite("\007Can't realloc description", NULL);
  498.                 rv = -1;
  499.                 goto exit_early;
  500.             }
  501.             }
  502.               
  503.             strcpy(tp->description, comment);
  504.         }
  505.         break;
  506.         }
  507.     }
  508.  
  509.     if(tp){
  510.         bld[bi++] = tp;
  511.     }
  512.     else{
  513.         if(file[0] != '['){
  514.         if((tp = NewAttach(file, atol(size), comment)) == NULL){
  515.           rv = -1;
  516.           goto exit_early;
  517.         }
  518.         bld[bi++] = tp;
  519.         }
  520.         else break;
  521.     }
  522.  
  523.     if(status < 0)
  524.       tp->flags |= A_ERR;        /* turn ON error bit */
  525.     else
  526.       tp->flags &= ~(A_ERR);    /* turn OFF error bit */
  527.     }
  528.  
  529.     if(bi){
  530.     for(i=0; i < bi-1; i++)        /* link together newly built list */
  531.       bld[i]->next = bld[i+1];
  532.  
  533.     bld[i]->next = NULL;            /* tie it off */
  534.     Pmaster->attachments = bld[0];
  535.     }
  536.     else
  537.       Pmaster->attachments = NULL;
  538.  
  539. exit_early:
  540.     if(knwn){
  541.     for(i = 0; i < ki; i++){    /* kill old/unused references */
  542.         if(knwn[i]){
  543.         ZotAttach(knwn[i]);
  544.         free((char *) knwn[i]);
  545.         }
  546.     }
  547.     free((void *)knwn);
  548.     }
  549.  
  550.     if(bld)
  551.       free((void *)bld);
  552.     
  553.     return(rv);
  554. }
  555.  
  556.  
  557.  
  558.  
  559. /*
  560.  * ParseAttach - given a header line and an offset into it, return with 
  561.  *         the three given fields filled in.  Size of fn and cmnt 
  562.  *         buffers should be passed in fnlen and cmntlen.
  563.  *         Always updates header fields that have changed or are 
  564.  *         fixed.  An error advances offset to next attachment.
  565.  *
  566.  *        returns: 1 if a field changed
  567.  *                       0 nothing changed
  568.  *                      -1 on error
  569.  */
  570. ParseAttach(lp, off, fn, fnlen, sz, cmnt, cmntlen, no)
  571. struct hdr_line **lp;                /* current header line      */
  572. int  *off;                    /* offset into that line    */
  573. char *fn;                                       /* return file name field   */
  574. int  fnlen;                                     /* fn buffer size           */
  575. char *sz, *cmnt;                /* places to return fields  */
  576. int  cmntlen, *no;                /* attachment number        */
  577. {
  578.     int  j, status, bod, eod = -1,
  579.          rv = 0,                /* return value             */
  580.      orig_offset,
  581.          lbln  = 0,                /* label'd attachment        */
  582.      hibit = 0,
  583.      quoted = 0,
  584.      escaped = 0;
  585.     off_t attsz;                /* attachment length        */
  586.     char tmp[1024],
  587.      c,
  588.      c_lookahead,
  589.         *p,
  590.         *lblsz = NULL,                /* label'd attchmnt's size  */
  591.          number[8];
  592.     register struct hdr_line  *lprev;
  593.     enum {                    /* parse levels             */
  594.     LWS,                    /* leading white space      */
  595.     NUMB,                    /* attachment number        */
  596.     WSN,                    /* white space after number */
  597.     TAG,                    /* attachments tag (fname)  */
  598.     WST,                    /* white space after tag    */
  599.     ASIZE,                    /* attachments size         */
  600.     SWS,                    /* white space after size   */
  601.     CMMNT,                    /* attachment comment       */
  602.     TG} level;                /* trailing garbage         */
  603.  
  604.     *fn = *sz = *cmnt = '\0';            /* initialize return strings */
  605.     p   = tmp;
  606.     orig_offset = bod = *off;
  607.  
  608.     level = LWS;                /* start at beginning */
  609.     while(*lp != NULL){
  610.  
  611.     if((c=(*lp)->text[*off]) == '\0'){    /* end of display line */
  612.         if(level == LWS && bod != *off){
  613.           (*lp)->text[bod] = '\0';
  614.           rv = 1;
  615.         }
  616.         lprev = *lp;
  617.         if((*lp = (*lp)->next) != NULL)
  618.           c = (*lp)->text[*off = bod = 0];    /* reset offset */
  619.     }
  620.  
  621.     if(c != '\0')
  622.       c_lookahead = (*lp)->text[*off + 1];
  623.  
  624.     switch(level){
  625.       case LWS:                /* skip leading white space */
  626.         if(isspace((unsigned char)c) || c == ','){
  627.         c = ' ';
  628.         break;
  629.         }
  630.         
  631.         if(c == '\0'){
  632.         if(bod > 0 && *off >= bod && lprev){
  633.             lprev->text[bod - 1] = '\0';
  634.             rv = 1;
  635.         }
  636.         }
  637.         else if(*off > bod && *lp){         /* wipe out whitespace */
  638.         (void)bcopy(&(*lp)->text[*off], &(*lp)->text[bod],
  639.                     strlen(&(*lp)->text[*off]) + 1); 
  640.         *off = bod;                    /* reset pointer */
  641.         rv = 1;
  642.         }
  643.         
  644.         if(c == '\0')
  645.           break;
  646.  
  647.         if(!isdigit((unsigned char)c)){     /* add a number */
  648.         sprintf(number, "%d. ", *no);
  649.         *no = 0;            /* no previous number! */
  650.         sinserts((*lp == NULL) ? &lprev->text[*off]
  651.                            : &(*lp)->text[*off],
  652.                  0, number, j=strlen(number));
  653.         *off += j - 1;
  654.         rv = 1;
  655.         level = TAG;            /* interpret the name */
  656.         break;
  657.         }
  658.         level = NUMB;
  659.       case NUMB:                /* attachment number */
  660.         if(c == '\0' || c == ','){        /* got to end, no number yet */
  661.         *p = '\0';
  662.         sprintf(number, "%d. ", *no);
  663.         *no = 0;            /* no previous number! */
  664.         if(c == '\0')
  665.           *lp = lprev;            /* go back and look at prev */
  666.  
  667.         c = (*lp)->text[*off = orig_offset];    /* reset offset */
  668.         sinserts((*lp == NULL) ? &lprev->text[*off]
  669.                            : &(*lp)->text[*off],
  670.                  0, number, j=strlen(number));
  671.         *off += j - 1;
  672.         rv = 1;
  673.         p = tmp;
  674.         level = WSN;            /* what's next... */
  675.         break;
  676.         }
  677.         else if(c == '.' && isspace((unsigned char)c_lookahead)){
  678.                         /* finished grabbing number   */
  679.                         /* if not space is not number */
  680.         /*
  681.          * replace number if it's not right
  682.          */
  683.         *p = '\0';
  684.         sprintf(number, "%d", *no);    /* record the current...  */
  685.         *no = atoi(tmp);        /* and the old place in list */
  686.         if(strcmp(number, tmp)){
  687.             if(p-tmp > *off){        /* where to begin replacemnt */
  688.             j = (p-tmp) - *off;
  689.             sinserts((*lp)->text, *off, "", 0);
  690.             sinserts(&lprev->text[strlen(lprev->text)-j], j, 
  691.                  number, strlen(number));
  692.             *off = 0;
  693.             }
  694.             else{
  695.             j = (*off) - (p-tmp);
  696.             sinserts((*lp == NULL) ? &lprev->text[j] 
  697.                                : &(*lp)->text[j], 
  698.                  p-tmp , number, strlen(number));
  699.             *off += strlen(number) - (p-tmp);
  700.             }
  701.             rv = 1;
  702.         }
  703.  
  704.         p = tmp;
  705.         level = WSN;            /* what's next... */
  706.         }
  707.         else if(c < '0' || c > '9'){    /* Must be part of tag */
  708.         sprintf(number, "%d. ", *no);
  709.         sinserts((*lp == NULL) ? &lprev->text[(*off) - (p - tmp)]
  710.                            : &(*lp)->text[(*off) - (p - tmp)],
  711.                  0, number, j=strlen(number));
  712.         *off += j;
  713.         level = TAG;            /* interpret the name */
  714.         goto process_tag;    /* in case already past end of tag */
  715.         }
  716.         else
  717.           *p++ = c;
  718.  
  719.         break;
  720.  
  721.       case WSN:                /* blast whitespace */
  722.         if(isspace((unsigned char)c) || c == '\0'){
  723.         break;
  724.         }
  725.         else if(c == '['){            /* labeled attachment */
  726.         lbln++;
  727.         }
  728.         else if(c == ',' || c == ' '){
  729.         emlwrite("\007Attchmnt: '%c' not allowed in file name", 
  730.               (void *)(int)c);
  731.         rv = -1;
  732.         level = TG;            /* eat rest of garbage */
  733.         break;
  734.         }
  735.         level = TAG;
  736.       case TAG:                /* get and check filename */
  737.                         /* or labeled attachment  */
  738. process_tag:                    /* enclosed in []         */
  739.         if(c == '\0'
  740.            || (lbln && c == ']')
  741.            || (quoted && p != tmp && c == '\"')
  742.            || (!(lbln || quoted)
  743.            && (isspace((unsigned char) c) || strchr(",(\"", c)))){
  744.         if(p == tmp){
  745.             if(c == '\"')
  746.               quoted++;
  747.         }
  748.         else{
  749.             *p = '\0';            /* got something */
  750.  
  751.             if (strlen(tmp) > fnlen)
  752.               emlwrite("File name too big!",NULL);
  753.             strncpy(fn, tmp, fnlen);    /* store file name */
  754.             if(!lbln){            /* normal file attachment */
  755.             if((gmode & MDTREE)
  756.                && !compresspath(opertree, fn, fnlen)){
  757.                 emlwrite(
  758.                 "Attachments allowed only from %s: too many ..'s",
  759.                 (gmode&MDSCUR) ? "home directory" : opertree);
  760.                 rv = -1;
  761.                 level = TG;
  762.                 break;
  763.             }
  764.             else{
  765.                 fixpath(fn, fnlen);
  766.                 if((status=fexist(fn, "r", &attsz)) != FIOSUC){
  767.                 fioperr(status, fn);
  768.                 rv = -1;
  769.                 level = TG;    /* munch rest of garbage */
  770.                 break;
  771.                 }
  772.                   
  773.                 if((gmode & MDTREE) && !in_oper_tree(fn)){
  774.                 emlwrite("\007Attachments allowed only from %s",
  775.                     (gmode&MDSCUR) ? "home directory"
  776.                            : opertree);
  777.                 rv = -1;
  778.                 level = TG;
  779.                 break;
  780.                 }
  781.             }
  782.  
  783.             if(strcmp(fn, tmp)){     /* fn changed: display it */
  784.                 if(*off >=  p - tmp){    /* room for it? */
  785.                 sinserts((*lp == NULL)? 
  786.                      &lprev->text[(*off)-(p-tmp)] :
  787.                      &(*lp)->text[(*off)-(p-tmp)],
  788.                      p-tmp, fn, j=strlen(fn));
  789.                 *off += j - (p - tmp);    /* advance offset */
  790.                 rv = 1;
  791.                 }
  792.                 else{
  793.                 emlwrite("\007Attchmnt: Problem displaying real file path", NULL);
  794.                 }
  795.             }
  796.             }
  797.             else{            /* labelled attachment! */
  798.             /*
  799.              * should explain about labelled attachments:
  800.              * these are attachments that came into the composer
  801.              * with meaningless file names (up to caller of 
  802.              * composer to decide), for example, attachments
  803.              * being forwarded from another message.  here, we
  804.              * just make sure the size stays what was passed
  805.              * to us.  The user is SOL if they change the label
  806.              * since, as it is now, after changed, it will
  807.              * just get dropped from the list of what gets 
  808.              * passed back to the caller.
  809.              */
  810.             PATMT *tp;
  811.  
  812.             if(c != ']'){        /* legit label? */
  813.                 emlwrite("\007Attchmnt: Expected ']' after \"%s\"",
  814.                      fn);
  815.                 rv = -1;
  816.                 level = TG;
  817.                 break;
  818.             }
  819.             strcat(fn, "]");
  820.  
  821.             /*
  822.              * This is kind of cheating since otherwise
  823.              * ParseAttach doesn't know about the attachment
  824.              * struct.  OK if filename's not found as it will
  825.              * get taken care of later...
  826.              */
  827.             tp = Pmaster->attachments; /* caller check Pmaster! */
  828.             j = 0;
  829.             while(tp != NULL){
  830.                 if(++j == *no){
  831.                 lblsz = tp->size;
  832.                 break;
  833.                 }
  834.                 tp = tp->next;
  835.             }
  836.  
  837.             if(tp == NULL){
  838.                 emlwrite("\007Attchmnt: Unknown reference: %s",fn);
  839.                 lblsz =  "XXX";
  840.             }
  841.             }
  842.  
  843.             p = tmp;            /* reset p in tmp */
  844.             level = WST;
  845.         }
  846.  
  847.         if(!lbln && c == '(')        /* no space 'tween file, size*/
  848.           level = ASIZE;
  849.         else if(c == '\0'
  850.             || (!(lbln || quoted) && (c == ',' || c == '\"'))){
  851.             strcpy(sz, (lblsz) ? lblsz : prettysz(attsz));
  852.             sprintf(tmp, " (%s) %s", sz, (c == '\"') ? "" : "\"\"");
  853.             sinserts((*lp == NULL) ? &lprev->text[*off] 
  854.                                : &(*lp)->text[*off],
  855.                  0, tmp, j = strlen(tmp));
  856.             *off += j;
  857.             rv = 1;
  858.             level = (c == '\"') ? CMMNT : TG;/* cmnt or eat trash */
  859.         }
  860.         }
  861.         else if(!(lbln || quoted)
  862.             && (c == ',' || c == ' ' || c == '[' || c == ']')){
  863.         emlwrite("\007Attchmnt: '%c' not allowed in file name",
  864.               (void *)(int)c);
  865.         rv = -1;            /* bad char in file name */
  866.         level = TG;            /* gobble garbage */
  867.         }
  868.         else
  869.           *p++ = c;                /* add char to name */
  870.  
  871.         break;
  872.  
  873.       case WST:                /* skip white space */
  874.         if(!isspace((unsigned char)c)){
  875.         /*
  876.          * whole attachment, comment or done! 
  877.          */
  878.         if(c == ',' || c == '\0' || c == '\"'){
  879.             strcpy(sz, (lblsz) ? lblsz : prettysz(attsz));
  880.             sprintf(tmp, " (%s) %s", sz, 
  881.                            (c == '\"') ? "" : "\"\"");
  882.             sinserts((*lp == NULL) ? &lprev->text[*off]
  883.                            : &(*lp)->text[*off],
  884.                  0, tmp, j = strlen(tmp));
  885.             *off += j;
  886.             rv = 1;
  887.             level = (c == '\"') ? CMMNT : TG;
  888.             lbln = 0;            /* reset flag */
  889.         }
  890.         else if(c == '('){        /* get the size */
  891.             level = ASIZE;
  892.         }
  893.         else{
  894.             emlwrite("\007Attchmnt: Expected '(' or '\"' after %s",fn);
  895.             rv = -1;            /* bag it all */
  896.             level = TG;
  897.         }
  898.         }
  899.         break;
  900.  
  901.       case ASIZE:                /* check size */
  902.         if(c == ')'){            /* finished grabbing size */
  903.         *p = '\0';
  904.         /*
  905.          * replace sizes if they don't match!
  906.          */
  907.         strcpy(sz, tmp);
  908.         if(strcmp(sz, (lblsz) ? lblsz : prettysz(attsz))){
  909.             strcpy(sz, (lblsz) ? lblsz : prettysz(attsz));
  910.             if(p-tmp > *off){        /* where to begin replacemnt */
  911.             j = (p-tmp) - *off;
  912.             sinserts((*lp)->text, *off, "", 0);
  913.             sinserts(&lprev->text[strlen(lprev->text)-j], j, 
  914.                  sz, strlen(sz));
  915.             *off = 0;
  916.             }
  917.             else{
  918.             j = (*off) - (p-tmp);
  919.             sinserts((*lp == NULL) ? &lprev->text[j]
  920.                                : &(*lp)->text[j],
  921.                  p-tmp , sz, strlen(sz));
  922.             *off += strlen(sz) - (p-tmp);
  923.             }
  924.             rv = 1;
  925.         }
  926.  
  927.         p = tmp;
  928.         level = SWS;            /* what's next... */
  929.         }
  930.         else if(c == '\0' || c == ','){
  931.         *p = '\0';
  932.         emlwrite("\007Attchmnt: Size field missing ')': \"%s\"", tmp);
  933.         rv = -1;
  934.         level = TG;
  935.         }
  936.         else
  937.           *p++ = c;
  938.  
  939.         break;
  940.  
  941.       case SWS:                /* skip white space */
  942.         if(!isspace((unsigned char)c)){
  943.         if(c == ','){            /* no description */
  944.             level = TG;            /* munch rest of garbage */
  945.             lbln = 0;            /* reset flag */
  946.         }
  947.         else if(c != '\"' && c != '\0'){
  948.             emlwrite("\007Attchmnt: Malformed comment, quotes required", NULL);
  949.             rv = -1;
  950.             level = TG;
  951.         }
  952.         else
  953.           level = CMMNT;
  954.         }
  955.         break;
  956.  
  957.       case CMMNT:                /* slurp up comment */
  958.         if((c == '\"' && !escaped) || c == '\0'){
  959.         *p = '\0';            /* cap it off */
  960.         p = tmp;            /* reset p */
  961.         if (strlen(tmp) > cmntlen){
  962.           emlwrite("Comment too long!",NULL);
  963.         }
  964.  
  965.         strncpy(cmnt,tmp,cmntlen-1);    /* copy the comment  */
  966.         if(c == '\0'){
  967.             emlwrite("\007Attchmnt: Closing quote required at end of comment", NULL);
  968.             rv = -1;
  969.         }
  970.         level = TG;            /* prepare for next one */
  971.         lbln = 0;            /* reset flag */
  972.         }
  973.         else if(c == '\\' && !escaped){    /* something escaped? */
  974.         escaped = 1;
  975.         }
  976.         else{
  977.         if(escaped){
  978.             if(c != '\"')        /* we only quote escapes */
  979.               *p++ = '\\';
  980.  
  981.             escaped = 0;
  982.         }
  983.  
  984.         if(((*p++ = c) & 0x80) && (gmode & MDHBTIGN) && !hibit++)
  985.           emlwrite(HIBIT_WARN, NULL);
  986.         }
  987.  
  988.         break;
  989.  
  990.       case TG:                /* get comma or final EOL */
  991.         if(eod < 0) 
  992.           eod = *off;
  993.         if(!isspace((unsigned char)c)){
  994.         switch(c){
  995.           case '\0':
  996.             if(eod != *off)
  997.               lprev->text[*off = eod] = '\0';
  998.             break;
  999.           case ',':
  1000.             if(eod != *off){
  1001.             (void)bcopy(&(*lp)->text[*off], &(*lp)->text[eod],
  1002.                             strlen(&(*lp)->text[*off]) + 1);
  1003.             *off = eod;
  1004.             rv = 1;
  1005.             }
  1006.             break;
  1007.           default:
  1008.             if(rv != -1)
  1009.               emlwrite("\007Attchmnt: Comma must separate attachments", NULL);
  1010.             rv = -1;
  1011.         }
  1012.         }
  1013.         break;
  1014.  
  1015.       default:                /* something's very wrong */
  1016.         emlwrite("\007Attchmnt: Weirdness in ParseAttach", NULL);
  1017.         return(-1);                /* just give up */
  1018.     }
  1019.  
  1020.     if(c == '\0')                /* we're done */
  1021.       break;
  1022.  
  1023.     (*off)++;
  1024.  
  1025.     /*
  1026.      * not in comment or label name? done. 
  1027.      */
  1028.     if(c == ',' && (level != CMMNT && !lbln))
  1029.       break;                /* put offset past ',' */
  1030.     }
  1031.  
  1032.     return(rv);
  1033. }
  1034.  
  1035.  
  1036.  
  1037. /*
  1038.  * NewAttach - given a filename (assumed to accessible) and comment, creat
  1039.  */
  1040. PATMT *NewAttach(f, l, c)
  1041. char *f;
  1042. long l;
  1043. char *c;
  1044. {
  1045.     PATMT  *tp;
  1046.  
  1047.     if((tp=(PATMT *)malloc(sizeof(PATMT))) == NULL){
  1048.     emlwrite("No memory to add attachment", NULL);
  1049.     return(NULL);
  1050.     }
  1051.     else
  1052.       memset(tp, 0, sizeof(PATMT));
  1053.  
  1054.     /* file and size malloc */
  1055.     if((tp->filename = (char *)malloc(strlen(f)+1)) == NULL){
  1056.     emlwrite("Can't malloc name for attachment", NULL);
  1057.     free((char *) tp);
  1058.     return(NULL);
  1059.     }
  1060.     strcpy(tp->filename, f);
  1061.  
  1062.     if(l > -1){
  1063.     tp->size = (char *)malloc(sizeof(char)*(strlen(prettysz((off_t)l))+1));
  1064.     if(tp->size == NULL){
  1065.         emlwrite("Can't malloc size for attachment", NULL);
  1066.         free((char *) tp->filename);
  1067.         free((char *) tp);
  1068.         return(NULL);
  1069.     }
  1070.     else
  1071.       strcpy(tp->size, prettysz((off_t)l));
  1072.     }
  1073.  
  1074.     /* description malloc */
  1075.     if((tp->description = (char *)malloc(strlen(c)+1)) == NULL){
  1076.     emlwrite("Can't malloc description for attachment", NULL);
  1077.     free((char *) tp->size);
  1078.     free((char *) tp->filename);
  1079.     free((char *) tp);
  1080.     return(NULL);
  1081.     }
  1082.     strcpy(tp->description, c);
  1083.  
  1084.     /* callback to show user the mime type that will be used for attachment */
  1085.     if(Pmaster->mimetype  && (*Pmaster->mimetype)(f) > 0){
  1086.     int rv ;
  1087.     
  1088.     clearcursor();
  1089.     mlerase();
  1090.     rv = (*Pmaster->showmsg)('x');
  1091.     ttresize();
  1092.     picosigs();
  1093.     if(rv)            /* Did showmsg corrupt the screen? */
  1094.       PaintBody(0);        /* Yes, repaint it */
  1095.     mpresf = 1;
  1096.     }
  1097.  
  1098.     return(tp);
  1099. }
  1100.  
  1101.  
  1102.  
  1103. /*
  1104.  * AttachError - Sniff list of attachments, returning TRUE if there's
  1105.  *               any sign of trouble...
  1106.  */
  1107. int
  1108. AttachError()
  1109. {
  1110.     PATMT *ap;
  1111.  
  1112.     if(!Pmaster)
  1113.       return(0);
  1114.  
  1115.     ap = Pmaster->attachments;
  1116.     while(ap){
  1117.     if((ap->flags) & A_ERR)
  1118.       return(1);
  1119.  
  1120.     ap = ap->next;
  1121.     }
  1122.  
  1123.     return(FALSE);
  1124. }
  1125.  
  1126.  
  1127. char *
  1128. QuoteAttach(fn)
  1129. char *fn;
  1130. {
  1131.     char *p;
  1132.  
  1133.     if(*fn && strpbrk(fn, " \t,(\"")){        /* Quote it? */
  1134.     p = &fn[strlen(fn)];
  1135.     *(p+2) = '\0';
  1136.     *(p+1) = '\"';
  1137.     do
  1138.       *p = *(p-1);
  1139.     while(--p != fn);
  1140.     *p = '\"';
  1141.     }
  1142.  
  1143.     return(fn);
  1144. }
  1145.  
  1146.  
  1147.  
  1148. void ZotAttach(p)
  1149. PATMT *p;
  1150. {
  1151.     if(!p)
  1152.       return;
  1153.     if(p->description)
  1154.       free((char *)p->description);
  1155.     if(p->filename){
  1156.     if(p->flags & A_TMP)
  1157.       unlink(p->filename);
  1158.  
  1159.     free((char *)p->filename);
  1160.     }
  1161.     if(p->size)
  1162.       free((char *)p->size);
  1163.     if(p->id)
  1164.       free((char *)p->id);
  1165.     p->next = NULL;
  1166. }
  1167. #endif    /* ATTACHMENTS */
  1168.  
  1169.  
  1170. /*
  1171.  * intag - return TRUE if i is in a column that makes up an
  1172.  *         attachment line number
  1173.  */
  1174. intag(s, i)
  1175. char *s;
  1176. int   i;
  1177. {
  1178.     char *p = s;
  1179.     int n = 0;
  1180.  
  1181.     while(*p != '\0' && (p-s) < 5){        /* is there a tag? it */
  1182.     if(n && *p == '.')            /* can't be more than 4 */
  1183.       return(i <= p-s);                /* chars long! */
  1184.  
  1185.     if(*p < '0' || *p > '9')
  1186.       break;
  1187.     else
  1188.       n = (n * 10) + (*p - '0');
  1189.  
  1190.     p++;
  1191.     }
  1192.  
  1193.     return(FALSE);
  1194. }
  1195.  
  1196.  
  1197. /*
  1198.  * prettysz - return pointer to string containing nice
  1199.  */
  1200. char *prettysz(l)
  1201. off_t l;
  1202. {
  1203.     static char b[32];
  1204.  
  1205.     if(l < 1000)
  1206.       sprintf(b, "%ld  B", (long)l);        /* xxx B */
  1207.     else if(l < 10000)
  1208.       sprintf(b, "%1.1f KB", (float)l/1000);    /* x.x KB */
  1209.     else if(l < 1000000)
  1210.       sprintf(b, "%ld KB", (long)l/1000L);    /* xxx KB */
  1211.     else if(l < 10000000)
  1212.       sprintf(b, "%1.1f MB", (float)l/1000000); /* x.x MB */
  1213.     else
  1214.       sprintf(b, "%ld MB", (long)l/1000000L);    /* xxx MB */
  1215.     return(b);
  1216. }
  1217.  
  1218.  
  1219. /*
  1220.  * sinserts - s insert into another string
  1221.  */
  1222. sinserts(ds, dl, ss, sl)
  1223. char *ds, *ss;                    /* dest. and source strings */
  1224. int  dl, sl;                    /* their lengths */
  1225. {
  1226.     char *dp, *edp;                /* pointers into dest. */
  1227.     int  j;                    /* jump difference */
  1228.  
  1229.     if(sl >= dl){                /* source bigger than dest. */
  1230.     dp = ds + dl;                /* shift dest. to make room */
  1231.     if((edp = strchr(dp, '\0')) != NULL){
  1232.         j = sl - dl;
  1233.  
  1234.         for( ;edp >= dp; edp--)
  1235.           edp[j] = *edp;
  1236.  
  1237.         while(sl--)
  1238.            *ds++ = *ss++;
  1239.     }
  1240.     else
  1241.       emlwrite("\007No end of line???", NULL);    /* can this happen? */
  1242.     }
  1243.     else{                    /* dest is longer, shrink it */
  1244.     j = dl - sl;                /* difference in lengths */
  1245.  
  1246.     while(sl--)                /* copy ss onto ds */
  1247.       *ds++ = *ss++;
  1248.  
  1249.     if(strlen(ds) > j){            /* shuffle the rest left */
  1250.         do
  1251.           *ds = ds[j];
  1252.         while(*ds++ != '\0');
  1253.     }
  1254.     else
  1255.       *ds = '\0';
  1256.     }
  1257. }
  1258.