/* 
   NSPathUtilities.m

   Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea.
   All rights reserved.

   Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>

   This file is part of libFoundation.

   Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted, provided
   that the above copyright notice appear in all copies and that both that
   copyright notice and this permission notice appear in supporting
   documentation.

   We disclaim all warranties with regard to this software, including all
   implied warranties of merchantability and fitness, in no event shall
   we be liable for any special, indirect or consequential damages or any
   damages whatsoever resulting from loss of use, data or profits, whether in
   an action of contract, negligence or other tortious action, arising out of
   or in connection with the use or performance of this software.
*/

#include <config.h>

#ifdef _POSIX_VERSION
#include <limits.h>			/* for PATH_MAX */
#else
#include <sys/param.h>			/* for MAXPATHLEN */
#endif

#ifndef PATH_MAX
#ifdef _POSIX_VERSION
#define PATH_MAX _POSIX_PATH_MAX
#else
#ifdef MAXPATHLEN
#define PATH_MAX MAXPATHLEN
#else
#define PATH_MAX 1024
#endif
#endif
#endif

#include <Foundation/common.h>

#include <Foundation/NSArray.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSString.h>
#include <Foundation/NSPathUtilities.h>
#include <Foundation/NSUtilities.h>
#include <Foundation/NSAccount.h>

/*
 * User Account Functions
 */

NSString* NSUserName(void)
{
    return [[NSUserAccount currentAccount] accountName];
}

NSString* NSHomeDirectory(void)
{
    return [[NSUserAccount currentAccount] homeDirectory];
}

NSString* NSHomeDirectoryForUser(NSString* userName)
{
    return [[NSUserAccount accountWithName:userName] homeDirectory];
}

NSString* NSFullUserName()
{
    return [[NSUserAccount currentAccount] fullName];
}

NSString* NSTemporaryDirectory()
{
    // WIN32
    return @"/tmp";
}

/*
 * String file naming functions
 */

static NSString* pathSeparator = @"/";
static NSString* extensionSeparator = @".";
static NSString* nullSeparator = @"";
static NSString* homeSeparator = @"~";
static NSString* parentDirName = @"..";
static NSString* selfDirName = @".";

// WIN32 - must be @"C:/" on WINDOWS
static NSString* rootPath = @"/";

@implementation NSString(FilePathMethods)

+ (NSString*)pathWithComponents:(NSArray*)components
{
    id str, result;
    int n = [components count];
    int i = 0;
    BOOL first = YES;
    
    if (!n)
	return nullSeparator;
    
    result = [[[NSMutableString alloc] init] autorelease];
    
    str = [components objectAtIndex:0];
    if ([str isEqualToString:nullSeparator] || 
	[str isEqualToString:pathSeparator]) {
	    i = 1;
	    [result appendString:rootPath];
    }
    
    while (i < n - 1) {
	if (!first)
	    [result appendString:pathSeparator];
	else
	    first = NO;
	[result appendString:[components objectAtIndex:i]];
	i++;
    }
    
    if (n > 1) {
	str = [components objectAtIndex:n-1];
	if ([str isEqualToString:nullSeparator]) 
	    [result appendString:pathSeparator];
	else {
	    if (!first)
		[result appendString:pathSeparator];
	    [result appendString:str];
	}
    }
    return result;
}

- (NSArray*)pathComponents
{
    NSMutableArray* components
	= [[self componentsSeparatedByString:pathSeparator] mutableCopy];
    int i;
    
    for (i = [components count]-1; i >= 0; i--)
	if ([[components objectAtIndex:i] isEqual:nullSeparator])
	    [components removeObjectAtIndex:i];
    
    if ([self hasPrefix:pathSeparator])
	[components insertObject:pathSeparator atIndex:0];

    if ([self hasSuffix:pathSeparator])
	[components addObject:nullSeparator];

    return components;
}

- (unsigned int)completePathIntoString:(NSString**)outputName
  caseSensitive:(BOOL)flag matchesIntoArray:(NSArray**)outputArray
  filterTypes:(NSArray*)filterTypes
{
    // TODO
    [self notImplemented:_cmd];
    return 0;
}

- (const char*)fileSystemRepresentation
{
    // WIN32
    return [self cString];
}

- (BOOL)getFileSystemRepresentation:(char*)buffer
  maxLength:(unsigned int)maxLength
{
    // WIN32
    NSRange left;
    
    [self getCString:buffer maxLength:maxLength
	range:NSMakeRange(0, [self length]) remainingRange:&left];
    return (left.length == 0) ? YES : NO;
}

- (BOOL)isAbsolutePath
{
    if (![self length])
	return NO;

    if ([self hasPrefix:rootPath] || [self hasPrefix:@"~"])
	return YES;
    
    return NO;
}

- (NSString*)lastPathComponent
{
    NSRange sepRange;
    NSRange lastRange = {0, 0};
    
    lastRange.length = [self length];
    if ([self hasSuffix:pathSeparator]) {
	if (lastRange.length == [pathSeparator length])
	    return nullSeparator;
	else
	    lastRange.length--;
    }

    sepRange = [self rangeOfString:pathSeparator
	options:NSBackwardsSearch range:lastRange];
    lastRange.location = sepRange.location + sepRange.length;
    lastRange.length = lastRange.length - lastRange.location;
    
    if (lastRange.location == 0)
	return [[self copyWithZone:[self zone]] autorelease];
    else
	return lastRange.length ? 
	    [self substringWithRange:lastRange] : nullSeparator;
}

- (NSString*)pathExtension
{
    NSRange sepRange, lastRange;
    NSString* lastComponent = [self lastPathComponent];
    int length = [lastComponent length];
    
    sepRange = [lastComponent rangeOfString:extensionSeparator 
    	options:NSBackwardsSearch];
    lastRange.location = sepRange.location + sepRange.length;
    lastRange.length = length - lastRange.location;
    
    return lastRange.length && sepRange.length ? 
    	[lastComponent substringWithRange:lastRange] : nullSeparator;
}

