home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / pine / pico / attach.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-06  |  21.2 KB  |  873 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: attach.c,v 4.8 1993/11/08 23:08:56 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.  * Copyright 1991-1993  University of Washington
  19.  *
  20.  *  Permission to use, copy, modify, and distribute this software and its
  21.  * documentation for any purpose and without fee to the University of
  22.  * Washington is hereby granted, provided that the above copyright notice
  23.  * appears in all copies and that both the above copyright notice and this
  24.  * permission notice appear in supporting documentation, and that the name
  25.  * of the University of Washington not be used in advertising or publicity
  26.  * pertaining to distribution of the software without specific, written
  27.  * prior permission.  This software is made available "as is", and
  28.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  29.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  30.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  31.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  32.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  33.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  34.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  35.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  36.  *
  37.  * Pine and Pico are trademarks of the University of Washington.
  38.  * No commercial use of these trademarks may be made without prior
  39.  * written permission of the University of Washington.
  40.  *
  41.  *
  42.  * NOTES:
  43.  *
  44.  *
  45.  */
  46. #include <stdio.h>
  47. #include <ctype.h>
  48. #include <math.h>
  49. #include "osdep.h"
  50. #include "pico.h"
  51. #include "estruct.h"
  52. #include "edef.h"
  53. #include "efunc.h"
  54.  
  55. #ifdef    ATTACHMENTS
  56.  
  57.  
  58. #ifdef    ANSI
  59.     int    ParseAttach(struct hdr_line **,int *,char *,char *,char *,int *);
  60.     PATMT *NewAttach(char *, long, char *);
  61.     void   ZotAttach(struct pico_atmt *);
  62.     int    sinserts(char *, int, char *, int);
  63. #else
  64.     int    ParseAttach();
  65.     PATMT *NewAttach();
  66.     void   ZotAttach();
  67.     int    sinserts();
  68. #endif
  69.  
  70.  
  71. /* 
  72.  * max number of attachments
  73.  */
  74. #define    MAXATCH    64
  75.  
  76.  
  77. /*
  78.  * AskAttach - ask for attachment fields and build resulting structure
  79.  *             return pointer to that struct if OK, NULL otherwise
  80.  */
  81. AskAttach(fn, sz, cmnt)
  82. char *fn, *sz, *cmnt;
  83. {
  84.     int       i, status;
  85.     long   l = 0;
  86.     char   bfn[NFILEN];
  87.  
  88.     i = 2;
  89.     fn[0] = '\0';
  90.     sz[0] = '\0';
  91.     cmnt[0] = '\0';
  92.  
  93.     while(i){
  94.  
  95.     if(i == 2){
  96.         wkeyhelp("GC00000T0000","Get Help,Cancel,To Files");
  97.         status = mlreply("File to attach: ", fn, NLINE, QFFILE);
  98.     }
  99.     else{
  100.         wkeyhelp("GC0000000000","Get Help,Cancel");
  101.         status = mlreply("Attachment comment: ", cmnt, NLINE, QNORML);
  102.     }
  103.  
  104.     switch(status){
  105.       case HELPCH:
  106.         if(i == 2)
  107.           emlwrite("No Attachment file help yet!", NULL);
  108.         else
  109.           emlwrite("No Attachment comment help yet!", NULL);
  110.  
  111. /* remove break and sleep when help text done to force redraw */     
  112.         sleep(3);
  113.         break;
  114.  
  115.       case (CTRL|'T'):
  116.         if(i != 2){
  117.         (*term.t_beep)();
  118.         break;
  119.         }
  120.  
  121.         *bfn = '\0';
  122.         if(*fn == '\0' || !isdir(fn, NULL))
  123.           strcpy(fn, gethomedir(NULL));
  124.  
  125.         if(FileBrowse(fn, bfn, sz) == 1){
  126.         strcat(fn, S_FILESEP);
  127.         strcat(fn, bfn);
  128.         i--;
  129.         }
  130.         else
  131.           *fn = '\0';
  132.  
  133.         refresh(FALSE, 1);
  134.         update();
  135.         break;
  136.  
  137.       case (CTRL|'L'):
  138.         refresh(FALSE, 1);
  139.         update();
  140.         continue;
  141.  
  142.       case ABORT:
  143.         emlwrite("\007Cancelled", NULL);
  144.         return(0);
  145.       case TRUE:                /* some comment */
  146.       case FALSE:                /* No comment */
  147.         if(i-- == 2){
  148.         fixpath(fn, NLINE);        /* names relative to ~ */
  149.         if((gmode&MDSCUR) && homeless(fn)){
  150.             emlwrite("\007Restricted mode allows attachments from home directory only", NULL);
  151.             return(0);
  152.         }
  153.  
  154.         if((status=fexist(fn, "r", &l)) != FIOSUC){ /* does file exist? */
  155.             fioperr(status, fn);
  156.             return(0);
  157.         }
  158.         strcpy(sz, prettysz(l));
  159.         }
  160.         else{
  161.         mlerase();
  162.         return(1);            /* mission accomplished! */
  163.         }
  164.  
  165.         break;
  166.       default:
  167.         break;
  168.     }
  169.     }
  170. }
  171.  
  172.  
  173.  
  174. /*
  175.  * SyncAttach - given a pointer to a linked list of attachment structures,
  176.  *              return with that structure sync'd with what's displayed.
  177.  *              delete any attachments in list of structs that's not on 
  178.  *              the display, and add any that aren't in list but on display.
  179.  */
  180. SyncAttach()
  181. {
  182.     int offset = 0,                /* the offset to begin       */
  183.         rv = 0,
  184.         ki = 0,                    /* number of known attmnts   */
  185.         bi = 0,                    /* build array index         */
  186.         na,                    /* old number of attachmnt   */
  187.         status, i, j, n;
  188.     char file[NLINE],                /* buffers to hold it all    */
  189.          size[32],
  190.          comment[1024];
  191.     struct hdr_line *lp;            /* current line in header    */
  192.     PATMT *tp, *knwn[MAXATCH], *bld[MAXATCH];
  193.     extern struct headerentry headents[];
  194.  
  195.     if(Pmaster == NULL)
  196.       return(-1);
  197.  
  198.     for(i=0;i<MAXATCH;i++)            /* bug - ever pop this? */
  199.     knwn[i] = bld[i] = NULL;        /* zero out table */
  200.     
  201.     tp = Pmaster->attachments;
  202.     while(tp != NULL){                /* fill table of     */
  203.     knwn[ki++] = tp;            /* known attachments */
  204.     tp = tp->next;
  205.     }
  206.  
  207.     n = 0;
  208.     lp = headents[ATTCHDR].hd_text;
  209.     while(lp != NULL){
  210.     na = ++n;
  211.  
  212.     if(status = ParseAttach(&lp, &offset, file, size, comment, &na))
  213.         rv = (rv < 0) ? rv : status ;    /* remember worst case */
  214.  
  215.     if(*file == '\0'){
  216.         if(n != na && na > 0 && na <= ki && (knwn[na-1]->flags&A_FLIT)){
  217.         bld[bi++] = knwn[na-1];
  218.         knwn[na-1] = NULL;
  219.         }
  220.         continue;
  221.     }
  222.  
  223.     if((gmode&MDSCUR) && homeless(file))
  224.       /* no attachments outsize ~ in secure mode! */
  225.       continue;
  226.  
  227.     tp = NULL;
  228.     for(i=0;i < ki; i++){            /* already know about it? */
  229.         /*
  230.          * this is kind of gruesome. what we want to do is keep track
  231.          * of literal attachment entries because they may not be 
  232.          * actual files we can access or that the user readily 
  233.          * access
  234.          */
  235.         if(knwn[i] && 
  236.            ((!(knwn[i]->flags&A_FLIT) && !strcmp(file, knwn[i]->filename))
  237.            || ((knwn[i]->flags&A_FLIT) && i+1 == na))){
  238.         tp = knwn[i];
  239.         knwn[i] = NULL;            /* forget we know about it */
  240.  
  241.         if(status == -1)        /* ignore garbage! */
  242.           break;
  243.  
  244.         if((tp->flags&A_FLIT) && strcmp(file, tp->filename)){
  245.             rv = 1;
  246.             if((j=strlen(file)) > strlen(tp->filename)){
  247.             if((tp->filename = (char *)realloc(tp->filename,
  248.                             sizeof(char)*(j+1))) == NULL){
  249.                 emlwrite("\007Can't realloc filename space",NULL);
  250.                 return(-1);
  251.             }
  252.             }
  253.  
  254.             strcpy(tp->filename, file);
  255.         }
  256.         else if(tp->size && strcmp(tp->size, size)){
  257.             rv = 1;
  258.             if((j=strlen(size)) > strlen(tp->size)){
  259.             if((tp->size=(char *)realloc(tp->size,
  260.                             sizeof(char)*(j+1))) == NULL){
  261.                 emlwrite("\007Can't realloc space for size", NULL);
  262.                 return(-1);
  263.             }
  264.             }
  265.  
  266.             strcpy(tp->size, size);
  267.         }
  268.  
  269.         if(strcmp(tp->description, comment)){    /* new comment */
  270.             rv = 1;
  271.             if((j=strlen(comment)) > strlen(tp->description)){
  272.             if((tp->description=(char *)realloc(tp->description,
  273.                         sizeof(char)*(j+1))) == NULL){
  274.                 emlwrite("\007Can't realloc description", NULL);
  275.                 return(-1);
  276.             }
  277.             }
  278.               
  279.             strcpy(tp->description, comment);
  280.         }
  281.         break;
  282.         }
  283.     }
  284.  
  285.     if(tp){
  286.         bld[bi++] = tp;
  287.     }
  288.     else{
  289.         if(file[0] != '['){
  290.         if((tp = NewAttach(file, atol(size), comment)) == NULL)
  291.           return(-1);
  292.         bld[bi++] = tp;
  293.         }
  294.     }
  295.  
  296.     if(status < 0)
  297.       tp->flags |= A_ERR;        /* turn ON error bit */
  298.     else
  299.       tp->flags &= ~(A_ERR);    /* turn OFF error bit */
  300.     }
  301.  
  302.     for(i=0; i < bi; i++)        /* link together newly built list */
  303.     bld[i]->next = bld[i+1];
  304.  
  305.     Pmaster->attachments = bld[0];
  306.  
  307.     for(i=0; i < ki; i++){        /* kill old/unused references */
  308.  
  309.     if(knwn[i]){
  310.         ZotAttach(knwn[i]);
  311.         free((char *) knwn[i]);
  312.     }
  313.     }
  314.  
  315.     return(rv);
  316. }
  317.  
  318.  
  319.  
  320.  
  321. /*
  322.  * ParseAttach - given a header line and an offset into it, return with 
  323.  *         the three given fields filled in.  Assumes the size of 
  324.  *         the buffers passed is large enough to hold what's there.
  325.  *         Always updates header fields that have changed or are 
  326.  *         fixed.  An error advances offset to next attachment.
  327.  *
  328.  *        returns: 1 if a field changed
  329.  *                       0 nothing changed
  330.  *                      -1 on error
  331.  */
  332. ParseAttach(lp, off, fn, sz, cmnt, no)
  333. struct hdr_line **lp;                /* current header line      */
  334. int  *off;                    /* offset into that line    */
  335. char *fn, *sz, *cmnt;                /* places to return fields  */
  336. int  *no;                    /* attachment number        */
  337. {
  338.     int  j, status,                /* various return codes     */
  339.          rv = 0,                /* return value             */
  340.          lbln  = 0;                /* label'd attachment        */
  341.     long l;                    /* attachment length        */
  342.     char c,
  343.         *p = s,
  344.         *lblsz = NULL,                /* label'd attchmnt's size  */
  345.          number[8];
  346.     register struct hdr_line  *lprev;
  347.     enum {                    /* parse levels             */
  348.     LWS,                    /* leading white space      */
  349.     NUMB,                    /* attachment number        */
  350.     WSN,                    /* white space after number */
  351.     TAG,                    /* attachments tag (fname)  */
  352.     WST,                    /* white space after tag    */
  353.     SIZE,                    /* attachments size         */
  354.     SWS,                    /* white space after size   */
  355.     COMMENT,                /* attachment comment       */
  356.     TG} level;                /* trailing garbage         */
  357.  
  358.     *fn = *sz = *cmnt = '\0';            /* initialize return strings */
  359.  
  360.     level = LWS;                /* start at beginning */
  361.     while(*lp != NULL){
  362.  
  363.     if((c=(*lp)->text[*off]) == '\0'){    /* end of display line */
  364.         lprev = *lp;
  365.         if((*lp = (*lp)->next) != NULL)
  366.           c = (*lp)->text[*off = 0];    /* reset offset */
  367.     }
  368.  
  369.     switch(level){
  370.       case LWS:                /* skip leading white space */
  371.         if(isspace(c) || c == '\0'){
  372.         break;
  373.         }
  374.         else if(c < '0' || c > '9'){    /* add a number */
  375.         sprintf(number, "%d. ", *no);
  376.         *no = 0;            /* no previous number! */
  377.         sinserts((*lp == NULL) ? &lprev->text[*off]
  378.                            : &(*lp)->text[*off],
  379.                  0, number, j=strlen(number));
  380.         *off += j - 1;
  381.         rv = 1;
  382.         if(c == ',')            /* special case */
  383.           (*off)++;
  384.         else
  385.           level = TAG;            /* interpret the name */
  386.         break;
  387.         }
  388.         level = NUMB;
  389.       case NUMB:                /* attachment number */
  390.         if(c == '.'){            /* finished grabbing size */
  391.         /*
  392.          * replace number if it's not right
  393.          */
  394.         *p = '\0';
  395.         sprintf(number, "%d", *no);    /* record the current...  */
  396.         *no = atoi(s);            /* and the old place in list */
  397.         if(strcmp(number, s)){
  398.             if(p-s > *off){        /* where to begin replacemnt */
  399.             j = (p-s) - *off;
  400.             sinserts((*lp)->text, *off, "", 0);
  401.             sinserts(&lprev->text[strlen(lprev->text)-j], j, 
  402.                  number, strlen(number));
  403.             *off = 0;
  404.             }
  405.             else{
  406.             j = (*off) - (p-s);
  407.             sinserts((*lp == NULL) ? &lprev->text[j] 
  408.                                : &(*lp)->text[j], 
  409.                  p-s , number, strlen(number));
  410.             *off += strlen(number) - (p-s);
  411.             }
  412.             rv = 1;
  413.         }
  414.         p = s;
  415.         level = WSN;            /* what's next... */
  416.         }
  417.         else if(c < '0' || c > '9'){
  418.         *p = '\0';
  419.         emlwrite("\007Attchmnt: Number field missing '.': \"%s\"", s);
  420.         rv = -1;
  421.         }
  422.         else
  423.           *p++ = c;
  424.  
  425.         break;
  426.  
  427.       case WSN:                /* blast whitespace */
  428.         if(isspace(c) || c == '\0'){
  429.         break;
  430.         }
  431.         else if(c == '['){            /* labeled attachment */
  432.         lbln++;
  433.         }
  434.         else if(!fallowc(c)){
  435.         emlwrite("\007Attchmnt: '%c' not allowed in file name", 
  436.               (void *)(int)c);
  437.         rv = -1;
  438.         level = TG;            /* eat rest of garbage */
  439.         break;
  440.         }
  441.         level = TAG;
  442.       case TAG:                /* get and check filename */
  443.                         /* or labeled attachment  */
  444.                         /* enclosed in []         */
  445.         if(c == '\0' || (!lbln && (isspace(c) || strchr(",(\"", c)))
  446.            || (lbln && c == ']')){
  447.         if(p != s){
  448.             *p = '\0';            /* got something */
  449.  
  450.             strcpy(fn, s);        /* store file name */
  451.             if(!lbln){            /* normal file attachment */
  452.             fixpath(fn, NLINE);
  453.             if((status=fexist(fn, "r", &l)) != FIOSUC){
  454.                 fioperr(status, fn);
  455.                 rv = -1;
  456.                 level = TG;        /* munch rest of garbage */
  457.                 break;
  458.             }
  459.  
  460.  
  461.             if((gmode&MDSCUR) && homeless(fn)){
  462.                 emlwrite("\007Restricted mode allows attachments from home directory only", NULL);
  463.                 rv = -1;
  464.                 level = TG;
  465.                 break;
  466.             }
  467.  
  468.             if(strcmp(fn, s)){    /* fn changed: display it */
  469.                 if(*off >=  p - s){    /* room for it? */
  470.                 sinserts((*lp == NULL)? 
  471.                      &lprev->text[(*off)-(p-s)] :
  472.                      &(*lp)->text[(*off)-(p-s)],
  473.                      p-s, fn, j=strlen(fn));
  474.                 *off += j - (p - s);    /* advance offset */
  475.                 rv = 1;
  476.                 }
  477.                 else{
  478.                 emlwrite("\007Attchmnt: Problem displaying real file path", NULL);
  479.                 }
  480.             }
  481.             }
  482.             else{            /* labelled attachment! */
  483.             /*
  484.              * should explain about labelled attachments:
  485.              * these are attachments that came into the composer
  486.              * with meaningless file names (up to caller of 
  487.              * composer to decide), for example, attachments
  488.              * being forwarded from another message.  here, we
  489.              * just make sure the size stays what was passed
  490.              * to us.  The user is SOL if they change the label
  491.              * since, as it is now, after changed, it will
  492.              * just get dropped from the list of what gets 
  493.              * passed back to the caller.
  494.              */
  495.             PATMT *tp;
  496.  
  497.             if(c != ']'){        /* legit label? */
  498.                 emlwrite("\007Attchmnt: Expected ']' after \"%s\"",
  499.                      fn);
  500.                 rv = -1;
  501.                 level = TG;
  502.                 break;
  503.             }
  504.             strcat(fn, "]");
  505.  
  506.             /*
  507.              * This is kind of cheating since otherwise
  508.              * ParseAttach doesn't know about the attachment
  509.              * struct.  OK if filename's not found as it will
  510.              * get taken care of later...
  511.              */
  512.             tp = Pmaster->attachments; /* caller check Pmaster! */
  513.             j = 0;
  514.             while(tp != NULL){
  515.                 if(++j == *no){
  516.                 lblsz = tp->size;
  517.                 break;
  518.                 }
  519.                 tp = tp->next;
  520.             }
  521.  
  522.             if(tp == NULL){
  523.                 emlwrite("\007Attchmnt: Unknown reference: %s",fn);
  524.                 lblsz =  "XXX";
  525.             }
  526.             }
  527.             p = s;            /* reset p in s */
  528.             level = WST;
  529.         }
  530.  
  531.         if(!lbln && c == '(')        /* no space 'tween file, size*/
  532.           level = SIZE;
  533.         else if(c == '\0' || (!lbln && (c == ',' || c == '\"'))){
  534.             strcpy(sz, (lblsz) ? lblsz : prettysz(l));
  535.             sprintf(s, " (%s) %s", sz, (c == '\"') ? "" : "\"\"");
  536.             sinserts((*lp == NULL) ? &lprev->text[*off] 
  537.                                : &(*lp)->text[*off],
  538.                  0, s, j = strlen(s));
  539.             *off += j;
  540.             rv = 1;
  541.             level = (c == '\"') ? COMMENT : TG;/* cmnt or eat trash */
  542.         }
  543.         }
  544.         else if(!lbln && (!fallowc(c) || c == '[' || c == ']')){
  545.         emlwrite("\007Attchmnt: '%c' not allowed in file name",
  546.               (void *)(int)c);
  547.         rv = -1;            /* bad char in file name */
  548.         level = TG;            /* gobble garbage */
  549.         }
  550.         else
  551.           *p++ = c;                /* add char to name */
  552.  
  553.         break;
  554.  
  555.       case WST:                /* skip white space */
  556.         if(!isspace(c)){
  557.         /*
  558.          * whole attachment, comment or done! 
  559.          */
  560.         if(c == ',' || c == '\0' || c == '\"'){
  561.             strcpy(sz, (lblsz) ? lblsz : prettysz(l));
  562.             sprintf(s, " (%s) %s", sz, 
  563.                            (c == '\"') ? "" : "\"\"");
  564.             sinserts((*lp == NULL) ? &lprev->text[*off]
  565.                            : &(*lp)->text[*off],
  566.                  0, s, j = strlen(s));
  567.             *off += j;
  568.             rv = 1;
  569.             level = (c == '\"') ? COMMENT : TG;
  570.             lbln = 0;            /* reset flag */
  571.         }
  572.         else if(c == '('){        /* get the size */
  573.             level = SIZE;
  574.         }
  575.         else{
  576.             emlwrite("\007Attchmnt: Expected '(' or '\"' after %s",fn);
  577.             rv = -1;            /* bag it all */
  578.             level = TG;
  579.         }
  580.         }
  581.         break;
  582.  
  583.       case SIZE:                /* check size */
  584.         if(c == ')'){            /* finished grabbing size */
  585.         *p = '\0';
  586.         /*
  587.          * replace sizes if they don't match!
  588.          */
  589.         strcpy(sz, s);
  590.         if(strcmp(sz, (lblsz) ? lblsz : prettysz(l))){
  591.             strcpy(sz, (lblsz) ? lblsz : prettysz(l));
  592.             if(p-s > *off){        /* where to begin replacemnt */
  593.             j = (p-s) - *off;
  594.             sinserts((*lp)->text, *off, "", 0);
  595.             sinserts(&lprev->text[strlen(lprev->text)-j], j, 
  596.                  sz, strlen(sz));
  597.             *off = 0;
  598.             }
  599.             else{
  600.             j = (*off) - (p-s);
  601.             sinserts((*lp == NULL) ? &lprev->text[j] 
  602.                                : &(*lp)->text[j], 
  603.                  p-s , sz, strlen(sz));
  604.             *off += strlen(sz) - (p-s);
  605.             }
  606.             rv = 1;
  607.         }
  608.         p = s;
  609.         level = SWS;            /* what's next... */
  610.         }
  611.         else if(c == '\0' || c == ','){
  612.         *p = '\0';
  613.         emlwrite("\007Attchmnt: Size field missing ')': \"%s\"", s);
  614.         rv = -1;
  615.         level = TG;
  616.         }
  617.         else
  618.           *p++ = c;
  619.  
  620.         break;
  621.  
  622.       case SWS:                /* skip white space */
  623.         if(!isspace(c)){
  624.         if(c == ','){            /* no description */
  625.             level = TG;            /* munch rest of garbage */
  626.             lbln = 0;            /* reset flag */
  627.         }
  628.         else if(c != '\"' && c != '\0'){
  629.             emlwrite("\007Attchmnt: Malformed comment, quotes required", NULL);
  630.             rv = -1;
  631.             level = TG;
  632.         }
  633.         else
  634.           level = COMMENT;
  635.         }
  636.         break;
  637.  
  638.       case COMMENT:                /* slurp up comment */
  639.         if(c == '\"' || c == '\0'){        /* got comment */
  640.         *p = '\0';            /* cap it off */
  641.         p = s;                /* reset p */
  642.         strcpy(cmnt,s);            /* copy the comment  */
  643.         if(c == '\0'){
  644.             emlwrite("\007Attchmnt: Closing quote required at end of comment", NULL);
  645.             rv = -1;
  646.         }
  647.         level = TG;            /* prepare for next one */
  648.         lbln = 0;            /* reset flag */
  649.         }
  650.         else
  651.           *p++ = c;
  652.  
  653.         break;
  654.  
  655.       case TG:                /* get comma or final EOL */
  656.         if(!isspace(c)){
  657.         if(c != ',' && c != '\0'){
  658.             if(rv != -1)
  659.               emlwrite("\007Attchmnt: Comma must separate attachments", NULL);
  660.             rv = -1;
  661.         }
  662.         }
  663.         break;
  664.  
  665.       default:                /* something's very wrong */
  666.         emlwrite("\007Attchmnt: Weirdness in ParseAttach", NULL);
  667.         return(-1);                /* just give up */
  668.     }
  669.  
  670.     if(c == '\0')                /* we're done */
  671.       break;
  672.  
  673.     (*off)++;
  674.  
  675.     /*
  676.      * not in comment or label name? done. 
  677.      */
  678.     if(c == ',' && (level != COMMENT && !lbln))
  679.       break;                /* put offset past ',' */
  680.     }
  681.  
  682.     return(rv);
  683. }
  684.  
  685.  
  686.  
  687. /*
  688.  * NewAttach - given a filename (assumed to accessible) and comment, creat
  689.  */
  690. PATMT *NewAttach(f, l, c)
  691. char *f;
  692. long l;
  693. char *c;
  694. {
  695.     PATMT  *tp;
  696.  
  697.     if((tp=(PATMT *)malloc(sizeof(PATMT))) == NULL){
  698.     emlwrite("No memory to add attachment", NULL);
  699.     return(NULL);
  700.     }
  701.     else{
  702.     tp->filename = tp->description = NULL;
  703.     tp->size = tp->id = NULL;
  704.     tp->next = NULL;
  705.     }
  706.  
  707.     /* file and size malloc */
  708.     if((tp->filename = (char *)malloc(strlen(f)+1)) == NULL){
  709.     emlwrite("Can't malloc name for attachment", NULL);
  710.     free((char *) tp);
  711.     return(NULL);
  712.     }
  713.     strcpy(tp->filename, f);
  714.  
  715.     if(l > -1){
  716.     strcpy(s, prettysz(l));
  717.     if((tp->size = (char *)malloc(sizeof(char)*(strlen(s)+1))) == NULL){
  718.         emlwrite("Can't malloc size for attachment", NULL);
  719.         free((char *) tp->filename);
  720.         free((char *) tp);
  721.         return(NULL);
  722.     }
  723.     else
  724.       strcpy(tp->size, s);
  725.     }
  726.  
  727.     /* description malloc */
  728.     if((tp->description = (char *)malloc(strlen(c)+1)) == NULL){
  729.     emlwrite("Can't malloc description for attachment", NULL);
  730.     free((char *) tp->size);
  731.     free((char *) tp->filename);
  732.     free((char *) tp);
  733.     return(NULL);
  734.     }
  735.     strcpy(tp->description, c);
  736.  
  737.     return(tp);
  738. }
  739.  
  740.  
  741.  
  742. /*
  743.  * AttachError - Sniff list of attachments, returning TRUE if there's
  744.  *               any sign of trouble...
  745.  */
  746. int
  747. AttachError()
  748. {
  749.     PATMT *ap;
  750.  
  751.     if(!Pmaster)
  752.       return(0);
  753.  
  754.     ap = Pmaster->attachments;
  755.     while(ap){
  756.     if((ap->flags) & A_ERR)
  757.       return(1);
  758.  
  759.     ap = ap->next;
  760.     }
  761.  
  762.     return(FALSE);
  763. }
  764.  
  765.  
  766.  
  767. void ZotAttach(p)
  768. PATMT *p;
  769. {
  770.     if(!p)
  771.       return;
  772.     if(p->description)
  773.       free((char *)p->description);
  774.     if(p->filename)
  775.       free((char *)p->filename);
  776.     if(p->size)
  777.       free((char *)p->size);
  778.     if(p->id)
  779.       free((char *)p->id);
  780.     p->next = NULL;
  781. }
  782. #endif    /* ATTACHMENTS */
  783.  
  784.  
  785. /*
  786.  * intag - return TRUE if i is in a column that makes up an
  787.  *         attachment line number
  788.  */
  789. intag(s, i)
  790. char *s;
  791. int   i;
  792. {
  793.     char *p = s;
  794.     int n = 0;
  795.  
  796.     while(*p != '\0' && (p-s) < 5){        /* is there a tag? it */
  797.     if(n && *p == '.')            /* can't be more than 4 */
  798.       return(i <= p-s);                /* chars long! */
  799.  
  800.     if(*p < '0' || *p > '9')
  801.       break;
  802.     else
  803.       n = (n * 10) + (*p - '0');
  804.  
  805.     p++;
  806.     }
  807.  
  808.     return(FALSE);
  809. }
  810.  
  811.  
  812. /*
  813.  * prettysz - return pointer to string containing nice
  814.  */
  815. char *prettysz(l)
  816. long l;
  817. {
  818.     static char b[32];
  819.  
  820.     if(l < 1000)
  821.       sprintf(b, "%d  B", l);            /* xxx B */
  822.     else if(l < 10000)
  823.       sprintf(b, "%1.1f KB", (float)l/1000);    /* x.x KB */
  824.     else if(l < 1000000)
  825.       sprintf(b, "%d KB", l/1000);        /* xxx KB */
  826.     else if(l < 10000000)
  827.       sprintf(b, "%1.1f MB", (float)l/1000000); /* x.x MB */
  828.     else
  829.       sprintf(b, "%d MB", l/1000000);        /* xxx MB */
  830.     return(b);
  831. }
  832.  
  833.  
  834. /*
  835.  * sinserts - s insert into another string
  836.  */
  837. sinserts(ds, dl, ss, sl)
  838. char *ds, *ss;                    /* dest. and source strings */
  839. int  dl, sl;                    /* their lengths */
  840. {
  841.     char *dp, *edp;                /* pointers into dest. */
  842.     int  j;                    /* jump difference */
  843.  
  844.     if(sl >= dl){                /* source bigger than dest. */
  845.     dp = ds + dl;                /* shift dest. to make room */
  846.     if((edp = strchr(dp, '\0')) != NULL){
  847.         j = sl - dl;
  848.  
  849.         for( ;edp >= dp; edp--)
  850.           edp[j] = *edp;
  851.  
  852.         while(sl--)
  853.            *ds++ = *ss++;
  854.     }
  855.     else
  856.       emlwrite("\007No end of line???", NULL);    /* can this happen? */
  857.     }
  858.     else{                    /* dest is longer, shrink it */
  859.     j = dl - sl;                /* difference in lengths */
  860.  
  861.     while(sl--)                /* copy ss onto ds */
  862.       *ds++ = *ss++;
  863.  
  864.     if(strlen(ds) > j){            /* shuffle the rest left */
  865.         do
  866.           *ds = ds[j];
  867.         while(*ds++ != '\0');
  868.     }
  869.     else
  870.       *ds = '\0';
  871.     }
  872. }
  873.