/*- 
 * 
 * New BSD License 2006
 *
 * Copyright (c) 2006, Jorgen Lundman
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 * 1 Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 2 Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following
 *   disclaimer in the documentation and/or other materials provided
 *   with the distribution.  
 * 3 Neither the name of the stuff nor the names of its contributors 
 *   may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

// $Id: misc.c,v 1.10 2006/06/30 01:22:58 lundman Exp $
// Miscellaneous utility functions
// Jorgen Lundman November 25th, 1999
// Copyright (c) 1999 HotGen Studios Ltd <www.hotgen.com>

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

#include "misc.h"
#include "lion.h"
//#include "fnmatch.h"


// Filled in with the matched character
char misc_digtoken_optchar = 0;

#if 1 // new and handles quotes
char *misc_digtoken(char **string,char *match) 
{
	if(string && *string && **string) {
    
		// Skip "whitespace", anything in 'match' until we have something
		// that doesn't.
		// However, if we hit a " we stop asap, and match only to next ".


		while(**string && strchr(match,**string)) {

			// If a char is the quote, and we match against quota, stop loop
			(*string)++;
			
		}


		//printf(" found %c\n", **string);
		// If the first char is now a quote, and we match against quotes:
		if (!strchr(match, '"') && (**string == '"')) {

			//printf("  hack\n");
			// Skip the quote we are on
			(*string)++;

			// change match to only have a quote in it, that way we will
			// only match another quote.
			match = "\"";

		}

    
		if(**string) { /* got something */
			char *token=*string;

			if((*string=strpbrk(*string,match))) {
	
				misc_digtoken_optchar = *(*string);

				// If we match " to we force it to only look for
				// matching " and not any of the alternatives?

				*(*string)++=(char)0;
				while(**string && strchr(match,**string))
					(*string)++;
	
			}  else
				*string = ""; /* must be at the end */
      
			return(token);
		}
	}
	return((char *)0);
}

#else

char *misc_digtoken(char **string,char *match) 
{
  if(string && *string && **string) {
    
    // Skip "whitespace", anything in 'match' until we have something
    // that doesn't.
    // However, if we hit a " we stop asap, and match only to next ".

    while(**string && strchr(match,**string)) {

      if (**string == '"') {

	(*string)++;
	match = "\"";
	break;

      }

      (*string)++;

    }
    
    if(**string) { /* got something */
      char *token=*string;
      
      if((*string=strpbrk(*string,match))) {
	
	misc_digtoken_optchar = *(*string);

	// If we match " to we force it to only look for
	// matching " and not any of the alternatives?

        *(*string)++=(char)0;
	while(**string && strchr(match,**string))
          (*string)++;
	
      }  else
        *string = ""; /* must be at the end */
      
      return(token);
    }
  }
  return((char *)0);
}

#endif


//
// If you want to check out unreadable code, check this out
//
int mystrccmp(register char *s1,register char *s2) {

  while((((*s1)>='a'&&(*s1)<='z')?(*s1)-32:*s1)==(((*s2)>='a'&&(*s2)<='z')?(*s2++)-32:*s2++))
    if(*s1++==0) return 0;
  return (*(unsigned char *)s1-*(unsigned char *)--s2);
}



char *mystrcpy(char *s)
{
  char *r;

  r = (char *) malloc(strlen(s)+1);
  if (!r) {
    perror("mystrcpy()");
    exit(1);
  }

  strcpy(r, s);
  return r;
}



// switch uses jump tables so they are O(0)
#define CONVCHAR(X, Y) switch((X)) {\
                    case '1':    \
                    case '2':    \
                    case '3':    \
                    case '4':    \
                    case '5':    \
                    case '6':    \
                    case '7':    \
                    case '8':    \
                    case '9':    \
                    case '0':    \
                      (Y)=(X)-'0';   \
                      break;     \
                    case 'a':    \
                    case 'b':    \
                    case 'c':    \
                    case 'd':    \
                    case 'e':    \
                    case 'f':    \
                      (Y)=(X)-'a'+10;\
                      break;     \
                    case 'A':    \
                    case 'B':    \
                    case 'C':    \
                    case 'D':    \
                    case 'E':    \
                    case 'F':    \
                      (Y)=(X)-'A'+10;\
                      break;     \
                    }


//
// Convert a hex string "4f32109fae" into the equivalent binary, return
// number of bytes
//
int  misc_hextobin(char *hex)
{
  unsigned char high, low;
  int result, out;

  result = 0;
  out = 0;

  while (hex[out]) {

    CONVCHAR(hex[out], high);
    out++;
    CONVCHAR(hex[out], low);
    out++;
    
    hex[result] = (high << 4) + low;
    result ++;
    
  }

  return result;

}


//
// Convert binary into hex string, reverse order so we can re-use memory
//
int  misc_bintohex(char *bin, int len)
{
  unsigned char hex, low, high;
  unsigned char table[16] = 
    {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};

  bin[len * 2] = 0; // terminate string


  for (len--; len >= 0; len--) {

    hex = bin[ len ];

    low = hex & 0x0f;
    high = (hex & 0xf0) >> 4;

    bin[ len * 2 + 1] = table[ low ];
    bin[ len * 2 ]    = table[ high  ];

  }

  return 0;

}



