/*
 * grn_util.c - Implementation of some project-wide functions
 *
 * $Id: grn_util.c,v 1.43 2000/07/19 06:26:21 sc Exp $
 */
/* Copyright (C) 1999-2000  Sergey Chernikov (sc@ivvs.ul.ru)
 *
 * Authors: Sergey Chernikov <sc@ivvs.ul.ru>
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
 */

#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <locale.h>
#include "grn_consts.h"
#include "grn_types.h"
#include "grn_vars.h"
#include "grn_util.h"
#include "grn_config.h"


gint grn_compare_strings(gchar *a, gchar *b)
{
  return strcmp(a, b);
}

void grn_appbar_push(GnomeAppBar *ab, gboolean update, gchar *fmt, ...)
{
  gchar *s_tmp;
  va_list args;
  
  if (! ab)  return;
  va_start(args, fmt);
  s_tmp = g_strdup_vprintf(fmt, args);
  va_end(args);
  grn_lock();
  gnome_appbar_push(ab, s_tmp);
  grn_unlock();
  if (update)  grn_main_iter();
  str_free(&s_tmp);
}

void grn_appbar_pop(GnomeAppBar *ab)
{
  if (! ab)  return;
  grn_lock();
  gnome_appbar_pop(ab);
  grn_unlock();
}

void grn_appbar_set_status(GnomeAppBar *ab, gchar *fmt, ...)
{
  gchar *s_tmp;
  va_list args;
  
  if (! ab)  return;
  va_start(args, fmt);
  s_tmp = g_strdup_vprintf(fmt, args);
  va_end(args);
  grn_lock();
  gnome_appbar_set_status(ab, s_tmp);
  grn_unlock();
  str_free(&s_tmp);
}

void grn_set_title(GtkWindow *w, gchar *fmt, ...)
{
  gchar *s_tmp ;
  va_list args;
  g_return_if_fail(w != NULL);
  
  va_start(args, fmt);
  s_tmp = g_strdup_vprintf(fmt, args);
  va_end(args);
  grn_lock();
  gtk_window_set_title(w, s_tmp);
  grn_unlock();
  str_free(&s_tmp);
}

void grn_set_contents(GtkWidget *contents)
{
  GtkWidget *old_contents = gtk_object_get_data(GTK_OBJECT(GRN->container), KEY_CONTENTS);

  if (old_contents)
  {
    grn_lock();
    gtk_widget_hide(old_contents);
    grn_unlock();
  }
  if (contents)
  {
    GtkWidget *w_focused;
    gchar *title;
    
    grn_lock();
    w_focused = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(contents), KEY_FOCUS));
    title = (gchar *) gtk_object_get_data(GTK_OBJECT(contents), KEY_TITLE);

    gtk_widget_show(contents);
    gtk_container_add(GRN->container, contents);
    gtk_object_set_data(GTK_OBJECT(GRN->window), KEY_WND, contents);
    grn_unlock();
    if (title)  grn_set_title(GTK_WINDOW(GRN->window), title);
    if (w_focused)
    {
      grn_lock();
      gtk_widget_grab_focus(w_focused);
      gtk_object_remove_data(GTK_OBJECT(GRN->window), KEY_FOCUS);
      gtk_object_set_data(GTK_OBJECT(GRN->window), KEY_FOCUS, w_focused);
      grn_unlock();
    }
  }
  grn_lock();
  gtk_object_set_data(GTK_OBJECT(GRN->container), KEY_CONTENTS, contents);
  grn_unlock();
}

void grn_widget_set_style(GtkWidget *w, const gchar *font_name,
			  GdkColor *fg, GdkColor *bg, GdkPixmap *bg_pm)
{
  GtkStyle *style = NULL;
  GdkColormap *cmap = NULL;
  g_return_if_fail(w != NULL);
  
  grn_lock();
  style = gtk_style_copy(gtk_widget_get_style(w));

  if (str_check(font_name))
  {
    GdkFont *font;
    gchar *fn = g_strdup(font_name);
    font = gdk_fontset_load(fn);
    if (font)
    {
      if (style->font)  gdk_font_unref(style->font);
      style->font = font;
    }
    else  g_warning(_("Error loading font '%s'"), font_name);
    str_free(&fn);
  }
  if (fg || bg)  cmap = gdk_colormap_get_system();
  if (fg)
  {
    if (gdk_color_alloc(cmap, fg))
    {
      if (GTK_IS_TEXT(w))  style->text[0] = *fg;
      else  style->fg[0] = *fg;
    }
    else  g_warning(_("Can't allocate foreground color"));
  }
  if (bg)
  {
    if (gdk_color_alloc(cmap, bg))
    {
      if (GTK_IS_TEXT(w))  style->base[0] = *bg;
      else  style->bg[0] = *bg;
    }
    else  g_warning(_("Can't allocate background color"));
  }
  if (bg_pm)  style->bg_pixmap[0] = bg_pm;

  gtk_widget_set_style(w, style);
  grn_unlock();
}

