fix bogus implementation of name overflow limiting
[unix-history] / usr / src / usr.sbin / sendmail / src / util.c
index 038ea79..e06b0fa 100644 (file)
@@ -1,13 +1,13 @@
 /*
  * Copyright (c) 1983 Eric P. Allman
 /*
  * Copyright (c) 1983 Eric P. Allman
- * Copyright (c) 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)util.c     6.19 (Berkeley) %G%";
+static char sccsid[] = "@(#)util.c     8.20 (Berkeley) %G%";
 #endif /* not lint */
 
 # include "sendmail.h"
 #endif /* not lint */
 
 # include "sendmail.h"
@@ -326,55 +326,121 @@ makelower(p)
 **             buf -- buffer to store result in.
 **
 **     Returns:
 **             buf -- buffer to store result in.
 **
 **     Returns:
-**             none.
+**             TRUE -- if the resulting message should be a MIME format.
+**             FALSE -- if MIME is not necessary.
 **
 **     Side Effects:
 **             none.
 */
 
 **
 **     Side Effects:
 **             none.
 */
 
-fullname(pw, buf)
-       register struct passwd *pw;
-       char *buf;
+/* values for should_quote */
+#define NO_QUOTE       0
+#define SHOULD_QUOTE   1
+#define SHOULD_MIME    2
+
+int
+       register unsigned char *gecos;
+       const unsigned char *login;
+       unsigned char *buf;
 {
 {
-       register char *p;
-       register char *bp = buf;
-       int l;
+       register unsigned char *bp = buf;
+       unsigned char *p;
+       int should_quote = NO_QUOTE;
        register char *p = pw->pw_gecos;
 
        register char *p = pw->pw_gecos;
 
-       if (*gecos == '*')
-               gecos++;
-
-       /* find length of final string */
-       l = 0;
-       for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
+       /* make sure specials, SPACE and CTLs are quoted within " " */
+       for (p = gecos; *p && *p != ',' && *p != ';' && *p != '%'; p++)
        {
        {
-               if (*p == '&')
-                       l += strlen(login);
-               else
-                       l++;
+               if (*p >= 0200)
+               {
+                       should_quote = SHOULD_MIME;
+                       break;
+               }
+               switch (*p)
+               {
+                 case '(':
+                 case ')':
+                 case '<':
+                 case '>':
+                 case '@':
+                 case ':':
+                 case '\\':
+                 case '"':
+                 case '.':
+                 case '[':
+                 case ']':
+                       should_quote = SHOULD_QUOTE;
+                       break;
+               }       
        }
        }
-
-       /* now fill in buf */
-       for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
+       if (should_quote == SHOULD_MIME)
        {
        {
-               if (*p == '&')
+               strcpy (bp, "=?iso-8859-1?Q?");
+               bp += 15;
+               for (p = gecos; *p && *p != ',' && *p != ';' && *p != '%'; p++)
                {
                {
-                       (void) strcpy(bp, pw->pw_name);
-                       *bp = toupper(*bp);
-                       while (*bp != '\0')
-                               bp++;
+                       if (*p == ' ')
+                               *bp++ = '_';
+                        else if (*p == '&')
+                        {
+                               (void) strcpy(bp, login);
+                               *bp = toupper(*bp);
+                               bp += strlen (bp);
+                       }
+                       else if (*p < 040 || *p >= 200 || 
+                                strchr("_?()<>@:\\\".[]", *p) != NULL)
+                       {
+                               *bp++ = '=';
+                               *bp++ = "0123456789ABCDEF"[(*p >> 4) & 0xf];
+                               *bp++ = "0123456789ABCDEF"[*p & 0xf];
+                       }
+                       else
+                               *bp++ = *p;
                }
                }
-               else
-                       *bp++ = *p;
+               strcpy (bp, "?= ");
+               bp += 3;
+       }
+       else
+       {
+               if (should_quote)
+                       *bp++ = '"';
+               for (p = gecos; *p && *p != ',' && *p != ';' && *p != '%'; p++)
+               {
+                       if (*p == '&')
+                       {
+                               (void) strcpy(bp, login);
+                               *bp = toupper(*bp);
+                               while (*bp != '\0')
+                                       bp++;
+                       }
+                       else
+                       {
+                               if (*p == '"')
+                                       *bp++ = '\\';
+                               *bp++ = *p;
+                       }
+               }
+               if (bp[-1] == '\\')
+                       *bp++ = '\\';
+               if (should_quote)
+                       *bp++ = '"';
        }
        }
+
        *bp = '\0';
        *bp = '\0';
+       return should_quote == SHOULD_MIME;
 }
 \f/*
 **  SAFEFILE -- return true if a file exists and is safe for a user.
 **
 **     Parameters:
 **             fn -- filename to check.
 }
 \f/*
 **  SAFEFILE -- return true if a file exists and is safe for a user.
 **
 **     Parameters:
 **             fn -- filename to check.
-**             uid -- uid to compare against.
+**             uid -- user id to compare against.
+**             gid -- group id to compare against.
+**             uname -- user name to compare against (used for group
+**                     sets).
+**             flags -- modifiers:
+**                     SF_MUSTOWN -- "uid" must own this file.
+**                     SF_NOSLINK -- file cannot be a symbolic link.
 **             mode -- mode bits that must match.
 **
 **     Returns:
 **             mode -- mode bits that must match.
 **
 **     Returns:
@@ -385,24 +451,139 @@ fullname(pw, buf)
 **             none.
 */
 
 **             none.
 */
 
+#include <grp.h>
+
+#ifndef S_IXOTH
+# define S_IXOTH       (S_IEXEC >> 6)
+#endif
+
+#ifndef S_IXGRP
+# define S_IXGRP       (S_IEXEC >> 3)
+#endif
+
+#ifndef S_IXUSR
+# define S_IXUSR       (S_IEXEC)
+#endif
+
 int
 int
-safefile(fn, uid, mode)
+safefile(fn, uid, gid, uname, flags, mode)
        char *fn;
        uid_t uid;
        char *fn;
        uid_t uid;
+       gid_t gid;
+       char *uname;
+       int flags;
        int mode;
 {
        int mode;
 {
+       register char *p;
+       register struct group *gr = NULL;
        struct stat stbuf;
 
        struct stat stbuf;
 
+       if (tTd(54, 4))
+               printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
+                       fn, uid, gid, flags, mode);
+       errno = 0;
+
+       for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
+       {
+               *p = '\0';
+               if (stat(fn, &stbuf) < 0)
+                       break;
+               if (stbuf.st_uid == uid && bitset(S_IXUSR, stbuf.st_mode))
+                       continue;
+               if (stbuf.st_gid == gid && bitset(S_IXGRP, stbuf.st_mode))
+                       continue;
+#ifndef NO_GROUP_SET
+               if (uname != NULL &&
+                   ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
+                    (gr = getgrgid(stbuf.st_gid)) != NULL))
+               {
+                       register char **gp;
+
+                       for (gp = gr->gr_mem; *gp != NULL; gp++)
+                               if (strcmp(*gp, uname) == 0)
+                                       break;
+                       if (*gp != NULL && bitset(S_IXGRP, stbuf.st_mode))
+                               continue;
+               }
+#endif
+               if (!bitset(S_IXOTH, stbuf.st_mode))
+                       break;
+       }
+       if (p != NULL)
+       {
+               int ret = errno;
+
+               if (ret == 0)
+                       ret = EACCES;
+               if (tTd(54, 4))
+                       printf("\t[dir %s] %s\n", fn, errstring(ret));
+               *p = '/';
+               return ret;
+       }
+
+#ifdef HASLSTAT
+       if ((bitset(SF_NOSLINK, flags) ? lstat(fn, &stbuf)
+                                      : stat(fn, &stbuf)) < 0)
+#else
        if (stat(fn, &stbuf) < 0)
        if (stat(fn, &stbuf) < 0)
+#endif
        {
                int ret = errno;
 
        {
                int ret = errno;
 
+               if (tTd(54, 4))
+                       printf("\t%s\n", errstring(ret));
+
                errno = 0;
                return ret;
        }
                errno = 0;
                return ret;
        }
-       if (stbuf.st_uid == uid && (stbuf.st_mode & mode) == mode)
+
+#ifdef S_ISLNK
+       if (bitset(SF_NOSLINK, flags) && S_ISLNK(stbuf.st_mode))
+       {
+               if (tTd(54, 4))
+                       printf("\t[mode %o]\tEPERM\n");
+               return EPERM;
+       }
+#endif
+
+       if (uid == 0)
+               mode >>= 6;
+       else if (stbuf.st_uid != uid)
+       {
+               mode >>= 3;
+               if (stbuf.st_gid == gid)
+                       ;
+#ifndef NO_GROUP_SET
+               else if (uname != NULL &&
+                        ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
+                         (gr = getgrgid(stbuf.st_gid)) != NULL))
+               {
+                       register char **gp;
+
+                       for (gp = gr->gr_mem; *gp != NULL; gp++)
+                               if (strcmp(*gp, uname) == 0)
+                                       break;
+                       if (*gp == NULL)
+                               mode >>= 3;
+               }
+#endif
+               else
+                       mode >>= 3;
+       }
+       if (tTd(54, 4))
+               printf("\t[uid %d, stat %o, mode %o] ",
+                       stbuf.st_uid, stbuf.st_mode, mode);
+       if ((stbuf.st_uid == uid || stbuf.st_uid == 0 ||
+            !bitset(SF_MUSTOWN, flags)) &&
+           (stbuf.st_mode & mode) == mode)
+       {
+               if (tTd(54, 4))
+                       printf("\tOK\n");
                return 0;
                return 0;
-       return EPERM;
+       }
+       if (tTd(54, 4))
+               printf("\tEACCES\n");
+       return EACCES;
 }
 \f/*
 **  FIXCRLF -- fix <CR><LF> in line.
 }
 \f/*
 **  FIXCRLF -- fix <CR><LF> in line.
@@ -447,6 +628,10 @@ fixcrlf(line, stripnl)
 **     whatever), so this tries to get around it.
 */
 
 **     whatever), so this tries to get around it.
 */
 
