/*----------------------------------------------------------------------

            T H E    P I N E    M A I L   S Y S T E M

   Laurence Lundblade and Mike Seibel
   Networks and Distributed Computing
   Computing and Communications
   University of Washington
   Administration Building, AG-44
   Seattle, Washington, 98195, USA
   Internet: lgl@CAC.Washington.EDU
             mikes@CAC.Washington.EDU

   Please address all bugs and comments to "pine-bugs@cac.washington.edu"

   Copyright 1989, 1990, 1991, 1992  University of Washington

    Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee to the University of
   Washington is hereby granted, provided that the above copyright notice
   appears in all copies and that both the above copyright notice and this
   permission notice appear in supporting documentation, and that the name
   of the University of Washington not be used in advertising or publicity
   pertaining to distribution of the software without specific, written
   prior permission.  This software is made available "as is", and
   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
   WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
   NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
   INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
   LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
   (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
   WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  

   Pine is in part based on The Elm Mail System:
    ***********************************************************************
    *  The Elm Mail System  -  $Revision: 2.13 $   $State: Exp $          *
    *                                                                     *
    * 			Copyright (c) 1986, 1987 Dave Taylor              *
    * 			Copyright (c) 1988, 1989 USENET Community Trust   *
    ***********************************************************************
 

  ----------------------------------------------------------------------*/

/*======================================================================
    strings.c
    Misc extra and useful string functions
      - rplstr         replace a substring with another string
      - sqzspaces      Squeeze out the extra blanks in a string
      - remove_trailing_white_space 
      - remove_leading_white_space 
      - strucmp        A case insensitive strcmp
      - struncmp       A case insensitve strncmp
      - srchstr        Search a string for a sub string
      - strindex       Replacement for strchr/index
      - strrindex      Replacement for strrchr/rindex
      - strcat_nocr    Concatenate one string to another i
      - crlf2lf        Convert carriage return line feeds to line feeds
      - month_abbrev   Return three letter abbreviations for months
      - month_num      Calculate month number from month/year string
      - cannon_date    Formalize format of a some what formatted date
      - pretty_command Return nice string describing character
      - blanks         Returns a string n blanks long
      - temp_file_name Returns a fairly uniq name for temp files
      - comatose       Format number with nice commas
      - byte_string    Format number of bytes with Kb, Mb, Gb or bytes
      - enth-string    Format number i.e. 1: 1st, 983: 983rd....

 ====*/

#include "headers.h"


/*----------------------------------------------------------------------
       Replace n characters in one string with another given string

   args: os -- the output string
         dl -- the number of character to delete from start of os
         is -- The string to insert
  
 Result: returns pointer in originl string to end of string just inserted
         First 
  ---*/
char *
rplstr(os,dl,is)
char *os,*is;
int dl;
{   
    register char *x1,*x2,*x3;
    int           diff;

    if(os == NULL)
        return(NULL);
       
    for(x1 = os; *x1; x1++);
    if(dl > x1 - os)
        dl = x1 - os;
        
    x2 = is;      
    if(is != NULL){
        while(*x2++);
        x2--;
    }

    if((diff = (x2 - is) - dl) < 0){
        x3 = os; /* String shrinks */
        if(is != NULL)
            for(x2 = is; *x2; *x3++ = *x2++); /* copy new string in */
        for(x2 = x3 - diff; *x2; *x3++ = *x2++); /* shift for delete */
        *x3 = *x2;
    } else {                
        /* String grows */
        for(x3 = x1 + diff; x3 >= os + (x2 - is); *x3-- = *x1--); /* shift*/
        for(x1 = os, x2 = is; *x2 ; *x1++ = *x2++);
        while(*x3) x3++;                 
    }
    return(x3);
}



/*----------------------------------------------------------------------
     Squeeze out blanks 
  ----------------------------------------------------------------------*/
void
sqzspaces(string)
     char *string;
{
    register char *a, *b;

    for(a = string, b = string; *a ; *b++ = *a++) {
        while(*a && isspace(*a))
          a++;
    }
    *b = '\0';
}