GdkPixmap *grn_load_pixmap(const gchar *pm_name)
{
  GnomePixmap *gpm;
  if (! str_check(pm_name))  return NULL;

  g_print(_("Loading image from '%s'..."), pm_name);
  grn_lock();
  gpm = GNOME_PIXMAP(gnome_pixmap_new_from_file(pm_name));
  if (! gpm)
  {
    g_print(_("Error!\n"));
    return NULL;
  }
  if (gpm->mask)  gdk_bitmap_unref(gpm->mask);
  grn_unlock();
  if (! gpm->pixmap)  g_print(_("Failed!\n"));
  else  g_print(_("Ok.\n"));
  return gpm->pixmap;
}

void grn_error(const gchar *area, gchar *message)
{
  gchar *s;
  if (! str_check(message))  return;
  
  if (area)  s = g_strdup_printf("%s:\n%s", area, message);
  else  s = g_strdup(message);
  grn_lock();
  gnome_app_error(GNOME_APP(GRN->window), s);
  grn_unlock();
  str_free(&s);
}

void grn_info(gchar *fmt, ...)
{
  va_list args;
  gchar *s;
  
  va_start(args, fmt);
  s = g_strdup_vprintf(fmt, args);
  va_end(args);

  grn_lock();
  gnome_app_message(GNOME_APP(GRN->window), s);
  grn_unlock();
  str_free(&s);
}


void grn_main_iter(void)
{
  grn_lock();
  while (gtk_events_pending())  gtk_main_iteration();
  grn_unlock();
}

GtkWidget *grn_get_widget(GtkObject *obj, gchar *name)
{
  GtkWidget *found_widget;
  
  grn_lock();
  found_widget = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(obj), name);
  grn_unlock();
#ifdef GRN_DEBUG
  if (! found_widget)
    printf("grn_get_widget@grn_util.c: Widget with name %s not found\n", name);
#endif
  return found_widget;
}

// efficient_append() from GGAD
void grn_g_list_append(GList **list, GList **list_end, gpointer data)
{
  g_return_if_fail(list != NULL);
  g_return_if_fail(list_end != NULL);
  
  if (*list == NULL)
  {
    g_assert(*list_end == NULL);
    *list = g_list_append(*list, data);
    *list_end = *list;
  }
  else  *list_end = g_list_append(*list_end, data)->next;
}

gchar *get_charset(void)
{
  gchar *s, *ret=NULL;
  
  s = g_strdup(setlocale(LC_CTYPE, NULL));
  if (s)
  {
    gchar **s_arr = g_strsplit(s, ".", 2);
    str_free(&s);
    if (str_check(s_arr[1]))  ret = g_strdup(s_arr[1]);
    g_strfreev(s_arr);
  }
  return ret;
}

static int locked = 0;
void grn_lock(void)
{
  if (pthread_self() != GRN->tid_queue)  return;
  if (locked > 0) ;// g_warning("Nested locking: %d", locked);
  else  gdk_threads_enter();
  locked++;
}
void grn_unlock(void)
{
  if (locked <= 0)  return;
  gdk_threads_leave();
  locked--;
  if (locked < 0)  locked = 0;
}


gchar *str_subst(const gchar *src, gchar *var, gchar *value)
{
  gchar *s;
  
  g_return_val_if_fail(src != NULL, NULL);
  g_return_val_if_fail(var != NULL, NULL);
  s = g_strdup(src);
  while (strstr(s, var))
  {
    gchar **arr = g_strsplit(s, var, 1);
    str_free(&s);
    s = g_strdup_printf("%s%s%s", (arr[0]!=NULL)?arr[0]:"", value, (arr[1]!=NULL)?arr[1]:"");
    str_free(&arr[0]);  str_free(&arr[1]);
  }
  return s;
}

gboolean str_check(const gchar *s)
{
  if (! s)  return FALSE;
  if (strlen(s) <= 0)  return FALSE;
  return TRUE;
}

gchar *str_copy(const gchar *s)
{
  if (! s)  return NULL;
  if (strlen(s) <= 0)  return NULL;
  return  g_strdup(s);
}

void str_add(gchar **str, gchar *text)
{
  gchar *ret;
  
  if (*str)
  {
    ret = g_strconcat(*str, text, NULL);
    str_free(str);
  }
  else  ret = g_strdup(text);
  *str = ret;
}

