#include "args.h"
#include "config.h"
/*
 * Copyright (c) 1986, 2014 by The Trustees of Columbia University in
 * the City of New York.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  + Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  + Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 *  + Neither the name of Columbia University nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 */

/*
 * misc.c - miscellaneous commands
 */

#include "mm.h"
#include "parse.h"
#include "help.h"
#include "rd.h"

#ifdef HAVE_QUOTAS
#   ifdef HAVE_QUOTACTL
#	include <ufs/quota.h>
#	include <mntent.h>
#   else
#	include <sys/quota.h>
#   endif
#endif

extern int memdebug;
extern string finger_command;

static int msgcmp ARGS((message *a, message *b));

#ifdef HAVE_QUOTACTL
char *getspecialname ARGS((dev_t filedev));
#endif

void
#if HAVE_STDC
cmd_version (int n)
#else /* K&R style */
cmd_version (n)
int n;
#endif /* HAVE_STDC */
{
    confirm ();
    printf ("%s\n", mm_version);
    puts("Copyright (C) 1986, 2006\n\
\tThe Trustees of Columbia University in the City of New York");
    printf ("Compiled %s\n", mm_compiled);
    printf ("Report bugs to %s\n", BUGSTO);
}

#ifdef HAVE_QUOTAS
#define ST_GOODQUOTA 0
#define ST_BADQUOTA  1
#define ST_NOQUOTA   2
#endif

void
#if HAVE_STDC
cmd_status (int n)
#else /* K&R style */
cmd_status (n)
int n;
#endif /* HAVE_STDC */
{
    int verbose = FALSE;
#ifdef HAVE_QUOTAS
    int quotaflag = ST_GOODQUOTA;
    struct dqblk qblk;
    struct stat sbuf;
#ifdef HAVE_QUOTACTL
    char *bdev;
#endif
#endif
    static fdb conffdb = { _CMCFM, CM_SDH, NULL, NULL,
			       "confirm for status summary ",
			       NULL, NULL};
    static keywrd ver_key[] = {
    	{ "verbose", 0 , (keyval) 0 },
    };
    static keytab vertab = { sizeof (ver_key) / sizeof (keywrd),
			      ver_key };
    static fdb verswi = { _CMKEY, 0, NULL, (pdat) &vertab };


    parse (fdbchn (&verswi, &conffdb, NULL), &pv, &used);
    if (used == &verswi) {
	confirm();
	verbose = TRUE;
    }

    show_route(TRUE);			/* show mail routing */

    if (cf) {
	int i, nrec = 0, ndel = 0, nuns = 0;
	for (i = 0; i < cf->count; i++)	{
	    if (cf->msgs[i+1].flags & M_DELETED)
		ndel++;
	    if (cf->msgs[i+1].flags & M_RECENT)
		nrec++;
	    if (!(cf->msgs[i+1].flags & M_SEEN))
		nuns++;
	}
	printf (" File %s (%s%s%s)\n",cf->filename, msg_ops[cf->type].typename,
		(cf->flags&(MF_DIRTY|MF_MODIFIED) ? ", modified" : ""),
		(cf->flags&MF_RDONLY) ? ", read-only" : "");
	printf (" %d messages, %d old, %d deleted, %d unseen, %dk Bytes\n",
		cf->count, cf->count - nrec, ndel, nuns,
		(int)(cf->size+512)/1024);
	printf (" Currently at message %d\n", cf->current);
    }
    else
	printf (" No current mail file\n");
    if (!verbose)
	return;				/* done */
#ifdef HAVE_QUOTAS
    if (cf == NULL)
	quotaflag = ST_NOQUOTA;
    else {
	if (fstat (fileno(cf->filep), &sbuf) != 0) {
	    perror (cf->filename);
	    quotaflag = ST_BADQUOTA;
	}
#ifdef HAVE_QUOTACTL
	else if ((bdev = getspecialname (sbuf.st_dev)) == NULL) {
	    fprintf (stderr, "%s: couldn't get block special device\n",
		     cf->filename);
	    quotaflag = ST_BADQUOTA;
	}
	else if (quotactl (Q_GETQUOTA, bdev, UID, &qblk) != 0)
	    quotaflag = ST_NOQUOTA;	/* assume this, random error codes */
#else
	else if (quota (Q_GETDLIM, UID, (int) sbuf.st_dev, &qblk) != 0)
	    quotaflag = ST_NOQUOTA;	/* assume this, random error codes */
#endif
	if (quotaflag != ST_NOQUOTA) {
	    printf (" Blocks free: ");
	    if (quotaflag == ST_BADQUOTA)
		printf ("unknown\n");
	    else {				/* ST_GOODQUOTA */
		u_long i;
		i = qblk.dqb_bhardlimit - qblk.dqb_curblocks;
		printf ("%ld (%ld kb)\n", i,
#if defined(__NeXT__)
			dbtob(i,1024)
#else
			(dbtob(i)>>10)
#endif
			);		/* 2^10 = 1 kb */
	    }
	}
    }
#endif
    printf (" User: %s <%s@%s>\n",
	    real_personal_name ? real_personal_name : "",
	    user_name, fullhostname);
    printf (" Process ID = %d, User ID = %d\n", PID, UID);
}

