/* filefuncs.c: Implementation of file-based functions in Meta-HTML. */

/* Author: Brian J. Fox (bfox@ai.mit.edu) Fri Mar 22 13:54:13 1996.

   This file is part of <Meta-HTML>(tm), a system for the rapid deployment
   of Internet and Intranet applications via the use of the Meta-HTML
   language.

   Copyright (c) 1995, 1996, Brian J. Fox (bfox@ai.mit.edu).
   Copyright (c) 1996, Universal Access Inc. (http://www.ua.com).

   Meta-HTML is free software; you can redistribute it and/or modify
   it under the terms of the UAI Free Software License as published
   by Universal Access Inc.; either version 1, or (at your option) any
   later version.

   This program 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
   UAI Free Software License for more details.

   You should have received a copy of the UAI Free Software License
   along with this program; if you have not, you may obtain one by
   writing to:

   Universal Access Inc.
   129 El Paseo Court
   Santa Barbara, CA
   93101  */

#include "language.h"

static void pf_get_file_properties (PFunArgs);
static void pf_set_file_properties (PFunArgs);
static void pf_directory_contents (PFunArgs);

static char *unix_username_from_uid (uid_t uid);
static void unix_add_date (Symbol *sym, long ticks);

PFunDesc filefunc_table[] = {
  { "GET-FILE-PROPERTIES",	0, 0, pf_get_file_properties },
  { "SET-FILE-PROPERTIES",	0, 0, pf_set_file_properties },
  { "DIRECTORY-CONTENTS",	0, 0, pf_directory_contents },
  { (char *)NULL,		0, 0, (PFunHandler *)NULL }
};

PACKAGE_INITIALIZER (initialize_file_functions)
void
initialize_file_functions (Package *package)
{
  register int i;
  Symbol *sym;

  for (i = 0; filefunc_table[i].tag != (char *)NULL; i++)
    {
      sym = symbol_intern_in_package (package, filefunc_table[i].tag);
      sym->type = symtype_FUNCTION;
      sym->values = (char **)(&filefunc_table[i]);
    }
}

/* <get-file-properties /www/nirvana/docs/welcome.mhtml>

     returns:
       ((name . "welcome.mhtml")
        (full-name . "/www/nirvana/docs/welcome.mhtml")
	(size . 2188)
	(type . FILE)
	(created 1 21 95)
	(written 1 21 95)
	(read 3 21 96)
	(creator . bfox))

     Values for TYPE: FILE, DIRECTORY, EXECUTABLE. */
static char *
get_file_properties (char *filename)
{
  char *result = (char *)NULL;

  if (filename != (char *)NULL)
    {
      struct stat finfo;
      int error;

      error = stat (filename, &finfo);

      if (error != -1)
	{
	  Package *package = symbol_get_package ((char *)NULL);
	  Symbol *sym;
	  char buffer[128];

	  /* FULL-NAME... */
	  sym = symbol_intern_in_package (package, "FULL-NAME");
	  symbol_add_value (sym, filename);

	  /* NAME... */
	  {
	    char *temp = strrchr (filename, '/');

	    if (temp != (char *)NULL)
	      temp++;
	    else
	      temp = filename;

	    sym = symbol_intern_in_package (package, "NAME");
	    symbol_add_value (sym, temp);
	  }

	  /* SIZE... */
	  sprintf (buffer, "%ld", (long)finfo.st_size);
	  sym = symbol_intern_in_package (package, "SIZE");
	  symbol_add_value (sym, buffer);

	  /* CREATED... */
	  sym = symbol_intern_in_package (package, "CREATED");
	  unix_add_date (sym, finfo.st_ctime);

	  /* WRITTEN... */
	  sym = symbol_intern_in_package (package, "WRITTEN");
	  unix_add_date (sym, finfo.st_mtime);

	  /* READ... */
	  sym = symbol_intern_in_package (package, "READ");
	  unix_add_date (sym, finfo.st_atime);

	  /* TYPE... */
	  sym = symbol_intern_in_package (package, "TYPE");
	  if (S_ISDIR (finfo.st_mode))
	    symbol_add_value (sym, "DIRECTORY");
	  else if ((S_IXOTH & finfo.st_mode))
	    symbol_add_value (sym, "EXECUTABLE");
	  else if (S_ISREG (finfo.st_mode))
	    symbol_add_value (sym, "FILE");

	  /* CREATOR... */
	  sym = symbol_intern_in_package (package, "CREATOR");
	  symbol_add_value (sym, unix_username_from_uid (finfo.st_uid));

	  result = package_to_alist (package, 0);
	  symbol_destroy_package (package);
	}
    }

  return (result);
}

