From andyb@wndrsvr.la.ca.us Tue Jun 16 17:25:45 1992
Date: Sat, 13 Jun 92 9:07:59 PDT
From: Andy Brager <andyb@wndrsvr.la.ca.us>
To: Laurence Lundblade <lgl@cac.washington.edu>
Subject: Re: port to System V complete

>    I'm hacking through the diffs now and seem to be missing pine/os-isc.c.

Full file included.

> Your right, the c-client stuff is a little bit much, but I think we can work
> something out. I was happy to see the chsize() function! My understanding was
> that there was no way to truncate a file on System V, which was going to
> hold up some ports. Can you send a man page for it? Thanks.

ISC did not deem it necessary to supply man pages for everything on the
system.  In particular, anything ported from Xenix as chsize() is, has
no man page.  Sorry!


-  Andy
/*----------------------------------------------------------------------

            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"

   Date:
   Last Edited:
      
   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 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: 1.1 $   $State: Exp $          *
    *                                                                     *
    * 			Copyright (c) 1986, 1987 Dave Taylor              *
    * 			Copyright (c) 1988, 1989 USENET Community Trust   *
    ***********************************************************************
  ----------------------------------------------------------------------*/

/*======================================================================
      os-unx.c

 This contains most of Pine's interface to the local operating system
and hardware. Hopefully this file, os-xxx.h and makefile.xxx are the
only one that have to be modified for any port. signals.c, ttyin.c,
and ttyout.c also have some dependencies. See the doc/tech-notes for
notes on porting Pine to other platforms.


        File System Access
           can_access      -- See if a file can be accessed
           file_size       -- Return the number of bytes in the file
           is_writetable   -- Check to see id directory exists and is writable
           create_mail_dir -- what to do if there's not mkdir function
           create_folder   -- create an empty folder
           rename_file     -- replacement for rename system call
           build_path      -- Put together a file system path
           expand_foldername -- Expand a folder name to full path
           fnexpand        -- Do filename exansion for csh style "~"
           filter_filename -- Make sure file name hasn't got weird chars
           disk_space      -- Count up the disk space free for a given path
           disk_quota      -- Find out the users disk quota
           simple_opendir  -- Open a directory 
           simple_readdir  -- Read file names out of ope directory
           simple_closedir -- Close directory
           read_file       -- Read whole file into memory (for small files)

        Abort
           coredump        -- Abort running Pine dumping core if possible

        System Name and Domain
           gethostname     -- Figure out the systems host name, however
           gethostdomain   -- Figure out the systems domain name

        Berkeley UNIX Job Control
           have_job_control-- Returns 1 if job control exists
           stop_process    -- What to do to stop process when it's time to stop

        System Error Messages (in case given one is a problem)
           error_name      -- Return short string describing error
           error_description -- Return strings describing error

        System Password and Accounts
           get_system_login    -- Returns userid 
           get_system_fullname -- Returns full name 
           get_system_homedir  -- Returns users home directoru
           get_system_passwd   -- Returns enscrypted password
           gcos_name           -- Parses full name from system
           local_name_lookup   -- Get full name of user on system
           chagne_passwd       -- Calls system password change

  ====*/


#include "headers.h"
#include <sys/stat.h>
#include <pwd.h>
#include <sys/wait.h>
#include <fcntl.h>




/*----------------------------------------------------------------------
       Check if we can access a file in a given way

   Args: filename  -- The file name to check
         mode      -- The mode ala the access() system call
 

 Result: returns 0 if the user can access the file
         errno if he can't. This routine used to fork of process and
         change uid and gid to make sure all went well. This was because
         elm, could be installed setuid. Pine cannot be. Maybe we'll need 
         more here some day.
 ----*/
int
can_access(file, mode)
char *file; 
int   mode;
{
	 return(access(file, mode));
}



/*----------------------------------------------------------------------
      Return the number of bytes in given file

    Args: name -- file name

  Result: the number of bytes in the file is returned or
          -1 on error in which case errno is valid
  ----------------------------------------------------------------------*/
