1 #include "util/angband-files.h"
2 #include "locale/japanese.h"
3 #include "system/angband-exceptions.h"
4 #include "util/string-processor.h"
8 void (*file_open_hook)(const std::filesystem::path &path, const FileOpenType ftype) = 0;
15 * For those systems that don't have "usleep()" but need it.
17 * Fake "usleep()" function grabbed from the inl netrek server -cba
19 int usleep(ulong usecs)
25 fd_set *no_fds = nullptr;
26 if (usecs > 4000000L) {
27 core(_("不当な usleep() 呼び出し", "Illegal usleep() call"));
30 timer.tv_sec = (usecs / 1000000L);
31 timer.tv_usec = (usecs % 1000000L);
32 if (select(nfds, no_fds, no_fds, no_fds, &timer) < 0) {
43 * Hack -- External functions
46 struct passwd *getpwuid(uid_t uid);
47 struct passwd *getpwnam(concptr name);
51 * Find a default user name from the system.
53 void user_name(char *buf, int id)
56 if ((pw = getpwuid(id))) {
57 (void)strcpy(buf, pw->pw_name);
63 if (islower(buf[0])) {
64 buf[0] = toupper(buf[0]);
70 strcpy(buf, "PLAYER");
75 std::filesystem::path path_parse(const std::filesystem::path &path)
79 * Extract a "parsed" path from an initial filename
80 * Normally, we simply copy the filename into the buffer
81 * But leading tilde symbols must be handled in a special way
82 * Replace "~user/" by the home directory of the user named "user"
83 * Replace "~/" by the home directory of the current user
85 const auto &file = path.string();
86 if (file.empty() || (file[0] != '~')) {
90 auto u = file.data() + 1;
91 auto s = angband_strstr(u, PATH_SEP);
92 constexpr auto user_size = 128;
93 char user[user_size]{};
94 if ((s != nullptr) && (s >= u + user_size)) {
95 THROW_EXCEPTION(std::runtime_error, "User name is too long!");
100 for (i = 0; u < s; ++i) {
115 pw = getpwuid(getuid());
119 THROW_EXCEPTION(std::runtime_error, "Failed to get User ID!");
126 std::stringstream ss;
127 ss << pw->pw_dir << s;
139 * Hack -- acquire a "temporary" file name if possible
141 * This filename is always in "system-specific" form.
143 static errr path_temp(char *buf, int max)
145 concptr s = tmpnam(nullptr);
150 #if !defined(WIN32) || (defined(_MSC_VER) && (_MSC_VER >= 1900))
151 (void)strnfmt(buf, max, "%s", s);
153 (void)strnfmt(buf, max, ".%s", s);
161 * @brief OSの差異を吸収しつつ、絶対パスを生成する.
162 * @param path file 引数があるディレクトリ
163 * @param file ファイル名またはディレクトリ名
165 std::filesystem::path path_build(const std::filesystem::path &path, std::string_view file)
167 if ((file[0] == '~') || (prefix(file, PATH_SEP)) || path.empty()) {
171 const auto path_ret = std::filesystem::path(path).append(file);
172 constexpr auto max_path_length = 1024;
173 const auto path_str = path_ret.string();
174 if (path_str.length() > max_path_length) {
175 THROW_EXCEPTION(std::runtime_error, format("Path is too long! %s", path_str.data()));
181 static std::string make_file_mode(const FileOpenMode mode, const bool is_binary)
183 std::stringstream ss;
185 case FileOpenMode::READ:
188 case FileOpenMode::WRITE:
191 case FileOpenMode::APPEND:
195 THROW_EXCEPTION(std::logic_error, "Invalid file mode is specified!");
206 * @brief OSごとの差異を吸収してファイルを開く
207 * @param path ファイルの相対パスまたは絶対パス
208 * @param mode ファイルを開くモード
209 * @param is_binary バイナリモードか否か (無指定の場合false:テキストモード)
212 FILE *angband_fopen(const std::filesystem::path &path, const FileOpenMode mode, const bool is_binary, const FileOpenType ftype)
216 const auto &parsed_path = path_parse(path);
217 const auto &open_mode = make_file_mode(mode, is_binary);
218 result = fopen(parsed_path.string().data(), open_mode.data());
219 if (result && mode != FileOpenMode::READ && file_open_hook) {
220 file_open_hook(path, ftype);
226 * Hack -- replacement for "fclose()"
228 errr angband_fclose(FILE *fff)
233 if (fclose(fff) == EOF) {
240 FILE *angband_fopen_temp(char *buf, int max)
242 strncpy(buf, "/tmp/anXXXXXX", max);
243 int fd = mkstemp(buf);
248 return fdopen(fd, "w");
250 #else /* HAVE_MKSTEMP */
251 FILE *angband_fopen_temp(char *buf, int max)
253 if (path_temp(buf, max)) {
256 return angband_fopen(buf, FileOpenMode::WRITE);
258 #endif /* HAVE_MKSTEMP */
261 * Hack -- replacement for "fgets()"
263 * Read a string, without a newline, to a file
265 * Process tabs, replace internal non-printables with '?'
267 errr angband_fgets(FILE *fff, char *buf, ulong n)
275 // Reserve for null termination
278 std::vector<char> file_read__tmp(FILE_READ_BUFF_SIZE);
279 if (fgets(file_read__tmp.data(), file_read__tmp.size(), fff)) {
281 guess_convert_to_system_encoding(file_read__tmp.data(), FILE_READ_BUFF_SIZE);
283 for (s = file_read__tmp.data(); *s; s++) {
286 * Be nice to the Macintosh, where a file can have Mac or Unix
287 * end of line, especially since the introduction of OS X.
288 * MPW tools were also very tolerant to the Unix EOL.
290 if (*s == '\r') *s = '\n';
291 #endif /* MACH_O_COCOA */
295 } else if (*s == '\t') {
301 while (0 != (i % 8)) {
306 else if (iskanji(*s)) {
315 } else if (iskana(*s)) {
323 else if (isprint((unsigned char)*s)) {
345 * Hack -- replacement for "fputs()"
346 * Dump a string, plus a newline, to a file
347 * Process internal weirdness?
349 errr angband_fputs(FILE *fff, concptr buf, ulong n)
352 (void)fprintf(fff, "%s\n", buf);
357 * Several systems have no "O_BINARY" flag
361 #endif /* O_BINARY */
364 * @brief OSごとの差異を吸収してファイルを削除する
365 * @param file ファイルの相対パスまたは絶対パス
367 void fd_kill(const std::filesystem::path &path)
369 const auto &parsed_path = path_parse(path);
372 std::filesystem::remove(parsed_path, ec);
376 * @brief OSごとの差異を吸収してファイルを移動する
377 * @param path_from 移動元のファイルの相対パスまたは絶対パス
378 * @param path_to 移動先のファイルの相対パスまたは絶対パス
380 void fd_move(const std::filesystem::path &path_from, const std::filesystem::path &path_to)
382 const auto &abs_path_from = path_parse(path_from);
383 const auto &abs_path_to = path_parse(path_to);
386 std::filesystem::rename(abs_path_from, abs_path_to, ec);
390 * @brief OSごとの差異を吸収してファイルを作成する
391 * @param path 作成先ファイルの相対パスまたは絶対パス
392 * @param can_write_group グループに書き込みを許可するか否か
394 int fd_make(const std::filesystem::path &path, bool can_write_group)
396 const auto permission = can_write_group ? 0644 : 0664;
397 const auto &parsed_path = path_parse(path);
398 return open(parsed_path.string().data(), O_CREAT | O_EXCL | O_WRONLY | O_BINARY, permission);
402 * @brief OSごとの差異を吸収してファイルを開く
403 * @param path ファイルの相対パスまたは絶対パス
404 * @param mode ファイルのオープンモード (読み書き、Append/Trunc等)
406 int fd_open(const std::filesystem::path &path, int mode)
408 const auto &path_abs = path_parse(path);
409 return open(path_abs.string().data(), mode | O_BINARY, 0);
413 * Hack -- attempt to lock a file descriptor
415 * Legal lock types -- F_UNLCK, F_RDLCK, F_WRLCK
417 errr fd_lock(int fd, int what)
419 what = what ? what : 0;
424 #if defined(SET_UID) && defined(LOCK_UN) && defined(LOCK_EX)
425 if (what == F_UNLCK) {
426 (void)flock(fd, LOCK_UN);
428 if (flock(fd, LOCK_EX) != 0) {
438 * Hack -- attempt to seek on a file descriptor
440 errr fd_seek(int fd, ulong n)
446 ulong p = lseek(fd, n, SEEK_SET);
455 * Hack -- attempt to read data from a file descriptor
457 errr fd_read(int fd, char *buf, ulong n)
464 if (read(fd, buf, 16384) != 16384) {
473 if (read(fd, buf, n) != (int)n) {
481 * Hack -- Attempt to write data to a file descriptor
483 errr fd_write(int fd, concptr buf, ulong n)
491 if (write(fd, buf, 16384) != 16384) {
500 if (write(fd, buf, n) != (int)n) {
508 * Hack -- attempt to close a file descriptor
510 errr fd_close(int fd)