/*
 * WMMail - Window Maker Mail
 *
 * wmutil.c: WMMail miscellaneous function library, ripped from
 *           Alfredo Kojima's Window Maker (http://www.windowmaker.org)
 *
 * Window Maker miscellaneous function library
 * 
 * Copyright (c) 1997 Alfredo K. Kojima
 * Modifications Copyright (c) 1998 Bryan Chan
 * 
 * This program 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 of the License, 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
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: wmutil.c.in,v 1.1 2000/07/02 20:39:17 bryan.chan Exp $
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <limits.h>
#include "list.h"


extern int   keep_quiet;  /* defined in global.c */
extern char *app_name;    /* defined in global.c */

static char *get_user_home_dir(char *);
static char *check_file(char *, char *, char *);
static char *next_token(char *, char **);
static void  parse_command(char *, char ***, int *);


#define MAX_ERRORMSG_LENGTH  256

void croak(char *fmt,...)
{
  va_list arg_ptr;
  static char buf[MAX_ERRORMSG_LENGTH];

  if (keep_quiet)
    return;

  va_start(arg_ptr, fmt);

  vsprintf(buf, fmt, arg_ptr);
  strcat(buf, "\n");
  fflush(stdout);
  fputs(app_name, stderr);
  fputs(": ", stderr);
  fputs(buf, stderr);
  fflush(stdout);
  fflush(stderr);

  va_end(arg_ptr);
}


void *wmalloc(size_t size)
{
  void *tmp;
    
  tmp = calloc(1, size);

  if (tmp == NULL)
  {
    croak("wmalloc() failed; retrying after 2 seconds");
    sleep(2);
    tmp = calloc(1, size);

    if (tmp == NULL)
    {
      croak("virtual memory exhausted");
      exit(-1);
    }
  }

  return tmp;
}


void *wrealloc(void *ptr, size_t newsize)
{
  void *nptr;

  if (!ptr)
    nptr = calloc (1, newsize);
  else
    nptr = realloc(ptr, newsize);

  if (nptr==NULL)
  {
    croak("wrealloc() failed");
    return NULL;
  }

  return nptr;
}


inline void wfree(void *ptr)
{
  if (ptr != NULL)
    free(ptr);
}


char *wstrdup(char *str)
{
  if (str == NULL)
    return NULL;

  return strcpy(wmalloc(strlen(str) + 1), str);
}


char *get_home_dir()
{
  char *home = getenv("HOME");
  struct passwd *user;

  if (home)
    return home;
    
  if ( !(user = getpwuid(getuid())) )
  {
    croak("cannot get password entry for UID %i", getuid());
    return "/";
  }

  if (!user->pw_dir)
    return "/";
  else
    return user->pw_dir;
}


static char *get_user_home_dir(char *username)
{
  struct passwd *user;
    
  if ( !(user = getpwnam(username)) )
  {
    croak("cannot get password entry for user %s", username);
    return NULL;
  }

  if (!user->pw_dir)
    return "/";
  else
    return user->pw_dir;
}


char *expand_path(char *path)
{
  char buffer2[PATH_MAX+2];
  char buffer[PATH_MAX+2];
  int i;

  memset(buffer, 0, PATH_MAX+2);
    
  if (*path=='~')
  {
    char *home;
        
    path++;
    if (*path=='/' || *path==0)
    {
      home = get_home_dir();
      strcat(buffer, home);
    }
    else
    {
      int j;

      j = 0;
      while (*path!=0 && *path!='/')
      {
        buffer2[j++] = *path;
        buffer2[j] = 0;
        path++;
      }

      home = get_user_home_dir(buffer2);

      if (!home)
        return NULL;

      strcat(buffer, home);
    }
  }
    
  i = strlen(buffer);

  while (*path!=0)
  {
    char *tmp;
        
    if (*path=='$')
    {
      int j = 0;
      path++;

      /* expand $(HOME) or $HOME style environment variables */
      if (*path=='(')
      {
        path++;

        while (*path!=0 && *path!=')')
        {
            buffer2[j++] = *(path++);
            buffer2[j] = 0;
        }

        if (*path==')')
          path++;

        tmp = getenv(buffer2);

        if (!tmp)
        {
          buffer[i] = 0;
          strcat(buffer, "$(");
          strcat(buffer, buffer2);
          strcat(buffer, ")");
          i += strlen(buffer2)+3;
        }
        else
        {
          strcat(buffer, tmp);
          i += strlen(tmp);
        }
      }
      else
      {
        while (*path!=0 && *path!='/')
        {
          buffer2[j++] = *(path++);
          buffer2[j] = 0;
        }

        tmp = getenv(buffer2);

        if (!tmp)
        {
          strcat(buffer, "$");
          strcat(buffer, buffer2);
          i += strlen(buffer2)+1;
        }
        else
        {
            strcat(buffer, tmp);
            i += strlen(tmp);
        }
      }            
    }
    else
    {
      buffer[i++] = *path;
      path++;
    }
  }
    
  return wstrdup(buffer);
}