long
file_size(name)
char *name;
{
    struct stat buffer;

    if (stat(name, &buffer) != 0)
        return(-1);
    return(buffer.st_size);
}



/*----------------------------------------------------------------------
      Check to see if a directory exists and is writable by us

   Args:  directory name

 Result:       returns 0 if it exists and is writable
                       1 if it is a directory, but is not writable
                       2 if it is not a directory
                       3 it doesn't exist.
  ----*/
is_writable_dir(file)
     char *file;
{
    struct stat sb;

   if(stat(file, &sb) < 0)
     /*--- It doesn't exist ---*/
     return(3);


   if(!(sb.st_mode & S_IFDIR))
     /*---- it's not a directory ---*/
     return(2);


   if(access(file, 07))
     return(1);
   else
     return(0);
}



/*----------------------------------------------------------------------
      Create the mail subdirectory.

  Args: dir -- Name of the directory to make
 
 Result: Directory is created.
  ----*/
create_mail_dir(dir)
     char *dir;
{
    if(mkdir(dir, 0700) < 0)
      return(-1);

    chmod(dir, 0700);
    /* Some systems need this, on others we don't care if it fails */
    chown(dir, getuid(), getgid());
    return(0);
}



/*----------------------------------------------------------------------
      Create a folder (create a specific named file)

   Args dir  --  Directory to create folder in
        file --  File name to create

 ----*/
create_folder(dir, file)
    char *dir, *file;
{
    /* this creates a new folders directory */
    char  fn[MAXPATH+1];
    int   x;

    if(strlen(dir) + strlen(file) + 1 > MAXPATH) {
        q_status_message1(2,4,
           "\007Error creating folder: full path name would be longer than %d",
                          (char *)MAXPATH);
    }
    build_path(fn, dir, file);
    x = creat(fn, 0600);
    if(x < 0) {
        q_status_message2(2, 4,"\007Error creating \"%s\" : %s\n",
                          pretty_fn(fn), error_description(errno));
    } else {
        close(x);
    }
}



/*----------------------------------------------------------------------
      Rename a file. This is mostly a replacement for the rename system call.
If the system call exists we just use it.
  ----*/

int
rename_file(tmpfname, fname)
char *tmpfname, *fname;
{
        return(rename(tmpfname, fname));
}



/*----------------------------------------------------------------------
   Isolate the putting a path name together so other file systems like DOS can 
   be dealt with easily

   Args: pathbuf -- Buffer to return formatted path in. Better be big enough!
         first_part - First part of path to construct
         second_part - second part, usually the file name

BUGS this is a first stab at dealing fs naming dependencies, and others 
still exist
 ----*/
void
build_path(pathbuf, first_part, second_part)
     char *pathbuf, *first_part, *second_part;
{
    sprintf(pathbuf, "%s/%s", first_part, second_part);
}



/*----------------------------------------------------------------------
        expand the given file name considering ~ and the folders_dir

   Args: filename  -- The file/path name to expand (in place)

 Result: returns 0 and q's status message if unsuccessful.
         Input string is over written with expanded name.
         Returns 1 if successful

BUG should limit length to MAXPATH
  ----*/
int
expand_foldername(filename)
     char *filename;
{
    char         temp_filename[MAXPATH+1];

    dprint(5, (debugfile, "=== expand_foldername called (%s) ===\n",filename));

    if(filename[0] != '{') {
        char *ill = filter_filename(filename);
        if(ill != NULL) {
            q_status_message1(1,3, "%s in folder name", ill);
            return(0);
        }
    }

    strcpy(temp_filename, filename);
    if(strucmp(temp_filename, "inbox") == 0) {
        strcpy(filename, ps_global->VAR_INBOX_PATH == NULL ? "inbox" :
               ps_global->VAR_INBOX_PATH);
    } else if(temp_filename[0] == '{') {
        strcpy(filename, temp_filename);
    } else if(strindex("./~", temp_filename[0]) != NULL 
	      && ps_global->restricted){
	q_status_message(1,3,"Pine demo only opens local folders");
	return(0);
    } else if(temp_filename[0] == '*') {
        strcpy(filename, temp_filename);
    } else if (temp_filename[0] == '~'){
        if(fnexpand(temp_filename, sizeof(temp_filename)) == NULL) {
            char *p = strindex(temp_filename, '/');
    	    if(p != NULL)
    	      *p = '\0';
    	    q_status_message1(1,2,
                    "Error expanding folder name: \"%s\" unknown user",
    	       temp_filename);
    	    return(0);
        }
        strcpy(filename, temp_filename);
    } else if(temp_filename[0] == '/') {
        strcpy(filename, temp_filename);
    } else {
        sprintf(filename,"%s/%s", ps_global->folders_dir, temp_filename);
    }
    dprint(5, (debugfile, "returning \"%s\"\n", filename));    
    return(1);
}