char *misc_strdup(char *s)
{
  char *result;

  result = (char *) malloc( strlen( s ) +1 );

  if (result)
    strcpy(result, s);

  return result;

}



time_t misc_getdate(char *date)
{
  struct tm tmw, *tmn;
  char *ar, *token, *r;
  int i;
  static char months[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
				"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

  // It is either
  // 'Nov 10 11:44'   for less than 6mnths old or
  // 'Mar 27  1999'   for more.

  memset(&tmw, 0, sizeof(tmw));
  ar = date;


  // read month
  if (!(token = misc_digtoken(&ar, " "))) goto getdate_error;

  for (i = 0; i < 12; i++)
    if (!mystrccmp(months[i], token)) {
      tmw.tm_mon = i;
      break;
    }

  if (i == 12) goto getdate_error;


  // get day of month
  if (!(token = misc_digtoken(&ar, " "))) goto getdate_error;

  tmw.tm_mday = atoi(token);


  // get either time, or, year
  if (!(token = misc_digtoken(&ar, " "))) goto getdate_error;


  if ((r = strchr(token, ':'))) {  
    
    // it's time
    *r = 0;

	tmn = localtime(&lion_global_time);

    if (!tmn) goto getdate_error;
    
    tmw.tm_hour = atoi(token);
    tmw.tm_min  = atoi(&r[1]);
    tmw.tm_year = tmn->tm_year;


    // Ok, if we are in January, but the Month read was Dec or
    // earlier, we need to go -1 on the year. I think this can be
    // expressed as now_month - read_month < -6
    if ((tmn->tm_mon - tmw.tm_mon) <= -6)
      tmw.tm_year -= 1;


  } else { 

    // It's the year
    tmw.tm_year = atoi(token) - 1900; // as according to the man page

  }


  // Convert it into a time_t, well, as close as possible.
  return mktime(&tmw);

 getdate_error:
  return 0;

}


char *misc_idletime(time_t time)
{ /* This support functionw as written by George Shearer (Dr_Delete) */
  
  static char workstr[100];
  unsigned short int days=(time/86400),hours,mins,secs;
  hours=((time-(days*86400))/3600);
  mins=((time-(days*86400)-(hours*3600))/60);
  secs=(time-(days*86400)-(hours*3600)-(mins*60));
  
  workstr[0]=(char)0;
  if(days)
    sprintf(workstr,"%dd",days);
  sprintf(workstr,"%s%s%02d",workstr,(workstr[0])?":":"",hours);
  sprintf(workstr,"%s%s%02d",workstr,(workstr[0])?":":"",mins);
  sprintf(workstr,"%s%s%02d",workstr,(workstr[0])?":":"",secs);
  if (!days && !hours && !mins && !secs)
    sprintf(workstr,"0 seconds");
  
  return(workstr);
}

char *misc_idletime2(time_t time)
{ /* This support functionw as written by George Shearer (Dr_Delete) */
  
  static char workstr[100];
  unsigned short int days=(time/86400),hours,mins,secs;
  hours=((time-(days*86400))/3600);
  mins=((time-(days*86400)-(hours*3600))/60);
  secs=(time-(days*86400)-(hours*3600)-(mins*60));
  
  workstr[0]=(char)0;
  if(days)
    sprintf(workstr,"%dd",days);
  sprintf(workstr,"%s%s%02d",workstr,(workstr[0])?":":"",hours);
  sprintf(workstr,"%s%s%02d",workstr,(workstr[0])?":":"",mins);
  sprintf(workstr,"%s%s%02d",workstr,(workstr[0])?":":"",secs);
  if (!days && !hours && !mins && !secs)
    sprintf(workstr,"0 seconds");
  
  return(workstr);
}



char *misc_makepath(char *cwd, char *file)
{
  static char result[8192];   // Buffer over flow
  int i;

  if (!file) {  // dir up instead
    
    strcpy(result, cwd);
    // If we end with '/' we truncate those.
    i = strlen(result) - 1;
    while ((i >= 0) && result[i] == '/') 
      result[i--] = 0;

    // Now seek back until we find a '/'
    while ((i >= 0) && result[i] != '/') i--;

    if (result[i] == '/') result[i] = 0;

    if ((i <= 0) || !result[0]) {
      result[0] = '/';
      result[1] = 0;
    }

#ifdef WIN32

    // In windows, if we've gone back to just "X:" make it "X:/"
    if ((result[1] == ':') &&
	(result[2] == 0) ) {
      
      result[2] = '/';
      result[3] = 0;

    }

#endif

    return result;
  }


  // If cwd ends with '/' don't add one.
  if ( cwd[ strlen(cwd) - 1 ] == '/')
    sprintf(result, "%s%s", cwd, file);
  else
    sprintf(result, "%s/%s", cwd, file);

  return result;
}




char *misc_pasv2port(char *pasv)
{
  static char result[1024];
  char *r;

  *result = 0;

  r = strchr(pasv, '(');

  if (r) {

    strcpy(result, &r[1]);
    r = strchr(result, ')');

    if (r) *r = 0;

  }

  return result;

}





char *misc_strjoin(char *a, char *b)
{
  char *result;
  int len_a, extra_slash;

  // Check if a ends with / or, b starts with /, if so dont
  // add a '/'.
  len_a = strlen(a);

  if ((a[ len_a - 1 ] != '/') && b[0] != '/')
    extra_slash = 1;
  else 
    extra_slash = 0;



  result = (char *) malloc(len_a + strlen(b) + 1 + extra_slash);

  if (!result) {
    perror("malloc");
    exit(2);
  }

  if (extra_slash)
    sprintf(result, "%s/%s", a, b);
  else
    sprintf(result, "%s%s", a, b);

  return result;
}

#if 0
int   misc_skipfile(char *patterns, char *name)
{
  char *tmp;
  char *pattern, *ar;

  tmp = strdup(patterns);
  ar = tmp;

  while (( pattern = misc_digtoken(&ar, " \""))) {

    
    if (!fnmatch(pattern, name, FNM_CASEFOLD)) {
      free(tmp);
      return 1;

    }      


  }


  free( tmp );

  return 0;
}
#endif


void misc_stripslash(char *s)
{
  int l;

  l = strlen(s) - 1;

  while ((l > 2) && (s[l] == '/')) s[l--] = 0;

}



void misc_undos_path(char *path)
{
  char *r;

  while ( (r = strchr(path, '\\')) )
    *r = '/';


}



char *misc_strip(char *s)
{
  char *r;

  if ((r = strchr(s, '\r'))) *r = 0;
  if ((r = strchr(s, '\n'))) *r = 0;

  return s;
}



//
// Take a string, and allocate a new string which is URL encoded,
// even if no encoding was needed, it is allocated.
// Is it worth running through once to know how much to allocate, or
// just allocate * 3 ? But to allocate *3 I need to call strlen anyway.
// (Although, some OS can do that based on longs)
//
// Only alphanumerics [0-9a-zA-Z], the special characters "$-_.!*'(),"
// Please note this is the QUERY STRING encoding. This means "+" is
// incoded as %2B and " " (space) is encoded as "+".
//
// As per RFC 1630 (RFC1630) "Universal Resource Identifiers in WWW: A Unifying Syntax for the Expression of Names and Addresses of Objects on the Network as used in the World-Wide Web"
//
char *misc_url_encode(char *s)
{
	char *result = NULL;
	int len = 0;
	char *r, *r2;
	static char hex[] = "0123456789ABCDEF";

	// How much space to allocate?
	while(1) {

		r = s;

		while (*r) {

			switch(tolower(*r)) {
			case 'a':
			case 'b':
			case 'c':
			case 'd':
			case 'e':
			case 'f':
			case 'g':
			case 'h':
			case 'i':
			case 'j':
			case 'k':
			case 'l':
			case 'm':
			case 'n':
			case 'o':
			case 'p':
			case 'q':
			case 'r':
			case 's':
			case 't':
			case 'u':
			case 'v':
			case 'w':
			case 'x':
			case 'y':
			case 'z':
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
			case '$':
			case '-':
			case '_':
			case '.':
			case '!':
			case '*':
			case '\'':
			case '(':
			case ')':
			case ',':
			case '/': // and slash man!
				len++;
				if (result) *r2++ = *r;
				break;
			case ' ': // space becomes plus
				len++;
				if (result) *r2++ = '+';
				break;
			default:
				len+=3;
				if (result) {
					*r2++ = '%';
					*r2++ = hex[((*r>>4)&0x0f)];
					*r2++ = hex[((*r)&0x0f)];
				}
			} // switch
			r++;
		} // while r

		if (result) {
			*r2 = 0;
			break;
		}

		if (!len) break;

		result = malloc(len + 1);

		if (!result) break;

		r2 = result;

	} // while 1		

	return result;

}




//
// Decode the url, allocate new area
//
char *misc_url_decode(char *s)
{
	char *result, *r, chr;

	result = strdup(s);

	if (!result) return NULL;

	r = result;

	while(*s) {

		if (*s != '%') {
			if (*s == '+') {  // plus becomes space
				*r++ = ' ';
				s++;
			} else {
				*r++ = *s++;  // copy verbatim
			}
		} else {              // otherwise, decode..
			chr = 0;
			s++;

			if (!*s) break;

			if ((toupper(*s) >= 'A') &&
				(toupper(*s) <= 'F'))
				chr = toupper(*s) - 'A' + 0x0A;
			else if ((*s >= '0') &&
					 (*s <= '9'))
				chr = *s - '0';
			s++;
			chr <<= 4;
			if ((toupper(*s) >= 'A') &&
				(toupper(*s) <= 'F'))
				chr |= (toupper(*s) - 'A' + 0x0A) & 0x0f;
			else if ((*s >= '0') &&
					 (*s <= '9'))
				chr |= (*s - '0') & 0x0f;
			s++;
			*r++ = chr;

		} // %

	}

	*r = 0;

	return result;

}



syntax highlighted by Code2HTML, v. 0.9.1