/*********************************************************************
 *
 *            EZFM---EZWGL based file manager for X11
 *
 *             Copyright (C) 1998  Maorong Zou
 *
 *
 *  This file is part of EZFM.
 *
 *  EZFM is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * EZFM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with EZFM; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 *************************************************************************/



#define _INFO_C_
#include "ezfm.h"

#define FILE_COMMAND      "/usr/bin/file"
/********************************************************************************/
char    *getFileShortInfo    MY_ANSIARGS((char *pathname, char *str));
char    *getFileOwnerString  MY_ANSIARGS((char *str, struct stat *sbuf));
char    *getFileGroupString  MY_ANSIARGS((char *str, struct stat *sbuf));
char    *getFilePermiString  MY_ANSIARGS((char *str, struct stat *sbuf));
char    *getFileMTimeString  MY_ANSIARGS((char *str, struct stat *sbuf));
char    *strStMode           MY_ANSIARGS((char *str, int mode));

char    *getFileType         MY_ANSIARGS((char *name, char *info_ret));
int      getFileInfo         MY_ANSIARGS((char *name, char *info_ret, struct stat *stat_ret));

/********************************************************************************
 *
 * Return stat info of pathname. Caller is responsible for supplying a larger
 * enough (356 bytes) storage in info_ret.  info_ret is filled as:
 *   the first 20 bytes:  "size Bytes"
 *   the next  30 bytes:  "modification time",
 *   the next  10 bytes:  "owner"
 *   the next  10 bytes:  "group",
 *   the next  15 bytes:  "mode",
 *   the next   5 bytes:  "nLinks",
 *   remaining 256 bytes: "typeInfo",
 *
 * Return value: total number of bytes set.
 */
int getFileInfo(fname, info_ret, stat_ret)
     char *fname, *info_ret; struct stat *stat_ret;
{
  struct stat  statbuf, *ptr;
  if(stat_ret == NULL) ptr = &statbuf;
  else ptr = stat_ret;
  if(lstat(fname, ptr) == 0 )
    {
      char *str, tbuf[256];
      sprintf(info_ret, "%d Bytes", ptr->st_size);
      sprintf(info_ret+20, "%s", getFileMTimeString(tbuf, ptr));
      sprintf(info_ret+50, "%s", getFileOwnerString(tbuf, ptr));
      sprintf(info_ret+60, "%s", getFileGroupString(tbuf, ptr));
      sprintf(info_ret+70, "%s", getFilePermiString(tbuf, ptr));
      sprintf(info_ret+85, "%d", ptr->st_nlink);
      str = getFileType(fname, tbuf);
      if(str)
	{
	  sprintf(info_ret+90, "%s", str);
	  return(90+strlen(str));
	}
      else
	{
	  info_ret[90] = '\0';
	  return(90);
	}
    }
  return(0);
}
/********************************************************************************
 *
 * execute 'file' and return the output in info_ret.
 */
char *getFileType(pathname, info_ret)
     char *pathname; char *info_ret;
{
  char  fileTypeInfo[256];
  int   status, pid, n, fd[2];

  if(pipe(fd) != 0) return(NULL);
  if((pid = fork()) < 0) return(NULL);

  if(pid == 0) /* child */
    {
      close(fd[0]); /* close read end of pipe */
      if( fd[1] != STDOUT_FILENO)
	{
	  if(dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO)
	    { fprintf(stderr, "dup2 failed\n"); exit(1);}
	  close(fd[1]);
	}
      execl(FILE_COMMAND, "file", pathname, NULL);
    }
  else  /* parent */
    {
      close(fd[1]); /* close write end of pipe */
      if( waitpid(pid, &status, 0) < 0) { close(fd[0]); return(NULL);}
      if(status) { close(fd[0]); return(NULL);}
      n = read(fd[0], fileTypeInfo, 255);
      close(fd[0]);
      if(n > 0)
	{
	  fileTypeInfo[n] = '\0';
	  if(fileTypeInfo[n-1]=='\n') fileTypeInfo[n-1] = '\0';
	  strcpy(info_ret, fileTypeInfo);
	  return(info_ret + strlen(pathname) + 2);
	}
      return(NULL);
    }
}
/***************************************************************
 *
 * Convert stat info to String.
 */
