diff -ruN parrot/MANIFEST parrot-modified/MANIFEST
--- parrot/MANIFEST	2003-09-11 06:36:15.000000000 +0300
+++ parrot-modified/MANIFEST	2003-09-11 06:57:01.000000000 +0300
@@ -294,6 +294,7 @@
 exec_save.c                                       []
 exec_start.c                                      []
 exit.c                                            []
+file_spec.c                                       []
 fingerprint_c.pl                                  []
 global_setup.c                                    []
 hash.c                                            []
@@ -1308,6 +1309,7 @@
 include/parrot/exec.h                             [devel]include
 include/parrot/exec_save.h                        [devel]include
 include/parrot/exit.h                             [devel]include
+include/parrot/file_spec.h                        [devel]include
 include/parrot/global_setup.h                     [devel]include
 include/parrot/hash.h                             [devel]include
 include/parrot/headers.h                          [devel]include
@@ -1875,6 +1877,7 @@
 t/op/comp.t                                       []
 t/op/conv.t                                       []
 t/op/debuginfo.t                                  []
+t/op/file_spec.t                                  []
 t/op/gc.t                                         []
 t/op/globals.t                                    []
 t/op/hacks.t                                      []
diff -ruN parrot/config/gen/config_h/config_h.in parrot-modified/config/gen/config_h/config_h.in
--- parrot/config/gen/config_h/config_h.in	2003-08-27 00:32:23.000000000 +0300
+++ parrot-modified/config/gen/config_h/config_h.in	2003-09-11 07:12:13.000000000 +0300
@@ -52,6 +52,8 @@
 #define PARROT_BIGENDIAN        ${bigendian}
 #define PARROT_PTR_ALIGNMENT    ${ptr_alignment}
 
+#define PARROT_OS_NAME_IS_${uc_osname}
+
 typedef Parrot_Int INTVAL;
 typedef Parrot_UInt UINTVAL;
 typedef Parrot_Float FLOATVAL;