void str_free(gchar **str)
{
  if (! str)  return;
  if (! *str)  return;
  g_free(*str);  *str = NULL;
}

void str_destroy(gchar *s)
{  str_free(&s);  }

void grn_subst_vars(gchar **src)
{
  gchar *ret, *tmp;
  gchar *ldate;
  struct utsname un;
  if (! src)  return;
  if (! str_check(*src))  return;
  
  ldate = date_get_local(time(NULL));
  ret = str_subst(*src, "@ctime", ldate);
  g_free(ldate);
  tmp = str_subst(ret, "@cname", grn_prefs.name);	g_free(ret);
  ret = str_subst(tmp, "@email", grn_prefs.email);	g_free(tmp);
  tmp = str_subst(ret, "@longpid", _(GRN_TITLE));	g_free(ret);
  ret = str_subst(tmp, "@pid", "Grn");			g_free(tmp);
  tmp = str_subst(ret, "@version", VERSION);		g_free(ret);
  ret = str_subst(tmp, "@ver", VERSION);		g_free(tmp);
  uname(&un);  ldate = g_strdup_printf("%s %s (%s)", un.sysname, un.release, un.machine);
  tmp = str_subst(ret, "@uname", ldate);		g_free(ret);
  g_free(ldate);
  ret = str_subst(tmp, "@un_s", un.sysname);		g_free(tmp);
  tmp = str_subst(ret, "@un_r", un.release);		g_free(ret);
  ret = str_subst(tmp, "@un_m", un.machine);		g_free(tmp);
  tmp = str_subst(ret, "@un_n", un.nodename);		g_free(ret);
  ret = str_subst(tmp, "@replyto", grn_prefs.replyto);	g_free(tmp);
  g_free(*src);  *src = ret;
}

gchar *grn_subst_tilde(const gchar *src)
{
  gchar *s, *ret;
  g_return_val_if_fail(src != NULL, NULL);

  if (src[0] != '~')  return g_strdup(src);
  s = g_strconcat(g_get_home_dir(), "/", NULL);
  ret = str_subst(src, "~/", s);
  str_free(&s);
  return ret;
}


t_msgheader *parse_hdr(gchar *src)
{
  gchar **sarr;
  gchar *tmp;
  t_msgheader *ret = NULL;
  g_return_val_if_fail(src != NULL, ret);
  
  sarr = g_strsplit(src, ": ", 1);
  if (sarr[0] && sarr[1])
  {
    ret = g_new(t_msgheader, 1);
    ret->name = g_strdup(sarr[0]);
    tmp = sarr[1];
    while (*tmp)  tmp++;
    ret->value = (gchar *) g_malloc(tmp - sarr[1] + 1);
    strncpy(ret->value, sarr[1], tmp - sarr[1]);
    ret->value[tmp - sarr[1]] = '\0';
  }
  g_strfreev(sarr);
  return ret;
}

gchar *get_hdr_val(gchar *src)
{
  gchar *ret = NULL;
  t_msgheader *mh;
  g_return_val_if_fail(src != NULL, ret);
  
  if ((mh = parse_hdr(src)) != NULL)
  {
    ret = g_strdup(mh->value);
    str_free(&(mh->name));  str_free(&(mh->value));
    g_free(mh);
  }
  return ret;
}


#define THE_YEAR_0	1970L
t_tz_table tz_table[] =
{
  {"EDT", -500},
  {"PST", -800},
  {"CST", -600},
  {"MST", -700},
  {"MET", 100},
  {"MEZ", 100},
  {"MSK", 300},
  {"MSD", 400},
  {"CET", 100},
  {"HKT", 800},
  {"JST", 900},
  {"CAST", 930},
  {"EAST", 1000},
  {"NZST", 1200},
  {"EET", 200},
  {"", 0}
};

