#ifndef SYSINFO_H
#define SYSINFO_H

extern unsigned long long Hertz;	/* clock tick frequency */

#define JT unsigned long long
extern int five_cpu_numbers(JT * uret, JT * nret, JT * sret, JT * iret,
		JT * iowait);
#undef JT

extern int loadavg(double *av1, double *av5, double *av15);
extern int uptime(double *uptime_secs, double *idle_secs);

struct meminfo_struct {
	struct {
		unsigned long long
		    total,
		    low_total,
		    high_total,
		    used,
		    low_used,
		    high_used,
		    free,
		    low_free,
		    high_free,
		    shared,
		    buffers,
		    cached,
		    swapcached,
		    active, inactive_dirty, inactive_clean, inactive_target;
	} mem;
	struct {
		unsigned long long total, used, free;
	} swap;
};

/*
 * XXX: When did gcc get support for named initializers?
 * They would be better here since they default to zero
 */
#define MEMINFO_ZERO { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0 } }

#define BAD_OPEN_MESSAGE					\
"Error: /proc must be mounted and readable\n"			\
"  To mount /proc at boot you need an /etc/fstab line like:\n"	\
"      /proc   /proc   proc    defaults\n"			\
"  In the meantime, `mount /proc /proc -t proc'\n"		\
"  To set the permissions, `chmod 755 /proc'\n"			\

#define STAT_FILE    "/proc/stat"
#define UPTIME_FILE  "/proc/uptime"
#define LOADAVG_FILE "/proc/loadavg"
#define MEMINFO_FILE "/proc/meminfo"

/* This macro opens filename only if necessary and seeks to 0 so
 * that successive calls to the functions are more efficient.
 * It also reads the current contents of the file into the global buf.
 */
#define FILE_TO_BUF(filename, fd) do{				\
    static int n;						\
    if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) {	\
	fprintf(stderr, BAD_OPEN_MESSAGE);			\
	close(fd);						\
	_exit(1);						\
    }								\
    lseek(fd, 0L, SEEK_SET);					\
    if ((n = read(fd, buf, sizeof buf - 1)) < 0) {		\
	perror(filename);					\
	close(fd);						\
	fd = -1;						\
	return 0;						\
    }								\
    buf[n] = '\0';						\
}while(0)

#define SET_IF_DESIRED(x,y)  if(x) *(x) = (y)	/* evals 'x' twice */

/* /proc files are output in C locale, while we might be using
 * some other locale.  Make sure we parse double values with
 * a "C" locale dot.
 */
#define ONE_DOUBLE(val, buf, failed) ({				\
    double _d;							\
    char *_p, *_q;						\
    val = strtoul(buf, &_p, 10);				\
    if (_p == buf && *_p != '.')				\
	failed = 1;						\
    if (*_p == '.') {						\
	_p++;							\
	if (*_p >= '0' && *_p <= '9') {				\
	    _d = strtoul(_p, &_q, 10);				\
	    while (_p != _q) _d /= 10, _p++;			\
	    if (val < 0)					\
		val -= _d;					\
	    else						\
		val += _d;					\
	}							\
    }								\
    _p; })

extern int meminfo(struct meminfo_struct *);
extern unsigned long long read_total_main(void);

#endif				/* SYSINFO_H */