struct passwd *getpwnam();

/*----------------------------------------------------------------------
       Expand the ~ in a file ala the csh (as home directory)

   Args: buf --  The filename to expand (nothing happens unless begins with ~)
         len --  The length of the buffer passed in (expansion is in place)

 Result: expanded string is returned using same storage as passed in
         if expansion fails, NULL is returned
 ----*/

char *
fnexpand(buf, len)
     char *buf;
     int len;
{
    struct passwd *pw;
    register char *x,*y;
    char name[20];
    
    if(*buf == '~') {
        for(x = buf+1, y = name; *x != '/' && *x != '\0'; *y++ = *x++);
        *y = '\0';
        if(x == buf + 1) 
          pw = getpwuid(getuid());
        else
          pw = getpwnam(name);
        if(pw == NULL)
          return(NULL);
        if(strlen(pw->pw_dir) + strlen(buf) > len) {
            return(NULL);
        }
        rplstr(buf, x - buf, pw->pw_dir);
    }
    return(len ? buf : NULL);
}



/*----------------------------------------------------------------------
    Filter file names for strange characters

   Args:  file  -- the file name to check
 
 Result: Returns NULL if file name is OK
         Returns formatted error message if it is not

  ----*/

char *
filter_filename(file)
     char *file;
{
#ifdef ALLOW_WEIRD
    static char illegal[] = {'\177', '\0'};
#else
    static char illegal[] = {'"', '#', '$', '%', '&', '\'','(', ')','*',
                          ',', ':', ';', '<', '=', '>', '?', '[', ']',
                          '\\', '^', '|', '\177', '\0'};
#endif
    static char error[100];
    char ill_file[MAXPATH+1], *ill_char, *ptr, e2[10];

    ptr = file;
    while (*ptr == ' ') ptr++;	/* leading spaces GONE! */

    if(*ptr == '*')
      ptr++; /* Allow * at beginning for news groups */

    while(*ptr && *ptr > ' ' && strindex(illegal, *ptr) == 0)
      ptr++;

    if(*ptr != '\0') {
        if(*ptr == ' ') {
            ill_char = "<space>";
        } else if(*ptr == '\n') {
            ill_char = "<newline>";
        } else if(*ptr == '\r') {
            ill_char = "<carriage return>";
        } else if(*ptr == '\t') {
    	    ill_char = "<tab>";
        } else if(*ptr < ' ') {
            sprintf(e2, "control-%c", *ptr + '@');
            ill_char = e2;
        } else if (*ptr == '\177') {
    	    ill_char = "<del>";
        } else {
    	    e2[0] = *ptr;
    	    e2[1] = '\0';
    	    ill_char = e2;
        }
        if(ptr != file) {
            strncpy(ill_file, file, ptr - file);
            ill_file[ptr - file] = '\0';
            sprintf(error,"Character \"%s\" after \"%s\" not allowed",ill_char,
                    ill_file);
        } else {
            sprintf(error,
                    "First character, \"%s\", not allowed",
                    ill_char);
        }
            
        return(error);
    }
    return(NULL);
}
        


FILE *fdopen();

/*----------------------------------------------------------------------
       find how much free disk space is on a named paritition

    Args: The name of the device or name of file on which device of concern is
 
  Result: returns number of K bytes of free space

   Return the free space on the named disk in 1K blocks
  by running df(1). This is of course sensitive to the output of df
  which changes from one UNIX to another. We try to avoid this by
  looking for the first line beginning with a '/' which is the
  first line that's not header. (The main variation is header length)
  -----*/