const gshort dpm[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

static gchar *skip_whitespace(gchar *src)
{
  gchar ch;
  
  if (! src)  return NULL;
  while (((ch = *src) == ' ') || (ch == '\t') || (ch == '\n') || (ch == '\r'))
    src++;
  return src;
}

static gint parse_timezone(gchar *tz)
{
  gchar ch;
  t_tz_table *table;
  
  table = tz_table;
  while ((ch = table->name[0]) != 0)
  {
    if ((*tz == ch) && (! strncmp(tz, table->name, sizeof(table->name))))
      return table->tz;
    table++;
  }
  return 0;
}

time_t parse_date(gchar *date)
{
  gchar ch;
  glong day=0, month=0, year=0, minutes=0, hours=0, tz=0;
  time_t seconds = 0;
  gchar smonth[4];
  gint sign, i;
  
  date = skip_whitespace(date);
  if ((! date) || (*date == 0))  return 0;

  while(isalpha((gint)*date))  date++;
  if (*date == ',')  date++;
  date = skip_whitespace(date);
  
  while (ch = *date, isdigit((gint) ch))
  {
    day = 10 * day + (glong) (ch - '0');
    date++;
  }
  if ((day <= 0) || (day > 31))  return 0;
  day--;
  
  date = skip_whitespace(date);
  strncpy(smonth, date, 3);  smonth[3] = '\0';
  if (! g_strncasecmp(smonth, "Jan", 3))  month = 1;
  else if (! g_strncasecmp(smonth, "Feb", 3))  month = 2;
  else if (! g_strncasecmp(smonth, "Mar", 3))  month = 3;
  else if (! g_strncasecmp(smonth, "Apr", 3))  month = 4;
  else if (! g_strncasecmp(smonth, "May", 3))  month = 5;
  else if (! g_strncasecmp(smonth, "Jun", 3))  month = 6;
  else if (! g_strncasecmp(smonth, "Jul", 3))  month = 7;
  else if (! g_strncasecmp(smonth, "Aug", 3))  month = 8;
  else if (! g_strncasecmp(smonth, "Sep", 3))  month = 9;
  else if (! g_strncasecmp(smonth, "Oct", 3))  month = 10;
  else if (! g_strncasecmp(smonth, "Nov", 3))  month = 11;
  else if (! g_strncasecmp(smonth, "Dec", 3))  month = 12;
  if (month == 0)  return 0;
  month--;
  while (isalpha((gint) *date))  date++;
  
  date = skip_whitespace(date);
  if (*date == '9')  year = 19;
  else if (*date == '0')  year = 20;
  while (ch = *date, isdigit((gint) ch))
  {
    year = year * 10 + (glong) (ch - '0');
    date++;
  }
  date = skip_whitespace(date);
  
  while (ch = *date, isdigit((gint) ch))
  {
    hours = hours * 10 + (glong) (ch - '0');
    date++;
  }
  if ((ch != ':') || (hours >= 24))  return 0;
  date++;
  
  while (ch = *date, isdigit((gint) ch))
  {
    minutes = minutes * 10 + (glong) (ch - '0');
    date++;
  }
  if (minutes >= 60)  return 0;
  
  if (ch == ':')
  {
    date++;
    while (ch = *date, isdigit((gint) ch))
    {
      seconds = seconds * 10 + (glong) (ch - '0');
      date++;
    }
    if (seconds >= 60)  return 0;
  }
  date = skip_whitespace(date);
  
  sign = 1;
  if (*date == '+')  date++;
  else if (*date == '-')
  {
    sign = -1;  date++;
  }
  while(ch = *date, isdigit((gint) ch))
  {
    tz = tz * 10 + (glong) (ch - '0');
    date++;
  }
  tz = sign * tz;
  
  date = skip_whitespace(date);
  if (isalpha((gint) *date))
  {
    sign = 1;
    tz = parse_timezone(date);
  }
  
  for (i=0; i<month; i++)  day += dpm[i];
  if (month > 0)
  {
    if ((year % 4) == 0)
    {
      day++;
      if ((year % 100) == 0)
      {
        day--;
	if ((year % 400) == 0)  day++;
      }
    }
  }
  year -= THE_YEAR_0;
  day += year * 365.25;
  
  hours -= tz / 100;
  minutes -= tz % 100;
  seconds += (time_t) (60L * (minutes + 60L * (hours + 24L * day)));

  return seconds;
}

gchar *date_get_local(time_t date)
{
  gchar *dow[7] = { _("Sun"), _("Mon"), _("Tue"), _("Wed"), _("Thu"),
                    _("Fri"), _("Sat") };
  gchar *month[12] = { _("Jan"), _("Feb"), _("Mar"), _("Apr"), _("May"),
                       _("Jun"), _("Jul"), _("Aug"), _("Sep"), _("Oct"),
		       _("Nov"), _("Dec")};
  struct tm *lt = localtime(&date);
#ifdef __SVR4
  return g_strdup_printf("%s, %s %02d %04d, %02d:%02d:%02d", dow[lt->tm_wday],
                         month[lt->tm_mon], lt->tm_mday, lt->tm_year+1900,
			 lt->tm_hour, lt->tm_min, lt->tm_sec);
#else
  return g_strdup_printf("%s, %s %02d %04d, %02d:%02d:%02d %s", dow[lt->tm_wday],
                         month[lt->tm_mon], lt->tm_mday, lt->tm_year+1900,
			 lt->tm_hour, lt->tm_min, lt->tm_sec, lt->tm_zone);
#endif
}


gint files_foreach(gchar *dirname, void callback(gchar *file))
{
  DIR *dir;
  struct dirent *ent;
  gchar *buf;
  gint i = 0;
  
  dir = opendir(dirname);
  if (dir)
  {
    while ((ent = readdir(dir)))
    {
      if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, ".."))
      {
        i++;
	buf = g_malloc(strlen(dirname) + strlen(ent->d_name) + 2);
	sprintf(buf, "%s/%s", dirname, ent->d_name);
	callback(buf);
	str_free(&buf);
      }
    }
    closedir(dir);
  }
  return i;
}


