/*
 * File: misc.c
 *
 * Copyright (C) 2000 Jorge Arellano Cid <jcid@dillo.org>,
 *                    Jörgen Viksell <vsksga@hotmail.com>
 *
 * 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.
 */

#include <gtk/gtk.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>

#include "misc.h"

/*
 * Prepend the users home-dir to 'file' string i.e,
 * pass in .dillo/bookmarks.html and it will return
 * /home/imain/.dillo/bookmarks.html
 *
 * Remember to g_free() returned value!
 */
gchar *a_Misc_prepend_user_home(const char *file)
{
   return ( g_strconcat(g_get_home_dir(), "/", file, NULL) );
}

/*
 * Escape characters as %XX sequences.
 * Return value: New string, or NULL if there's no need to escape.
 */
gchar *a_Misc_escape_chars(const gchar *str, gchar *esc_set)
{
   static const char *hex = "0123456789ABCDEF";
   gchar *p = NULL;
   GString *gstr;
   gint i;

   for (i = 0; str[i]; ++i)
      if (str[i] <= 0x1F || str[i] == 0x7F || strchr(esc_set, str[i]))
         break;

   if (str[i]) {
      /* needs escaping */
      gstr = g_string_sized_new(64);
      for (i = 0; str[i]; ++i) {
         if (str[i] <= 0x1F || str[i] == 0x7F || strchr(esc_set, str[i])) {
            g_string_append_c(gstr, '%');
            g_string_append_c(gstr, hex[(str[i] >> 4) & 15]);
            g_string_append_c(gstr, hex[str[i] & 15]);
         } else {
            g_string_append_c(gstr, str[i]);
         }
      }
      p = gstr->str;
      g_string_free(gstr, FALSE);
   }
   return p;
}

/*
 * Use character stuffing for characters given in 'esc_set'.
 * Return value: New string, or NULL if there's no need to stuff.
 */
gchar *a_Misc_stuff_chars(const gchar *str, const gchar *esc_set)
{
   gint i;
   gchar *p, *sstr = NULL;

   if ((p = strpbrk(str, esc_set))) {
      /* start stuffing */
      GString *gstr = g_string_new(str);
      for (i = p - str; gstr->str[i]; ++i)
         if (strchr(esc_set, gstr->str[i])) {
            g_string_insert_c(gstr, i, gstr->str[i]);
            ++i;
         }
      sstr = gstr->str;
      g_string_free(gstr, FALSE);
   }
   return sstr;
}

/*
 * Case insensitive strstr
 */
gchar *a_Misc_stristr(char *src, char *str)
{
   int i, j;

   for (i = 0, j = 0; src[i] && str[j]; ++i)
      if (tolower(src[i]) == tolower(str[j])) {
         ++j;
      } else if (j) {
         i -= j;
         j = 0;
      }

   if (!str[j])                 /* Got all */
      return (src + i - j);
   return NULL;
}

/*
 * strsep implementation
 */
gchar *a_Misc_strsep(char **orig, const char *delim)
{
   gchar *str, *p;

   if (!(str = *orig))
      return NULL;

   p = strpbrk(str, delim);
   if (p) {
      *p++ = 0;
      *orig = p;
   } else {
      *orig = NULL;
   }
   return str;
}

#define TAB_SIZE 8
/*
 * Takes a string and converts any tabs to spaces.
 */
gchar *a_Misc_expand_tabs(const char *str)
{
   GString *New = g_string_new("");
   int len, i, j, pos, old_pos;
   char *val;

   if ( (len = strlen(str)) ) {
      for (pos = 0, i = 0; i < len; i++) {
         if (str[i] == '\t') {
            /* Fill with whitespaces until the next tab. */
            old_pos = pos;
            pos += TAB_SIZE - (pos % TAB_SIZE);
            for (j = old_pos; j < pos; j++)
               g_string_append_c(New, ' ');
         } else {
            g_string_append_c(New, str[i]);
            pos++;
         }
      }
   }
   val = New->str;
   g_string_free(New, FALSE);
   return val;
}

/*
 * Split a string into tokens, at any character contained by delim,
 * and return the starting and ending positions within the string. For
 * n tokens, the returned array has at least 2 * n + 1 elements, and
 * contains the start of token i at 2 * i, the end at 2 * i + 1. The
 * array is terminated by -1.
 */
gint *a_Misc_strsplitpos(const gchar *str, const gchar *delim)
{
   gint array_max = 4;
   gint *array = g_new(gint, array_max);
   gint n = 0;
   gint p1 = 0, p2;

   while (TRUE) {
      while (str[p1] != 0 && strchr(delim, str[p1]) != NULL)
         p1++;
      if (str[p1] == 0)
         break;

      p2 = p1;
      while (str[p2] != 0 && strchr(delim, str[p2]) == NULL)
         p2++;

      if (array_max < 2 * n + 3) {
         array_max <<= 2;
         array = g_realloc(array, array_max * sizeof(gint));
      }

      array[2 * n] = p1;
      array[2 * n + 1] = p2;
      n++;

      if (str[p2] == 0)
         break;
      else {
         p1 = p2;
      }
   }

   array[2 * n] = -1;
   return array;
}

/*
 * Return a copy of an array which was created by a_Misc_strsplitpos.
 */
gint *a_Misc_strsplitposdup(gint *pos)
{
   gint n = 0;
   gint *pos2;
   while (pos[2 * n] != -1)
      n++;
   pos2 = g_new(gint, 2 * n + 1);
   memcpy(pos2, pos, (2 * n + 1) * sizeof(gint));
   return pos2;
}

/*
 * Detects 'Content-Type' when the server does not supply one.
 * It uses the magic(5) logic from file(1). Currently, it
 * only checks the few mime types that Dillo supports.
 *
 * 'Data' is a pointer to the first bytes of the raw data.
 */
const gchar *a_Misc_get_content_type_from_data(void *Data, size_t Size)
{
   static const gchar *Types[] = {
      "application/octet-stream",
      "text/html", "text/plain",
      "image/gif", "image/png", "image/jpeg",
   };
   gint Type = 0;
   gchar *p = Data;
   size_t i;

   /* HTML try */
   for (i = 0; i < Size && isspace(p[i]); ++i);
   if ((Size - i >= 5 && !g_strncasecmp(p+i, "<html", 5)) ||
       (Size - i >= 5 && !g_strncasecmp(p+i, "<head", 5)) ||
       (Size - i >= 6 && !g_strncasecmp(p+i, "<title", 6)) ||
       (Size - i >= 14 && !g_strncasecmp(p+i, "<!doctype html", 14))) {
      Type = 1;

   /* Images */
   } else if (Size >= 4 && !g_strncasecmp(p, "GIF8", 4)) {
      Type = 3;
   } else if (Size >= 4 && !g_strncasecmp(p, "\x89PNG", 4)) {
      Type = 4;
   } else if (Size >= 2 && !g_strncasecmp(p, "\xff\xd8", 2)) {
      /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking
       * at the character representation should be machine independent. */
      Type = 5;

   /* Text */
   } else {
      /* We'll assume ASCII if chars are below 128 (after all, this
       * is a last resort when the server doesn't send Content-Type) */
      Size = MIN (Size, 256);
      for (i = 0; i < Size; i++)
         if ((guchar) p[i] > 127)
            break;
      Type = (i < 12 || i < Size) ? 0 : 2;
   }

   return (Types[Type]);
}

