#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <utmp.h>
#include <sys/ioctl.h>
#include "uptime.h"

static char buf[128];
static double av[3];
static char *sprint_uptime(void);

int uptime(double *uptime_secs, double *idle_secs) {
    static int got_uptime = 0;
    static double up = 0, idle = 0;

    if (!got_uptime) {
      FILE *f;
      f = fopen("/proc/uptime", "r");
      if (!f) return -1;
      if (fscanf(f, "%lf %lf", &up, &idle) < 2) {
        fprintf(stderr, "bad data in /proc/uptime\n");
        return 0;
      }
      fclose(f);
      got_uptime = 1;
    }
    if (uptime_secs) *uptime_secs = up;
    if (idle_secs) *idle_secs = idle;
    return up;  /* assume never be zero seconds in practice */
}

void loadavg(double *av1, double *av5, double *av15) {
    static int got_load = 0;
    static double avg_1 = 0, avg_5 = 0, avg_15 = 0;

    if (!got_load) {
      FILE *f;
      f = fopen("/proc/loadavg", "r");
      if (!f) return;
      if (fscanf(f, "%lf %lf %lf", &avg_1, &avg_5, &avg_15) < 3) {
        fprintf(stderr, "bad data in /proc/loadavg\n");
        exit(1);
      }
      got_load = 1;
    }
    if (av1) *av1 = avg_1;
    if (av5) *av5 = avg_5;
    if (av15) *av15 = avg_15;
}

char *sprint_uptime(void) {
  struct utmp *utmpstruct;
  int upminutes, uphours, updays;
  int pos;
  struct tm *realtime;
  time_t realseconds;
  int numuser;
  double uptime_secs, idle_secs;

/* first get the current time */

  time(&realseconds);
  realtime = localtime(&realseconds);
  pos = sprintf(buf, " %2d:%02d%s  ",
		realtime->tm_hour%12 ? realtime->tm_hour%12 : 12,
		realtime->tm_min, realtime->tm_hour > 11 ? "pm" : "am");

/* read and calculate the amount of uptime */

  uptime(&uptime_secs, &idle_secs);

  updays = (int) uptime_secs / (60*60*24);
  strcat (buf, "up ");
  pos += 3;
  if (updays)
    pos += sprintf(buf + pos, "%d day%s, ", updays, (updays != 1) ? "s" : "");
  upminutes = (int) uptime_secs / 60;
  uphours = upminutes / 60;
  uphours = uphours % 24;
  upminutes = upminutes % 60;
  if(uphours)
    pos += sprintf(buf + pos, "%2d:%02d, ", uphours, upminutes);
  else
    pos += sprintf(buf + pos, "%d min, ", upminutes);

/* count the number of users */

  numuser = 0;
  setutent();
  while ((utmpstruct = getutent())) {
    if ((utmpstruct->ut_type == USER_PROCESS) &&
       (utmpstruct->ut_name[0] != '\0'))
      numuser++;
  }
  endutent();

  pos += sprintf(buf + pos, "%2d user%s, ", numuser, numuser == 1 ? "" : "s");

  loadavg(&av[0], &av[1], &av[2]);

  pos += sprintf(buf + pos, " load average: %.2f, %.2f, %.2f",
		 av[0], av[1], av[2]);

  return buf;
}

void print_uptime(void)
{
  printf("%s\n", sprint_uptime());
}

/* This is a trivial uptime program.  I hereby release this program
 * into the public domain.  I disclaim any responsibility for this
 * program --- use it at your own risk.  (as if there were any.. ;-)
 * -michaelkjohnson (johnsonm@sunsite.unc.edu)
 *
 * Modified by Larry Greenfield to give a more traditional output,
 * count users, etc.  (greenfie@gauss.rutgers.edu)
 *
 * Modified by mkj again to fix a few tiny buglies.
 *
 * Modified by J. Cowley to add printing the uptime message to a
 * string (for top) and to optimize file handling.  19 Mar 1993.
 *
 */


