#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <sys/param.h>
#include <string.h>
#include <com_err.h>
#include <pwd.h>

#include "xznol.h"
#include "util.h"
#include "mymalloc.h"

char *expand_file_name(char *f)
{
   char *str = (char *) mymalloc(MAXPATHLEN);
   char *tmp;
   struct passwd *p;

   if (f[0] == '~') {
      if ((tmp = (char *) getenv("HOME")) ||
	  ((p = getpwuid(getuid())) &&
	   (tmp = p->pw_dir))) {
	 strcpy(str, tmp);
	 strcat(str, f+1);
      } else {
	 fprintf(stderr, "Lives are on sale at K-Mart this week, get one!\n");
      }
   } else {
      strcpy(str, f);
   }

   return(str);
}

void load_file(table *t)
{
   char *filename;
   FILE *f;
   char buf[BUFSIZ];
   char *info;
   int i;

   filename = table_file(t);

   if ((f = fopen(filename, "r")) == NULL) {
      com_err("xznol", errno, "while opening %s\n", filename);
      return;
   }

   while (1) {
      info = NULL;

      /* loop, reading comments.  stop on non-comment */
      do {
	 if (fgets(buf, BUFSIZ, f) == NULL) {
	    if (ferror(f))
	       com_err("xznol", errno, "while reading %s\n", filename);
	    goto loaddone;
	 }
	 if (buf[0] != '#') break;
	 info = strappend(info, buf+1);
      } while (1);

      /* if the next line is blank or begins with whitespace, throw out
	 the comment block */
      if (isspace(buf[0])) {
	 myfree(info);
	 continue;
      }

      /* otherwise, buf contains the username.  add it. */
      for (i=0; !isspace(buf[i]); i++) ;
      buf[i] = '\0';
      add_user(t, my_strdup(buf), info);
   }
   /*NOTREACHED*/

 loaddone:
   close(f);
}

void save_info(table *t, user *u, char *newinfo)
{
/* semantics:

   If there is a read error or a write error, immediately bail out,
   	print an error, and revert to the previous file.
   If the rename prints an error, print another error.
*/

   char *filename;
   char oldname[MAXPATHLEN];
   FILE *old, *new;
   char buf[BUFSIZ];
   char *username, *info, *tmp;

   filename = table_file(t);

   strcpy(oldname, filename);
   strcat(oldname, ".old");

   if (rename(filename, oldname) == -1) {
      com_err("xznol", errno, "while backing up %s\n", filename);
      return;
   }

   if ((old = fopen(oldname, "r")) == NULL) {
      com_err("xznol", errno, "while opening %s\n", oldname);
      goto backout;
   }

   if ((new = fopen(filename, "w")) == NULL) { 
      com_err("xznol", errno, "while opening %s for write\n", filename);
      fclose(old);
      goto backout;
   }
      
   username = get_user_fnum_text(u, LINE_USER);

   /* finally.  old and new are open */

   while (1) {
      info = NULL;

      /* loop, reading comments.  stop on non-comment */
      do {
	 if (fgets(buf, BUFSIZ, old) == NULL) {
	    if (ferror(old))
	       goto saverr;
	    else
	       goto savedone;
	 }
	 if (buf[0] != '#') break;
	 info = strappend(info, buf);
      } while (1);

      /* if the next line is blank or begins with whitespace, write out
	 the comment block and line */
      if (isspace(buf[0])) {
	 if ((fputs(info, new) == EOF) ||
	     (fputs(buf, new) == EOF))
	    goto saverr;
	 myfree(info);
	 continue;
      }

      /* otherwise, buf contains the username.  If the username is the
	 username being saved, save the new info and the username.
	 Otherwise, just write the old info and username back out again. */
      if (strncmp(buf, username, strlen(username))) {
	 if ((info && (fputs(info, new) == EOF)) ||
	     (fputs(buf, new) == EOF))
	    goto saverr;
      } else {
	 if (*newinfo) {
	    /* write out the info, commented */
	    tmp = newinfo;

	    /* write the first # */
	    if (putc('#', new) == EOF)
	       goto saverr;

	    while(*tmp) {
	       /* write the character */
	       if (putc(*tmp, new) == EOF)
		  goto saverr;
	       if (*tmp == '\n') {
		  /* if the character was a newline, and was last, stop */
		  if (*(tmp+1) == '\0') break;

		  /* otherwise, write a # character */
		  if (putc('#', new) == EOF)
		     goto saverr;
	       }
	       tmp++;
	    }

	    /* to get here, either a final \n was ignored, or there was
	       no final \n.  Write one. 
	       Also, write the username and a newline for that */
	    if ((putc('\n', new) == EOF) ||
		(fputs(username, new) == EOF) ||
		(putc('\n', new) == EOF))
	       goto saverr;
	 }

	 myfree(info);
      }
   }
   /*NOTREACHED*/

 savedone:
   fclose(old);
   if (fclose(new) == EOF) {
      com_err("xznol", errno, "while closing %s\n", filename);
      goto backout;
   }
   return;

 saverr:
   com_err("xznol", errno, "while writing %s\n", filename);
   /* close, don't check error return.  We're punting, anyway. */
   fclose(old);
   fclose(new);

 backout:
   if (rename(oldname, filename) == -1)
      com_err("xznol", errno, "while restoring %s!  Panic!\n", filename);

}

void save_newuser(table *t, char *username)
{
   char *filename;
   FILE *f;
   int i;
   
   for (i=0; !isspace(username[i]); i++) ;
   username[i] = '\0';

   filename = table_file(t);

   if ((f = fopen(filename, "a")) == NULL) { 
      com_err("xznol", errno, "while opening %s for write\n", filename);
   }
      
   if ((fputs(username, f) == EOF) ||
       (putc('\n', f) == EOF)) {
      com_err("xznol", errno, "while writing %s\n", filename);
      fclose(f);
      return;
   }

   if (fclose(f) == EOF)
      com_err("xznol", errno, "while closing %s\n", filename);
}