/*----------------------------------------------------------------------  
       Remove trailing white space from a string in place
  
  Args: string -- string to remove space from
  ----*/
void
removing_leading_white_space(string)
     char *string;
{
    register char *p, *q;
    for(p = string; *p && isspace(*p); p++);
    if(p == string)
      return;
    for(q = string; *p; *q++ = *p++);
    *q = '\0';
}



/*----------------------------------------------------------------------  
       Remove trailing white space from a string in place
  
  Args: string -- string to remove space from
  ----*/
void
removing_trailing_white_space(string)
     char *string;
{
    register char *p;
    for(p = string + strlen(string) - 1; p > string && isspace(*p); p--);
    if(p != string)
      *++p = '\0';
}



/*--------------------------------------------------
     A case insensitive strcmp()     
  
   Args: o, r -- The two strings to compare

 Result: integer indicating which is greater
  ---*/
strucmp(o, r)
     register char *r, *o;
{
    if(r == NULL && o == NULL)
      return(0);
    if(o == NULL)
      return(1);
    if(r == NULL)
      return(-1);

    while(*o && *r && (isupper(*o) ? tolower(*o) : *o) ==
	              (isupper(*r) ? tolower(*r) : *r)) {
	o++, r++;
    }
    return((isupper(*o) ? tolower(*o): *o)-(isupper(*r) ? tolower(*r) : *r));
}



/*----------------------------------------------------------------------
     A case insensitive strncmp()     
  
   Args: o, r -- The two strings to compare
         n    -- length to stop comparing strings at

 Result: integer indicating which is greater
   
  ----*/
struncmp(o, r, n)
     register char *r, *o;
     register int   n;
{
    if(r == NULL && o == NULL)
      return(0);
    if(o == NULL)
      return(1);
    if(r == NULL)
      return(-1);

    n--;
    while(n && *o && *r &&
          (isupper(*o)? tolower(*o): *o) == (isupper(*r)? tolower(*r): *r)) {
	o++; r++; n--;
    }
    return((isupper(*o)? tolower(*o): *o) - (isupper(*r)? tolower(*r): *r));
}



/*----------------------------------------------------------------------
        Search one string for another

   Args:  is -- The string to search in, the larger string
          ss -- The string to search for, the smaller string

   Search for any occurance of ss in the is, and return a pointer
   into the string is when it is found. The search is case indepedent.
  ----*/

char *	    
srchstr(is,ss)
register char *is,*ss;
{                    
    register char *sx,*sy, *ss_store;
    char          *ss_store_nr, *rv;
    char          temp[251];
    
    if(is == NULL || ss == NULL)
        return(NULL);

    if(strlen(ss) > sizeof(temp) - 2)
      ss_store = fs_get(strlen(ss) + 1);
    else
      ss_store = temp;
    for(sx = ss, sy = ss_store; *sx != '\0' ; sx++, sy++)
      *sy = isupper(*sx) ? tolower(*sx) : *sx;
    *sy = *sx;

    rv = NULL;
    while(*is != '\0'){
        for(sx = is, sy = ss_store; ((*sx == *sy) ||
                  ((isupper(*sx) ? tolower(*sx): *sx) == *sy)) && *sy;
	                                                        sx++, sy++);
        if(!*sy) {
            rv = is;
            break;
        }
        is++;
    }
    if(ss_store != temp) {
        ss_store_nr = ss_store;
        fs_give((void **)&ss_store_nr);
    }
    return(rv);
}



/*----------------------------------------------------------------------
    A replacement for strchr or index ...

 ....so we don't have to worry if it's there or not. We bring our own.
If we really care about efficiency and think the local one is more
efficient the local one can be used, but most of the things that take
a long time are in the c-client and not in pine.
 ----*/