disk_space(device_name)
     char *device_name;
{
    int            pipe_ends[2], pid, child;
    int            n;
    int            stx;
    FILE          *df_s;
    register char *p;
    register char *q;
    char           buf[1000];

    /*---- The number of lines to skip in the header -----*/
    n = 1;


    if(pipe(pipe_ends)< 0) {
        perror("pipe");
        return(-1);
    }

    if((child = fork()) == 0) {
	close(1);
	dup2(pipe_ends[1], 1);
	if(execl("/bin/df","df", device_name, NULL) < 0)
	  perror("exec df");
    }

    close(pipe_ends[1]);
    df_s = fdopen(pipe_ends[0], "r");
    while(n >= 0) {
	if(fgets(buf, sizeof(buf), df_s) == NULL)
	  return(-2);
	n--;
    }
    
    dprint(8, (debugfile, "DF output, --%s--\n", buf));
    /* Skip the filesystem */
    p = buf;
    for(;*p != ' '; p++);
    for(;*p == ' '; p++);
    /* Skip over the Kbytes field */
    for(;*p != ' '; p++);
    for(;*p == ' '; p++);

    for(q = p;*q != ' ';q++);
    *q = '\0';


    pid = 0;
    while(pid != child)
        pid = wait(&stx);

    fclose(df_s);
    dprint(8, (debugfile, "Disk ready for atoi  --%s--\n", p));
    return(atoi(p));
}


    
#if defined(USE_QUOTAS)

#undef BSD
#include <sys/param.h> 
#include <sys/quota.h>

/*----------------------------------------------------------------------
   Return space left in disk quota on file system given files is in

    Args: path - Path name of file or directory on file system of concern
                   (any file will do)
          over - pointer to flag that is set if the user is over quota


 Returns: The number of bytes free in disk quota as per the soft limit.
          -1 is returned on an error looking up quota
           0 is returned if there is no quota

BUG:  If there's more than 2.1Gb free this function will break
  ----*/
long
disk_quota(path, over)
     char *path;
     int  *over;
{
    struct stat statx;
    struct dqblk quotax;
    long         q;

    dprint(5, (debugfile, "quota_check path: %s\n", path));
    if(stat(path, &statx) < 0) {
        return(-1);
    }

    *over = 0;
    errno = 0;

    dprint(7, (debugfile, "Quota check: UID:%d  stat: %d %x\n", 
           getuid(), statx.st_dev, statx.st_dev));
    if(quota(Q_GETDLIM, getuid(), statx.st_dev, &quotax) < 0) {
        dprint(5, (debugfile, "Quota failed : %s\n",
                   error_description(errno)));
        if(errno == ESRCH)
          return(0); /* No limit */
        else
          return(-1); /* Some thing went wrong */
    }

    dprint(5,(debugfile,"Quota: bsoftlimit:%d  bhardlimit:%d  curblock:%d\n",
          quotax.dqb_bsoftlimit, quotax.dqb_bhardlimit, quotax.dqb_curblocks));

    /* Some confusion on the type of bsoftlimit. The include file says
       unsigned, but -1 seems to indicate no quota */
    if(quotax.dqb_bsoftlimit == 0 || (long)quotax.dqb_bsoftlimit == -1) 
      return(0);

    q = (quotax.dqb_bsoftlimit - quotax.dqb_curblocks) * 1024;
    if(q < 0) {
        q = -q;
        *over = 1;
    }
    dprint(5, (debugfile, "disk_quota returning :%d,  over:%d\n", q, *over));
    return(q);
}



#endif /* USE_QUOTAS */




/*----------------------------------------------------------------------
      Very simple, minimal and machine independent directory access
  
  This just allows opening of one directory at a time and getting the 
files in that directory.
 ----*/

#include <sys/dir.h>
#include <sys/dirent.h>


static DIR *directory;