char *getFileOwnerString(str, stat_buf)
     char *str; struct stat *stat_buf;
{
  uid_t uid = stat_buf->st_uid;
  struct passwd *pw = getpwuid(uid);

  if(pw != NULL) strcpy(str, pw->pw_name);
  else sprintf(str, "%ld", uid);
  return(str);
}

char *getFileGroupString(str, stat_buf)
     char *str; struct stat *stat_buf;
{
  gid_t gid = stat_buf->st_gid;
  struct group *gp = getgrgid(gid);

  if(gp != NULL) strcpy(str, gp->gr_name);
  else sprintf(str, "%ld", gid);
  return(str);
}

char *getFilePermiString(str, stat_buf)
     char *str; struct stat *stat_buf;
{
  return(strStMode(str, stat_buf->st_mode));
}

char *strStMode(str, st_mode)
     char *str; int st_mode;
{
  int islnk = S_ISLNK(st_mode);
  
  str[0] =  (S_ISDIR(st_mode)) ? 'd' : islnk ? 'l' : '-';
  
  str[1] = (st_mode & S_IRUSR) ? 'r' : '-'; 
  str[2] = (st_mode & S_IWUSR) ? 'w' : '-'; 
  str[3] = (st_mode & S_IXUSR) ? 'x' : '-'; 

  str[4] = (st_mode & S_IRGRP) ? 'r' : '-'; 
  str[5] = (st_mode & S_IWGRP) ? 'w' : '-'; 
  str[6] = (st_mode & S_IXGRP) ? 'x' : '-'; 

  str[7] = (st_mode & S_IROTH) ? 'r' : '-'; 
  str[8] = (st_mode & S_IWOTH) ? 'w' : '-'; 
  str[9] = (st_mode & S_IXOTH) ? 'x' : '-'; 
  
  str[10] = '\0';

  return(str);
}

char *getFileMTimeString(str, sbuf)
     char *str; struct stat *sbuf;
{
  time_t currentTime = time(NULL);
  strcpy(str, ctime(&(sbuf->st_ctime)) + 4 );
  if( (int)(currentTime - sbuf->st_ctime) < SECONDS_IN_YEAR) str[16] = '\0';
  else str[20]='\0';
  return(str);
}
/***************************************************************
 *
 *  summarize the stat info of 'path' and return it to str_ret.
 */
char *getFileShortInfo(path, str_ret)
     char *path, *str_ret;
{
  struct stat stat_buf;
  int status = lstat(path, &stat_buf);
  if(status >= 0)
    {
      int slash = 0, len = strlen(path);
      char *ptr, name[128];
      if(path[len-1] == '/') {  path[len-1] = '\0'; slash = 1;}
      ptr = strrchr(path, '/');
      if(len > 1 && ptr) strcpy(name, ptr+1);
      else strcpy(name, path);
      if(slash)  path[len-1] = '/';
      if(S_ISLNK(stat_buf.st_mode))
	{
	  char tmpstr[128];
	  int  cnt = readlink(path, tmpstr, 127); 
	  if(cnt > 0)
	    {
	      tmpstr[cnt] = '\0';
	      sprintf(str_ret, "%s: symbolic link to '%s' ", name, tmpstr);
	    }
	  else
	    sprintf(str_ret, "%s: broken symbolic link", name);
	}
      else
	{
	  char owner[10], grp[10], date[32], perms[16];
	  time_t currentTime = time(NULL);
	  
	  (void)getFileOwnerString(owner, &stat_buf);
	  (void)getFileGroupString(grp, &stat_buf);
	  (void)getFilePermiString(perms, &stat_buf);
	  (void)getFileMTimeString(date, &stat_buf);
	  sprintf(str_ret, "%s:  %d bytes,  %s,  %s/%s,  %s", name, stat_buf.st_size,
		  date, owner, grp, perms);
	}
    }
  else str_ret[0]='\0';
  return(str_ret);
}
/********************************************************************************/
#undef _INFO_C_
