+#if defined(START_MY_CXT) && defined(MY_CXT_CLONE)
+# define USE_MY_CXT 1
+#else
+# define USE_MY_CXT 0
+#endif
+
+#if USE_MY_CXT
+# define MY_CXT_KEY "Cwd::_guts"XS_VERSION
+typedef struct {
+ SV *empty_string_sv, *slash_string_sv;
+} my_cxt_t;
+START_MY_CXT
+# define dUSE_MY_CXT dMY_CXT
+# define EMPTY_STRING_SV MY_CXT.empty_string_sv
+# define SLASH_STRING_SV MY_CXT.slash_string_sv
+# define POPULATE_MY_CXT do { \
+ MY_CXT.empty_string_sv = newSVpvs(""); \
+ MY_CXT.slash_string_sv = newSVpvs("/"); \
+ } while(0)
+#else
+# define dUSE_MY_CXT dNOOP
+# define EMPTY_STRING_SV sv_2mortal(newSVpvs(""))
+# define SLASH_STRING_SV sv_2mortal(newSVpvs("/"))
+#endif
+
+#define invocant_is_unix(i) THX_invocant_is_unix(aTHX_ i)
+static
+bool
+THX_invocant_is_unix(pTHX_ SV *invocant)
+{
+ /*
+ * This is used to enable optimisations that avoid method calls
+ * by knowing how they would resolve. False negatives, disabling
+ * the optimisation where it would actually behave correctly, are
+ * acceptable.
+ */
+ return SvPOK(invocant) && SvCUR(invocant) == 16 &&
+ !memcmp(SvPVX(invocant), "File::Spec::Unix", 16);
+}
+
+#define unix_canonpath(p) THX_unix_canonpath(aTHX_ p)
+static
+SV *
+THX_unix_canonpath(pTHX_ SV *path)
+{
+ SV *retval;
+ char const *p, *pe, *q;
+ STRLEN l;
+ char *o;
+ STRLEN plen;
+ SvGETMAGIC(path);
+ if(!SvOK(path)) return &PL_sv_undef;
+ p = SvPV_nomg(path, plen);
+ if(plen == 0) return newSVpvs("");
+ pe = p + plen;
+ retval = newSV(plen);
+#ifdef SvUTF8
+ if(SvUTF8(path)) SvUTF8_on(retval);
+#endif
+ o = SvPVX(retval);
+ if(DOUBLE_SLASHES_SPECIAL && p[0] == '/' && p[1] == '/' && p[2] != '/') {
+ q = memchr(p+2, '/', pe-(p+2));
+ if(!q) q = pe;
+ l = q - p;
+ memcpy(o, p, l);
+ p = q;
+ o += l;
+ }
+ /*
+ * The transformations performed here are:
+ * . squeeze multiple slashes
+ * . eliminate "." segments, except one if that's all there is
+ * . eliminate leading ".." segments
+ * . eliminate trailing slash, unless it's all there is
+ */
+ if(p[0] == '/') {
+ *o++ = '/';
+ while(1) {
+ do { p++; } while(p[0] == '/');
+ if(p[0] == '.' && p[1] == '.' && (p+2 == pe || p[2] == '/')) {
+ p++;
+ /* advance past second "." next time round loop */
+ } else if(p[0] == '.' && (p+1 == pe || p[1] == '/')) {
+ /* advance past "." next time round loop */
+ } else {
+ break;
+ }
+ }
+ } else if(p[0] == '.' && p[1] == '/') {
+ do {
+ p++;
+ do { p++; } while(p[0] == '/');
+ } while(p[0] == '.' && p[1] == '/');
+ if(p == pe) *o++ = '.';
+ }
+ if(p == pe) goto end;
+ while(1) {
+ q = memchr(p, '/', pe-p);
+ if(!q) q = pe;
+ l = q - p;
+ memcpy(o, p, l);
+ p = q;
+ o += l;
+ if(p == pe) goto end;
+ while(1) {
+ do { p++; } while(p[0] == '/');
+ if(p == pe) goto end;
+ if(p[0] != '.') break;
+ if(p+1 == pe) goto end;
+ if(p[1] != '/') break;
+ p++;
+ }
+ *o++ = '/';
+ }
+ end: ;
+ *o = 0;
+ SvPOK_on(retval);
+ SvCUR_set(retval, o - SvPVX(retval));
+ return retval;
+}