/*----------------------------------------------------------------------
     Open a directory for subsequent reading of filenames within  

    Args: dir -- The name of the directory to open

  Result: 0 is successful, -1 if not
  ---*/
simple_opendir(dir)
     char *dir;
{
    directory = opendir(dir);
    if(directory == NULL)
      return(-1);
    return(0);
}

/*----------------------------------------------------------------------
    Returns the next file name in the directory opened by simple_opendir

     Args:  

  Returns: string with name of file or NULL
 ----*/
char *
simple_readdir()
{
    register struct direct    *dp;

    if(directory == NULL)
      return(NULL);
    dp = readdir(directory);
    if(dp == NULL)
      return(NULL);
    return(dp->d_name);
}

/*----------------------------------------------------------------------
   Close the currently open directory  
 ----*/
void
simple_closedir()
{
    closedir(directory);
}





/*----------------------------------------------------------------------
    Read whole file into memory

  Args: filename -- path name of file to read

  Result: Returns pointer to malloced memory with the contents of the file
          or NULL

This won't work very well if the file has NULLs in it and is mostly
intended for fairly small text files.
  ----------------------------------------------------------------------*/
char *
read_file(filename)
     char *filename;
{
    int         fd;
    struct stat statbuf;
    char       *buf;

    fd = open(filename, O_RDONLY);
    if(fd < 0)
      return(NULL);

    fstat(fd, &statbuf);

    buf = fs_get(statbuf.st_size + 1);

    if(read(fd, buf, statbuf.st_size) != statbuf.st_size) {
        close(fd);
        return(NULL);
    }
    close(fd);

    buf[statbuf.st_size]= '\0';

    return(buf);
}








/*----------------------------------------------------------------------
     Abort with a core dump

  Some systems can't abort() from a signal handler if the signal handler installed catchs the signal abort() uses to actually to the abort. That is even resetting the signal default doesn't occur until the program leaves the handler routine. This winds up causing infinite loops. To avoid this we just never catch SIGFPE which is assumed to be very rare in Pine and just kill ourselves with it when we want to abort with a core dump.
 ----*/
void
coredump()
{
    abort();
}
    




#include <netdb.h>

/*----------------------------------------------------------------------
       Get the current host and domain names

    Args: hostname   -- buffer to return the hostname in
          hsize      -- size of buffer above
          domainname -- buffer to return domain name in
          dsize      -- size of buffer above

  Result: The system host and domain names are returned. If the full host
          name is akbar.cac.washington.edu then the hostname is
          cac.washington.edu.

On Internet connected hosts this look up uses /etc/hosts and DNS to
figure all this out. On other less well connected machines some other
file may be read. If there is no notion of a domain name the domain
name maybe left blank. On a PC where there really isn't a host name
this should return blank strings. The .pinerc will take care of
configuring the domain names. That is this should only return the
native systems idea of what the names are if the system has such

The unused routine above might provide some clues for Xenix and such.
 ----*/
void
getdomainnames(hostname, hsize, domainname, dsize)
     char *hostname, *domainname;
     int   hsize, dsize;
{
    char           *dn, hname[MAX_ADDRESS+1];
    struct hostent *he;

    gethostname(hname, MAX_ADDRESS);

    he = gethostbyname(hname);

    if(he == NULL && strlen(hname) == 0) {
        strcpy(hostname, "");
    } else if(he == NULL) {
        strncpy(hostname, hname, hsize - 1);
    } else {
        strncpy(hostname, he->h_name, hsize-1);
    }
    hostname[hsize-1] = '\0';


    if((dn = strindex(hostname, '.')) != NULL) {
        strncpy(domainname, dn+1, dsize-1);
    } else {
        strncpy(domainname, hostname, dsize-1);
    }
    domainname[dsize-1] = '\0';

}









/*----------------------------------------------------------------------
     This routine returns 1 if jobs control SIGTSTP / ^Z is available
  ----*/
have_job_control()
{
    return(1);
}
  


/*----------------------------------------------------------------------
    If we don't have job control, this routine is never called.
  ----*/
stop_process()
{
    kill(0, SIGSTOP); 
}