char *
strindex(buffer, ch)
    char *buffer;
    int ch;
{
	/** Returns a pointer to the first occurance of the character
	    'ch' in the specified string or NULL if it doesn't occur **/

        do
	  if(*buffer == ch)
	    return(buffer);
	while(*buffer++ != '\0');

	return(NULL);
}

char *
strrindex(buffer, ch)
    char *buffer;
    int   ch;
{
	/** Returns a pointer to the last occurance of the character
	    'ch' in the specified string or NULL if it doesn't occur **/

	char *address = NULL;

	do
	  if(*buffer == ch)
	    address = buffer;
	while (*buffer++ != '\0');

	return(address);
}



/*----------------------------------------------------------------------
       Concatenate strings, not copying carriage returns

  Input: s1 -- string to add on to the end of
         s2 -- string to add 

  Result: strings are concatenated and resulting string is returned
  -----*/
char *           
strcat_nocr(s1, s2)
     char *s1, *s2;
{
    char *start = s1;
    /*-- get to end of s1 --*/
    while(*s1) s1++;

    while(*s2)
      if(*s2 != '\r')
	*s1++ = *s2++;
      else
	s2++;

    *s1 = *s2;

    return(start);
}



/*----------------------------------------------------------------------
  Turn a string with carriage return/line feeds in it into one with out.
  It's done in place because the string will shrink

  Args: string to operate on
  ----*/     
void
crlf2lf(string)
     char *string;
{
    register char *p,*q;

    for(p = string, q = string; *p ; *q++ = *p++)
	if(*p == '\r' && *(p+1) == '\n')
	  p++;
    *q = '\0';
}


    
char *xdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL};

char *
month_abbrev(month_num)
     int month_num;
{
    static char *xmonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
    if(month_num < 1 || month_num > 12)
      return("xxx");
    return(xmonths[month_num - 1]);
}

char *
week_abbrev(week_day)
     int week_day;
{
    return(xdays[week_day]);
}