diff -ruN parrot/config/gen/config_h.pl parrot-modified/config/gen/config_h.pl
--- parrot/config/gen/config_h.pl	2003-08-27 00:32:22.000000000 +0300
+++ parrot-modified/config/gen/config_h.pl	2003-09-11 07:12:41.000000000 +0300
@@ -9,6 +9,7 @@
 @args=();
 
 sub runstep {
+  Configure::Data->set(uc_osname => uc($^O));
   genfile('config/gen/config_h/config_h.in', 'include/parrot/config.h',
           commentType => '/*',
           ignorePattern => 'PARROT_CONFIG_DATE');
diff -ruN parrot/config/gen/makefiles/root.in parrot-modified/config/gen/makefiles/root.in
--- parrot/config/gen/makefiles/root.in	2003-09-11 06:36:39.000000000 +0300
+++ parrot-modified/config/gen/makefiles/root.in	2003-09-11 07:10:15.000000000 +0300
@@ -114,7 +114,7 @@
 	headers$(O) dod$(O) method_util$(O) exit$(O) \
 	misc$(O) spf_render$(O) spf_vtable$(O) datatypes$(O) fingerprint$(O) \
 	nci$(O) cpu_dep$(O) ${asmfun_o} tsq$(O) longopt$(O) events$(O) \
-	dynext$(O) utils$(O)
+	dynext$(O) utils$(O) file_spec$(O)
 
 O_FILES = $(INTERP_O_FILES) $(IO_O_FILES) $(CLASS_O_FILES) \
 	$(ENCODING_O_FILES) $(CHARTYPE_O_FILES)
@@ -505,6 +505,8 @@
 
 utils$(O) : $(GENERAL_H_FILES)
 
+file_spec$(O) : $(GENERAL_H_FILES)
+
 spf_render$(O) : $(GENERAL_H_FILES)
 
 spf_vtable$(O) : $(GENERAL_H_FILES)
diff -ruN parrot/core.ops parrot-modified/core.ops
--- parrot/core.ops	2003-08-30 03:39:39.000000000 +0300
+++ parrot-modified/core.ops	2003-09-11 07:07:02.000000000 +0300
@@ -1024,6 +1024,73 @@
 
 ###############################################################################
 
+=head2 File Spec
+
+Operations that are performed on file specifications
+
+=over 4
+
+=cut
+
+########################################
+
+=item B<curdir>(out STR)
+
+Returns a string representation of the current directory
+
+=cut
+
+inline op curdir(out STR) {
+  file_spec_curdir(interpreter, &$1);
+  goto NEXT();
+}
+
+########################################
+
+=item B<concat_dirnames>(inout STR, inconst STR)
+
+=item B<concat_dirnames>(out   STR, inconst STR, inconst STR)
+
+Concatenates two directory names
+
+=cut
+
+inline op concat_dirnames(inout STR, in STR) {
+  file_spec_concat_dirnames(interpreter, $1, $2, &$1);
+  goto NEXT();
+}
+
+inline op concat_dirnames(out STR, in STR, in STR) {
+  file_spec_concat_dirnames(interpreter, $2, $3, &$1);
+  goto NEXT();
+}
+
+########################################
+
+=item B<append_filename>(inout STR, inconst STR)
+
+=item B<append_filename>(out   STR, inconst STR, inconst STR)
+
+Appends a file name to a path name
+
+=cut
+
+inline op append_filename(inout STR, in STR) {
+  file_spec_append_filename(interpreter, $1, $2, &$1);
+  goto NEXT();
+}
+
+inline op append_filename(out STR, in STR, in STR) {
+  file_spec_append_filename(interpreter, $2, $3, &$1);
+  goto NEXT();
+}
+
+=back
+
+=cut
+
+###############################################################################
+
 =head1 COPYRIGHT
 
 Copyright (C) 2001-2003 The Perl Foundation.  All rights reserved.
diff -ruN parrot/file_spec.c parrot-modified/file_spec.c
--- parrot/file_spec.c	1970-01-01 03:00:00.000000000 +0300
+++ parrot-modified/file_spec.c	2003-09-11 09:05:34.000000000 +0300
@@ -0,0 +1,412 @@
+/* file_spec.c
+ *  Copyright: 2001-2003 The Perl Foundation.  All Rights Reserved.
+ *  CVS Info
+ *     $Id:
+ *  Overview:
+ *     File Spec API
+ *  Data Structure and Algorithms:
+ *  History:
+ *  Notes:
+ *  References:
+ *     A good deal of stuff is borrowed from File::Spec, the Perl5 module
+ */
+
+#include "parrot/parrot.h"
+
+void
+file_spec_curdir(struct Parrot_Interp *interpreter, STRING **curdir_ptr)
+{
+    *curdir_ptr = string_make(interpreter, PARROT_FS_CURDIR,
+                              PARROT_FS_CURDIR_LEN, NULL, 0, NULL);
+}
+
+void
+file_spec_concat_dirnames(struct Parrot_Interp *interpreter,
+                          STRING *dirname_a, STRING *dirname_b,
+                          STRING **pathname_ab_ptr)
+{   
+    PARROT_FS_BOOL dirname_a_is_empty  = PARROT_FS_FALSE;
+    PARROT_FS_BOOL dirname_a_is_curdir = PARROT_FS_FALSE;
+    PARROT_FS_BOOL dirname_b_is_empty  = PARROT_FS_FALSE;
+    PARROT_FS_BOOL dirname_b_is_curdir = PARROT_FS_FALSE;
+    
+    const ENCODING *encoding;
+    const CHARTYPE *type;
+    
+    STRING *curdir;
+    STRING *dirsep;
+    STRING *dirname_start;
+    STRING *dirname_end;
+    STRING *pathname_ab;
+    
+    void   *pathname_ab_curstart;
+    UINTVAL pathname_ab_curstrlen;
+    UINTVAL pathname_ab_curbufused;
+    
+    if (dirname_a == NULL || string_length(dirname_a) == 0)
+        dirname_a_is_empty = PARROT_FS_TRUE;
+    if (dirname_b == NULL || string_length(dirname_b) == 0)
+        dirname_b_is_empty = PARROT_FS_TRUE;
+    
+    if (dirname_a_is_empty && dirname_b_is_empty)
+    {
+        *pathname_ab_ptr = string_make(interpreter, "", 0, NULL, 0, NULL);
+        return;
+    }
+    
+    if (!dirname_a_is_empty)
+    {
+        encoding = dirname_a->encoding;
+        type     = dirname_a->type;
+        
+        if (!dirname_b_is_empty && (dirname_b->encoding != encoding
+                || dirname_b->type != type))
+            dirname_b = string_transcode(interpreter, dirname_b, encoding,
+                                         type, NULL);
+    }
+    else
+    {
+        encoding = dirname_b->encoding;
+        type     = dirname_b->type;
+    }
+    
+    curdir = string_make(interpreter, PARROT_FS_CURDIR, PARROT_FS_CURDIR_LEN,
+                         encoding, 0, type);
+    
+    if (!string_compare(interpreter, dirname_a, curdir))
+        dirname_a_is_curdir = PARROT_FS_TRUE;
+    if (!string_compare(interpreter, dirname_b, curdir))
+        dirname_b_is_curdir = PARROT_FS_TRUE;
+    
+    if (dirname_a_is_curdir && dirname_b_is_empty
+        ||
+        dirname_a_is_empty  && dirname_b_is_curdir
+        ||
+        dirname_a_is_curdir && dirname_b_is_curdir)
+    {
+        *pathname_ab_ptr = curdir;
+        return;
+    }
+    
+    if (PARROT_FS_DIRSEP_LEN && !dirname_a_is_empty && !dirname_b_is_empty)
+        dirsep         = string_make(interpreter, PARROT_FS_DIRSEP,
+                                     PARROT_FS_DIRSEP_LEN, encoding,
+                                     0, type);
+    if (PARROT_FS_DIRNAME_START_LEN)
+        dirname_start = string_make(interpreter, PARROT_FS_DIRNAME_START,
+                                     PARROT_FS_DIRNAME_START_LEN, encoding,
+                                     0, type);
+    if (PARROT_FS_DIRNAME_END_LEN)
+        dirname_end   = string_make(interpreter, PARROT_FS_DIRNAME_END,
+                                     PARROT_FS_DIRNAME_END_LEN, encoding,
+                                     0, type);
+    
+    pathname_ab_curbufused =
+        (PARROT_FS_DIRNAME_START_LEN ? dirname_start->bufused : 0)
+        + (dirname_a_is_empty ? 0 : dirname_a->bufused)
+        + (dirname_a_is_empty || dirname_b_is_empty ? 0 : dirsep->bufused)
+        + (dirname_b_is_empty ? 0 : dirname_b->bufused)
+        + (PARROT_FS_DIRNAME_END_LEN ? dirname_end->bufused : 0);
+    
+    pathname_ab = string_make(interpreter, NULL, pathname_ab_curbufused,
+                              encoding, 0, type);
+    
+    pathname_ab_curstart   = pathname_ab->strstart;
+    pathname_ab_curstrlen  = 0;
+    pathname_ab_curbufused = 0;
+    
+    if (PARROT_FS_DIRNAME_START_LEN)
+    {
+        mem_sys_memcopy(pathname_ab_curstart, dirname_start->strstart,
+                        dirname_start->bufused);
+        pathname_ab_curstart    = (void *)((ptrcast_t)pathname_ab_curstart
+                                  + dirname_start->bufused);
+        pathname_ab_curstrlen  += PARROT_FS_DIRNAME_START_LEN;
+        pathname_ab_curbufused += dirname_start->bufused;
+    }
+
+#define DIRNAME_COPY(x) \
+    if (!dirname_##x##_is_empty && !dirname_##x##_is_curdir) \
+    { \
+        UINTVAL     dirname_##x##_curstrlen  = dirname_##x##->strlen; \
+        UINTVAL     dirname_##x##_curbufused = dirname_##x##->bufused; \
+         \
+        const void *dirname_##x##_curstart   = dirname_##x##->strstart; \
+        const void *dirname_##x##_curend     = \
+            (void *)((ptrcast_t)dirname_##x##_curstart \
+                + dirname_##x##_curbufused); \
+         \
+        if (PARROT_FS_DIRNAME_START_LEN \
+            && dirname_##x##_curstrlen > PARROT_FS_DIRNAME_START_LEN) \
+        { \
+            INTVAL cmp = 0; \
+            const char *dirname_start_curstart = dirname_start->strstart; \
+             \
+            if (encoding->index == enum_encoding_singlebyte) \
+                cmp = memcmp(dirname_##x##_curstart, dirname_start_curstart, \
+                             PARROT_FS_DIRNAME_START_LEN); \
+            else \
+            { \
+                const char *dirname_start_curend = \
+                    (void *)((ptrcast_t)dirname_start_curstart \
+                        + dirname_start->bufused); \
+                 \
+                while (cmp == 0 \
+                       && dirname_start_curstart < dirname_start_curend) \
+                { \
+                    INTVAL chr_a = \
+                        dirname_##x##->encoding->decode( \
+                            dirname_##x##_curstart); \
+                    INTVAL chr_b = \
+                        dirname_start->encoding->decode( \
+                            dirname_start_curstart); \
+                     \
+                    cmp = chr_a - chr_b; \
+                     \
+                    dirname_##x##_curstart      = \
+                        dirname_##x##->encoding->skip_forward( \
+                            dirname_##x##_curstart, 1); \
+                    dirname_start_curstart = \
+                        dirname_start->encoding->skip_forward( \
+                            dirname_start_curstart, 1); \
+                } \
+                 \
+                dirname_##x##_curstart = dirname_##x##->strstart; \
+            } /* if (encoding->index == enum_encoding_singlebyte) */ \
+             \
+            if (cmp == 0) \
+            { \
+                dirname_##x##_curstart    = \
+                    (void *)((ptrcast_t)dirname_##x##_curstart \
+                        + dirname_start->bufused); \
+                dirname_##x##_curstrlen  -= PARROT_FS_DIRNAME_START_LEN; \
+                dirname_##x##_curbufused -= dirname_start->bufused; \
+            } \
+        } /* if (PARROT_FS_DIRNAME_START_LEN */ \
+         \
+        if (PARROT_FS_DIRNAME_END_LEN \
+            && dirname_##x##_curstrlen > PARROT_FS_DIRNAME_START_LEN \
+                                     + PARROT_FS_DIRNAME_END_LEN) \
+        { \
+            INTVAL cmp = 0; \
+            const char *dirname_end_curstart = dirname_end->strstart; \
+            const char *dirname_end_curend   = \
+                    (void *)((ptrcast_t)dirname_end_curstart \
+                        + dirname_end->bufused); \
+             \
+            if (encoding->index == enum_encoding_singlebyte) \
+                cmp = memcmp((void *)((ptrcast_t)dirname_##x##_curend \
+                             - dirname_end->bufused), \
+                             (void *)((ptrcast_t)dirname_end_curend \
+                             - dirname_end->bufused), \
+                             PARROT_FS_DIRNAME_END_LEN); \
+            else \
+            { \
+                while (cmp == 0 \
+                       && dirname_end_curstart < dirname_end_curend) \
+                { \
+                    INTVAL chr_a, chr_b; \
+                     \
+                    dirname_##x##_curend      = \
+                        dirname_##x##->encoding->skip_backward( \
+                            dirname_##x##_curend, 1); \
+                    dirname_end_curend = \
+                        dirname_end->encoding->skip_backward( \
+                            dirname_end_curend, 1); \
+                     \
+                    chr_a = \
+                        dirname_##x##->encoding->decode(dirname_##x##_curend); \
+                    chr_b = \
+                        dirname_end->encoding->decode(dirname_end_curend); \
+                     \
+                    cmp = chr_a - chr_b; \
+                } \
+            } /* if (encoding->index == enum_encoding_singlebyte) */ \
+             \
+            if (cmp == 0) \
+            { \
+                dirname_##x##_curstrlen  -= PARROT_FS_DIRNAME_END_LEN; \
+                dirname_##x##_curbufused -= dirname_end->bufused; \
+            } \
+        } /* if (PARROT_FS_DIRNAME_END_LEN */ \
+         \
+        mem_sys_memcopy(pathname_ab_curstart, dirname_##x##_curstart, \
+                        dirname_##x##_curbufused); \
+        pathname_ab_curstart    = (void *)((ptrcast_t)pathname_ab_curstart \
+                                  + dirname_##x##_curbufused); \
+        pathname_ab_curstrlen  += dirname_##x##_curstrlen; \
+        pathname_ab_curbufused += dirname_##x##_curbufused; \
+    } /* if (!dirname_##x##_is_empty && !dirname_##x##_is_curdir) */ \
+/* #define DIRNAME_COPY(x) */
+    
+    DIRNAME_COPY(a)
+    
+    if (!dirname_a_is_empty  && !dirname_b_is_empty
+        &&
+        !dirname_a_is_curdir && !dirname_b_is_curdir)
+    {
+        mem_sys_memcopy(pathname_ab_curstart, dirsep->strstart,
+                        dirsep->bufused);
+        pathname_ab_curstart    = (void *)((ptrcast_t)pathname_ab_curstart
+                                  + dirsep->bufused);
+        pathname_ab_curstrlen  += PARROT_FS_DIRSEP_LEN;
+        pathname_ab_curbufused += dirsep->bufused;
+    }
+    
+    DIRNAME_COPY(b)
+    
+#undef DIRNAME_COPY
+    
+    if (PARROT_FS_DIRNAME_END_LEN)
+    {
+        mem_sys_memcopy(pathname_ab_curstart, dirname_end->strstart,
+                        dirname_end->bufused);
+        pathname_ab_curstrlen  += PARROT_FS_DIRNAME_END_LEN;
+        pathname_ab_curbufused += dirname_end->bufused;
+    }
+    
+    pathname_ab->strlen  = pathname_ab_curstrlen;
+    pathname_ab->bufused = pathname_ab_curbufused;
+    
+    *pathname_ab_ptr = pathname_ab;
+}
+
+void
+file_spec_append_filename(struct Parrot_Interp *interpreter,
+                          STRING *pathname_a, STRING *filename_b,
+                          STRING **pathname_ab_ptr)
+{
+    PARROT_FS_BOOL pathname_a_is_empty  = PARROT_FS_FALSE;
+    PARROT_FS_BOOL pathname_a_is_curdir = PARROT_FS_FALSE;
+    PARROT_FS_BOOL filename_b_is_empty  = PARROT_FS_FALSE;
+    
+    const ENCODING *encoding;
+    const CHARTYPE *type;
+    
+    STRING *curdir;
+    STRING *filesep;
+    STRING *filename_start;
+    STRING *filename_end;
+    STRING *pathname_ab;
+    
+    void   *pathname_ab_curstart;
+    UINTVAL pathname_ab_strlen;
+    UINTVAL pathname_ab_bufused;
+    
+    if (pathname_a == NULL  || string_length(pathname_a) == 0)
+        pathname_a_is_empty  = PARROT_FS_TRUE;
+    if (filename_b == NULL || string_length(filename_b) == 0)
+        filename_b_is_empty = PARROT_FS_TRUE;
+    
+    if (filename_b_is_empty)
+    {
+        *pathname_ab_ptr = string_make(interpreter, "", 0, NULL, 0, NULL);
+        return;
+    }
+    
+    if (!pathname_a_is_empty)
+    {
+        encoding = pathname_a->encoding;
+        type     = pathname_a->type;
+        
+        if (!filename_b_is_empty && (filename_b->encoding != encoding
+                || filename_b->type != type))
+            filename_b = string_transcode(interpreter, filename_b, encoding,
+                                         type, NULL);
+    }
+    else
+    {
+        encoding = filename_b->encoding;
+        type     = filename_b->type;
+    }
+    
+    curdir = string_make(interpreter, PARROT_FS_CURDIR, PARROT_FS_CURDIR_LEN,
+                         encoding, 0, type);
+    
+    if (!string_compare(interpreter, pathname_a, curdir))
+        pathname_a_is_curdir = PARROT_FS_TRUE;
+    
+    if (PARROT_FS_FILESEP_LEN)
+        filesep        = string_make(interpreter, PARROT_FS_FILESEP,
+                                     PARROT_FS_FILESEP_LEN, encoding,
+                                     0, type);
+    if (PARROT_FS_FILENAME_START_LEN)
+        filename_start = string_make(interpreter, PARROT_FS_FILENAME_START,
+                                     PARROT_FS_FILENAME_START_LEN, encoding,
+                                     0, type);
+    if (PARROT_FS_FILENAME_END_LEN)
+        filename_end   = string_make(interpreter, PARROT_FS_FILENAME_END,
+                                     PARROT_FS_FILENAME_END_LEN, encoding,
+                                     0, type);
+    
+    pathname_ab_strlen =
+        (pathname_a_is_empty || pathname_a_is_curdir ?
+            0 : pathname_a->strlen + PARROT_FS_FILESEP_LEN)
+        + PARROT_FS_FILENAME_START_LEN + filename_b->strlen
+        + PARROT_FS_FILENAME_END_LEN;
+    
+    pathname_ab_bufused =
+        (pathname_a_is_empty || pathname_a_is_curdir ?
+            0 : pathname_a->bufused
+            + (PARROT_FS_FILESEP_LEN ? filesep->bufused : 0))
+        + (PARROT_FS_FILENAME_START_LEN ? filename_start->bufused : 0)
+        + filename_b->bufused
+        + (PARROT_FS_FILENAME_END_LEN ? filename_end->bufused : 0);
+    
+    pathname_ab = string_make(interpreter, NULL, pathname_ab_bufused,
+                              encoding, 0, type);
+    
+    pathname_ab_curstart = pathname_ab->strstart;
+    
+    if (!pathname_a_is_empty && !pathname_a_is_curdir)
+    {
+        mem_sys_memcopy(pathname_ab_curstart, pathname_a->strstart,
+                        pathname_a->bufused);
+        pathname_ab_curstart = (void *)((ptrcast_t)pathname_ab_curstart
+                                + pathname_a->bufused);
+        
+        if (PARROT_FS_FILESEP_LEN)
+        {
+            mem_sys_memcopy(pathname_ab_curstart, filesep->strstart,
+                            filesep->bufused);
+            pathname_ab_curstart = (void *)((ptrcast_t)pathname_ab_curstart
+                                   + filesep->bufused);
+        }
+    }
+    
+    if (PARROT_FS_FILENAME_START_LEN)
+    {
+        mem_sys_memcopy(pathname_ab_curstart, filename_start->strstart,
+                        filename_start->bufused);
+        pathname_ab_curstart = (void *)((ptrcast_t)pathname_ab_curstart
+                               + filename_start->bufused);
+    }
+    
+    mem_sys_memcopy(pathname_ab_curstart, filename_b->strstart,
+                    filename_b->bufused);
+    pathname_ab_curstart = (void *)((ptrcast_t)pathname_ab_curstart
+                           + filename_b->bufused);
+    
+    if (PARROT_FS_FILENAME_END_LEN)
+    {
+        mem_sys_memcopy(pathname_ab_curstart, filename_end->strstart,
+                        filename_end->bufused);
+        pathname_ab_curstart = (void *)((ptrcast_t)pathname_ab_curstart
+                               + filename_end->bufused);
+    }
+    
+    pathname_ab->strlen  = pathname_ab_strlen;
+    pathname_ab->bufused = pathname_ab_bufused;
+    
+    *pathname_ab_ptr = pathname_ab;
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: bsd
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vim: expandtab shiftwidth=4:
+*/
diff -ruN parrot/include/parrot/file_spec.h parrot-modified/include/parrot/file_spec.h
--- parrot/include/parrot/file_spec.h	1970-01-01 03:00:00.000000000 +0300
+++ parrot-modified/include/parrot/file_spec.h	2003-09-11 09:06:26.000000000 +0300
@@ -0,0 +1,104 @@
+/* file_spec.h
+ *  Copyright: 2001-2003 The Perl Foundation.  All Rights Reserved.
+ *  CVS Info
+ *     $Id:
+ *  Overview:
+ *     File Spec API
+ *  Data Structure and Algorithms:
+ *  History:
+ *  Notes:
+ *  References:
+ *     A good deal of stuff is borrowed from File::Spec, the Perl5 module
+ */
+
+#if !defined(PARROT_FILE_SPEC_H_GUARD)
+#define PARROT_FILE_SPEC_H_GUARD
+
+#define PARROT_FS_BOOL   unsigned char
+#define PARROT_FS_TRUE   1
+#define PARROT_FS_FALSE  0
+
+#if defined PARROT_OS_NAME_IS_VMS
+#  define PARROT_FS_CURDIR              "[]"
+#  define PARROT_FS_CURDIR_LEN          2
+#elif defined PARROT_OS_NAME_IS_MAC
+#  define PARROT_FS_CURDIR              ":"
+#  define PARROT_FS_CURDIR_LEN          1
+#else   
+#  define PARROT_FS_CURDIR              "."
+#  define PARROT_FS_CURDIR_LEN          1
+#endif
+
+#if defined PARROT_OS_NAME_IS_VMS
+#  define PARROT_FS_DIRSEP              "."
+#  define PARROT_FS_DIRSEP_LEN          1
+#elif defined PARROT_OS_NAME_IS_MAC
+#   define PARROT_FS_DIRSEP             ":"
+#   define PARROT_FS_DIRSEP_LEN         1
+#elif defined PARROT_OS_NAME_IS_MSWIN32
+#  define PARROT_FS_DIRSEP              "\\"
+#  define PARROT_FS_DIRSEP_LEN          1
+#else
+#   define PARROT_FS_DIRSEP             "/"
+#   define PARROT_FS_DIRSEP_LEN         1
+#endif
+
+#if defined PARROT_OS_NAME_IS_VMS
+#  define PARROT_FS_DIRNAME_START       "["
+#  define PARROT_FS_DIRNAME_START_LEN   1
+#  define PARROT_FS_DIRNAME_END         "]"
+#  define PARROT_FS_DIRNAME_END_LEN     1
+#elif defined PARROT_OS_NAME_IS_MAC
+#  define PARROT_FS_DIRNAME_START       ":"
+#  define PARROT_FS_DIRNAME_START_LEN   1
+#  define PARROT_FS_DIRNAME_END         ""
+#  define PARROT_FS_DIRNAME_END_LEN     0
+#else
+#  define PARROT_FS_DIRNAME_START       ""
+#  define PARROT_FS_DIRNAME_START_LEN   0
+#  define PARROT_FS_DIRNAME_END         ""
+#  define PARROT_FS_DIRNAME_END_LEN     0
+#endif
+
+#if defined(PARROT_OS_NAME_IS_VMS) || defined(PARROT_OS_NAME_IS_MAC)
+#  define PARROT_FS_FILESEP              ""
+#  define PARROT_FS_FILESEP_LEN          0
+#elif defined PARROT_OS_NAME_IS_MSWIN32
+#  define PARROT_FS_FILESEP              "\\"
+#  define PARROT_FS_FILESEP_LEN          1
+#else
+#   define PARROT_FS_FILESEP             "/"
+#   define PARROT_FS_FILESEP_LEN         1
+#endif
+
+#if defined PARROT_OS_NAME_IS_MAC
+#  define PARROT_FS_FILENAME_START      ":"
+#  define PARROT_FS_FILENAME_START_LEN  1
+#else
+#  define PARROT_FS_FILENAME_START      ""
+#  define PARROT_FS_FILENAME_START_LEN  0
+#endif
+
+#define PARROT_FS_FILENAME_END          ""
+#define PARROT_FS_FILENAME_END_LEN      0
+
+void file_spec_curdir(struct Parrot_Interp *interpreter, STRING **curdir_ptr);
+void file_spec_concat_dirnames(struct Parrot_Interp *interpreter,
+                               STRING *dirname_a, STRING *dirname_b,
+                               STRING **pathname_ab_ptr);
+void file_spec_append_filename(struct Parrot_Interp *interpreter,
+                               STRING *pathname_a, STRING *filename_b,
+                               STRING **pathname_ab_ptr);
+
+#endif /* if !defined(PARROT_FILE_SPEC_H_GUARD) */
+
+
+/*
+ * Local variables:
+ * c-indentation-style: bsd
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vim: expandtab shiftwidth=4:
+*/
diff -ruN parrot/include/parrot/parrot.h parrot-modified/include/parrot/parrot.h
--- parrot/include/parrot/parrot.h	2003-08-13 02:58:35.000000000 +0300
+++ parrot-modified/include/parrot/parrot.h	2003-09-11 07:05:44.000000000 +0300
@@ -228,6 +228,7 @@
 #include "parrot/tsq.h"
 #include "parrot/longopt.h"
 #include "parrot/objects.h"
+#include "parrot/file_spec.h"
 #endif
 
 /*
diff -ruN parrot/t/op/file_spec.t parrot-modified/t/op/file_spec.t
--- parrot/t/op/file_spec.t	1970-01-01 03:00:00.000000000 +0300
+++ parrot-modified/t/op/file_spec.t	2003-09-11 07:00:11.000000000 +0300
@@ -0,0 +1,654 @@
+#! perl -w
+
+use Parrot::Test tests => 28;
+
+$os = $^O;
+
+if ($os eq 'VMS') {
+    $curdir = '[]';
+} elsif ($os eq 'Mac') {
+    $curdir = ':';
+} else {
+    $curdir = '.';
+}
+
+
+### test #1
+#
+output_is(<<'CODE', <<"OUTPUT", 'curdir');
+    curdir S0
+    print S0
+    print "\n"
+    end
+CODE
+$curdir
+OUTPUT
+###
+
+
+### test #2
+#
+output_is(<<'CODE', <<'OUTPUT', 'two-arg concat_dirnames/"" and ""');
+    set S0, ""
+    set S1, ""
+    concat_dirnames S0, S1
+    eq S0, "", ok1
+    print "not "
+ok1:print "ok1\n"
+    eq S1, "", ok2
+    print "not "
+ok2:print "ok2\n"
+    end
+CODE
+ok1
+ok2
+OUTPUT
+###
+
+
+### test #3
+#
+output_is(<<'CODE', <<'OUTPUT', 'two-arg catdir/null and ""');
+    null S0
+    set S1, ""
+    concat_dirnames S0, S1
+    eq S0, "", ok1
+    print "not "
+ok1:print "ok1\n"
+    eq S1, "", ok2
+    print "not "
+ok2:print "ok2\n"
+    end
+CODE
+ok1
+ok2
+OUTPUT
+###
+
+
+### test #4
+#
+output_is(<<'CODE', <<'OUTPUT', 'two-arg catdir/null and null');
+    null S0
+    null S1
+    concat_dirnames S0, S1
+    eq S0, "", ok1
+    print "not "
+ok1:print "ok1\n"
+    null S0
+    eq S1, S0, ok2
+    print "not "
+ok2:print "ok2\n"
+    end
+CODE
+ok1
+ok2
+OUTPUT
+###
+
+
+### test #5
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/curdir and null');
+    curdir S0
+    null S1
+    concat_dirnames S0, S1
+    print S0
+    print "\n"
+    null S0
+    eq S0, S1, ok
+    print "not "
+ok: print "ok\n"
+    end
+CODE
+$curdir
+ok
+OUTPUT
+###
+
+
+### test #6
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/"" and curdir');
+    set S0, ""
+    curdir S1
+    concat_dirnames S0, S1
+    print S0
+    print "\n"
+    print S1
+    print "\n"
+    end
+CODE
+$curdir
+$curdir
+OUTPUT
+###
+
+
+### test #7
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/curdir and curdir');
+    curdir S0
+    curdir S1
+    concat_dirnames S0, S1
+    print S0
+    print "\n"
+    end
+CODE
+$curdir
+OUTPUT
+###
+
+
+if ($os eq 'VMS') {
+    $path = '[foo]';
+    $len  = 5;
+} elsif ($os eq 'Mac') {
+    $path = ':foo';
+    $len  = 4;
+} else {
+    $path = 'foo';
+    $len  = 3;
+}
+
+
+### test #8
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/"" and foo');
+    set S0, ""
+    set S1, "foo"
+    concat_dirnames S0, S1
+    print S0
+    print "\n"
+    set I0, 0
+    length I0, S0
+    print I0
+    print "\n"
+    print S1
+    print "\n"
+    end
+CODE
+$path
+$len
+foo
+OUTPUT
+###
+
+
+### test #9
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/foo and null');
+    set S0, "foo"
+    null S1
+    concat_dirnames S0, S1
+    print S0
+    print "\n"
+    set I0, 0
+    length I0, S0
+    print I0
+    print "\n"
+    null S0
+    eq S0, S1, ok
+    print "not "
+ok: print "ok\n"
+    end
+CODE
+$path
+$len
+ok
+OUTPUT
+###
+
+
+### test #10
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/foo_path and curdir');
+    set S0, ""
+    set S1, "foo"
+    concat_dirnames S0, S1
+    curdir S2
+    concat_dirnames S0, S2
+    print S0
+    print "\n"
+    end
+CODE
+$path
+OUTPUT
+###
+
+
+### test #11
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/curdir and foo_path');
+    curdir S0
+    set S1, ""
+    concat_dirnames S0, S1
+    set S2, "foo"
+    concat_dirnames S0, S2
+    print S0
+    print "\n"
+    end
+CODE
+$path
+OUTPUT
+###
+
+
+### test #12
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/curdir and foo');
+    curdir S0
+    set S1, "foo"
+    concat_dirnames S0, S1
+    print S0
+    print "\n"
+    end
+CODE
+$path
+OUTPUT
+###
+
+
+### test #13
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/curdir and foo');
+    curdir S0
+    set S1, "foo"
+    concat_dirnames S0, S1
+    print S0
+    print "\n"
+    end
+CODE
+$path
+OUTPUT
+###
+
+
+if ($os eq 'VMS') {
+    $path = '[foo.bar]';
+    $len  = 9;
+} elsif ($os eq 'Mac') {
+    $path = ':foo:bar';
+    $len  = 8;
+} elsif ($os eq 'MSWin32') {
+    $path = 'foo\bar';
+    $len  = 7;
+} else {
+    $path = 'foo/bar';
+    $len  = 7;
+}
+
+
+### test #14
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir');
+    set S0, "foo"
+    set S1, "bar"
+    concat_dirnames S0, S1
+    print S0
+    print "\n"
+    set I0, 0
+    length I0, S0
+    print I0
+    print "\n"
+    print S1
+    print "\n"
+    end
+CODE
+$path
+$len
+bar
+OUTPUT
+###
+
+
+if ($os eq 'VMS') {
+    $path = '[foo.bar.baz]';
+    $len  = 13;
+} elsif ($os eq 'Mac') {
+    $path = ':foo:bar:baz';
+    $len  = 12;
+} elsif ($os eq 'MSWin32') {
+    $path = 'foo\bar\baz';
+    $len  = 11;
+} else {
+    $path = 'foo/bar/baz';
+    $len  = 11;
+}
+
+
+### test #15
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir');
+    set S0, "foo"
+    set S1, "bar"
+    concat_dirnames S0, S1
+    set S2, "baz"
+    concat_dirnames S0, S2
+    print S0
+    print "\n"
+    set I0, 0
+    length I0, S0
+    print I0
+    print "\n"
+    end
+CODE
+$path
+$len
+OUTPUT
+###
+
+
+### test #16
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/utf8 dirnames');
+    set S0, "foo"
+    find_encoding I0, "utf8"
+    transcode S0, S0, I0
+    set S1, "bar"
+    concat_dirnames S0, S1
+    set S2, "baz"
+    concat_dirnames S0, S2
+    print S0
+    print "\n"
+    set I0, 0
+    length I0, S0
+    print I0
+    print "\n"
+    end
+CODE
+$path
+$len
+OUTPUT
+###
+
+
+### test #17
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/utf8 dirnames');
+    set S0, "foo"
+    set S1, "bar"
+    find_encoding I0, "utf8"
+    transcode S1, S1, I0
+    concat_dirnames S0, S1
+    set S2, "baz"
+    transcode S2, S2, I0
+    concat_dirnames S0, S2
+    print S0
+    print "\n"
+    set I0, 0
+    length I0, S0
+    print I0
+    print "\n"
+    end
+CODE
+$path
+$len
+OUTPUT
+###
+
+
+### test #18
+#
+output_is(<<'CODE', <<"OUTPUT", 'three-arg catdir');
+    set S0, "foo"
+    set S1, "bar"
+    concat_dirnames S3, S0, S1
+    set S2, "baz"
+    concat_dirnames S3, S3, S2
+    print S3
+    print "\n"
+    set I0, 0
+    length I0, S3
+    print I0
+    print "\n"
+    print S0
+    print "\n"
+    print S1
+    print "\n"
+    print S2
+    print "\n"
+    end
+CODE
+$path
+$len
+foo
+bar
+baz
+OUTPUT
+###
+
+
+### test #19
+#
+output_is(<<'CODE', <<'OUTPUT', 'two-arg concat_dirnames/"" and ""');
+    set S0, ""
+    set S1, ""
+    append_filename S0, S1
+    eq S0, "", ok1
+    print "not "
+ok1:print "ok1\n"
+    eq S1, "", ok2
+    print "not "
+ok2:print "ok2\n"
+    end
+CODE
+ok1
+ok2
+OUTPUT
+###
+
+
+### test #20
+#
+output_is(<<'CODE', <<'OUTPUT', 'two-arg catdir/null and ""');
+    null S0
+    set S1, ""
+    append_filename S0, S1
+    eq S0, "", ok1
+    print "not "
+ok1:print "ok1\n"
+    eq S1, "", ok2
+    print "not "
+ok2:print "ok2\n"
+    end
+CODE
+ok1
+ok2
+OUTPUT
+###
+
+
+### test #21
+#
+output_is(<<'CODE', <<'OUTPUT', 'two-arg catdir/null and null');
+    null S0
+    null S1
+    append_filename S0, S1
+    eq S0, "", ok1
+    print "not "
+ok1:print "ok1\n"
+    null S0
+    eq S1, S0, ok2
+    print "not "
+ok2:print "ok2\n"
+    end
+CODE
+ok1
+ok2
+OUTPUT
+###
+
+
+### test #22
+#
+output_is(<<'CODE', <<'OUTPUT', 'two-arg catdir/path and ""');
+    set S0, "foo"
+    set S1, "bar"
+    concat_dirnames S0, S1
+    set S2, ""
+    append_filename S0, S2
+    eq S0, "", ok
+    print "not "
+ok: print "ok\n"
+    end
+CODE
+ok
+OUTPUT
+###
+
+
+if ($os eq 'Mac') {
+    $path = ':foo';
+    $len  = 4;
+} else {
+    $path = 'foo';
+    $len  = 3;
+}
+
+
+### test #23
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/"" and foo');
+    set S0, ""
+    set S1, "foo"
+    append_filename S0, S1
+    print S0
+    print "\n"
+    set I0, 0
+    length I0, S0
+    print I0
+    print "\n"
+    end
+CODE
+$path
+$len
+OUTPUT
+###
+
+
+### test #24
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/curdir and foo');
+    curdir S0
+    set S1, "foo"
+    append_filename S0, S1
+    print S0
+    print "\n"
+    set I0, 0
+    length I0, S0
+    print I0
+    print "\n"
+    end
+CODE
+$path
+$len
+OUTPUT
+###
+
+
+if ($os eq 'VMS') {
+    $path = '[foo.bar]baz';
+    $len  = 12;
+} elsif ($os eq 'Mac') {
+    $path = ':foo:bar:baz';
+    $len  = 12;
+} elsif ($os eq 'MSWin32') {
+    $path = 'foo\bar\baz';
+    $len  = 11;
+} else {
+    $path = 'foo/bar/baz';
+    $len  = 11;
+}
+
+
+### test #25
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir');
+    set S0, "foo"
+    set S1, "bar"
+    concat_dirnames S0, S1
+    set S2, "baz"
+    append_filename S0, S2
+    print S0
+    print "\n"
+    set I0, 0
+    length I0, S0
+    print I0
+    print "\n"
+    end
+CODE
+$path
+$len
+OUTPUT
+###
+
+
+### test #26
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/utf8 path and foo');
+    set S0, "foo"
+    set S1, "bar"
+    concat_dirnames S0, S1
+    find_encoding I0, "utf8"
+    transcode S0, S0, I0
+    set S2, "baz"
+    append_filename S0, S2
+    print S0
+    print "\n"
+    end
+CODE
+$path
+OUTPUT
+###
+
+
+### test #27
+#
+output_is(<<'CODE', <<"OUTPUT", 'two-arg catdir/path and utf8 foo');
+    set S0, "foo"
+    set S1, "bar"
+    concat_dirnames S0, S1
+    set S2, "baz"
+    find_encoding I0, "utf8"
+    transcode S2, S2, I0
+    append_filename S0, S2
+    print S0
+    print "\n"
+    end
+CODE
+$path
+OUTPUT
+###
+
+
+### test #28
+#
+output_is(<<'CODE', <<"OUTPUT", 'three-arg catdir');
+    set S0, "foo"
+    set S1, "bar"
+    concat_dirnames S3, S0, S1
+    set S2, "baz"
+    append_filename S3, S3, S2
+    print S3
+    print "\n"
+    set I0, 0
+    length I0, S3
+    print I0
+    print "\n"
+    print S0
+    print "\n"
+    print S1
+    print "\n"
+    print S2
+    print "\n"
+    end
+CODE
+$path
+$len
+foo
+bar
+baz
+OUTPUT
+###
+