#ifdef HAVE_QUOTAS
#ifdef HAVE_QUOTACTL
/*
 * getspecialname:
 * get the block special device name for this major/minor number
 * cache the result, since they'll probably run stat on the same file
 * over and over (or at least the same filesystem)
 */
char *
getspecialname(filedev)
dev_t filedev;
{
    FILE *mntf;
    struct mntent *ent;
    struct stat statb;
    static dev_t oldfiledev = 0;
    static char *oldspecialname = NULL;

    if (oldfiledev == filedev)		/* same partition as last time */
	return (oldspecialname);

    if ((mntf = setmntent (MNTTAB, "r")) == NULL)
	return (NULL);
    while ((ent = getmntent(mntf)) != NULL) {
	if (stat(ent->mnt_dir, &statb) != 0) /* look at device */
	    return (NULL);
	if (statb.st_dev == filedev) {	/* the right raw device? */
	    endmntent(mntf);		/* be neat */
	    safe_free (oldspecialname);
	    if ((oldspecialname = (char *)malloc (strlen (ent->mnt_fsname) +1))
		!= NULL){
		strcpy (oldspecialname, ent->mnt_fsname); /* cache it */
		oldfiledev = filedev;
	    }
	    else
		oldfiledev = 0;		/* flag no cached name */
	    return (ent->mnt_fsname);
	}
    }
    endmntent(mntf);
    return (NULL);			/* didn't find it */
}
#endif /* HAVE_QUOTACTL */
#endif /* HAVE_QUOTAS */

void
blank (VOID)
{
    if (cmcsb._cmoj)
    {
	cmcls();
	return;
    }
    /* XXX shouldn't have to do it this way, but cmcsb._cmoj is
       NULL when we're throwing away output inside init files, but
       we want to let users clear the screen in that context */
    cmcsb._cmoj = stdout;
    cmcls();
    cmcsb._cmoj = (FILE *) NULL;
}

void
#if HAVE_STDC
cmd_blank (int cmd)
#else /* K&R style */
cmd_blank (cmd)
int cmd;
#endif /* HAVE_STDC */
{
    confirm ();
    blank ();
}

void
#if HAVE_STDC
cmd_daytime (int n)
#else /* K&R style */
cmd_daytime (n)
int n;
#endif /* HAVE_STDC */
{
    static fdb seq_fdb_tad = { _CMTAD };

    parse (fdbchn (&seq_fdb_tad, &cfm_fdb, nil), &pv, &used);
    if (used == &seq_fdb_tad) {
	time_t t = datimetogmt (&pv._pvtad);
	confirm ();
	printf ("Local time: %s\n", daytime (t));
	return;
    }
    printf ("%s\n", daytime ((time_t) 0));
}

void
#if HAVE_STDC
cmd_echo (int n)
#else /* K&R style */
cmd_echo (n)
int n;
#endif /* HAVE_STDC */
{
    parse_text ("string to echo", nil);
    printf ("%s\n", atmbuf);
}

int
#if HAVE_STDC
ustrncmp(const char *str1, const char *str2, int n)
#else /* K&R style */
ustrncmp(str1, str2, n)
const char *str1, *str2;
int n;
#endif /* HAVE_STDC */
{
    char s1, s2;
    while(n > 0) {
 	s1 = islower(*str1) ? toupper(*str1) : *str1;
 	s2 = islower(*str2) ? toupper(*str2) : *str2;

	if (s1 == s2) {
	    if (s1 == '\0') return(0);
	    else {
		str1++; str2++; n--;
	    }
	    continue;
	}
	if (s1 > s2) return(1);
	if (s1 < s2) return(-1);
    }
    return(0);
}

#define used used_		/* void warnings about shadowed global */