/* The following 2 functions were borrowed from pan-0.7.2 */
gchar* get_host_name(void)
{
  gchar *ptr;
  static gchar hostname[256];

  hostname[0] = '\0';

  if ((gethostname(hostname, sizeof(hostname))) == -1)
  {
    if ((ptr = getenv("HOST")) != NULL)
      strncpy(hostname, ptr, MAX_HOSTNAME_LEN);
    else if ((ptr = getenv("HOSTNAME")) != NULL)
      strncpy(hostname, ptr, MAX_HOSTNAME_LEN);
    else  hostname[0] = '\0';
  }
  hostname[MAX_HOSTNAME_LEN] = '\0';
  ptr = strtok (hostname, ".");
  return g_strdup ( ptr );
}

gchar *get_fqdn (gchar *host)
{
  static gchar name[512];
  static gchar h[MAX_HOSTNAME_LEN];
  char line[1025];
  char *cp, *domain;
  static char fqdn[1024];
  struct hostent *hp;
  struct in_addr in;
  FILE *inf;

  *fqdn = '\0';
  domain = NULL;

  name[MAX_HOSTNAME_LEN] = '\0';
  if (! str_check(host))  strncpy(h, get_host_name(), MAX_HOSTNAME_LEN);
  else  strncpy(h, host, MAX_HOSTNAME_LEN);

  if (str_check(h))
  {
    if (strchr(h, '.'))  return h;
    strncpy(name, h, MAX_HOSTNAME_LEN);
  }
  else {
    if (gethostname(name, MAX_HOSTNAME_LEN))  return NULL;
  }

  if ('0' <= *name && *name <= '9')
  {
    in.s_addr = inet_addr(name);
    if ((hp = gethostbyaddr((char *) &in.s_addr, 4, AF_INET)))
      in.s_addr = (*hp->h_addr);
    return (hp && strchr(hp->h_name, '.') ? hp->h_name : (gchar *) inet_ntoa(in));
  }
  if ((hp = gethostbyname(name)) && !strchr(hp->h_name, '.'))
    if ((hp = gethostbyaddr(hp->h_addr, hp->h_length, hp->h_addrtype)))
      in.s_addr = (*hp->h_addr);

  sprintf(fqdn, "%s", hp ? strchr(hp->h_name, '.')
          ? hp->h_name : (gchar *) inet_ntoa(in) : "");
  if (!*fqdn || (fqdn[strlen(fqdn) - 1] <= '9'))
  {
    *fqdn = '\0';
    inf = fopen("/etc/resolv.conf", "r");
    if (inf)
    {
      while (fgets(line, 1024, inf))
      {
	line[1024] = '\0';
	g_strchug(line);  g_strchomp(line);
	if (strncmp(line, "domain ", 7) == 0)
	{
	  domain = line + 7;
	  break;
	}
	if (strncmp(line, "search ", 7) == 0)
	{
	  domain = line + 7;
	  cp = strchr(domain, ' ');
	  if (cp)  *cp = '\0';
	  break;
	}
      }
      if (domain)  sprintf(fqdn, "%s.%s", name, domain);
      fclose(inf);
    }
  }
  return (fqdn);
}

gchar *c2s(GdkColor c)
{
  return g_strdup_printf("#%02X%02X%02X", c.red/256, c.green/256, c.blue/256);
}


void grn_save_ws(gint idx, GtkWidget *w)
{
  gint x, y;
  g_return_if_fail(w != NULL);
  
  grn_prefs.ws[idx].width = w->allocation.width;
  grn_prefs.ws[idx].height = w->allocation.height;
  gdk_window_get_root_origin(w->window, &x, &y);
  grn_prefs.ws[idx].x = x;  grn_prefs.ws[idx].y = y;
}


syntax highlighted by Code2HTML, v. 0.9.1