char *find_resource(char *resource, char *folders)
{
  char *prefix, *path;

  /* try to find resources in the following locations (in order):
   *
   *   1. $GNUSTEP_USER_ROOT/folders/resource
   *   2. ~/GNUstep/folders/resource
   *   3. $GNUSTEP_LOCAL_ROOT/folders/resource
   *   4. /usr/local/GNUstep/folders/resource
   *   5. /usr/X11R6/GNUstep/folders/resource
   *   6. $GNUSTEP_SYSTEM_ROOT/folders/resource
   *   7. /usr/GNUstep/folders/resource
   */

  if (prefix = getenv("GNUSTEP_USER_ROOT"))
    if (path = check_file(prefix, folders, resource))
      return path;

  if (get_home_dir())
  {
    prefix = wmalloc(strlen(get_home_dir()) + 9);
    strcpy(prefix, get_home_dir());
    strcat(prefix, "/GNUstep");

    if (path = check_file(prefix, folders, resource))
    {
      wfree(prefix);
      return path;
    }
    else
      wfree(prefix);
  }

  if (prefix = getenv("GNUSTEP_LOCAL_ROOT"))
    if (path = check_file(prefix, folders, resource))
      return path;

  if (path = check_file("/usr/X11R6/GNUstep", folders, resource))
    return path;

  if (path = check_file("/usr/local/GNUstep", folders, resource))
    return path;

  if (prefix = getenv("GNUSTEP_SYSTEM_ROOT"))
    if (path = check_file(prefix, folders, resource))
      return path;

  if (path = check_file("/usr/GNUstep", folders, resource))
    return path;

  return (char *) NULL;
}


static char *check_file(char *path, char *folders, char *resource)
{
  char *buf;
  int   bufsize;

  bufsize = strlen(path) + strlen(resource) + (folders ? strlen(folders) : 0);
  bufsize += 3;    /* account for slashes and terminating NULL */
  buf = (char *) wmalloc(bufsize);

  strcpy(buf, path);

  if (folders)
  {
    strcat(buf, "/");
    strcat(buf, folders);
  }

  strcat(buf, "/");
  strcat(buf, resource);

#ifdef DEBUG
  croak("checking %s", buf);
#endif

  if (access(buf, F_OK) != 0)
  {
    wfree(buf);
    buf = NULL;
  }

  return buf;
}


pid_t exec_command(char *command)
{
  pid_t pid;
  char **argv;
  int argc;

  parse_command(command, &argv, &argc);

  if (argv==NULL)
    return 0;

  if ((pid=fork())==0)
  {
    char **args;
    int i;

#ifdef HAVE_SETPGID
    setpgid(0, 0);
#endif
        
    args = malloc(sizeof(char*)*(argc+1));

    if (!args)
      exit(111);

    for (i=0; i<argc; i++)
      args[i] = argv[i];

    args[argc] = NULL;
    execvp(argv[0], args);
    exit(111);
  }

  while (argc > 0)
    wfree(argv[--argc]);

  wfree(argv);

  return pid;
}


/* parse_command: divides a command line into a argv/argc pair */

#define PRC_ALPHA        0
#define PRC_BLANK        1
#define PRC_ESCAPE       2
#define PRC_DQUOTE       3
#define PRC_EOS          4
#define PRC_SQUOTE       5

typedef struct {
  short nstate;
  short output;
} DFA;


static DFA mtable[9][6] = {
  {{3,1},{0,0},{4,0},{1,0},{8,0},{6,0}},
  {{1,1},{1,1},{2,0},{3,0},{5,0},{1,1}},
  {{1,1},{1,1},{1,1},{1,1},{5,0},{1,1}},
  {{3,1},{5,0},{4,0},{1,0},{5,0},{6,0}},
  {{3,1},{3,1},{3,1},{3,1},{5,0},{3,1}},
  {{-1,-1},{0,0},{0,0},{0,0},{0,0},{0,0}}, /* final state */
  {{6,1},{6,1},{7,0},{6,1},{5,0},{3,0}},
  {{6,1},{6,1},{6,1},{6,1},{5,0},{6,1}},
  {{-1,-1},{0,0},{0,0},{0,0},{0,0},{0,0}}, /* final state */
};


static char* next_token(char *word, char **next)
{
  char *ptr;
  char *ret, *t;
  int state, ctype;

  t = ret = wmalloc(strlen(word)+1);
  ptr = word;
    
  state = 0;
  *t = 0;
  while (1)
  {
    if (*ptr==0) 
        ctype = PRC_EOS;
    else if (*ptr=='\\')
        ctype = PRC_ESCAPE;
    else if (*ptr=='"')
        ctype = PRC_DQUOTE;
    else if (*ptr=='\'')
        ctype = PRC_SQUOTE;
    else if (*ptr==' ' || *ptr=='\t')
        ctype = PRC_BLANK;
    else
        ctype = PRC_ALPHA;

    if (mtable[state][ctype].output)
    {
      *t = *ptr; t++;
      *t = 0;
    }

    state = mtable[state][ctype].nstate;
    ptr++;

    if (mtable[state][0].output<0)
      break;
  }

  if (*ret==0)
    t = NULL;
  else
    t = wstrdup(ret);

  wfree(ret);
    
  if (ctype==PRC_EOS)
    *next = NULL;
  else
    *next = ptr;
    
  return t;
}


static void parse_command(char *command, char ***argv, int *argc)
{
  LinkedList *list = NULL;
  char *token, *line;
  int count, i;

  line = command;

  do
  {
    token = next_token(line, &line);
    if (token)
      list = list_cons(token, list);
  } while (token!=NULL && line!=NULL);

  count = list_length(list);
  *argv = wmalloc(sizeof(char*)*count);
  i = count;

  while (list!=NULL)
  {
    (*argv)[--i] = list->head;
    list_remove_head(&list);
  }

  *argc = count;
}


void unescape(char *s)
{
  int i, j;

  for (i = 0, j = 0; s[j] != '\0'; i++, j++)
  {
    if (s[j] != '\\') 
      s[i] = s[j];
    else 
      s[i] = s[++j];
  } 

  s[i] = '\0';
}


syntax highlighted by Code2HTML, v. 0.9.1