/* LightSword Updater (update) - a command line to automatically         */
/* update newest files across two directories                            */
/* Author - Mike Farrell                                                 */

/* TODO:  Enable multi argument passing by letter (-vbf) */

/*Defines*******************************************************************/
#define VERSION_STR   "0.4b"

#ifndef __linux
  #warning Untested on non-linux platforms
#endif

/*Includes******************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>

/*Prototypes****************************************************************/
char yes_or_no();
char update(char *fname);
char file_compare(char *f1, char *f2);
void print_errno();
void do_copy(char *src, char *dest);


/*Globals*******************************************************************/
char other_dir[128] = "",
     verbose = 0, ask = 1, make_backups = 0;

/*yes_or_no()***************************************************************/
/*ask a question*/
char yes_or_no()
{
  char c = 0;

  printf("[Y/N] ");
  while(!isprint(c))
    c = getchar();

/*  c = fgetc(stdin);
    fflush(stdin);
    fscanf(stdin, "%c", &c);*/

  return (c == 'y') ? 1 : 0;
}

/*print_errno()*************************************************************/
/*print out error message*/
void print_errno()
{
  switch(errno)
  {
    case ENOENT:
      fprintf(stderr, "file doesn't exist");
    break;
    case EACCES:
      fprintf(stderr, "persmission denied");
    break;
    default:
      fprintf(stderr, "unknown error");
    break;
  }
}

/*update()******************************************************************/
/*Do the actual process of updating the file*/
char update(char *fname)
{
  char buf[64];
  struct stat s;
  time_t time1, time2;
  off_t size1, size2;

  if(stat(fname, &s) == -1)
  {
    fprintf(stderr, "Error reading %s (", fname);
    print_errno();
    fprintf(stderr, ")\n");
    return 0;
  }
  time1 = s.st_mtime;
  size1 = s.st_size;

  sprintf(buf, "%s/%s", other_dir, fname);
  if(stat(buf, &s) == -1)
  {
    fprintf(stderr, "Error reading %s (", buf);
    print_errno();
    fprintf(stderr, ")\n");
    return 0;
  }
  time2 = s.st_mtime;
  size2 = s.st_size;

  if(file_compare(fname, buf) == 0)
  {
    if(verbose) printf("%s is the same as %s...leaving alone..\n", fname, buf);
    return 1;
  }

  if(time1 > time2)
  {
    if(verbose) printf("%s > %s\n", fname, buf);
    if(ask)
    {
/*   This is a stupid warning
     if(size1 < size2)
        printf("%s: Size is greater...use caution..\n", fname);*/

      printf("Overwrite %s?", buf);
      if(yes_or_no())
        do_copy(fname, buf);
    }
    else do_copy(fname, buf);
  }
  else if(time1 < time2)
  {
    /*
    if(size1 > size2)
      printf("%s: Size is greater...use caution..\n", buf);*/

    if(verbose) printf("%s < %s\n", fname, buf);
    if(ask)
    {
      printf("Overwrite %s?", fname);
      if(yes_or_no())
        do_copy(buf, fname);
    }
    else do_copy(buf, fname);
  }
  else if(verbose) printf("%s = %s\n", fname, buf);


  return 1;
}

/*File compare**************************************************************/
/*returns 0 if two files are identical*/
char file_compare(char *f1, char *f2)
{
  FILE *file1 = fopen(f1, "rb"), *file2 = fopen(f2, "rb");
  char ret = 0;
  int b1, b2;

  if(!file1 || !file2)
  {
    if(file1)
      fclose(file1);
    if(file2)
      fclose(file2);
    return -1;
  }

  while(1)
  {
    b1 = fgetc(file1);
    b2 = fgetc(file2);

    if(b1 != b2)
    {
      ret = 1;
      break;
    }

    if(b1 == EOF || b2 == EOF)
      break;
  }

  return ret;
}


/*do_copy*******************************************************************/
/*copy one file onto another making backups if necessary*/
void do_copy(char *src, char *dest)
{
  char fmt[256];

  if(make_backups)
  {
    sprintf(fmt, "cp \"%s\" \"%s\".upd_bkp~", dest, dest);

    if(verbose)
      printf("Backed up %s as %s.upd_bkp~\n", dest, dest);
    system(fmt);
  }

  sprintf(fmt, "cp -v \"%s\" \"%s\"", src, dest);
  system("echo -en \"\033[36m\"");
  system(fmt);
  system("echo -en \"\033[0m\"");
}


/*Main**********************************************************************/
int main(int argc, char *argv[])
{
  unsigned short i;
  char ok;
  struct stat s;

  /*help message*/
  if(argc <= 2)
  {
    printf("Update version %s\nUpdates files between directories based off of modification times..\n\n", VERSION_STR);
    printf("Usage: -[options] update file1 file2 file3 /path/to/directory\n\n");
    printf("-f, --force      disable confirmation for replacing files\n");
    printf("-v, --verbose    be verbose\n");
    printf("-b, --backup     make backups (file.upd_bkp~)\n");
    printf("\n");
    printf("Report bugs to Mike Farrell <gccdragoonkain@yahoo.com>\n");

    return 0;
  }

  /*handle command line options*/
  for(i = 1; i < argc-1; i++) if(argv[i][0] == '-')
  {
    if(strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--force") == 0)
      ask = 0;
    else if(strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0)
      verbose = 1;
    else if(strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--backup") == 0)
      make_backups = 1;
    else
    {
      fprintf(stderr, "Unknown option \"%s\"..\n", argv[i]);
      return 1;
    }
  }


  /*Check last argument to be a directory...*/
  stat(argv[argc-1], &s);
  if(!S_ISDIR(s.st_mode))
  {
    fprintf(stderr, "Last argument must be a directory...\n");
    return 0;
  }

  /*Update the files*/
  strcpy(other_dir, argv[argc-1]);
  printf("Updading from %s...\n", other_dir);
  for(i = 1; i < argc-1; i++)
  {
    if(argv[i][0] != '-') update(argv[i]);
  }

  return 0;
}

/*End of prog***************************************************************/

