Index: Headers/Foundation/NSUUID.h
===================================================================
--- Headers/Foundation/NSUUID.h	(revision 0)
+++ Headers/Foundation/NSUUID.h	(working copy)
@@ -0,0 +1,56 @@
+/* Interface for NSUUID for GNUStep
+   Copyright (C) 2013 Free Software Foundation, Inc.
+
+   Written by:  Graham Lee <graham@iamleeg.com>
+   Created: 2013
+   
+   This file is part of the GNUstep Base Library.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+   
+   This library 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
+   Library General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02111 USA.
+   */ 
+
+#ifndef __NSUUID_h_GNUSTEP_BASE_INCLUDE
+#define __NSUUID_h_GNUSTEP_BASE_INCLUDE
+
+#import	<Foundation/NSObject.h>
+
+#if	defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef unsigned char uuid_t[16];
+
+@class NSString;
+
+@interface NSUUID : NSObject <NSCopying, NSCoding>
+{
+  @private
+  uuid_t uuid;
+}
+
++ (id)UUID;
+- (id)initWithUUIDString:(NSString *)string;
+- (id)initWithUUIDBytes:(uuid_t)bytes;
+- (NSString *)UUIDString;
+- (void)getUUIDBytes:(uuid_t)bytes;
+
+@end
+
+#if     defined(__cplusplus)
+}
+#endif
+
+#endif /* __NSUUID_h_GNUSTEP_BASE_INCLUDE */
Index: Source/GNUmakefile
===================================================================
--- Source/GNUmakefile	(revision 37356)
+++ Source/GNUmakefile	(working copy)
@@ -291,6 +291,7 @@
 NSTextCheckingResult.m\
 NSURLHandle.m \
 NSUserDefaults.m \
+NSUUID.m \
 NSValue.m \
 NSValueTransformer.m \
 NSXMLDocument.m \
@@ -451,6 +452,7 @@
 NSURLResponse.h \
 NSUserDefaults.h \
 NSUtilities.h \
+NSUUID.h \
 NSValue.h \
 NSValueTransformer.h \
 NSXMLDocument.h \