+#ifndef O_ACCMODE
+# define O_ACCMODE     (O_RDONLY|O_WRONLY|O_RDWR)
+#endif
+
 struct omodes
 {
        int     mask;
 struct omodes
 {
        int     mask;
@@ -469,7 +654,6 @@ dfopen(filename, omode, cmode)
        int cmode;
 {
        register int tries;
        int cmode;
 {
        register int tries;
-       FILE *fp;
        int fd;
        register struct omodes *om;
        struct stat st;
        int fd;
        register struct omodes *om;
        struct stat st;
@@ -497,10 +681,13 @@ dfopen(filename, omode, cmode)
                        locktype = LOCK_EX;
                else
                        locktype = LOCK_SH;
                        locktype = LOCK_EX;
                else
                        locktype = LOCK_SH;
-               (void) lockfile(fd, filename, locktype);
+               (void) lockfile(fd, filename, NULL, locktype);
                errno = 0;
        }
                errno = 0;
        }
-       return fdopen(fd, om->farg);
+       if (fd < 0)
+               return NULL;
+       else
+               return fdopen(fd, om->farg);
 }
 \f/*
 **  PUTLINE -- put a line like fputs obeying SMTP conventions
 }
 \f/*
 **  PUTLINE -- put a line like fputs obeying SMTP conventions
@@ -531,8 +718,8 @@ putline(l, fp, m)
        /* strip out 0200 bits -- these can look like TELNET protocol */
        if (bitnset(M_7BITS, m->m_flags))
        {
        /* strip out 0200 bits -- these can look like TELNET protocol */
        if (bitnset(M_7BITS, m->m_flags))
        {
-               for (p = l; svchar = *p; ++p)
-                       if (svchar & 0200)
+               for (p = l; (svchar = *p) != '\0'; ++p)
+                       if (bitset(0200, svchar))
                                *p = svchar &~ 0200;
        }
 
                                *p = svchar &~ 0200;
        }
 
@@ -543,6 +730,9 @@ putline(l, fp, m)
                if (p == NULL)
                        p = &l[strlen(l)];
 
                if (p == NULL)
                        p = &l[strlen(l)];
 
+               if (TrafficLogFile != NULL)
+                       fprintf(TrafficLogFile, "%05d >>> ", getpid());
+
                /* check for line overflow */
                while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
                {
                /* check for line overflow */
                while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
                {
@@ -551,17 +741,30 @@ putline(l, fp, m)
                        svchar = *q;
                        *q = '\0';
                        if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
                        svchar = *q;
                        *q = '\0';
                        if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
+                       {
                                (void) putc('.', fp);
                                (void) putc('.', fp);
+                               if (TrafficLogFile != NULL)
+                                       (void) putc('.', TrafficLogFile);
+                       }
                        fputs(l, fp);
                        (void) putc('!', fp);
                        fputs(m->m_eol, fp);
                        fputs(l, fp);
                        (void) putc('!', fp);
                        fputs(m->m_eol, fp);
+                       if (TrafficLogFile != NULL)
+                               fprintf(TrafficLogFile, "%s!\n%05d >>> ",
+                                       l, getpid());
                        *q = svchar;
                        l = q;
                }
 
                /* output last part */
                if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
                        *q = svchar;
                        l = q;
                }
 
                /* output last part */
                if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
+               {
                        (void) putc('.', fp);
                        (void) putc('.', fp);
+                       if (TrafficLogFile != NULL)
+                               (void) putc('.', TrafficLogFile);
+               }
+               if (TrafficLogFile != NULL)
+                       fprintf(TrafficLogFile, "%.*s\n", p - l, l);
                for ( ; l < p; ++l)
                        (void) putc(*l, fp);
                fputs(m->m_eol, fp);
                for ( ; l < p; ++l)
                        (void) putc(*l, fp);
                fputs(m->m_eol, fp);
@@ -618,6 +821,10 @@ xfclose(fp, a, b)
 {
        if (tTd(53, 99))
                printf("xfclose(%x) %s %s\n", fp, a, b);
 {
        if (tTd(53, 99))
                printf("xfclose(%x) %s %s\n", fp, a, b);
+#ifdef XDEBUG
+       if (fileno(fp) == 1)
+               syserr("xfclose(%s %s): fd = 1", a, b);
+#endif
        if (fclose(fp) < 0 && tTd(53, 99))
                printf("xfclose FAILURE: %s\n", errstring(errno));
 }
        if (fclose(fp) < 0 && tTd(53, 99))
                printf("xfclose FAILURE: %s\n", errstring(errno));
 }
@@ -629,6 +836,7 @@ xfclose(fp, a, b)
 **             siz -- size of buf.
 **             fp -- file to read from.
 **             timeout -- the timeout before error occurs.
 **             siz -- size of buf.
 **             fp -- file to read from.
 **             timeout -- the timeout before error occurs.
+**             during -- what we are trying to read (for error messages).
 **
 **     Returns:
 **             NULL on error (including timeout).  This will also leave
 **
 **     Returns:
 **             NULL on error (including timeout).  This will also leave
@@ -640,17 +848,18 @@ xfclose(fp, a, b)
 */
 
 static jmp_buf CtxReadTimeout;
 */
 
 static jmp_buf CtxReadTimeout;
+static int     readtimeout();
 
 char *
 
 char *
-sfgets(buf, siz, fp, timeout)
+sfgets(buf, siz, fp, timeout, during)
        char *buf;
        int siz;
        FILE *fp;
        time_t timeout;
        char *buf;
        int siz;
        FILE *fp;
        time_t timeout;
+       char *during;
 {
        register EVENT *ev = NULL;
        register char *p;
 {
        register EVENT *ev = NULL;
        register char *p;
-       static int readtimeout();
 
        /* set the timeout */
        if (timeout != 0)
 
        /* set the timeout */
        if (timeout != 0)
@@ -659,12 +868,16 @@ sfgets(buf, siz, fp, timeout)
                {
 # ifdef LOG
                        syslog(LOG_NOTICE,
                {
 # ifdef LOG
                        syslog(LOG_NOTICE,
-                           "timeout waiting for input from %s\n",
-                           CurHostName? CurHostName: "local");
+                           "timeout waiting for input from %s during %s\n",
+                           CurHostName? CurHostName: "local", during);
 # endif
                        errno = 0;
 # endif
                        errno = 0;
-                       usrerr("451 timeout waiting for input");
+                       usrerr("451 timeout waiting for input during %s",
+                               during);
                        buf[0] = '\0';
                        buf[0] = '\0';
+#ifdef XDEBUG
+                       checkfd012(during);
+#endif
                        return (NULL);
                }
                ev = setevent(timeout, readtimeout, 0);
                        return (NULL);
                }
                ev = setevent(timeout, readtimeout, 0);
@@ -688,8 +901,12 @@ sfgets(buf, siz, fp, timeout)
        if (p == NULL)
        {
                buf[0] = '\0';
        if (p == NULL)
        {
                buf[0] = '\0';
+               if (TrafficLogFile != NULL)
+                       fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
                return (NULL);
        }
                return (NULL);
        }
+       if (TrafficLogFile != NULL)
+               fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
        if (SevenBit)
                for (p = buf; *p != '\0'; p++)
                        *p &= ~0200;
        if (SevenBit)
                for (p = buf; *p != '\0'; p++)
                        *p &= ~0200;
@@ -819,7 +1036,7 @@ bool
 atobool(s)
        register char *s;
 {
 atobool(s)
        register char *s;
 {
-       if (*s == '\0' || strchr("tTyY", *s) != NULL)
+       if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
                return (TRUE);
        return (FALSE);
 }
                return (TRUE);
        return (FALSE);
 }
@@ -860,10 +1077,15 @@ atooct(s)
 **             none.
 */
 
 **             none.
 */
 
+int
 waitfor(pid)
        int pid;
 {
 waitfor(pid)
        int pid;
 {
+#ifdef WAITUNION
+       union wait st;
+#else
        auto int st;
        auto int st;
+#endif
        int i;
 
        do
        int i;
 
        do
@@ -872,8 +1094,12 @@ waitfor(pid)
                i = wait(&st);
        } while ((i >= 0 || errno == EINTR) && i != pid);
        if (i < 0)
                i = wait(&st);
        } while ((i >= 0 || errno == EINTR) && i != pid);
        if (i < 0)
-               st = -1;
-       return (st);
+               return -1;
+#ifdef WAITUNION
+       return st.w_status;
+#else
+       return st;
+#endif
 }
 \f/*
 **  BITINTERSECT -- tell if two bitmaps intersect
 }
 \f/*
 **  BITINTERSECT -- tell if two bitmaps intersect
@@ -943,16 +1169,230 @@ strcontainedin(a, b)
        register char *a;
        register char *b;
 {
        register char *a;
        register char *b;
 {
-       int l;
-
-       l = strlen(a);
-       for (;;)
+       int la;
+       int lb;
+       int c;
+
+       la = strlen(a);
+       lb = strlen(b);
+       c = *a;
+       if (isascii(c) && isupper(c))
+               c = tolower(c);
+       for (; lb-- >= la; b++)
        {
        {
-               b = strchr(b, a[0]);
-               if (b == NULL)
-                       return FALSE;
-               if (strncmp(a, b, l) == 0)
+               if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
+                       continue;
+               if (strncasecmp(a, b, la) == 0)
                        return TRUE;
                        return TRUE;
-               b++;
        }
        }
+       return FALSE;
+}
+\f/*
+**  CHECKFD012 -- check low numbered file descriptors
+**
+**     File descriptors 0, 1, and 2 should be open at all times.
+**     This routine verifies that, and fixes it if not true.
+**
+**     Parameters:
+**             where -- a tag printed if the assertion failed
+**
+**     Returns:
+**             none
+*/
+
+checkfd012(where)
+       char *where;
+{
+#ifdef XDEBUG
+       register int i;
+       struct stat stbuf;
+
+       for (i = 0; i < 3; i++)
+       {
+               if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
+               {
+                       /* oops.... */
+                       int fd;
+
+                       syserr("%s: fd %d not open", where, i);
+                       fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
+                       if (fd != i)
+                       {
+                               (void) dup2(fd, i);
+                               (void) close(fd);
+                       }
+               }
+       }
+#endif /* XDEBUG */
+}
+\f/*
+**  PRINTOPENFDS -- print the open file descriptors (for debugging)
+**
+**     Parameters:
+**             logit -- if set, send output to syslog; otherwise
+**                     print for debugging.
+**
+**     Returns:
+**             none.
+*/
+
+#include <netdb.h>
+#include <arpa/inet.h>
+
+printopenfds(logit)
+       bool logit;
+{
+       register int fd;
+       extern int DtableSize;
+
+       for (fd = 0; fd < DtableSize; fd++)
+               dumpfd(fd, FALSE, logit);
+}
+\f/*
+**  DUMPFD -- dump a file descriptor
+**
+**     Parameters:
+**             fd -- the file descriptor to dump.
+**             printclosed -- if set, print a notification even if
+**                     it is closed; otherwise print nothing.
+**             logit -- if set, send output to syslog instead of stdout.
+*/
+
+dumpfd(fd, printclosed, logit)
+       int fd;
+       bool printclosed;
+       bool logit;
+{
+       register struct hostent *hp;
+       register char *p;
+       struct sockaddr_in sin;
+       auto int slen;
+       struct stat st;
+       char buf[200];
+
+       p = buf;
+       sprintf(p, "%3d: ", fd);
+       p += strlen(p);
+
+       if (fstat(fd, &st) < 0)
+       {
+               if (printclosed || errno != EBADF)
+               {
+                       sprintf(p, "CANNOT STAT (%s)", errstring(errno));
+                       goto printit;
+               }
+               return;
+       }
+
+       slen = fcntl(fd, F_GETFL, NULL);
+       if (slen != -1)
+       {
+               sprintf(p, "fl=0x%x, ", slen);
+               p += strlen(p);
+       }
+
+       sprintf(p, "mode=%o: ", st.st_mode);
+       p += strlen(p);
+       switch (st.st_mode & S_IFMT)
+       {
+#ifdef S_IFSOCK
+         case S_IFSOCK:
+               sprintf(p, "SOCK ");
+               p += strlen(p);
+               slen = sizeof sin;
+               if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0)
+                       sprintf(p, "(badsock)");
+               else
+               {
+                       hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
+                       sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
+                                                  : hp->h_name, ntohs(sin.sin_port));
+               }
+               p += strlen(p);
+               sprintf(p, "->");
+               p += strlen(p);
+               slen = sizeof sin;
+               if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0)
+                       sprintf(p, "(badsock)");
+               else
+               {
+                       hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
+                       sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
+                                                  : hp->h_name, ntohs(sin.sin_port));
+               }
+               break;
+#endif
+
+         case S_IFCHR:
+               sprintf(p, "CHR: ");
+               p += strlen(p);
+               goto defprint;
+
+         case S_IFBLK:
+               sprintf(p, "BLK: ");
+               p += strlen(p);
+               goto defprint;
+
+         default:
+defprint:
+               sprintf(p, "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld",
+                       major(st.st_dev), minor(st.st_dev), st.st_ino,
+                       st.st_nlink, st.st_uid, st.st_gid, st.st_size);
+               break;
+       }
+
+printit:
+       if (logit)
+               syslog(LOG_DEBUG, "%s", buf);
+       else
+               printf("%s\n", buf);
+}
+\f/*
+**  SHORTENSTRING -- return short version of a string
+**
+**     If the string is already short, just return it.  If it is too
+**     long, return the head and tail of the string.
+**
+**     Parameters:
+**             s -- the string to shorten.
+**             m -- the max length of the string.
+**
+**     Returns:
+**             Either s or a short version of s.
+*/
+
+#ifndef MAXSHORTSTR
+# define MAXSHORTSTR   83
+#endif
+
+char *
+shortenstring(s, m)
+       register char *s;
+       int m;
+{
+       int l;
+       static char buf[MAXSHORTSTR + 1];
+
+       l = strlen(s);
+       if (l < m)
+               return s;
+       if (m > MAXSHORTSTR)
+               m = MAXSHORTSTR;
+       else if (m < 10)
+       {
+               if (m < 5)
+               {
+                       strncpy(buf, s, m);
+                       buf[m] = '\0';
+                       return buf;
+               }
+               strncpy(buf, s, m - 3);
+               strcpy(buf + m - 3, "...");
+               return buf;
+       }
+       m = (m - 3) / 2;
+       strncpy(buf, s, m);
+       strcpy(buf + m, "...");
+       strcpy(buf + m + 3, s + l - m);
+       return buf;
 }
 }