123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161 |
- /* melder_files.cpp
- *
- * Copyright (C) 1992-2008,2010-2018 Paul Boersma, 2013 Tom Naughton
- *
- * This code is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This code 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this work. If not, see <http://www.gnu.org/licenses/>.
- */
- /*
- * pb 2002/03/07 GPL
- * rvs&pb 2002/03/07 url support
- * pb 2002/03/10 Mach compatibility
- * pb 2003/09/12 MelderFile_getMacType
- * pb 2003/09/14 MelderDir_relativePathToFile
- * pb 2004/09/25 use /tmp as temporary directory
- * pb 2004/10/16 C++ compatible structs
- * pb 2005/11/07 Windows: use %USERPROFILE% rather than %HOMESHARE%%HOMEPATH%
- * rvs&pb 2005/11/18 url support
- * pb 2006/01/21 MelderFile_writeText does not create temporary file
- * pb 2006/08/03 openForWriting
- * rvs 2006/08/12 curl: do not fail on error
- * pb 2006/08/12 check whether unicodeName exists
- * pb 2006/10/28 erased MacOS 9 stuff
- * Erez Volk 2007/05/14 FLAC support
- * pb 2007/05/28 wchar
- * pb 2007/06/09 more wchar
- * pb 2007/08/12 more wchar
- * pb 2007/10/05 FSFindFolder
- * pb 2008/11/01 warn after finding final tabs (not just spaces) in file names
- * pb 2010/12/14 more high Unicode compatibility
- * pb 2011/04/05 C++
- */
- #if defined (UNIX)
- #include <unistd.h>
- #include <sys/stat.h>
- #endif
- #if defined (CURLPRESENT)
- #include <curl/curl.h>
- #endif
- #ifdef _WIN32
- #include <windows.h>
- #endif
- #if defined (macintosh)
- #include "macport_on.h"
- #include <CoreFoundation/CoreFoundation.h>
- #include "macport_off.h"
- #endif
- #include <errno.h>
- #include "melder.h"
- #include "../kar/UnicodeData.h"
- //#include "flac_FLAC_stream_encoder.h"
- extern "C" int FLAC__stream_encoder_finish (FLAC__StreamEncoder *);
- extern "C" void FLAC__stream_encoder_delete (FLAC__StreamEncoder *);
- #if defined (macintosh)
- #include <sys/stat.h>
- #define UNIX
- #include <unistd.h>
- #endif
- static char32 theShellDirectory [kMelder_MAXPATH+1];
- void Melder_rememberShellDirectory () {
- structMelderDir shellDir { };
- Melder_getDefaultDir (& shellDir);
- str32cpy (theShellDirectory, Melder_dirToPath (& shellDir));
- }
- conststring32 Melder_getShellDirectory () {
- return & theShellDirectory [0];
- }
- void Melder_str32To8bitFileRepresentation_inplace (conststring32 string, char *utf8) {
- #if defined (macintosh)
- /*
- On the Mac, the POSIX path name is stored in canonically decomposed UTF-8 encoding.
- The path is probably in precomposed UTF-32.
- So we first convert to UTF-16, then turn into CFString, then decompose, then convert to UTF-8.
- */
- UniChar unipath [kMelder_MAXPATH+1];
- int64 n = str32len (string), n_utf16 = 0;
- for (int64 i = 0; i < n; i ++) {
- char32 kar = (char32) string [i]; // change sign (bit 32 is never used)
- if (kar <= 0x00'FFFF) {
- unipath [n_utf16 ++] = (UniChar) kar; // including null byte; guarded truncation
- } else if (kar <= 0x10'FFFF) {
- kar -= 0x01'0000;
- unipath [n_utf16 ++] = (UniChar) (0x00'D800 | (kar >> 10)); // correct truncation, because UTF-32 has fewer than 27 bits (in fact it has 21 bits)
- unipath [n_utf16 ++] = (UniChar) (0x00'DC00 | (kar & 0x00'03FF));
- } else {
- unipath [n_utf16 ++] = UNICODE_REPLACEMENT_CHARACTER;
- }
- }
- unipath [n_utf16] = u'\0';
- CFStringRef cfpath = CFStringCreateWithCharacters (nullptr, unipath, n_utf16);
- CFMutableStringRef cfpath2 = CFStringCreateMutableCopy (nullptr, 0, cfpath);
- CFRelease (cfpath);
- CFStringNormalize (cfpath2, kCFStringNormalizationFormD); // Mac requires decomposed characters
- CFStringGetCString (cfpath2, (char *) utf8, kMelder_MAXPATH+1, kCFStringEncodingUTF8); // Mac POSIX requires UTF-8
- CFRelease (cfpath2);
- #elif defined (UNIX) || defined (__CYGWIN__)
- Melder_32to8_inplace (string, utf8);
- #elif defined (_WIN32)
- int n = str32len (string), i, j;
- for (i = 0, j = 0; i < n; i ++) {
- utf8 [j ++] = string [i] <= 255 ? string [i] : '?'; // the usual replacement on Windows
- }
- utf8 [j] = '\0';
- #else
- //#error Unsupported platform.
- #endif
- }
- #if defined (UNIX)
- void Melder_8bitFileRepresentationToStr32_inplace (const char *path8, char32 *path32) {
- #if defined (macintosh)
- CFStringRef cfpath = CFStringCreateWithCString (nullptr, path8, kCFStringEncodingUTF8);
- if (! cfpath) {
- /*
- Probably something wrong, like a disk was disconnected in the meantime.
- */
- Melder_8to32_inplace (path8, path32, kMelder_textInputEncoding::UTF8);
- Melder_throw (U"Unusual error finding or creating file ", path32, U".");
- }
- CFMutableStringRef cfpath2 = CFStringCreateMutableCopy (nullptr, 0, cfpath);
- CFRelease (cfpath);
- CFStringNormalize (cfpath2, kCFStringNormalizationFormC); // Praat requires composed characters
- integer n_utf16 = CFStringGetLength (cfpath2);
- integer n_utf32 = 0;
- for (integer i = 0; i < n_utf16; i ++) {
- char32 kar1 = CFStringGetCharacterAtIndex (cfpath2, i);
- if (kar1 >= 0x00'D800 && kar1 <= 0x00'DBFF) {
- char32 kar2 = (char32) CFStringGetCharacterAtIndex (cfpath2, ++ i); // convert up
- if (kar2 >= 0x00'DC00 && kar2 <= 0x00'DFFF) {
- kar1 = (((kar1 & 0x3FF) << 10) | (kar2 & 0x3FF)) + 0x01'0000;
- } else {
- kar1 = UNICODE_REPLACEMENT_CHARACTER;
- }
- }
- path32 [n_utf32 ++] = kar1;
- }
- path32 [n_utf32] = U'\0';
- CFRelease (cfpath2);
- #else
- Melder_8to32_inplace (path8, path32, kMelder_textInputEncoding::UTF8);
- #endif
- }
- #endif
- conststring32 MelderFile_name (MelderFile file) {
- #if defined (UNIX)
- char32 *slash = str32rchr (file -> path, U'/');
- return slash ? slash + 1 : file -> path;
- #elif defined (_WIN32)
- char32 *backslash = str32rchr (file -> path, U'\\');
- return backslash ? backslash + 1 : file -> path;
- #else
- return nullptr;
- #endif
- }
- conststring32 MelderDir_name (MelderDir dir) {
- #if defined (UNIX)
- char32 *slash = str32rchr (dir -> path, U'/');
- return slash ? slash + 1 : dir -> path;
- #elif defined (_WIN32)
- char32 *backslash = str32rchr (dir -> path, U'\\');
- return backslash ? backslash + 1 : dir -> path;
- #else
- return nullptr;
- #endif
- }
- void Melder_pathToDir (conststring32 path, MelderDir dir) {
- str32cpy (dir -> path, path);
- }
- void Melder_pathToFile (conststring32 path, MelderFile file) {
- /*
- * This handles complete path names only.
- *
- * Used if we know for sure that we have a complete path name,
- * i.e. if the program determined the name (fileselector, printing, prefs).
- */
- str32cpy (file -> path, path);
- }
- void Melder_relativePathToFile (conststring32 path, MelderFile file) {
- /*
- * This handles complete and partial path names,
- * and translates slashes to native directory separators.
- *
- * Used if we do not know for sure that we have a complete path name,
- * i.e. if the user determined the name (scripting).
- */
- #if defined (UNIX)
- /*
- * We assume that Unix complete path names start with a slash.
- */
- if (path [0] == U'~' && path [1] == U'/') {
- Melder_sprint (file -> path,kMelder_MAXPATH+1, Melder_peek8to32 (getenv ("HOME")), & path [1]);
- } else if (path [0] == U'/' || str32equ (path, U"<stdout>") || str32str (path, U"://")) {
- str32cpy (file -> path, path);
- } else {
- structMelderDir dir { };
- Melder_getDefaultDir (& dir); // BUG
- if (dir. path [0] == U'/' && dir. path [1] == U'\0') {
- Melder_sprint (file -> path,kMelder_MAXPATH+1, U"/", path);
- } else {
- Melder_sprint (file -> path,kMelder_MAXPATH+1, dir. path, U"/", path);
- }
- }
- #elif defined (_WIN32)
- /*
- * We assume that Win32 complete path names look like:
- * C:\WINDOWS\CTRL32.DLL
- * LPT1:
- * \\host\path
- */
- structMelderDir dir { };
- if (path [0] == U'~' && path [1] == U'/') {
- Melder_getHomeDir (& dir);
- Melder_sprint (file -> path,kMelder_MAXPATH+1, dir. path, & path [1]);
- for (;;) {
- char32 *slash = str32chr (file -> path, U'/');
- if (! slash) break;
- *slash = U'\\';
- }
- return;
- }
- if (str32chr (path, U'/') && ! str32str (path, U"://")) {
- char32 winPath [kMelder_MAXPATH+1];
- Melder_sprint (winPath,kMelder_MAXPATH+1, path);
- for (;;) {
- char32 *slash = str32chr (winPath, U'/');
- if (! slash) break;
- *slash = U'\\';
- }
- Melder_relativePathToFile (winPath, file);
- return;
- }
- if (str32chr (path, U':') || path [0] == U'\\' && path [1] == U'\\' || str32equ (path, U"<stdout>")) {
- Melder_sprint (file -> path,kMelder_MAXPATH+1, path);
- } else {
- Melder_getDefaultDir (& dir); // BUG
- Melder_sprint (file -> path,kMelder_MAXPATH+1,
- dir. path,
- dir. path [0] != U'\0' && dir. path [str32len (dir. path) - 1] == U'\\' ? U"" : U"\\",
- path);
- }
- #endif
- }
- conststring32 Melder_dirToPath (MelderDir dir) {
- return & dir -> path [0];
- }
- conststring32 Melder_fileToPath (MelderFile file) {
- return & file -> path [0];
- }
- void MelderFile_copy (MelderFile file, MelderFile copy) {
- str32cpy (copy -> path, file -> path);
- }
- void MelderDir_copy (MelderDir dir, MelderDir copy) {
- str32cpy (copy -> path, dir -> path);
- }
- bool MelderFile_equal (MelderFile file1, MelderFile file2) {
- return str32equ (file1 -> path, file2 -> path);
- }
- bool MelderDir_equal (MelderDir dir1, MelderDir dir2) {
- return str32equ (dir1 -> path, dir2 -> path);
- }
- void MelderFile_setToNull (MelderFile file) {
- file -> path [0] = U'\0';
- }
- bool MelderFile_isNull (MelderFile file) {
- return ! file || file -> path [0] == U'\0';
- }
- void MelderDir_setToNull (MelderDir dir) {
- dir -> path [0] = U'\0';
- }
- bool MelderDir_isNull (MelderDir dir) {
- return dir -> path [0] == U'\0';
- }
- void MelderDir_getFile (MelderDir parent, conststring32 fileName, MelderFile file) {
- #if defined (UNIX)
- if (parent -> path [0] == U'/' && parent -> path [1] == U'\0') {
- Melder_sprint (file -> path,kMelder_MAXPATH+1, U"/", fileName);
- } else {
- Melder_sprint (file -> path,kMelder_MAXPATH+1, parent -> path, U"/", fileName);
- }
- #elif defined (_WIN32)
- if (str32rchr (file -> path, U'\\') - file -> path == str32len (file -> path) - 1) {
- Melder_sprint (file -> path,kMelder_MAXPATH+1, parent -> path, fileName);
- } else {
- Melder_sprint (file -> path,kMelder_MAXPATH+1, parent -> path, U"\\", fileName);
- }
- #endif
- }
- void MelderDir_relativePathToFile (MelderDir dir, conststring32 path, MelderFile file) {
- structMelderDir saveDir { };
- Melder_getDefaultDir (& saveDir);
- Melder_setDefaultDir (dir);
- Melder_relativePathToFile (path, file);
- Melder_setDefaultDir (& saveDir);
- }
- #ifndef UNIX
- static void Melder_getDesktop (MelderDir dir) {
- dir -> path [0] = U'\0';
- }
- #endif
- void MelderFile_getParentDir (MelderFile file, MelderDir parent) {
- #if defined (UNIX)
- /*
- The parent of /usr/hello.txt is /usr.
- The parent of /hello.txt is /.
- */
- str32cpy (parent -> path, file -> path);
- char32 *slash = str32rchr (parent -> path, U'/');
- if (slash) *slash = U'\0';
- if (parent -> path [0] == U'\0') str32cpy (parent -> path, U"/");
- #elif defined (_WIN32)
- /*
- The parent of C:\WINDOWS\CTRL.DLL is C:\WINDOWS.
- The parent of E:\Praat.exe is E:\.
- The parent of \\Swine\Apps\init.txt is \\Swine\Apps.
- The parent of \\Swine\init.txt is \\Swine\. (BUG ?)
- */
- str32cpy (parent -> path, file -> path);
- char32 *colon = str32chr (parent -> path, U':');
- if (colon) {
- char32 *backslash = str32rchr (parent -> path, U'\\');
- if (backslash) { // C:\WINDOWS\CTRL.DLL or C:\AUTOEXEC.BAT
- if (backslash - colon == 1) { // C:\AUTOEXEC.BAT
- * (backslash + 1) = U'\0'; // C:\ - !!! aargh this was a bug after converting this line to line comments
- } else { // C:\WINDOWS\CTRL.DLL
- *backslash = U'\0'; // C:\WINDOWS
- }
- } else { /* ??? */
- Melder_getDesktop (parent); // empty string
- }
- } else if (parent -> path [0] == U'\\' && parent -> path [1] == U'\\') {
- char32 *backslash = str32rchr (parent -> path + 2, U'\\');
- if (backslash) { // \\Swine\Apps\init.txt or \\Swine\init.txt
- char32 *leftBackslash = str32chr (parent -> path + 2, U'\\');
- if (backslash - leftBackslash == 0) { // \\Swine\init.txt
- * (backslash + 1) = U'\0'; // \\Swine\ - !!! dear developer, don't delete this hyphen, lest the line ends in a backslash
- } else { // \\Swine\Apps\init.txt
- *backslash = U'\0'; // \\Swine\Apps
- }
- } else { // \\init.txt ???
- Melder_getDesktop (parent); // empty string
- }
- } else { // unknown path type
- Melder_getDesktop (parent); // empty string
- }
- #endif
- }
- void MelderDir_getParentDir (MelderDir dir, MelderDir parent) {
- #if defined (UNIX)
- /*
- The parent of /usr/local is /usr.
- The parent of /usr is /.
- The parent of / is "".
- */
- str32cpy (parent -> path, dir -> path);
- char32 *slash = str32rchr (parent -> path, U'/');
- if (slash) {
- if (slash - parent -> path == 0) {
- if (slash [1] == U'\0') { // child is "/"
- parent -> path [0] = U'\0'; // parent is ""
- } else { // child is "/usr"
- slash [1] = '\0'; // parent is "/"
- }
- } else { // child is "/usr/local"
- *slash = U'\0'; // parent is "/usr"
- }
- } else {
- parent -> path [0] = U'\0'; // some failure; desktop
- }
- #elif defined (_WIN32)
- /*
- The parent of C:\WINDOWS is C:\.
- The parent of E:\ is the desktop.
- The parent of \\Swine\ is the desktop. (BUG ?)
- */
- str32cpy (parent -> path, dir -> path);
- char32 *colon = str32chr (parent -> path, U':');
- if (colon) {
- int length = str32len (parent -> path);
- char32 *backslash = str32rchr (parent -> path, U'\\');
- if (backslash) { // C:\WINDOWS\FONTS or C:\WINDOWS or C:\ - (cannot add a line comment with a backslash)
- if (backslash - parent -> path == length - 1) { // C:\ -
- Melder_getDesktop (parent); // empty string
- } else if (backslash - colon == 1) { // C:\WINDOWS
- * (backslash + 1) = U'\0'; // C:\ -
- } else { // C:\WINDOWS\FONTS
- *backslash = U'\0'; // C:\WINDOWS
- }
- } else { // LPT1: ???
- Melder_getDesktop (parent); // empty string
- }
- } else if (parent -> path [0] == U'\\' && parent -> path [1] == U'\\') {
- int length = str32len (parent -> path);
- char32 *backslash = str32rchr (parent -> path + 2, U'\\');
- if (backslash) { // \\Swine\Apps\Praats or \\Swine\Apps or \\Swine\ -
- if (backslash - parent -> path == length - 1) { // \\Swine\ -
- Melder_getDesktop (parent); // empty string
- } else { // \\Swine\Apps\Praats or \\Swine\Apps
- char32 *leftBackslash = str32chr (parent -> path + 2, U'\\');
- if (backslash - leftBackslash == 0) { // \\Swine\Apps
- * (backslash + 1) = U'\0'; // \\Swine\ -
- } else { // \\Swine\Apps\Praats
- *backslash = U'\0'; // \\Swine\Apps
- }
- }
- } else { // \\Swine ???
- Melder_getDesktop (parent); // empty string
- }
- } else { // unknown path type.
- Melder_getDesktop (parent); // empty string
- }
- #endif
- }
- bool MelderDir_isDesktop (MelderDir dir) {
- return dir -> path [0] == U'\0';
- }
- void MelderDir_getSubdir (MelderDir parent, conststring32 subdirName, MelderDir subdir) {
- #if defined (UNIX)
- if (parent -> path [0] == U'/' && parent -> path [1] == U'\0') {
- Melder_sprint (subdir -> path,kMelder_MAXPATH+1, U"/", subdirName);
- } else {
- Melder_sprint (subdir -> path,kMelder_MAXPATH+1, parent -> path, U"/", subdirName);
- }
- #elif defined (_WIN32)
- int length = str32len (parent -> path);
- char32 *backslash = str32rchr (parent -> path, U'\\');
- if (backslash && backslash - parent -> path == length - 1) { // C:\ or \\Swine\ -
- Melder_sprint (subdir -> path, kMelder_MAXPATH+1, parent -> path, subdirName);
- } else { // C:\WINDOWS or \\Swine\Apps or even C:
- Melder_sprint (subdir -> path,kMelder_MAXPATH+1, parent -> path, U"\\", subdirName);
- }
- #endif
- }
- void Melder_getHomeDir (MelderDir homeDir) {
- #if defined (UNIX)
- char *home = getenv ("HOME");
- str32cpy (homeDir -> path, home ? Melder_peek8to32 (home) : U"/");
- #elif defined (_WIN32)
- WCHAR driveW [kMelder_MAXPATH+1], pathW [kMelder_MAXPATH+1];
- DWORD n = GetEnvironmentVariableW (L"USERPROFILE", pathW, kMelder_MAXPATH+1);
- if (n > kMelder_MAXPATH) Melder_throw (U"Home directory name too long.");
- if (n > 0) {
- Melder_sprint (homeDir -> path,kMelder_MAXPATH+1, Melder_peekWto32 (pathW));
- return;
- }
- n = GetEnvironmentVariableW (L"HOMEDRIVE", driveW, kMelder_MAXPATH+1);
- if (n > kMelder_MAXPATH) Melder_throw (U"Home drive name too long.");
- if (n > 0) {
- GetEnvironmentVariable (L"HOMEPATH", pathW, kMelder_MAXPATH+1);
- Melder_sprint (homeDir -> path,kMelder_MAXPATH+1, Melder_peekWto32 (driveW), Melder_peekWto32 (pathW));
- return;
- }
- MelderDir_setToNull (homeDir); // Windows 95 and 98: alas
- #endif
- }
- void Melder_getPrefDir (MelderDir prefDir) {
- #if defined (macintosh)
- structMelderDir homeDir { };
- Melder_getHomeDir (& homeDir);
- Melder_sprint (prefDir -> path,kMelder_MAXPATH+1, homeDir. path, U"/Library/Preferences");
- #elif defined (UNIX)
- /*
- * Preferences files go into the home directory.
- */
- Melder_getHomeDir (prefDir);
- #elif defined (_WIN32)
- /*
- * On Windows 95, preferences files went in the Windows directory.
- * On shared systems (NT, 2000, XP), preferences files go into the home directory.
- * TODO: at some point, these files should be moved to HOME\AppData\Roaming\Praat.
- */
- Melder_getHomeDir (prefDir);
- #endif
- }
- void Melder_getTempDir (MelderDir tempDir) {
- #if defined (macintosh)
- Melder_sprint (tempDir -> path,kMelder_MAXPATH+1, Melder_peek8to32 (getenv ("TMPDIR"))); // or append /TemporaryItems
- // confstr with _CS_DARWIN_USER_TEMP_DIR
- #else
- (void) tempDir;
- #endif
- }
- #ifdef CURLPRESENT
- static int curl_initialized = 0;
- static size_t write_URL_data_to_file (void *buffer, size_t size, size_t nmemb, void *userp) {
- return fwrite (buffer, size, nmemb, userp);
- }
- static size_t read_URL_data_from_file (void *buffer, size_t size, size_t nmemb, void *userp) {
- return fread (buffer, size, nmemb, userp);
- }
- #endif
- FILE * Melder_fopen (MelderFile file, const char *type) {
- if (MelderFile_isNull (file)) Melder_throw (U"Cannot open null file.");
- if (! Melder_isTracing)
- Melder_assert (str32equ (Melder_double (1.5), U"1.5")); // check locale settings; because of the required file portability Praat cannot stand "1,5"
- /*
- * On the Unix-like systems (including MacOS), the path has to be converted to 8-bit characters in UTF-8 encoding.
- * On MacOS, the characters also have to be decomposed.
- */
- char utf8path [kMelder_MAXPATH+1];
- Melder_str32To8bitFileRepresentation_inplace (file -> path, utf8path);
- FILE *f;
- file -> openForWriting = ( type [0] == 'w' || type [0] == 'a' || strchr (type, '+') );
- if (str32equ (file -> path, U"<stdout>") && file -> openForWriting) {
- f = stdout;
- #ifdef CURLPRESENT
- } else if (strstr (utf8path, "://") && file -> openForWriting) {
- Melder_assert (type [0] == 'w'); // reject "append" and "random" access
- f = tmpfile (); // open a temporary file for writing
- } else if (strstr (utf8path, "://") && ! file -> openForWriting) {
- CURLcode CURLreturn;
- CURL *CURLhandle;
- char errorbuffer [CURL_ERROR_SIZE] = "";
- f = tmpfile (); // open a temporary file for writing
- if (! curl_initialized) {
- CURLreturn = curl_global_init (CURL_GLOBAL_ALL);
- curl_initialized = 1;
- };
- CURLhandle = curl_easy_init (); // initialize session
- /*
- * Set up the connection parameters.
- */
- /* Debugging: Verbose messages */
- /* CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_VERBOSE, 1); */
- /* Do not fail on error. */
- CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_FAILONERROR, 0);
- /* Store error messages in a buffer. */
- CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_ERRORBUFFER, errorbuffer);
- /* The file stream to store the URL. */
- CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_FILE, f);
- /* The function to write to the file, necessary for Win32. */
- CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_WRITEFUNCTION, write_URL_data_to_file);
- /* The actual URL to handle. */
- CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_URL, utf8path);
- /* Get the URL and write it to the given file. */
- CURLreturn = curl_easy_perform (CURLhandle);
- /* Handle errors. */
- if (CURLreturn) {
- Melder_appendError (Melder_peek8to32 (errorbuffer));
- f = nullptr;
- };
- /* Clean up session. */
- curl_easy_cleanup (CURLhandle);
- /* Do something with the file. Why? */
- if (f) rewind (f);
- #endif
- } else {
- #if defined (_WIN32) && ! defined (__CYGWIN__)
- f = _wfopen (Melder_peek32toW (file -> path), Melder_peek32toW (Melder_peek8to32 (type)));
- #else
- f = fopen ((char *) utf8path, type);
- #endif
- }
- if (! f) {
- char32 *path = file -> path;
- Melder_appendError (U"Cannot ", type [0] == 'r' ? U"open" : type [0] == 'a' ? U"append to" : U"create",
- U" file ", file, U".");
- if (path [0] == U'\0')
- Melder_appendError (U"Hint: empty file name.");
- else if (path [0] == U' ' || path [0] == U'\t')
- Melder_appendError (U"Hint: file name starts with a space or tab.");
- else if (path [str32len (path) - 1] == U' ' || path [str32len (path) - 1] == U'\t')
- Melder_appendError (U"Hint: file name ends in a space or tab.");
- else if (str32chr (path, U'\n'))
- Melder_appendError (U"Hint: file name contains a newline symbol.");
- throw MelderError ();
- return nullptr;
- }
- return f;
- }
- void Melder_fclose (MelderFile file, FILE *f) {
- if (! f) return;
- #if defined (CURLPRESENT)
- if (str32str (file -> wpath, U"://") && file -> openForWriting) {
- unsigned char utf8path [kMelder_MAXPATH+1];
- Melder_str32To8bitFileRepresentation_inplace (file -> path, utf8path);
- /* Rewind the file. */
- if (f) rewind (f);
- CURLcode CURLreturn;
- CURL *CURLhandle;
- char errorbuffer [CURL_ERROR_SIZE] = "";
- /* Start global init (necessary only ONCE). */
- if (! curl_initialized) {
- CURLreturn = curl_global_init (CURL_GLOBAL_ALL);
- curl_initialized = 1;
- };
- CURLhandle = curl_easy_init (); /* Initialize session. */
- /*
- * Set up the connection parameters.
- */
- /* Debugging: Verbose messages */
- /* CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_VERBOSE, 1); */
- /* Catch FILE: protocol errors. No solution yet */
- if (str32str (file -> path, U"file://") || str32str (file -> path, U"FILE://")) {
- CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_VERBOSE, 1);
- }
- /* Do not return Error pages, just fail. */
- CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_FAILONERROR, 1);
- /* Store error messages in a buffer. */
- CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_ERRORBUFFER, errorbuffer);
- /* Send header. */
- CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_HEADER, 1);
- /* Upload. */
- CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_UPLOAD, 1);
- /* The actual URL to handle. */
- CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_URL, utf8path);
- /* The function to write to the peer, necessary for Win32. */
- CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_READFUNCTION, read_URL_data_from_file);
- CURLreturn = curl_easy_setopt (CURLhandle, CURLOPT_READDATA, f);
- /* Get the URL and write the file to it. */
- CURLreturn = curl_easy_perform (CURLhandle);
- /* Handle errors. */
- if (CURLreturn) {
- curl_easy_cleanup (CURLhandle);
- f = nullptr;
- Melder_throw (Melder_peek8to32 (errorbuffer), U"\n");
- };
- /* Clean up session */
- curl_easy_cleanup (CURLhandle);
- }
- #endif
- if (f != stdout && fclose (f) == EOF)
- Melder_throw (U"Error closing file ", file, U".");
- }
- void Melder_files_cleanUp () {
- #if defined (CURLPRESENT)
- if (curl_initialized) {
- curl_global_cleanup ();
- curl_initialized = 0;
- };
- #endif
- }
- bool MelderFile_exists (MelderFile file) {
- #if defined (UNIX)
- char utf8path [kMelder_MAXPATH+1];
- Melder_str32To8bitFileRepresentation_inplace (file -> path, utf8path);
- struct stat statistics;
- return ! stat (utf8path, & statistics);
- #else
- try {
- autofile f = Melder_fopen (file, "rb");
- f.close (file);
- return true;
- } catch (MelderError) {
- Melder_clearError ();
- return false;
- }
- #endif
- }
- bool MelderFile_readable (MelderFile file) {
- try {
- autofile f = Melder_fopen (file, "rb");
- f.close (file);
- return true;
- } catch (MelderError) {
- Melder_clearError ();
- return false;
- }
- }
- integer MelderFile_length (MelderFile file) {
- #if defined (UNIX)
- char utf8path [kMelder_MAXPATH+1];
- Melder_str32To8bitFileRepresentation_inplace (file -> path, utf8path);
- struct stat statistics;
- if (stat ((char *) utf8path, & statistics) != 0) return -1;
- return statistics. st_size;
- #else
- try {
- autofile f = Melder_fopen (file, "r");
- fseek (f, 0, SEEK_END);
- integer length = ftell (f);
- f.close (file);
- return length;
- } catch (MelderError) {
- Melder_clearError ();
- return -1;
- }
- #endif
- }
- void MelderFile_delete (MelderFile file) {
- if (! file) return;
- #if defined (UNIX)
- char utf8path [kMelder_MAXPATH+1];
- Melder_str32To8bitFileRepresentation_inplace (file -> path, utf8path);
- remove ((char *) utf8path);
- #elif defined (_WIN32)
- DeleteFile (Melder_peek32toW (file -> path));
- #endif
- }
- char32 * Melder_peekExpandBackslashes (conststring32 message) {
- static char32 names [11] [kMelder_MAXPATH+1];
- static int index = 0;
- if (++ index == 11) index = 0;
- char32 *to = & names [index] [0];
- for (const char32 *from = & message [0]; *from != '\0'; from ++, to ++) {
- *to = *from;
- if (*from == U'\\') { * ++ to = U'b'; * ++ to = U's'; }
- }
- *to = U'\0';
- return & names [index] [0];
- }
- conststring32 MelderFile_messageName (MelderFile file) {
- return Melder_cat (U"“", file -> path, U"”"); // BUG: is cat allowed here?
- }
- void Melder_getDefaultDir (MelderDir dir) {
- #if defined (UNIX)
- char path [kMelder_MAXPATH+1];
- getcwd (path, kMelder_MAXPATH+1);
- Melder_8bitFileRepresentationToStr32_inplace (path, dir -> path);
- #elif defined (_WIN32)
- static WCHAR dirPathW [kMelder_MAXPATH+1];
- GetCurrentDirectory (kMelder_MAXPATH+1, dirPathW);
- Melder_sprint (dir -> path,kMelder_MAXPATH+1, Melder_peekWto32 (dirPathW));
- #endif
- }
- void Melder_setDefaultDir (MelderDir dir) {
- #if defined (UNIX)
- chdir (Melder_peek32to8 (dir -> path));
- #elif defined (_WIN32)
- SetCurrentDirectory (Melder_peek32toW (dir -> path));
- #endif
- }
- void MelderFile_setDefaultDir (MelderFile file) {
- structMelderDir dir { };
- MelderFile_getParentDir (file, & dir);
- Melder_setDefaultDir (& dir);
- }
- void Melder_createDirectory (MelderDir parent, conststring32 dirName, int mode) {
- #if defined (UNIX)
- structMelderFile file { };
- if (dirName [0] == U'/') {
- Melder_sprint (file. path,kMelder_MAXPATH+1, dirName); // absolute path
- } else if (parent -> path [0] == U'/' && parent -> path [1] == U'\0') {
- Melder_sprint (file. path,kMelder_MAXPATH+1, U"/", dirName); // relative path in root directory
- } else {
- Melder_sprint (file. path,kMelder_MAXPATH+1, parent -> path, U"/", dirName); // relative path
- }
- char utf8path [kMelder_MAXPATH+1];
- Melder_str32To8bitFileRepresentation_inplace (file. path, utf8path);
- if (mkdir (utf8path, mode) == -1 && errno != EEXIST) // ignore if directory already exists
- Melder_throw (U"Cannot create directory ", & file, U".");
- #elif defined (_WIN32)
- structMelderFile file { };
- SECURITY_ATTRIBUTES sa;
- (void) mode;
- sa. nLength = sizeof (SECURITY_ATTRIBUTES);
- sa. lpSecurityDescriptor = nullptr;
- sa. bInheritHandle = false;
- if (str32chr (dirName, U':') || dirName [0] == U'/' && dirName [1] == U'/') {
- Melder_sprint (file. path,kMelder_MAXPATH+1, dirName); // absolute path
- } else {
- Melder_sprint (file. path,kMelder_MAXPATH+1, parent -> path, U"/", dirName); // relative path
- }
- if (! CreateDirectoryW (Melder_peek32toW (file. path), & sa) && GetLastError () != ERROR_ALREADY_EXISTS) // ignore if directory already exists
- Melder_throw (U"Cannot create directory ", & file, U".");
- #else
- //#error Unsupported operating system.
- #endif
- }
- static size_t fread_multi (char *buffer, size_t numberOfBytes, FILE *f) {
- off_t offset = 0;
- size_t numberOfBytesRead = 0;
- const size_t chunkSize = 1'000'000'000;
- while (numberOfBytes > chunkSize) {
- size_t numberOfBytesReadInChunk = fread (buffer + offset, sizeof (char), chunkSize, f);
- numberOfBytesRead += numberOfBytesReadInChunk;
- if (numberOfBytesReadInChunk < chunkSize)
- return numberOfBytesRead;
- numberOfBytes -= chunkSize;
- offset += chunkSize;
- }
- size_t numberOfBytesReadInLastChunk = fread (buffer + offset, sizeof (char), numberOfBytes, f);
- numberOfBytesRead += numberOfBytesReadInLastChunk;
- return numberOfBytesRead;
- }
- autostring32 MelderFile_readText (MelderFile file, autostring8 *string8) {
- try {
- int type = 0; // 8-bit
- autostring32 text;
- autofile f = Melder_fopen (file, "rb");
- if (fseeko (f, 0, SEEK_END) < 0)
- Melder_throw (U"Cannot count the bytes in the file.");
- Melder_assert (sizeof (off_t) >= 8);
- int64 length = ftello (f);
- rewind (f);
- if (length >= 2) {
- int firstByte = fgetc (f), secondByte = fgetc (f);
- if (firstByte == 0xFE && secondByte == 0xFF) {
- type = 1; // big-endian 16-bit
- } else if (firstByte == 0xFF && secondByte == 0xFE) {
- type = 2; // little-endian 16-bit
- } else if (firstByte == 0xEF && secondByte == 0xBB && length >= 3) {
- int thirdByte = fgetc (f);
- if (thirdByte == 0xBF)
- type = -1; // UTF-8 with BOM
- }
- }
- if (type <= 0) {
- if (type == -1) {
- length -= 3;
- fseeko (f, 3, SEEK_SET);
- } else {
- rewind (f); // length and type already set correctly.
- }
- autostring8 text8bit (length);
- Melder_assert (text8bit);
- size_t numberOfBytesRead = fread_multi (text8bit.get(), (size_t) length, f);
- Melder_require ((int64) numberOfBytesRead == length,
- U"The file contains ", length, U" bytes",
- type == -1 ? U" after the byte-order mark" : U"",
- U", but we could read only ", numberOfBytesRead, U" of them."
- );
- text8bit [length] = '\0';
- /*
- * Count and repair null bytes.
- */
- if (length > 0) {
- int64 numberOfNullBytes = 0;
- for (char *p = & text8bit [length - 1]; (int64) (p - text8bit.get()) >= 0; p --) {
- if (*p == '\0') {
- numberOfNullBytes += 1;
- /*
- * Shift.
- */
- for (char *q = p; (int64) (q - text8bit.get()) < length; q ++)
- *q = q [1];
- }
- }
- if (numberOfNullBytes > 0) {
- Melder_warning (U"Ignored ", numberOfNullBytes, U" null bytes in text file ", file, U".");
- }
- }
- if (string8) {
- *string8 = text8bit.move();
- (void) Melder_killReturns_inplace (string8->get());
- return autostring32(); // OK
- } else {
- text = Melder_8to32 (text8bit.get(), kMelder_textInputEncoding::UNDEFINED);
- }
- } else {
- length = length / 2 - 1; // Byte Order Mark subtracted. Length = number of UTF-16 codes
- text = autostring32 (length + 1);
- if (type == 1) {
- for (int64 i = 0; i < length; i ++) {
- char16 kar1 = bingetu16 (f);
- if (kar1 < 0xD800) {
- text [i] = (char32) kar1; // convert up without sign extension
- } else if (kar1 < 0xDC00) {
- length --;
- char16 kar2 = bingetu16 (f);
- if (kar2 >= 0xDC00 && kar2 <= 0xDFFF) {
- text [i] = (char32) (0x010000 +
- (char32) (((char32) kar1 & 0x0003FF) << 10) +
- (char32) ((char32) kar2 & 0x0003FF));
- } else {
- text [i] = UNICODE_REPLACEMENT_CHARACTER;
- }
- } else if (kar1 < 0xE000) {
- text [i] = UNICODE_REPLACEMENT_CHARACTER;
- } else {
- text [i] = (char32) kar1; // convert up without sign extension
- }
- }
- } else {
- for (int64 i = 0; i < length; i ++) {
- char16 kar1 = bingetu16LE (f);
- if (kar1 < 0xD800) {
- text [i] = (char32) kar1; // convert up without sign extension
- } else if (kar1 < 0xDC00) {
- length --;
- char16 kar2 = bingetu16LE (f);
- if (kar2 >= 0xDC00 && kar2 <= 0xDFFF) {
- text [i] = (char32) (0x01'0000 +
- (char32) (((char32) kar1 & 0x00'03FF) << 10) +
- (char32) ((char32) kar2 & 0x00'03FF));
- } else {
- text [i] = UNICODE_REPLACEMENT_CHARACTER;
- }
- } else if (kar1 < 0xE000) {
- text [i] = UNICODE_REPLACEMENT_CHARACTER;
- } else if (kar1 <= 0xFFFF) {
- text [i] = (char32) kar1; // convert up without sign extension
- } else {
- Melder_fatal (U"MelderFile_readText: unsigned short greater than 0xFFFF: should not occur.");
- }
- }
- }
- text [length] = U'\0';
- (void) Melder_killReturns_inplace (text.get());
- }
- f.close (file);
- return text;
- } catch (MelderError) {
- Melder_throw (U"Error reading file ", file, U".");
- }
- }
- void Melder_fwrite32to8 (conststring32 string, FILE *f) {
- /*
- * Precondition:
- * the string's encoding is UTF-32.
- * Failure:
- * if the precondition does not hold, we don't crash,
- * but the characters that are written may be incorrect.
- */
- for (const char32* p = string; *p != U'\0'; p ++) {
- char32 kar = *p;
- if (kar <= 0x00'007F) {
- #ifdef _WIN32
- if (kar == U'\n')
- fputc (13, f);
- #endif
- fputc ((int) kar, f); // because fputc wants an int instead of an uint8 (guarded conversion)
- } else if (kar <= 0x00'07FF) {
- fputc (0xC0 | (kar >> 6), f);
- fputc (0x80 | (kar & 0x00'003F), f);
- } else if (kar <= 0x00FFFF) {
- fputc (0xE0 | (kar >> 12), f);
- fputc (0x80 | ((kar >> 6) & 0x00'003F), f);
- fputc (0x80 | (kar & 0x00'003F), f);
- } else {
- fputc (0xF0 | (kar >> 18), f);
- fputc (0x80 | ((kar >> 12) & 0x00'003F), f);
- fputc (0x80 | ((kar >> 6) & 0x00'003F), f);
- fputc (0x80 | (kar & 0x00'003F), f);
- }
- }
- }
- void MelderFile_writeText (MelderFile file, conststring32 text, kMelder_textOutputEncoding outputEncoding) {
- if (! text)
- text = U"";
- autofile f = Melder_fopen (file, "wb");
- if (outputEncoding == kMelder_textOutputEncoding::UTF8) {
- Melder_fwrite32to8 (text, f);
- } else if ((outputEncoding == kMelder_textOutputEncoding::ASCII_THEN_UTF16 && Melder_isValidAscii (text)) ||
- (outputEncoding == kMelder_textOutputEncoding::ISO_LATIN1_THEN_UTF16 && Melder_isEncodable (text, kMelder_textOutputEncoding_ISO_LATIN1)))
- {
- #ifdef _WIN32
- #define flockfile(f) (void) 0
- #define funlockfile(f) (void) 0
- #define putc_unlocked putc
- #endif
- flockfile (f);
- size_t n = str32len (text);
- for (size_t i = 0; i < n; i ++) {
- char32 kar = text [i];
- #ifdef _WIN32
- if (kar == U'\n')
- putc_unlocked (13, f);
- #endif
- putc_unlocked (kar, f);
- }
- funlockfile (f);
- } else {
- binputu16 (0xFEFF, f); // Byte Order Mark
- size_t n = str32len (text);
- for (size_t i = 0; i < n; i ++) {
- char32 kar = text [i];
- #ifdef _WIN32
- if (kar == U'\n')
- binputu16 (13, f);
- #endif
- if (kar <= 0x00'FFFF) {
- binputu16 ((char16) kar, f); // guarded conversion down
- } else if (kar <= 0x10'FFFF) {
- kar -= 0x010000;
- binputu16 (0xD800 | (uint16) (kar >> 10), f);
- binputu16 (0xDC00 | (uint16) ((char16) kar & 0x3ff), f);
- } else {
- binputu16 (UNICODE_REPLACEMENT_CHARACTER, f);
- }
- }
- }
- f.close (file);
- }
- void MelderFile_appendText (MelderFile file, conststring32 text) {
- if (! text) text = U"";
- autofile f1;
- try {
- f1.reset (Melder_fopen (file, "rb"));
- } catch (MelderError) {
- Melder_clearError (); // it's OK if the file didn't exist yet...
- MelderFile_writeText (file, text, Melder_getOutputEncoding ()); // because then we just "write"
- return;
- }
- /*
- * The file already exists and is open. Determine its type.
- */
- int firstByte = fgetc (f1), secondByte = fgetc (f1);
- f1.close (file);
- int type = 0;
- if (firstByte == 0xfe && secondByte == 0xff) {
- type = 1; // big-endian 16-bit
- } else if (firstByte == 0xff && secondByte == 0xfe) {
- type = 2; // little-endian 16-bit
- }
- if (type == 0) {
- kMelder_textOutputEncoding outputEncoding = Melder_getOutputEncoding ();
- if (outputEncoding == kMelder_textOutputEncoding::UTF8) { // TODO: read as file's encoding
- autofile f2 = Melder_fopen (file, "ab");
- Melder_fwrite32to8 (text, f2);
- f2.close (file);
- } else if ((outputEncoding == kMelder_textOutputEncoding::ASCII_THEN_UTF16 && Melder_isEncodable (text, kMelder_textOutputEncoding_ASCII))
- || (outputEncoding == kMelder_textOutputEncoding::ISO_LATIN1_THEN_UTF16 && Melder_isEncodable (text, kMelder_textOutputEncoding_ISO_LATIN1)))
- {
- /*
- * Append ASCII or ISOLatin1 text to ASCII or ISOLatin1 file.
- */
- autofile f2 = Melder_fopen (file, "ab");
- int64 n = str32len (text);
- for (int64 i = 0; i < n; i ++) {
- char32 kar = text [i];
- #ifdef _WIN32
- if (kar == U'\n')
- fputc (13, f2);
- #endif
- fputc ((char8) kar, f2);
- }
- f2.close (file);
- } else {
- /*
- * Convert to wide character file.
- */
- autostring32 oldText = MelderFile_readText (file);
- autofile f2 = Melder_fopen (file, "wb");
- binputu16 (0xfeff, f2);
- int64 n = str32len (oldText.get());
- for (int64 i = 0; i < n; i ++) {
- char32 kar = oldText [i];
- #ifdef _WIN32
- if (kar == U'\n')
- binputu16 (13, f2);
- #endif
- if (kar <= 0x00'FFFF) {
- binputu16 ((uint16) kar, f2); // guarded conversion down
- } else if (kar <= 0x10'FFFF) {
- kar -= 0x01'0000;
- binputu16 ((uint16) (0x00'D800 | (kar >> 10)), f2);
- binputu16 ((uint16) (0x00'DC00 | (kar & 0x00'03ff)), f2);
- } else {
- binputu16 (UNICODE_REPLACEMENT_CHARACTER, f2);
- }
- }
- n = str32len (text);
- for (int64 i = 0; i < n; i ++) {
- char32 kar = text [i];
- #ifdef _WIN32
- if (kar == U'\n')
- binputu16 (13, f2);
- #endif
- if (kar <= 0x00FFFF) {
- binputu16 ((uint16) kar, f2); // guarded conversion down
- } else if (kar <= 0x10'FFFF) {
- kar -= 0x01'0000;
- binputu16 ((uint16) (0x00'D800 | (kar >> 10)), f2);
- binputu16 ((uint16) (0x00'DC00 | (kar & 0x00'03ff)), f2);
- } else {
- binputu16 (UNICODE_REPLACEMENT_CHARACTER, f2);
- }
- }
- f2.close (file);
- }
- } else {
- autofile f2 = Melder_fopen (file, "ab");
- int64 n = str32len (text);
- for (int64 i = 0; i < n; i ++) {
- if (type == 1) {
- char32 kar = text [i];
- #ifdef _WIN32
- if (kar == U'\n')
- binputu16 (13, f2);
- #endif
- if (kar <= 0x00'FFFF) {
- binputu16 ((uint16) kar, f2); // guarded conversion down
- } else if (kar <= 0x10'FFFF) {
- kar -= 0x01'0000;
- binputu16 ((uint16) (0x00'D800 | (kar >> 10)), f2);
- binputu16 ((uint16) (0x00'DC00 | (kar & 0x00'03ff)), f2);
- } else {
- binputu16 (UNICODE_REPLACEMENT_CHARACTER, f2);
- }
- } else {
- char32 kar = text [i];
- #ifdef _WIN32
- if (kar == U'\n')
- binputu16LE (13, f2);
- #endif
- if (kar <= 0x00'FFFF) {
- binputu16LE ((uint16) kar, f2); // guarded conversion down
- } else if (kar <= 0x10FFFF) {
- kar -= 0x01'0000;
- binputu16LE ((uint16) (0x00'D800 | (kar >> 10)), f2);
- binputu16LE ((uint16) (0x00'DC00 | (kar & 0x00'03ff)), f2);
- } else {
- binputu16LE (UNICODE_REPLACEMENT_CHARACTER, f2);
- }
- }
- }
- f2.close (file);
- }
- }
- /* End of file melder_files.cpp */
|