- (NSString*)stringByAppendingPathComponent:(NSString*)aString
{
    NSString* str;
    
    str = [self hasSuffix:pathSeparator] ? nullSeparator : pathSeparator;

    return [aString length] ? 
	[self stringByAppendingString:([self length] ?
	    [str stringByAppendingString:aString] 
	    : aString)]
	: [[self copyWithZone:[self zone]] autorelease];
}

- (NSArray*)stringsByAppendingPaths:(NSArray*)paths
{
    NSMutableArray* array = [[[NSMutableArray alloc] init] autorelease];
    int i, n;
    
    for (i = 0, n = [paths count]; i < n; i++)
	[array addObject:[self stringByAppendingPathComponent:
	    [paths objectAtIndex:i]]];
    return array;
}

- (NSString*)stringByAppendingPathExtension:(NSString*)aString
{
    return [aString length] ? 
	[self stringByAppendingString:
	    [extensionSeparator stringByAppendingString:aString]] 
	: [[self copyWithZone:[self zone]] autorelease];
}

- (NSString*)stringByDeletingLastPathComponent
{
    NSRange range = {0, [self length]};
    
    if (range.length == 0)
	return nullSeparator;
    
    if ([self isEqualToString:pathSeparator])
	return pathSeparator;
    
    range.length--;
    range = [self rangeOfString:pathSeparator
	options:NSBackwardsSearch range:range];

    if (range.length == 0)
	return nullSeparator;
    if (range.location == 0)
	return pathSeparator;

    return [self substringWithRange:NSMakeRange(0, range.location)];
}

- (NSString*)stringByDeletingPathExtension
{
    NSRange range = {0, [self length]};
    NSRange extSep, patSep;
    
    if (range.length == 0)
	return nullSeparator;
    
    if ([self hasSuffix:pathSeparator]) {
	if (range.length == 1)
	    return [[self copyWithZone:[self zone]] autorelease];
	else
	    range.length--;
    }
    
    extSep = [self rangeOfString:extensionSeparator
	options:NSBackwardsSearch range:range];

    if (extSep.length) {
	patSep = [self rangeOfString:pathSeparator
	    options:NSBackwardsSearch range:range];
	if (patSep.length) {
	    if (patSep.location + 1 == extSep.location) {
		range.length = patSep.location;
	    }
	    else {
		if (extSep.location > patSep.location)
		    range.length = extSep.location;
		/* else leave as is */
	    }
	}
	else {
	    range.length = extSep.location;
	}
    }
    
    return [self substringWithRange:range];
}

- (NSString*)stringByAbbreviatingWithTildeInPath
{
    NSString* home = NSHomeDirectory();
    int homeLength = [home length];
    
    if (![self hasPrefix:home])
	return self;
	
    home = [self substringWithRange:
		NSMakeRange(homeLength, [self length] - homeLength)];
    
    return [homeSeparator stringByAppendingString:
	([home length] ? home : pathSeparator)];
}

- (NSString*)stringByExpandingTildeInPath
{
    NSString* rest;
    NSString* home;
    int index;
    int hlen;
    
    if (![self hasPrefix:homeSeparator])
	return self;
    
    index = [self indexOfString:pathSeparator];
    hlen = [homeSeparator length];
    
    if (index == hlen)
	home = NSHomeDirectory();
    else
	home = NSHomeDirectoryForUser([self substringWithRange:
	    NSMakeRange(hlen, (index == NSNotFound) ?  
		[self length] - hlen : index - hlen)]);
    
    if (index == NSNotFound)
	rest = nullSeparator;
    else
	rest = [self substringWithRange:
	    NSMakeRange(index + 1, [self length] - index - 1)];
    
    return [home stringByAppendingPathComponent:rest];
}

- (NSString*)stringByResolvingSymlinksInPath
{
    // WIN32 - no symlinks; just convert and stat the path using real names
    
    char resolved[PATH_MAX];
    const char* source = [self cString];
    char *resove_symlinks_in_path(const char *, char*);
    
    if (!resove_symlinks_in_path(source, resolved))
	return nil;
	
    return [NSString stringWithCString:resolved];
}

- (NSString*)stringByStandardizingPath
{
    if ([self isAbsolutePath]) {
	return [self stringByResolvingSymlinksInPath];
    }
    else {
	struct stat stat_buf;
	NSString* path;
	NSMutableArray* components = [[self pathComponents] mutableCopy];
	int i, n;
	
	n = [components count];
	/* remove "//" and "/./" components */
	for (i = n - 1; i >= 0; i--) {
	    NSString* comp = [components objectAtIndex:i];
	    
	    if ([comp length] == 0 || [comp isEqualToString:selfDirName]) {
		[components removeObjectAtIndex:i];
		continue;
	    }
	}
	/* compact ".../dir1/../dir2/..." into ".../dir2/..." */
	for (i = 1; i < n; i++) {
	    if ([[components objectAtIndex:i] isEqualToString:parentDirName] 
		&& i > 0 &&
	     ![[components objectAtIndex:i-1] isEqualToString:parentDirName]) {
		i -= 1;
		[components removeObjectAtIndex:i];
		[components removeObjectAtIndex:i];
	    }
	}
	
	path = [NSString pathWithComponents:components];
	[components release];
	
	if (stat([path cString], &stat_buf) != -1)
	    return nil;
	else
	    return path;
    }
}

@end

/*
 * Used for forcing linking of this category
 */

void __dummyNSStringFilePathfile ()
{
    __dummyNSStringFilePathfile();
}