/*----------------------------------------------------------------------
   This is here in case errno on your system is so brain damaged that it's 
 unusuable. Hopefully it'll get very little use.


    This routine maps error numbers to error names and error messages.
    These are all directly ripped out of the include file errno.h, and
    are HOPEFULLY standardized across the different breeds of Unix!!

    If (alas) yours are different, you should be able to use awk to
    mangle your errno.h file quite simply...

**/

extern char *sys_errlist[];

#ifdef NO_KNOWN_SYSTEM_WITHOUT_ERRNO_YET

char *err_name[] = { 
/* 0 */	        "NOERROR", "No error status currently",
/* 1 */		"EPERM",   "Not super-user",
/* 2 */		"ENOENT",  "No such file or directory",
/* 3 */		"ESRCH",   "No such process",
/* 4 */		"EINTR",   "Interrupted system call",
/* 5 */		"EIO",     "I/O error",
/* 6 */		"ENXIO",   "No such device or address",
/* 7 */		"E2BIG",   "Arg list too long",
/* 8 */		"ENOEXEC", "Exec format error",
/* 9 */		"EBADF",   "Bad file number",
/* 10 */	"ECHILD",  "No children",
/* 11 */	"EAGAIN",  "No more processes",
/* 12 */	"ENOMEM",  "Not enough core",
/* 13 */	"EACCES",  "Permission denied",
/* 14 */	"EFAULT",  "Bad address",
/* 15 */	"ENOTBLK", "Block device required",
/* 16 */	"EBUSY",   "Mount device busy",
/* 17 */	"EEXIST",  "File exists",
/* 18 */	"EXDEV",   "Cross-device link",
/* 19 */	"ENODEV",  "No such device",
/* 20 */	"ENOTDIR", "Not a directory",
/* 21 */	"EISDIR",  "Is a directory",
/* 22 */	"EINVAL",  "Invalid argument",
/* 23 */	"ENFILE",  "File table overflow",
/* 24 */	"EMFILE",  "Too many open files",
/* 25 */	"ENOTTY",  "Not a typewriter",
/* 26 */	"ETXTBSY", "Text file busy",
/* 27 */	"EFBIG",   "File too large",
/* 28 */	"ENOSPC",  "No space left on device",
/* 29 */	"ESPIPE",  "Illegal seek",
/* 30 */	"EROFS",   "Read only file system",
/* 31 */	"EMLINK",  "Too many links",
/* 32 */	"EPIPE",   "Broken pipe",
/* 33 */	"EDOM",    "Math arg out of domain of func",
/* 34 */	"ERANGE",  "Math result not representable",
/* 35 */	"ENOMSG",  "No message of desired type",
/* 36 */	"EIDRM",   "Identifier removed"
	};
#endif

char *strcpy();

/*----------------------------------------------------------------------
      Return short error name string for error number
 
   Args: errnumber -- The system error number (errno)

 Result: error name
  ----*/
char *error_name(errnumber)
int errnumber;
{
    static char buffer[50];
#ifdef NO_KNOWN_SYSTEM_WITHOUT_ERRNO_YET
    if (errnumber < 0 || errnumber > 36) 
#endif
      sprintf(buffer,"ERR-UNKNOWN (%d)", errnumber);
#ifdef NO_KNOWN_SYSTEM_WITHOUT_ERRNO_YET
    else
      strcpy(buffer, err_name[2*errnumber]);
#endif

    return( (char *) buffer);
}



/*----------------------------------------------------------------------
       return string describing the error

   Args: errnumber -- The system error number (errno)(

 Result:  sentence long string describing the error 
  ----*/

char *error_description(errnumber)
int errnumber;
{
    static char buffer[50];

#ifdef NO_KNOWN_SYSTEM_WITHOUT_ERRNO_YET
    if (errnumber < 0 || errnumber > 36) 
      sprintf(buffer,"Unknown error - %d - No description", errnumber);
    else
      strcpy(buffer, err_name[2*errnumber + 1]);
#else
    strcpy(buffer, sys_errlist[errnumber]);
#endif

    return ( (char *) buffer);
}