void
#if HAVE_STDC
cmd_cd (int n)
#else /* K&R style */
cmd_cd (n)
int n;
#endif /* HAVE_STDC */
{
    pval parseval;			/* ccmd parse return structure */
    fdb *used;				/* which fdb was used */
    static fdb conffdb = { _CMCFM, CM_SDH, NULL, NULL,
			       "confirm to connect to your home directory",
			       NULL, NULL};
    static fdb dirfdb = { _CMFIL, FIL_DIR,
			      NULL, NULL, "directory", NULL, NULL };

    noise ("to directory");

    dirfdb._cmdef = HOME;		/* set default */
    parse (fdbchn(&dirfdb,&conffdb,NULL), &parseval, &used);
    if (used == &dirfdb) {
	confirm();
	if (chdir (parseval._pvfil[0]) != 0) {
	    perror ("cd");
	    return;
	}
	return;
    }
    /* else cd to HOME */
    if (HOME == NULL) {
	fprintf (stderr, "Can't find home directory.\n");
	return;
    }
    if (chdir (HOME) != 0) {
	perror ("cd");
	return;
    }
}

#undef used

void
#if HAVE_STDC
cmd_pwd (int n)
#else /* K&R style */
cmd_pwd (n)
int n;
#endif /* HAVE_STDC */
{
  char wd[MAXPATHLEN];

  confirm();
  if ((char *)getwd(wd) == NULL) {
    fprintf (stderr, "%s\n", wd);
    return;
  }
  else
    printf ("%s\n", wd);
}


static int
#if HAVE_STDC
msgcmp(message *a, message *b)
#else /* K&R style */
msgcmp(a,b)
message *a, *b;
#endif /* HAVE_STDC */
{
    if (a->date < b->date)
	return(-1);
    if (a->date > b->date)
	return(1);
    return(0);
}

#define CS (cf->sequence)
#define PS (cf->prev_sequence)
#undef RS				/* HP-UX already defined this */
#define RS (cf->read_sequence)

void
#if HAVE_STDC
cmd_sort(int n)
#else /* K&R style */
cmd_sort(n)
int n;
#endif /* HAVE_STDC */
{
    int i;

    if (!check_cf(O_RDWR))		/* precheck for file */
	return;
    noise ("chronologically");
    confirm();
    if (!check_cf(O_WRONLY))		/* check for writeable file */
        return;
    qsort(&cf->msgs[1], cf->count, sizeof(message), (QSORT_FUN_TYPE)msgcmp);
    clear_sequence(CS);
    clear_sequence(RS);
    clear_sequence(PS);
    cf->flags |= MF_DIRTY;
    for(i = 0; i < cf->count; i++)
	cf->msgs->flags |= M_MODIFIED;
}

void
#if HAVE_STDC
cmd_alias (int n)
#else /* K&R style */
cmd_alias (n)
int n;
#endif /* HAVE_STDC */
{
    confirm();
    printhelp ("alias", HELP_TOP, HELP_TOP);
}

void
#if HAVE_STDC
cmd_finger(int n)
#else /* K&R style */
cmd_finger(n)
int n;
#endif /* HAVE_STDC */
{
    static fdb cmdfdb = { _CMTXT };
    char cmd[BUFSIZ];

    parse (&cmdfdb, &pv, &used);	/* line of text */
    if (strlen(finger_command) == 0)
	cmerr ("no finger command defined - use SET FINGER-COMMAND to define one");
    strcpy(cmd, finger_command);
    strcat(cmd, " ");
    strcat(cmd, pv._pvstr);
    shell(cmd);
}


/*
 * check the messages in cf, and see if they look ok...
 * something is trashing fuat's mail file.  maybe we can spot it.
 */


void
#if HAVE_STDC
debug_validate_msgvec(const char *str)
#else /* K&R style */
debug_validate_msgvec(str)
const char *str;
#endif /* HAVE_STDC */
{
    int i;
    char *cp;
    int uhoh = 0;

    if (cf == NULL || cf->msgs == NULL)
	return;
    if (memdebug) {
	for (i = 1; i <= cf->count; i++) {
	    cp = hfind("from", cf->msgs[i].text);
	    if (cp == NULL) {
		if (uhoh == 0)
		    fprintf(stderr,"%s\n",str);
		fprintf(stderr,
			"***** Message %d no longer has a from field!!!\n",i);
		uhoh++;
	    }
	}
	if (uhoh) {
	    maybe_abort("Some corrupted messages");
	}
#ifdef MDEBUG
	m_checkranges();
#endif /* MDEBUG */
    }
}