static void
unix_add_date (Symbol *sym, long ticks)
{
  struct tm *ti = localtime ((const time_t *)&ticks);
  char buffer[20];

  sprintf (buffer, "%02d", ti->tm_mon + 1);
  symbol_add_value (sym, buffer);

  sprintf (buffer, "%02d", ti->tm_mday);
  symbol_add_value (sym, buffer);

  sprintf (buffer, "%02d", ti->tm_year);
  symbol_add_value (sym, buffer);

  sprintf (buffer, "%02d", ti->tm_hour);
  symbol_add_value (sym, buffer);

  sprintf (buffer, "%02d", ti->tm_min);
  symbol_add_value (sym, buffer);

  sprintf (buffer, "%02d", ti->tm_sec);
  symbol_add_value (sym, buffer);
}

static char *
unix_username_from_uid (uid_t uid)
{
  static uid_t last_uid = -1;
  static char last_name[50];

  if (uid != last_uid)
    {
      struct passwd *entry = getpwuid (uid);

      last_name[0] = '\0';
      last_uid = uid;

      if (entry != (struct passwd *)NULL)
	strcpy (last_name, entry->pw_name);

    }
  return (last_name);
}

/* File properties are returned in an alist representing operating system
   information about the named path.  Pathnames must be given fully; they
   are *not* relative to web space in any way.  This is probably a huge
   security hole, but it can't be any worse than CGI-EXEC.  Yeeesh. */
static void
pf_get_file_properties (PFunArgs)
{
  char *filename = mhtml_evaluate_string (get_positional_arg (vars, 0));
  char *result = get_file_properties (filename);

  if (result != (char *)NULL)
    {
      bprintf_insert (page, start, "%s", result);
      *newstart += strlen (result);
      free (result);
    }

  if (filename != (char *)NULL)
    free (filename);
}

/* Not yet implemented. */
static void
pf_set_file_properties (PFunArgs)
{
}

/* Return a directory as an alist of alists. */
static void
pf_directory_contents (PFunArgs)
{
  char *dirname = mhtml_evaluate_string (get_positional_arg (vars, 0));
  char *matching = mhtml_evaluate_string (get_value (vars, "matching"));

  if (!empty_string_p (dirname))
    {
      DIR *dir = opendir (dirname);
  
      if (dir != (DIR *)NULL)
	{
	  char *packname;
	  Package *package;
	  Symbol *sym;
	  struct dirent *entry;
	  char buffer[1024];
	  int offset = 0;
	  regex_t re;
	  regmatch_t where[2];
	  int check_re = 0;

	  if (!empty_string_p (matching))
	    {
	      regcomp (&re, matching, REG_EXTENDED);
	      check_re = 1;
	    }

	  packname = mhtml_evaluate_string (get_positional_arg (vars, 1));
	  if ((packname != (char *)NULL) && empty_string_p (packname))
	    package = CurrentPackage;
	  else
	    package = symbol_get_package (packname);

	  sprintf (buffer, "%s", dirname);
	  offset = strlen (buffer);
	  if (buffer[offset - 1] != '/')
	    buffer[offset++] = '/';

	  while ((entry = readdir (dir)) != (struct dirent *)NULL)
	    {
	      char *info;

	      strncpy (&buffer[offset], entry->d_name, D_NAMELEN (entry));
	      buffer[offset + D_NAMELEN (entry)] = '\0';

	      if ((check_re == 0) ||
		  (regexec (&re, buffer + offset, 1, where, 0) == 0))
		{
		  info = get_file_properties (buffer);
		  sym = symbol_intern_in_package (package, buffer + offset);
		  symbol_add_value (sym, info);
		  if (info != (char *)NULL)
		    free (info);
		}
	    }

	  if (packname != (char *)NULL)
	    {
	      free (packname);
	    }
	  else
	    {
	      char *result = package_to_alist (package, 0);
	      if (!empty_string_p (result))
		{
		  bprintf_insert (page, start, "%s\n", result);
		  *newstart += 1 + strlen (result);
		}

	      if (result != (char *)NULL)
		free (result);
	    }

	  if (check_re)
	    regfree (&re);
	}
    }

  if (matching != (char *)NULL) free (matching);
  if (dirname != (char *)NULL) free (dirname);
}