/*----------------------------------------------------------------------
  This collection of routines looks up the login name and password on the
system. For things like PC's it's OK for these to return NULL system there
is no system login. Other code will figure out who the user actually is.
  ----*/
static struct passwd *unix_pwd = NULL;
static int            pwd_looked_up = 0;

static char *gcos_name();

char *
get_system_login()
{
    if(unix_pwd == NULL) {
        if(!pwd_looked_up) {
            unix_pwd = getpwuid(getuid());
            pwd_looked_up = 1;
        }
        if(unix_pwd == NULL)
          return(NULL);
    }
    return(unix_pwd->pw_name);
}

char *
get_system_fullname()
{
    if(unix_pwd == NULL) {
        if(!pwd_looked_up) {
            unix_pwd = getpwuid(getuid());
            pwd_looked_up = 1;
        }
        if(unix_pwd == NULL)
          return(NULL);
    }
    return(gcos_name(unix_pwd->pw_gecos, unix_pwd->pw_name));
}

char *
get_system_homedir()
{
    if(unix_pwd == NULL) {
        if(!pwd_looked_up){
            unix_pwd = getpwuid(getuid());
            pwd_looked_up = 1;
        }
        if(unix_pwd == NULL)
          return(NULL);
    }
    return(unix_pwd->pw_dir);
}

char *
get_system_passwd()
{
    if(unix_pwd == NULL) {
        if(!pwd_looked_up) {
            unix_pwd = getpwuid(getuid());
            pwd_looked_up = 1;
        }
        if(unix_pwd == NULL)
          return(NULL);
    }
    return(unix_pwd->pw_passwd);
}



/*----------------------------------------------------------------------
      Pull the name out of the gcos field if we have that sort of /etc/passwd

   Args: gcos_field --  The long name or GCOS field to be parsed
         logname    --  Replaces occurances of & with logname string

 Result: returns pointer to buffer with name
  ----*/
static char *
gcos_name(gcos_field, logname)
char *logname, *gcos_field;
{

    /*----- Berkeley style long names  (default) ------*/ 

    static char fullname[MAX_FULLNAME+1];
    register char *fncp, *gcoscp, *lncp, *end;


    /* full name is all chars up to first ',' (or whole gcos, if no ',') */
    /* replace any & with logname in upper case */

    for(fncp = fullname, gcoscp= gcos_field, end = fullname + MAX_FULLNAME - 1;
        (*gcoscp != ',' && *gcoscp != '\0' && fncp != end);
	gcoscp++) {

	if(*gcoscp == '&') {
	    for(lncp = logname; *lncp; fncp++, lncp++)
		*fncp = toupper(*lncp);
	} else {
	    *fncp++ = *gcoscp;
	}
    }
    
    *fncp = '\0';
    return(fullname);
}

    

/*----------------------------------------------------------------------
      Look up a userid on the local system and return rfc822 address

 Args: name  -- possible login name on local system

 Result: returns NULL or pointer to static string rfc822 address.
  ----*/
char *
local_name_lookup(name)
     char *name;
{
    struct passwd *pw;
    static char    buf[100];

    pw = getpwnam(name);
    if(pw == NULL)
      return(NULL);

    strcpy(buf, gcos_name(pw->pw_gecos, name));

    return(buf);
}



/*----------------------------------------------------------------------
       Call the system to change the passwd
 
It would be nice to talk to the passwd program via a pipe or ptty so the
user interface could be consistent, but we can't count on the the prompts
and responses from the passwd program to be regular so we just let the user 
type at the passwd program with some screen space, hope he doesn't scroll 
off the top and repaint when he's done.
 ----*/        
change_passwd()
{
    char cmd_buf[100];

    int i;
    for(i = 1; i < ps_global->ttyo->screen_rows; i ++) {
        MoveCursor(i, 0);
        CleartoEOLN();
    }

    MoveCursor(5, 0);
    fflush(stdout);

    Raw(0);
    strcpy(cmd_buf, "passwd");
    system(cmd_buf);
    sleep(3);
    Raw(1);
    init_username(ps_global);
}


    