Index: Source/NSUUID.m
===================================================================
--- Source/NSUUID.m	(revision 0)
+++ Source/NSUUID.m	(working copy)
@@ -0,0 +1,270 @@
+/** NSUUID - Representation of universally unique identifiers.
+   Copyright (C) 2013 Free Software Foundation, Inc.
+
+   Written by:  Graham Lee <graham@iamleeg.com>
+   Created: November 2013
+
+   This file is part of the GNUstep Base Library.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02111 USA.
+
+   */
+
+#import "common.h"
+#import "Foundation/NSCoder.h"
+#import "Foundation/NSUUID.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+static int uuid_from_string(const char *string, unsigned char *uuid);
+static void string_from_uuid(const unsigned char *uuid, char *string);
+static int random_uuid(unsigned char *uuid);
+
+static const int kUUIDStringLength = 36;
+static const int kUnformattedUUIDStringLength = 32;
+static const int kUUIDByteCount = 16;
+
+
+/**
+ * A representation of universally unique identifiers (UUIDs).
+ */
+@implementation NSUUID
+
++ (id)UUID
+{
+  id u = [[self alloc] init];
+  return AUTORELEASE(u);
+}
+
+- (id)init
+{
+  uuid_t localUUID;
+  int result = random_uuid(localUUID);
+  if (result != 0)
+    {
+      DESTROY(self);
+      return nil;
+    }
+  return [self initWithUUIDBytes:localUUID];
+}
+
+- (id)initWithUUIDString:(NSString *)string
+{
+  uuid_t localUUID;
+  const char *cString = [string cStringUsingEncoding:NSASCIIStringEncoding];
+  int parseResult = uuid_from_string(cString, localUUID);
+  if (parseResult != 0)
+    {
+      DESTROY(self);
+      return nil;
+    }
+  return [self initWithUUIDBytes:localUUID];
+}
+
+- (id)initWithUUIDBytes:(uuid_t)bytes
+{
+  self = [super init];
+  if (self)
+    {
+      memcpy(self->uuid, bytes, kUUIDByteCount);
+    }
+  return self;
+}
+
+- (NSString *)UUIDString
+{
+  char uuidChars[kUUIDStringLength + 1];
+  string_from_uuid(uuid, uuidChars);
+  NSString *string = [[NSString alloc] initWithCString:uuidChars encoding:NSASCIIStringEncoding];
+  return AUTORELEASE(string);
+}
+
+- (void)getUUIDBytes:(uuid_t)bytes
+{
+  memcpy(bytes, uuid, kUUIDByteCount);
+}
+
+- (BOOL)isEqual:(NSUUID *)other
+{
+  if (![other isKindOfClass:[NSUUID class]])
+    {
+      return NO;
+    }
+  int comparison = memcmp(self->uuid, other->uuid, kUUIDByteCount);
+  return (comparison == 0);
+}
+
+- (NSUInteger)hash
+{
+  // more expensive than casting but that's not alignment-safe
+  NSUInteger uintegerArray[kUUIDByteCount/sizeof(NSUInteger)];
+  memcpy(uintegerArray, uuid, kUUIDByteCount);
+  NSUInteger hash = 0;
+  for (int i = 0; i < kUUIDByteCount/sizeof(NSUInteger); i++)
+    {
+      hash ^= uintegerArray[i];
+    }
+  return hash;
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+  return RETAIN(self);
+}
+
+static NSString *uuidKey = @"uuid";
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+  if ([aCoder allowsKeyedCoding])
+    {
+      [aCoder encodeBytes:uuid length:kUUIDByteCount forKey:uuidKey];
+    }
+  else
+    {
+      [aCoder encodeBytes:uuid length:kUUIDByteCount];
+    }
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+  self = [super init];
+  if (self)
+    {
+      NSUInteger decodedLength = 0;
+      const uint8_t *decodedUUID;
+      if ([aDecoder allowsKeyedCoding])
+	{
+	  decodedUUID = [aDecoder decodeBytesForKey:uuidKey returnedLength:&decodedLength];
+	}
+      else
+	{
+	  decodedUUID = [aDecoder decodeBytesWithReturnedLength:&decodedLength];
+	}
+      if (decodedLength == kUUIDByteCount)
+	{
+	  memcpy(uuid, decodedUUID, kUUIDByteCount);
+	}
+      else
+	{
+	  DESTROY(self);
+	  return nil;
+	}
+    }
+  return self;
+}
+@end
+
+static int uuid_from_string(const char *string, unsigned char *uuid)
+{
+  if (strlen(string) != kUUIDStringLength)
+    {
+      return -1;
+    }
+  for (int i = 0; i < kUUIDStringLength; i++)
+    {
+      char c = string[i];
+      if ((i == 8) || (i == 13) || (i == 18) || (i == 23))
+	{
+	  if (c != '-') 
+	    {
+	      return -1;
+	    }
+	}
+      else
+	{
+	  if (!isxdigit(c))
+	    {
+	      return -1;
+	    }
+	}
+    }
+  char unformatted[kUnformattedUUIDStringLength];
+  strncpy(unformatted, string, 8);
+  strncpy(unformatted+8, string+9, 4);
+  strncpy(unformatted+12, string+14, 4);
+  strncpy(unformatted+16, string+19, 4);
+  strncpy(unformatted+20, string+24, 12);
+
+  for (int i = 0; i < kUUIDByteCount; i++)
+    {
+      {
+	char thisDigit[3];
+	thisDigit[0] = unformatted[2*i];
+	thisDigit[1] = unformatted[2*i+1];
+	thisDigit[2] = 0;
+	uuid[i] = strtoul(thisDigit, NULL, kUUIDByteCount);
+      }
+    }
+  return 0;
+}
+
+static void string_from_uuid(const unsigned char *uuid, char *string)
+{
+  char unformatted[kUnformattedUUIDStringLength];
+  for (int i = 0; i < kUUIDByteCount; i++)
+    {
+      unsigned char byte = uuid[i];
+      char thisPair[3];
+      snprintf(thisPair, 3, "%02X", byte);
+      strncpy(unformatted + 2*i, thisPair, 2);
+    }
+  strncpy(string, unformatted, 8);
+  string[8] = '-';
+  strncpy(string + 9, unformatted + 8, 4);
+  string[13] = '-';
+  strncpy(string + 14, unformatted + 12, 4);
+  string[18] = '-';
+  strncpy(string + 19, unformatted + 16, 4);
+  string[23] = '-';
+  strncpy(string + 24, unformatted + 20, 12);
+  string[kUUIDStringLength] = '\0';
+}
+
+static int random_uuid(unsigned char *uuid)
+{
+  /*Only supporting Version 4 UUIDs (see RFC4412, section 4.4),
+   *consistent with Apple.  Other variants suffer from privacy
+   *problems (and are more work...)
+   */
+  int devUrandom = open("/dev/urandom", O_RDONLY);
+  if (devUrandom == -1)
+    {
+      return -1;
+    }
+  ssize_t bytesRead = read(devUrandom, uuid, kUUIDByteCount);
+  close(devUrandom);
+  if (bytesRead != kUUIDByteCount)
+    {
+      return -1;
+    }
+  // as required by the RFC, bits 48-51 should contain 0b0100 (4)
+  // and bits 64-65 should contain 0b01 (1)
+  unsigned char timeByte = uuid[6];
+  timeByte = (4 << 8) + (timeByte & 0x0f);
+  uuid[7] = timeByte;
+
+  unsigned char sequenceByte = uuid[8];
+  sequenceByte = (1 << 6) + (sequenceByte & 0x3f);
+  uuid[8] = sequenceByte;
+
+  return 0;
+}
Index: Tests/base/NSUUID/TestInfo
===================================================================
Index: Tests/base/NSUUID/basic.m
===================================================================
--- Tests/base/NSUUID/basic.m	(revision 0)
+++ Tests/base/NSUUID/basic.m	(working copy)
@@ -0,0 +1,56 @@
+#include <Foundation/NSAutoreleasePool.h>
+#include <Foundation/NSString.h>
+#include <Foundation/NSUUID.h>
+#include "Testing.h"
+#include "ObjectTesting.h"
+
+int main(int argc, char **argv)
+{
+  NSAutoreleasePool *arp = [NSAutoreleasePool new];
+  NSUUID *uuid1, *uuid2;
+  NSString *uuidString = @"2139CD2E-15E6-4C37-84DA-1E18EEBFAB4A";
+
+  uuid_t uuidBytes = { 0x80, 0x1f, 0x3a, 0x01, 0x95, 0x7c, 0x45, 0x0f,
+		       0xaf, 0xf2, 0x1b, 0xe9, 0x59, 0xf5, 0x89, 0x54 };
+
+  TEST_FOR_CLASS(@"NSUUID", [NSUUID alloc],
+		 "+[NSUUID alloc] returns a NSUUID");
+  TEST_FOR_CLASS(@"NSUUID", [NSUUID UUID],
+		 "+[NSUUID UUID] returns a UUID");
+
+  uuid1 = [[NSUUID alloc] initWithUUIDString:@"test"];
+  PASS(uuid1 == nil, "Don't create a UUID from an invalid string");
+
+  uuid1 = [[NSUUID alloc] initWithUUIDString:uuidString];
+  PASS(uuid1 != nil, "Create a UUID from a valid string");
+  PASS_EQUAL([uuid1 UUIDString], uuidString,
+	     "Derive a stable UUID string value");
+
+  uuid2 = [[NSUUID alloc] initWithUUIDString:uuidString];
+  PASS_EQUAL(uuid1, uuid2, "UUIDs representing the same value are considered equal");
+  PASS([uuid1 hash] == [uuid2 hash], "Equal objects have equal hashes");
+
+  DESTROY(uuid2);
+  uuid2 = [[NSUUID alloc] initWithUUIDBytes:uuidBytes];
+  PASS(![uuid1 isEqual:uuid2], "UUIDs representing different values should not be considered equal");
+
+  uuid_t otherBytes = {0};
+  [uuid2 getUUIDBytes:otherBytes];
+
+  int comparison = memcmp(uuidBytes, otherBytes, 16);
+  PASS(comparison == 0, "Get a stable value for the UUID bytes");
+  DESTROY(uuid2);
+
+  uuid2 = [uuid1 copy];
+  PASS_EQUAL(uuid1, uuid2, "-[NSUUID copy] returns an identical object");
+  DESTROY(uuid2);
+  
+  NSData *coded = [NSKeyedArchiver archivedDataWithRootObject:uuid1];
+  uuid2 = [NSKeyedUnarchiver unarchiveObjectWithData:coded];
+  PASS_EQUAL(uuid1, uuid2, "UUID survives a round-trip through archiver");
+  DESTROY(uuid1);
+
+  [arp release];
+  arp = nil;
+  return 0;
+}