days_in_month(month, year)
     int month, year;
{
    static int d_i_m[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
    if(month == 2) 
      return(((year%4) == 0 && (year%100) != 0) ? 29 : 28);
    else
      return(d_i_m[month]);
}
    


/*----------------------------------------------------------------------
      Return month number of month named in string
  
   Args: s -- string with 3 letter month abbreviation of form mmm-yyyy
 
 Result: Returns month number with January, year 1900, 2000... being 0;
         -1 if no month/year is matched
 ----*/
int
month_num(s)
     char *s;
{
    int month, year;
    int i;

    for(i = 0; i < 12; i++){
        if(struncmp(month_abbrev(i+1), s, 3) == 0)
          break;
    }
    if(i == 12)
      return(-1);

    year = atoi(s + 4);
    if(year == 0)
      return(-1);

    month = (year < 100 ? year + 1900 : year)  * 12 + i;
    return(month);
}

/*----------------------------------------------------------------------

  ----*/
void
parse_date(given_date, d)
     char        *given_date;
     struct date *d;
{
    char *p, **i, *q;
    int   month;

    d->sec   = -1;
    d->minute= -1;
    d->hour  = -1;
    d->day   = -1;
    d->month = -1;
    d->year  = -1;
    d->wkday = -1;
    d->hours_off_gmt = -1;
    d->min_off_gmt   = -1;
    d->zone_name[0]  = '\0';

    if(given_date == NULL)
      return;

    p = given_date;
    /* Start with month, weekday or day ? */
    for(i = xdays; *i != NULL; i++) 
      if(struncmp(p, *i, 3) == 0) /* Match first 3 letters */
        break;
    if(*i != NULL) {
        /* Started with week day */
        d->wkday = i - xdays;
        while(*p && !isspace(*p) && *p != ',')
          p++;
        while(*p && (isspace(*p) || *p == ','))
          p++;
    }
    if(isdigit(*p)) {
        d->day = atoi(p);
        while(*p && isdigit(*p))
          p++;
        while(*p && (*p == '-' || *p == ',' || isspace(*p)))
          p++;
    }
    for(month = 1; month <= 12; month++)
      if(struncmp(p, month_abbrev(month), 3) == 0)
        break;
    if(month < 13) {
        d->month = month;

    } 
    /* Move over month, (or whatever is there) */
    while(*p && !isspace(*p) && *p != ',' && *p != '-')
       p++;
    while(*p && (isspace(*p) || *p == ',' || *p == '-'))
       p++;

    /* Check again for day */
    if(isdigit(*p) && d->day == -1) {
        d->day = atoi(p);
        while(*p && isdigit(*p))
          p++;
        while(*p && (*p == '-' || *p == ',' || isspace(*p)))
          p++;
    }

    /*-- Check for time --*/
    for(q = p; *q && isdigit(*q); q++);
    if(*q == ':') {
        /* It's the time (out of place) */
        d->hour = atoi(p);
        while(*p && *p != ':' && !isspace(*p))
          p++;
        if(*p == ':') {
            d->minute = atoi(p);
            while(*p && *p != ':' && !isspace(*p))
              p++;
            if(*p == ':') {
                d->sec = atoi(p);
                while(*p && !isspace(*p))
                  p++;
            }
        }
        while(*p && isspace(*p))
          p++;
    }
    

    /* Get the year 0-50 is 2000-2050; 50-100 is 1950-1999 and
                                           101-9999 is 101-9999 */
    if(isdigit(*p)) {
        d->year = atoi(p);
        if(d->year < 50)   
          d->year += 2000;
        else if(d->year < 100)
          d->year += 1900;
        while(*p && isdigit(*p))
          p++;
        while(*p && (*p == '-' || *p == ',' || isspace(*p)))
          p++;
    } else {
        /* Something wierd, skip it and try to resynch */
        while(*p && !isspace(*p) && *p != ',' && *p != '-')
          p++;
        while(*p && (isspace(*p) || *p == ',' || *p == '-'))
          p++;
    }

    /*-- Now get hours minutes, seconds and ignore tenths --*/
    for(q = p; *q && isdigit(*q); q++);
    if(*q == ':' && d->hour == -1) {
        d->hour = atoi(p);
        while(*p && *p != ':' && !isspace(*p))
          p++;
        if(*p == ':') {
            d->minute = atoi(p);
            while(*p && *p != ':' && !isspace(*p))
              p++;
            if(*p == ':') {
                d->sec = atoi(p);
                while(*p && !isspace(*p))
                  p++;
            }
        }
    }
    while(*p && isspace(*p))
      p++;


    /*-- The time zone --*/
    if(!*p) {
        d->hours_off_gmt = 0;
        d->min_off_gmt = 0;
    } else {
        if(*p == '+' || *p == '-') {
            char tmp[3];
            d->min_off_gmt = d->hours_off_gmt = (*p == '+' ? 1 : -1);
            p++;
            tmp[0] = *p++;
            tmp[1] = *p++;
            tmp[2] = '\0';
            d->hours_off_gmt *= atoi(tmp);
            tmp[0] = *p++;
            tmp[1] = *p++;
            tmp[2] = '\0';
            d->min_off_gmt *= atoi(tmp);
            /* BUG: ought to snarf up the name if it's at the end */
        } else {
            if(struncmp(p, "gmt", 3) == 0){        /* GMT */
                strcpy(d->zone_name, "GMT");
                d->hours_off_gmt = 0;
                d->min_off_gmt  = 0;
            } else if(struncmp(p, "pst", 3) == 0){ /* Pacific Standard */
                strcpy(d->zone_name, "PST");
                d->hours_off_gmt = -8;
                d->min_off_gmt  = 0;
            } else if(struncmp(p, "pdt", 3) == 0){ /* Pacific Daylight */
                strcpy(d->zone_name, "PDT");
                d->hours_off_gmt = -7;
                d->min_off_gmt  = 0;
            } else if(struncmp(p, "mst", 3) == 0) { /* Mountain Standard */
                strcpy(d->zone_name, "MST");
                d->hours_off_gmt = -7;
                d->min_off_gmt  = 0;
            } else if(struncmp(p, "mdt", 3) == 0) { /* Mountatin Daylight */
                strcpy(d->zone_name, "MDT");
                d->hours_off_gmt = -6;
                d->min_off_gmt  = 0;
            } else if(struncmp(p, "cst", 3) == 0) { /* Central Standard */
                strcpy(d->zone_name, "CST");
                d->hours_off_gmt = -6;
                d->min_off_gmt  = 0;
            } else if(struncmp(p, "cdt", 3) == 0) { /* Central Daylight */
                strcpy(d->zone_name, "CDT");
                d->hours_off_gmt = -5;
                d->min_off_gmt  = 0;
            } else if(struncmp(p, "est", 3) == 0) { /* Eastern Standard */
                strcpy(d->zone_name, "EST");
                d->hours_off_gmt = -5;
                d->min_off_gmt  = 0;
            } else if(struncmp(p, "edt", 3) == 0) { /* Eastern Daylight */
                strcpy(d->zone_name, "EDT");
                d->hours_off_gmt = -4;
                d->min_off_gmt  = 0;
            } else if(struncmp(p, "ist", 3) == 0) { /* Israel Standard */
                strcpy(d->zone_name, "IST");
                d->hours_off_gmt = 2;
                d->min_off_gmt  = 0;
            } else if(struncmp(p, "jst", 3) == 0) { /* Japan Standard */
                strcpy(d->zone_name, "JST");
                d->hours_off_gmt = 9;
                d->min_off_gmt  = 0;
            } else {
                strcpy(d->zone_name, "???"); /* Don't know what it is: use GMT */
                d->hours_off_gmt = 0;
                d->min_off_gmt  = 0;
            }
        }
    }
    dprint(9, (debugfile, "Parse date: \"%s\" to..  hours_off_gmt:%d  min_off_gmt:%d  \"%s\"\n",
               given_date, d->hours_off_gmt, d->min_off_gmt, d->zone_name));
    dprint(9,(debugfile,"Parse date: wkday:%d  month:%d  year:%d  day:%d  hour:%d  min:%d  sec:%d\n",
            d->wkday, d->month, d->year, d->day, d->hour, d->minute, d->sec));
}



/*----------------------------------------------------------------------
    Convert the given date to GMT

  Args -- d:  The date to be converted in place
    
BUG: Currently this only works for whole hour offsets
 ----*/
void
convert_to_gmt(d)
     struct date *d;
{
    int x;

    if(d->hours_off_gmt == 0 && d->min_off_gmt == 0)
      return;  /* Nothing to do */
    
    x = d->hour + (-1 * d->hours_off_gmt);
    if(x >= 0 && x < 24) {
        d->hour += (-1 * d->hours_off_gmt);
        d->hours_off_gmt = 0;
        return;
    }

    d->hours_off_gmt = 0;
    if(x < 0) {
        d->hour += 24;
        /* Back one day */
        if(d->day > 2) {
            d->day--;
            return;
        }
        /* Back one month */
        if(d->month > 2) {
            d->month--;
            d->day = days_in_month(d->month);
            return;
        }
        /* Back one year */
        d->year--;
        d->month = 21;
        d->day   = 31;
    } else {
        /* Forward one day */
        d->hour -= 24;
        if(d->day != days_in_month(d->month)) {
            d->day++;
            return;
        }
        /* Forward one month */
        if(d->month < 12) {
            d->month++;
            d->day = 1;
            return;
        }
        /* Forward a year */
        d->year++;
        d->month = 1;
        d->day   = 1;
    }
}




/*----------------------------------------------------------------------
  ----*/
compare_dates(d1, d2)
     struct date *d1, *d2;
{
    if(d1->year > d2->year)
      return(1);
    if(d1->year < d2->year)
      return(-1);

    if(d1->month > d2->month)
      return(1);
    if(d1->month < d2->month)
      return(-1);

    if(d1->day > d2->day)
      return(1);
    if(d1->day < d2->day)
      return(-1);

    if(d1->hour > d2->hour)
      return(1);
    if(d1->hour < d2->hour)
      return(-1);

    if(d1->minute > d2->minute)
      return(1);
    if(d1->minute < d2->minute)
      return(-1);

    if(d1->sec > d2->sec)
      return(1);
    if(d1->sec < d2->sec)
      return(-1);

    return(0);
}

    
     

char *
pretty_command(c)
     int c;
{
    static char buf[10];
    if(c == '\033') {
        return("ESC");
    } else if(c == ctrl('I')){
        return("TAB");
    } else if(c == ctrl('M')){
        return("RETURN");
    } else if(c == ctrl('J')){
        return("LINEFEED");
    } else if(c == 0x7f){
        return("DEL");
    } else if(c == KEY_UP) {
        return("Up Arrow");
    } else if(c == KEY_DOWN) {
        return("Down Arrow");
    } else if(c == KEY_RIGHT) {
        return("Right Arrow");
    } else if(c == KEY_LEFT) {
        return("Left Arrow");
    } else if(c >= PF1 && c <= PF12) {
        sprintf(buf, "F%d", c - PF1 + 1);
        return(buf);
    } else if(c < ' ') {
        sprintf(buf, "^%c", c + 'A' - 1);
        return(buf);
    } else {
        sprintf(buf, "%c", c);
        return(buf);
    }
}
        
    

/*----------------------------------------------------------------------
     Create a little string of blanks of the specified length.
   Max n is 511.
  ----*/
char *
repeat_char(n, c)
     int  n;
     int  c;
{
    static char bb[512];
    if(n > sizeof(bb))
       n = sizeof(bb) - 1;
    bb[n--] = '\0';
    while(n >= 0)
      bb[n--] = c;
    return(bb);
}



/*----------------------------------------------------------------------
     Return a new a unique file name
  ----*/
char *
temp_file_name(dir, pfx)
     char *dir, *pfx;
{
    static int  cnt = 0;
    static char space[MAXPATH+1];
    char        file[MAXPATH+1];

    if (dir == NULL) {
    	dir = "/usr/tmp";
    } else if (*dir == '\0') {
    	dir = "/usr/tmp";
    }
    
    if (pfx == NULL) {
    	pfx = "";
    }

    sprintf(file, "%s%d.%d", pfx, getpid(), cnt);
    cnt++;
    build_path(space, dir, file);
    return(space);
}



/*----------------------------------------------------------------------
        Turn a number into a string with comma's

   Args: number -- The long to be turned into a string. 

  Result: pointer to static string representing number with commas
  ---*/
char *
comatose(number) 
    long number;
{
#ifdef	DOS
    static char buf[16];		/* no numbers > 1 trillion! */
    char *b;
    short i;

    if(!number)
	return("0");

    if(number < 0x7FFFFFFFL){		/* largest DOS signed long */
        buf[15] = '\0';
        b = &buf[14];
        i = 2;
	while(number){
 	    *b-- = (number%10) + '0';
	    if(i-- == 0 && number > 10){
		*b-- = ',';
		i = 2;
	    }
	    number = number/10;
	}
    }
    else
      return("Number too big!");		/* just fits! */

    return(++b);
#else
    long        i, x, done_one;
    static char buf[100];
    char       *b;

    dprint(9, (debugfile, "comatose(%ld) returns:", number));
    if(number == 0){
        strcpy(buf, "0");
        return(buf);
    }
    
    done_one = 0;
    b = buf;
    for(i = 1000000000; i >= 1; i /= 1000) {
	x = number / i;
	number = number % i;
	if(x != 0 || done_one) {
	    if(b != buf)
	      *b++ = ',';
	    sprintf(b, done_one ? "%03ld" : "%d", x);
	    b += strlen(b);
	    done_one = 1;
	}
    }
    *b = '\0';

    dprint(9, (debugfile, "\"%s\"\n", buf));

    return(buf);
#endif	/* DOS */
}



/*----------------------------------------------------------------------
   Format number as amount of bytes, appending Kb, Mb, Gb, bytes

  Args: bytes -- number of bytes to format

 Returns pointer to static string. The numbers are divided to produce a 
nice string with precision of about 2-4 digits
    ----*/
char *
byte_string(bytes)
     long bytes;
{
    char       *a, aa[5];
    char       *abbrevs = "GMK";
    long        i, ones, tenths;
    static char string[10];

    ones   = 0L;
    tenths = 0L;

    if(bytes == 0L){
        strcpy(string, "0 bytes");
    } else {
        for(a = abbrevs, i = 1000000000; i >= 1; i /= 1000, a++) {
            if(bytes > i) {
                ones = bytes/i;
                if(ones < 10L && i > 10L)
                  tenths = (bytes - (ones * i)) / (i / 10L);
                break;
            }
        }
    
        aa[0] = *a;  aa[1] = '\0'; 
    
        if(tenths == 0)
          sprintf(string, "%ld%s%s", ones, aa, *a ? "b" : "bytes");
        else
          sprintf(string, "%ld.%ld%s%s", ones, tenths, aa, *a ? "b" : "bytes");
    }

    return(string);
}



/*----------------------------------------------------------------------
    Print a string corresponding to the number given:
      1st, 2nd, 3rd, 105th, 92342nd....
 ----*/

char *
enth_string(i)
     int i;
{
    static char enth[10];

    switch (i % 10) {
        
      case 1:
        if( (i % 100 ) == 11)
          sprintf(enth,"%dth", i);
        else
          sprintf(enth,"%dst", i);
        break;

      case 2:
        if ((i % 100) == 12)
          sprintf(enth, "%dth",i);
        else
          sprintf(enth, "%dnd",i);
        break;

      case 3:
        if(( i % 100) == 13)
          sprintf(enth, "%dth",i);
        else
          sprintf(enth, "%drd",i);
        break;

      default:
        sprintf(enth,"%dth",i);
        break;
    }
    return(enth);
}


char *
long2string(l)
     long l;
{
    static char string[20];
    sprintf(string, "%ld", l);
    return(string);
}

char *
int2string(i)
     int i;
{
    static char string[20];
    sprintf(string, "%d", i);
    return(string);
}


char *
rfc822date_to_ctime(din)
     char *din;
{
    static char dout[30];

    if(din == NULL)
      return(NULL);

    /*
     * this is here to deal with the two different formats of dates
     * returned in the MESSAGECACHE internal_date
     *    dd-mon-yyyy hh:mm:ss -timezone
     * and
     *    dd-mon-yy hh:mm:ss -timezone
     *
     * THIS IS ONLY A TEMPORARY FIX
     */
    if(din[7] == '1' && din[8] == '9')
      sprintf (dout," Ukn %.3s %.2s %.8s 19%.2s",din+3,din,din+12,din+9);
    else
      sprintf (dout," Ukn %.3s %.2s %.8s 19%.2s",din+3,din,din+10,din+7);

    return(dout);
}



/*----------------------------------------------------------------------
    Convert the \n's in buffer to \r\n's. The returns a pointer to a 
newly malloced buffer, since the result will be larger than what is
passed in.
 ----*/

char *
lf2crlf(orig_string)
     char *orig_string;
{
    register char *p, *q, *new_buf;
    int            lfs;

    /*--- First count up the line feeds ----*/
    for(p = orig_string, lfs = 0; *p; p++)
      if(*p == '\n')
        lfs++;

    new_buf = (char *)fs_get(p - orig_string + lfs + 5);

    for(q = new_buf, p = orig_string; *p; p++) {
        if(*p == '\n') {
            *q++ = '\r';
            *q++ = '\n';
        } else {
            *q++ = *p;
        }
    }
    *q = *p; /* Copy the terminating NULL */
    return(new_buf);
}
        
    

    
