static char *device_name();

#include <fcntl.h>
#include <sys/fs/ufs_quota.h>

/*
 * define the "quotactl" function as in Solaris 1, based on ioctl().
 * By Marc Mazuhelli <mazu@dmi.usherb.ca>
 * The "special" parameter is any file on the file system,
 * not the block special device name as in Solaris 1.
 * Thanks to veronica@solution.maths.unsw.edu.au who provided
 * the idea and the basis for this function.
 *
 * [ Apparently quotactl used to exist in SunOS but no longer exists in ]
 * [ Solaris. This is an equivalent. If you are running on a system     ]
 * [ which has quotactl, comment this routine out or use sunquota.      ]
 */

int
quotactl(int cmd, char *special, uid_t uid, struct dqblk * addr)
{
    struct quotctl  op;
    int             fd = open(special, O_RDONLY);

    if (fd < 0)
        return -1;

    op.op = cmd;
    op.uid = uid;
    op.addr = (caddr_t) addr;

    if (ioctl(fd, Q_QUOTACTL, &op) < 0) {
        close(fd);
        return -1;
    }
    close(fd);
    return (0);
}


/*----------------------------------------------------------------------
   Return space left in disk quota on file system which given path is in.

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

 Returns: If *over = 0, the number of bytes free in disk quota as per
          the soft limit.
	  If *over = 1, the number of bytes *over* quota.
          -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;
{
    static int   no_quota = 0;
    struct stat  statx;
    struct dqblk quotax;
    long         q;
    char        *dname;

    if(no_quota)
      return(0L); /* If no quota the first time, then none the second. */

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

    *over = 0;
    errno = 0;

    dname = device_name(statx.st_dev);
    if(dname == NULL)
      return(-1L);

    dprint(7, (debugfile, "Quota check: UID:%d  device: %s\n", 
           getuid(), dname ? dname : "?"));
    if(quotactl(Q_GETQUOTA, dname, getuid(), (char *)&quotax) < 0) {
        dprint(5, (debugfile, "Quota failed : %s\n",
                   error_description(errno)));
        return(-1L); /* Something went wrong */
    }

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

    if(quotax.dqb_bsoftlimit == -1)
      return(-1L);

    q = (quotax.dqb_bsoftlimit - quotax.dqb_curblocks) * 512;    

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


/*----------------------------------------------------------------------
 *		devNumToName
 *
 *	This routine is here so that ex can get a device name to check
 *	disk quotas.  One might wonder, why not use getmntent(), rather
 *	than read /etc/mtab in this crude way?  The problem with getmntent
 *	is that it uses stdio, and ex/vi pointedly doesn't.
 ----*/
static  char
*device_name(st_devArg)
    dev_t st_devArg;
{
#ifndef MTABNAME
#define MTABNAME "/etc/mtab"
#endif
    char *mtab;
    static char devName[48];
    static char *answer = (char *) 0;
    struct stat devStat;
    static dev_t st_dev;
    int nb, cur, bol;
    char c;
    int dname;

    if (st_devArg == st_dev)
      return answer;

    mtab = read_file(MTABNAME);
    if(mtab == NULL)
      return((char *)NULL);

    /* Initialize save data. */
    st_dev = st_devArg;
    answer = (char *) 0;
    nb = strlen(mtab);

    for (cur=bol=0, dname=1; cur < nb; ++cur) {

	if (dname && (mtab[cur] <= ' ')) {
	/*	Space, tab or other such character has been found,
		presumably marking the end of the device name string. */
	
	    dname = 0;
	    c = mtab[cur];	/* Save current character. */
	    mtab[cur] = 0;	/* C zero-terminated string. */

	    /*	Get device number, via stat().  If it's the right
		number, copy the string and return its address. */
	    if (stat (&mtab[bol], &devStat) == 0) {
		if (devStat.st_rdev == st_dev) {
		    if ((cur - bol + 1) < sizeof (devName)) {
			strcpy (devName, &mtab[bol]);
                        answer = &devName[0];
			return(answer);
		    }
		}
	    }
	    mtab[cur] = c;
	}
	if (mtab[cur] == '\n') {
	    dname = 1;
	    bol = cur + 1;
	}
    }
    answer = NULL;

    return(answer);
}
