From bf13bd51a710b76ed6e88582e5e3fce153dfbfa1 Mon Sep 17 00:00:00 2001 From: Bike Date: Mon, 17 Jun 2024 12:55:43 -0400 Subject: [PATCH 1/5] Allow non-base-strings in pathnames Note that this is NOT a complete solution to #1595, as the actual OS interface (calls to open(3), etc.) is not really prepared for non-base-strings. It will probably have to do some encoding. --- src/core/pathname.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/core/pathname.cc b/src/core/pathname.cc index 260f3f7ba6..dc43fc8e55 100644 --- a/src/core/pathname.cc +++ b/src/core/pathname.cc @@ -1174,11 +1174,11 @@ T_sp clasp_namestring(T_sp tx, int flags) { TYPE_ERROR(tx, Cons_O::createList(cl::_sym_or, cl::_sym_string, cl::_sym_Pathname_O)); Pathname_sp x = cl__pathname(tx); - /* INV: Pathnames can only be created by mergin, parsing namestrings + /* INV: Pathnames can only be created by merging, parsing namestrings * or using clasp_make_pathname(). In all of these cases Clasp will complain * at creation time if the pathname has wrong components. */ - T_sp buffer = clasp_make_string_output_stream(); //(128, 1); + T_sp buffer = clasp_make_string_output_stream(STRING_OUTPUT_STREAM_DEFAULT_SIZE, true); logical = core__logical_pathname_p(x); host = x->_Host; if (logical) { @@ -1346,9 +1346,6 @@ CL_DEFUN T_mv cl__parse_namestring(T_sp thing, T_sp host, T_sp tdefaults, Fixnum if (default_host.nilp() && tempdefaults.notnilp()) { default_host = gc::As(tempdefaults)->_Host; } -#ifdef CLASP_UNICODE - thing = coerce::coerce_to_base_string(thing); -#endif p = sequenceKeywordStartEnd(cl::_sym_parse_namestring, gc::As(thing), start, end); output = clasp_parseNamestring(thing, p.start, p.end, &ee, default_host); start = make_fixnum(static_cast(ee)); From 0d901290044fe051ec5fa1e6b1dd8b6a4c1c9ed1 Mon Sep 17 00:00:00 2001 From: Bike Date: Mon, 24 Jun 2024 12:30:24 -0400 Subject: [PATCH 2/5] Define claspCharacter to be char32_t Since they are codepoints in UTF-32. This will HOPEFULLY make a few parts of using C++ easier, like making pathnames. This makes the error message when reading dense arrays wrong very slightly worse, but that's an implementation-specific thing anyway. --- include/clasp/core/configure_clasp.h | 2 +- src/core/character.cc | 4 ---- src/core/lispStream.cc | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/include/clasp/core/configure_clasp.h b/include/clasp/core/configure_clasp.h index 7ff7ddec95..630801552b 100644 --- a/include/clasp/core/configure_clasp.h +++ b/include/clasp/core/configure_clasp.h @@ -195,7 +195,7 @@ typedef int32_t Fixnum; // Signed Fixnum immediate value // ---------------------------------------------------------------------------- typedef unsigned char claspChar; -typedef unsigned int claspCharacter; +typedef char32_t claspCharacter; #define CLASP_CHAR(x) ((x)&0xff) // ---------------------------------------------------------------------------- diff --git a/src/core/character.cc b/src/core/character.cc index 4d78297590..f3e2e1cc53 100644 --- a/src/core/character.cc +++ b/src/core/character.cc @@ -47,10 +47,6 @@ namespace core { #include "character-generated.cc" -void handleWideCharactersError(claspCharacter cc) { - SIMPLE_ERROR("A wide character with the value {} was encountered in a function that needed a base-char", cc); -} - CL_LAMBDA(arg); CL_DECLARE(); CL_DOCSTRING(R"dx(Returns true if character is a graphic character other than space-like characters; diff --git a/src/core/lispStream.cc b/src/core/lispStream.cc index 6530584fb1..dd843c6f33 100644 --- a/src/core/lispStream.cc +++ b/src/core/lispStream.cc @@ -1450,7 +1450,7 @@ void read_array_readable_binary(T_sp stream, size_t num6bit, void* start, void* read_array_readable_binary(stream, num6bit, start, end); \ claspCharacter c = stream_read_char(stream); \ if (c != ' ') \ - SIMPLE_ERROR("Expected space at end of dense blob - got #\\{} ", c); \ + SIMPLE_ERROR("Expected space at end of dense blob"); \ return svf; \ } From 932bb4cd8556687397a1158503e8fee40a1f0030 Mon Sep 17 00:00:00 2001 From: Bike Date: Mon, 24 Jun 2024 14:34:23 -0400 Subject: [PATCH 3/5] Transmit non-base-chars in pathnames to OS properly Fixes #1595 There's a lot of get_std_string lurking around though. There might be a couple more holes. --- include/clasp/core/array.h | 4 +++ include/clasp/core/string.h | 6 ++++ src/core/lispStream.cc | 10 +++---- src/core/string.cc | 21 ++++++++++++++ src/core/unixfsys.cc | 55 +++++++++++++++++-------------------- 5 files changed, 61 insertions(+), 35 deletions(-) diff --git a/include/clasp/core/array.h b/include/clasp/core/array.h index 8eec5f8282..1f3c1439b1 100644 --- a/include/clasp/core/array.h +++ b/include/clasp/core/array.h @@ -258,6 +258,8 @@ class Array_O : public General_O { // // String functions virtual std::string get_std_string() const = 0; + // Get a string usable as a path. Has to take care of encoding. + virtual std::string get_path_string() const = 0; virtual vector arrayDimensionsAsVector() const = 0; // ------------------------------------------------------------ // @@ -379,6 +381,7 @@ class MDArray_O : public Array_O { size_t fillPointer() const override { return this->_FillPointerOrLengthOrDummy; }; virtual bool equalp(T_sp other) const override; virtual std::string get_std_string() const override { notStringError(this->asSmartPtr()); } + virtual std::string get_path_string() const override { notStringError(this->asSmartPtr()); } virtual vector arrayDimensionsAsVector() const override { vector dims; for (size_t i(0); i < this->_Dimensions.length(); ++i) { @@ -469,6 +472,7 @@ class AbstractSimpleVector_O : public Array_O { }; virtual T_sp replaceArray(T_sp other) override { notAdjustableError(core::_sym_replaceArray, this->asSmartPtr()); }; virtual std::string get_std_string() const override { notStringError(this->asSmartPtr()); }; + virtual std::string get_path_string() const override { notStringError(this->asSmartPtr()); }; virtual void ranged_sxhash(HashGenerator& hg, size_t start, size_t end) const { TYPE_ERROR(this->asSmartPtr(), Cons_O::createList(cl::_sym_string, cl::_sym_bit_vector)); }; diff --git a/include/clasp/core/string.h b/include/clasp/core/string.h index 9aa0cba758..b4953691de 100644 --- a/include/clasp/core/string.h +++ b/include/clasp/core/string.h @@ -107,6 +107,7 @@ class SimpleBaseString_O : public specialized_SimpleBaseString { virtual std::string get_std_string() const final { return this->length() == 0 ? string("") : string((char*)&(*this)[0], this->length()); }; + virtual std::string get_path_string() const final { return get_std_string(); } virtual std::string __repr__() const override; virtual void sxhash_(HashGenerator& hg) const final { this->ranged_sxhash(hg, 0, this->length()); } virtual void ranged_sxhash(HashGenerator& hg, size_t start, size_t end) const final { @@ -190,6 +191,8 @@ class SimpleCharacterString_O : public specialized_SimpleCharacterString { virtual bool equal(T_sp other) const final; virtual bool equalp(T_sp other) const final; virtual std::string get_std_string() const final; + std::u32string get_u32_string() const; + virtual std::string get_path_string() const final; virtual std::string __repr__() const final; public: @@ -301,6 +304,7 @@ class Str8Ns_O : public template_Vector { virtual void __write__(T_sp strm) const final; virtual void __writeString(size_t istart, size_t iend, T_sp stream) const final; // implemented in write_array.cc virtual std::string get_std_string() const final { return std::string((const char*)this->begin(), this->length()); }; + virtual std::string get_path_string() const final { return get_std_string(); } virtual std::string __repr__() const override; public: // Str8Ns specific functions @@ -362,6 +366,8 @@ class StrWNs_O : public template_Vectorget_std_string(); + string fname = filename->get_path_string(); T_sp temp_name = nil(); bool appending = false, created = false; ASSERT(filename); @@ -180,7 +180,7 @@ T_sp stream_open(T_sp fn, StreamDirection direction, StreamIfExists if_exists, S case StreamIfExists::new_version: case StreamIfExists::supersede: temp_name = core__mkstemp(filename); - f = safe_open(core__coerce_to_filename(temp_name)->get_std_string().c_str(), base | O_CREAT, mode); + f = safe_open(core__coerce_to_filename(temp_name)->get_path_string().c_str(), base | O_CREAT, mode); unlikely_if(f < 0) FEcannot_open(fn); break; case StreamIfExists::append: @@ -4925,12 +4925,12 @@ CFileStream_sp CFileStream_O::make(T_sp fname, int fd, StreamDirection direction if (fstat_error != 0) { SIMPLE_ERROR("Unable to create stream for file descriptor and while running fstat another error occurred -> fd: {} name: {} " "mode: %s error: %s | fstat_error = %d info.st_mode = %08x%s", - fd, gc::As(fname)->get_std_string().c_str(), mode, strerror(errno), fstat_error, info.st_mode, + fd, gc::As(fname)->get_path_string().c_str(), mode, strerror(errno), fstat_error, info.st_mode, string_mode(info.st_mode)); } SIMPLE_ERROR( "Unable to create stream for file descriptor %ld name: %s mode: %s error: %s | fstat_error = %d info.st_mode = %08x%s", fd, - gc::As(fname)->get_std_string().c_str(), mode, strerror(errno), fstat_error, info.st_mode, + gc::As(fname)->get_path_string().c_str(), mode, strerror(errno), fstat_error, info.st_mode, string_mode(info.st_mode)); } return CFileStream_O::make(fname, fp, direction, byte_size, flags, external_format, tempName, created); @@ -4961,7 +4961,7 @@ void CFileStream_O::set_buffering_mode(T_sp mode) { void CFileStream_O::fixupInternalsForSnapshotSaveLoad(snapshotSaveLoad::Fixup* fixup) { if (snapshotSaveLoad::operation(fixup) == snapshotSaveLoad::LoadOp) { - std::string name = gc::As(_filename)->get_std_string(); + std::string name = gc::As(_filename)->get_path_string(); T_sp stream = this->asSmartPtr(); if (name == "*STDIN*") { _file = stdin; diff --git a/src/core/string.cc b/src/core/string.cc index 7718a05a2d..7ebedcd813 100644 --- a/src/core/string.cc +++ b/src/core/string.cc @@ -1,4 +1,5 @@ #include +#include // get_path_string #include #include #include @@ -1253,6 +1254,15 @@ std::string SimpleCharacterString_O::get_std_string() const { return sout; } +std::u32string SimpleCharacterString_O::get_u32_string() const { + return std::u32string(&(*this)[0], this->length()); +} + +std::string SimpleCharacterString_O::get_path_string() const { + // make C++ figure out the native filesystem encoding. + return std::filesystem::path(get_u32_string()).string(); +} + std::string SimpleCharacterString_O::__repr__() const { return escaped_string(this->get_std_string()); } // ------------------------------------------------------------ @@ -1389,6 +1399,17 @@ std::string StrWNs_O::get_std_string() const { return sout; } +std::u32string StrWNs_O::get_u32_string() const { + std::u32string sout(this->length(), ' '); + for (size_t i(0), iEnd(this->length()); i < iEnd; ++i) + sout[i] = (*this)[i]; + return sout; +} + +std::string StrWNs_O::get_path_string() const { + return std::filesystem::path(get_u32_string()).string(); +} + std::string StrWNs_O::__repr__() const { return escaped_string(this->get_std_string()); } SimpleString_sp StrWNs_O::asMinimalSimpleString() const { diff --git a/src/core/unixfsys.cc b/src/core/unixfsys.cc index a9faad5a18..ec20b10586 100644 --- a/src/core/unixfsys.cc +++ b/src/core/unixfsys.cc @@ -471,12 +471,7 @@ CL_DEFUN T_sp ext__chdir(T_sp dir, T_sp change_default_pathname_defaults) { T_sp tdir = clasp_namestring(dir, true); LIKELY_if(cl__stringp(tdir)) { String_sp sdir = gc::As_unsafe(tdir); -#if 0 - if (sdir->get_std_string().substr(0,strlen("/Users/meister/Development/cando-demos")) == "/Users/meister/Development/cando-demos") { - printf("%s:%d:%s set break-point here\n", __FILE__, __LINE__, __FUNCTION__ ); - } -#endif - Integer_sp result = Integer_O::create((gc::Fixnum)safe_chdir(sdir->get_std_string().c_str(), nil())); + Integer_sp result = Integer_O::create((gc::Fixnum)safe_chdir(sdir->get_path_string().c_str(), nil())); // printf("%s:%d:%s After safe_chdir\n", __FILE__, __LINE__, __FUNCTION__ ); if (change_default_pathname_defaults.notnilp()) { clasp_write_string(fmt::format("Changing *default-pathname-defaults* because change-default-pathname-defaults -> {}\n", @@ -647,18 +642,18 @@ static Symbol_sp file_kind(const char* filename, bool follow_links) { static Symbol_sp smart_file_kind(String_sp sfilename, bool follow_links) { ASSERT(cl__stringp(sfilename)); if (follow_links) { - Symbol_sp kind_follow_links = file_kind((char*)(sfilename->get_std_string().c_str()), true); + Symbol_sp kind_follow_links = file_kind((char*)(sfilename->get_path_string().c_str()), true); if (kind_follow_links.notnilp()) { return kind_follow_links; } else { // If its a broken link return _sym_file - Symbol_sp kind_no_follow_links = file_kind((char*)(sfilename->get_std_string().c_str()), false); + Symbol_sp kind_no_follow_links = file_kind((char*)(sfilename->get_path_string().c_str()), false); if (kind_no_follow_links.nilp()) return nil(); return kw::_sym_broken_link; } } else { - Symbol_sp kind = file_kind((char*)(sfilename->get_std_string().c_str()), false); + Symbol_sp kind = file_kind((char*)(sfilename->get_path_string().c_str()), false); return kind; } } @@ -689,7 +684,7 @@ CL_DEFUN T_sp core__readlink(String_sp filename) { do { output = Str8Ns_O::make(size + 2, '*', true, clasp_make_fixnum(0)); clasp_disable_interrupts(); - written = readlink((char*)filename->get_std_string().c_str(), (char*)output->rowMajorAddressOfElement_(0), size); + written = readlink((char*)filename->get_path_string().c_str(), (char*)output->rowMajorAddressOfElement_(0), size); clasp_enable_interrupts(); size += 256; } while (written == size - 256); @@ -813,7 +808,7 @@ static Pathname_mv file_truename(T_sp pathname, T_sp filename, int flags) { SIMPLE_ERROR("Unprintable pathname {} found in TRUENAME", _rep_(pathname)); } } - kind = file_kind((char*)gc::As(filename)->get_std_string().c_str(), false); + kind = file_kind((char*)gc::As(filename)->get_path_string().c_str(), false); // kind = smart_file_kind( filename, false); if (kind.nilp()) { CANNOT_OPEN_FILE_ERROR(filename); @@ -824,7 +819,7 @@ static Pathname_mv file_truename(T_sp pathname, T_sp filename, int flags) { * the other hand, if the link is broken - return file * truename "as is". */ struct stat filestatus; - if (safe_stat(gc::As(filename)->get_std_string().c_str(), &filestatus) < 0) + if (safe_stat(gc::As(filename)->get_path_string().c_str(), &filestatus) < 0) return Values(pathname, kind); /* The link might be a relative pathname. In that case we have * to merge with the original pathname */ @@ -1001,12 +996,12 @@ CL_DEFUN T_mv cl__rename_file(T_sp oldn, T_sp newn, T_sp if_exists) { if (if_exists == _lisp->_true()) { T_sp dkind = core__file_kind(new_filename, false); if (dkind == kw::_sym_directory) { - rmtree(new_filename->get_std_string().c_str()); + rmtree(new_filename->get_path_string().c_str()); } } { clasp_disable_interrupts(); - if (rename((char*)old_filename->get_std_string().c_str(), (char*)new_filename->get_std_string().c_str()) == 0) { + if (rename((char*)old_filename->get_path_string().c_str(), (char*)new_filename->get_path_string().c_str()) == 0) { goto SUCCESS; } } @@ -1044,7 +1039,7 @@ CL_DEFUN T_sp cl__delete_file(T_sp file) { int ok; clasp_disable_interrupts(); - ok = (isdir ? rmdir : unlink)((char*)filename->get_std_string().c_str()); + ok = (isdir ? rmdir : unlink)((char*)filename->get_path_string().c_str()); clasp_enable_interrupts(); if (ok < 0) { @@ -1085,7 +1080,7 @@ CL_DEFUN Number_sp cl__file_write_date(T_sp pathspec) { String_sp filename = coerce_to_posix_filename(pathname); struct stat filestatus; time = nil(); - if (safe_stat((char*)filename->get_std_string().c_str(), &filestatus) >= 0) { + if (safe_stat((char*)filename->get_path_string().c_str(), &filestatus) >= 0) { Number_sp accJan1st1970UT(Integer_O::create((gc::Fixnum)(24 * 60 * 60))); accJan1st1970UT = contagion_mul(accJan1st1970UT, Integer_O::create((gc::Fixnum)(17 + 365 * 70))); time = Integer_O::create((gc::Fixnum)filestatus.st_mtime); @@ -1105,7 +1100,7 @@ CL_DEFUN T_sp cl__file_author(T_sp file) { Pathname_sp pn = cl__pathname(file); String_sp filename = coerce_to_posix_filename(pn); struct stat filestatus; - if (safe_stat((char*)filename->get_std_string().c_str(), &filestatus) < 0) { + if (safe_stat((char*)filename->get_path_string().c_str(), &filestatus) < 0) { std::string msg = "Unable to read file author for ~S." "~%C library error: ~S"; T_sp c_error = clasp_strerror(errno); @@ -1172,12 +1167,12 @@ Pathname_sp clasp_homedir_pathname(T_sp tuser) { } else { namestring = SimpleBaseString_O::make("/"); } - if (namestring->get_std_string().c_str()[0] == '~') { - SIMPLE_ERROR("Not a valid home pathname {}", namestring->get_std_string()); + if (namestring->get_path_string().c_str()[0] == '~') { + SIMPLE_ERROR("Not a valid home pathname {}", namestring->get_path_string()); } i = namestring->length(); - if (!IS_DIR_SEPARATOR(namestring->get_std_string().c_str()[i - 1])) - namestring = SimpleBaseString_O::make(namestring->get_std_string() + DIR_SEPARATOR); + if (!IS_DIR_SEPARATOR(namestring->get_path_string().c_str()[i - 1])) + namestring = SimpleBaseString_O::make(namestring->get_path_string() + DIR_SEPARATOR); return gc::As(cl__parse_namestring(namestring)); } @@ -1218,7 +1213,7 @@ static T_sp list_directory(T_sp base_dir, T_sp text_mask, T_sp pathname_mask, in MultipleValues& mvn = core::lisp_multipleValues(); clasp_disable_interrupts(); - dir = opendir((char*)gc::As(prefix)->get_std_string().c_str()); + dir = opendir((char*)gc::As(prefix)->get_path_string().c_str()); if (dir == NULL) { out = nil(); goto OUTPUT; @@ -1290,7 +1285,7 @@ CL_DEFUN T_sp core__mkstemp(String_sp thetemplate) { SIMPLE_ERROR("In {} the template is NIL", __FUNCTION__); thetemplate = core__coerce_to_filename(thetemplate); stringstream outss; - outss << thetemplate->get_std_string(); + outss << thetemplate->get_path_string(); outss << "XXXXXX"; string outname = outss.str(); std::vector dst_path(outname.begin(), outname.end()); @@ -1321,7 +1316,7 @@ CL_DEFUN T_sp core__mkstemp_fd(String_sp thetemplate) { SIMPLE_ERROR("In {} the template is NIL", __FUNCTION__); thetemplate = core__coerce_to_filename(thetemplate); stringstream outss; - outss << thetemplate->get_std_string(); + outss << thetemplate->get_path_string(); outss << "XXXXXX"; string outname = outss.str(); std::vector dst_path(outname.begin(), outname.end()); @@ -1355,7 +1350,7 @@ CL_DEFUN T_sp core__mkdtemp(String_sp thetemplate) { SIMPLE_ERROR("In {} the template is NIL", __FUNCTION__); thetemplate = core__coerce_to_filename(thetemplate); stringstream outss; - outss << thetemplate->get_std_string(); + outss << thetemplate->get_path_string(); outss << "XXXXXX"; string outname = outss.str(); std::vector dst_path(outname.begin(), outname.end()); @@ -1546,7 +1541,7 @@ DOCGROUP(clasp); CL_DEFUN void core__chmod(T_sp file, T_sp mode) { mode_t code = clasp_to_uint32_t(mode); T_sp filename = coerce_to_posix_filename(file); - unlikely_if(chmod((char*)gc::As(filename)->get_std_string().c_str(), code)) { + unlikely_if(chmod((char*)gc::As(filename)->get_path_string().c_str(), code)) { T_sp c_error = clasp_strerror(errno); std::string msg = "Unable to change mode of file ~S to value ~O" "~%C library error: ~S"; @@ -1573,9 +1568,9 @@ CL_DEFUN T_sp core__copy_file(T_sp orig, T_sp dest) { SIMPLE_ERROR("In {} the destination pathname is NIL", __FUNCTION__); String_sp sdest = core__coerce_to_filename(dest); clasp_disable_interrupts(); - in = fopen(sorig->get_std_string().c_str(), "r"); + in = fopen(sorig->get_path_string().c_str(), "r"); if (in) { - out = fopen(sdest->get_std_string().c_str(), "w"); + out = fopen(sdest->get_path_string().c_str(), "w"); if (out) { unsigned char* buffer = (unsigned char*)malloc(1024); cl_index size; @@ -1791,7 +1786,7 @@ CL_DEFUN T_sp core__mkdir(T_sp directory, T_sp mode) { #if defined(CLASP_MS_WINDOWS_HOST) ok = mkdir((char*)filename->c_str()); #else - ok = mkdir((char*)filename->get_std_string().c_str(), modeint); + ok = mkdir((char*)filename->get_path_string().c_str(), modeint); #endif // clasp_enable_interrupts(); @@ -2176,7 +2171,7 @@ DOCGROUP(clasp); CL_DEFUN T_mv ext__stat(T_sp pathname) { struct stat sb; String_sp filename = gc::As(cl__namestring(cl__translate_logical_pathname(pathname))); - if (stat(filename->get_std_string().c_str(), &sb) == -1) + if (stat(filename->get_path_string().c_str(), &sb) == -1) return Values(nil()); else return Values(Integer_O::create(sb.st_size), Integer_O::create((gctools::Fixnum)sb.st_mtime), From c4f6b382123c564e388df30c13e62472f47da725 Mon Sep 17 00:00:00 2001 From: Bike Date: Mon, 24 Jun 2024 15:04:37 -0400 Subject: [PATCH 4/5] Print stream objects more correctly Respecting extended characters, at least --- include/clasp/core/lispStream.h | 1 + src/core/write_ugly.cc | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/include/clasp/core/lispStream.h b/include/clasp/core/lispStream.h index b677f90299..cb1efa0754 100644 --- a/include/clasp/core/lispStream.h +++ b/include/clasp/core/lispStream.h @@ -866,6 +866,7 @@ class FileStream_O : public AnsiStream_O { _external_format(nil()), _format(nil()){}; virtual string __repr__() const override; + virtual void __write__(T_sp stream) const override; virtual bool has_file_position() const; cl_index consume_byte_stack(unsigned char* c, cl_index n); ListenResult _fd_listen(int fd); diff --git a/src/core/write_ugly.cc b/src/core/write_ugly.cc index f2eba25f56..7a88890b4e 100644 --- a/src/core/write_ugly.cc +++ b/src/core/write_ugly.cc @@ -105,6 +105,14 @@ void Pathname_O::__write__(T_sp strm) const { write_ugly_object(namestring, strm); } +void FileStream_O::__write__(T_sp stream) const { + clasp_write_string("#<", stream); + write_ugly_object(this->_instanceClass()->_className(), stream); + stream_write_char(stream, ' '); + write_ugly_object(this->pathname(), stream); + stream_write_char(stream, '>'); +} + void Instance_O::__write__(T_sp stream) const { clasp_write_string(_rep_(this->asSmartPtr()), stream); } void FuncallableInstance_O::__write__(T_sp stream) const { clasp_write_string(_rep_(this->asSmartPtr()), stream); } From 8451d5559f6f5cbf6ede17ddee5697d4fc3f71ad Mon Sep 17 00:00:00 2001 From: Bike Date: Mon, 24 Jun 2024 19:18:02 -0400 Subject: [PATCH 5/5] Add release note --- RELEASE_NOTES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f523994eae..1e84d5bd91 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,8 @@ +# Version 2.7.0 (LLVM15-18) Pending + +## Fixed +* Pathnames and filesystem operations support Unicode (#1595). + # Version 2.6.0 (LLVM15-18) 2024-06-03 ## Added