1 #include "util/angband-files.h"
2 #include "locale/japanese.h"
3 #include "system/angband-exceptions.h"
4 #include "util/string-processor.h"
13 * For those systems that don't have "usleep()" but need it.
15 * Fake "usleep()" function grabbed from the inl netrek server -cba
17 int usleep(ulong usecs)
23 fd_set *no_fds = nullptr;
24 if (usecs > 4000000L) {
25 core(_("不当な usleep() 呼び出し", "Illegal usleep() call"));
28 timer.tv_sec = (usecs / 1000000L);
29 timer.tv_usec = (usecs % 1000000L);
30 if (select(nfds, no_fds, no_fds, no_fds, &timer) < 0) {
41 * Hack -- External functions
44 struct passwd *getpwuid(uid_t uid);
45 struct passwd *getpwnam(concptr name);
49 * Find a default user name from the system.
51 void user_name(char *buf, int id)
54 if ((pw = getpwuid(id))) {
55 (void)strcpy(buf, pw->pw_name);
61 if (islower(buf[0])) {
62 buf[0] = toupper(buf[0]);
68 strcpy(buf, "PLAYER");
73 std::filesystem::path path_parse(const std::filesystem::path &path)
77 * Extract a "parsed" path from an initial filename
78 * Normally, we simply copy the filename into the buffer
79 * But leading tilde symbols must be handled in a special way
80 * Replace "~user/" by the home directory of the user named "user"
81 * Replace "~/" by the home directory of the current user
83 const auto &file = path.string();
84 if (file.empty() || (file[0] != '~')) {
88 auto u = file.data() + 1;
89 auto s = angband_strstr(u, PATH_SEP);
90 constexpr auto user_size = 128;
91 char user[user_size]{};
92 if ((s != nullptr) && (s >= u + user_size)) {
93 THROW_EXCEPTION(std::runtime_error, "User name is too long!");
98 for (i = 0; u < s; ++i) {
113 pw = getpwuid(getuid());
117 THROW_EXCEPTION(std::runtime_error, "Failed to get User ID!");
124 std::stringstream ss;
125 ss << pw->pw_dir << s;
137 * Hack -- acquire a "temporary" file name if possible
139 * This filename is always in "system-specific" form.
141 static errr path_temp(char *buf, int max)
143 auto s = tmpnam(nullptr);
148 #if !defined(WIN32) || (defined(_MSC_VER) && (_MSC_VER >= 1900))
149 (void)strnfmt(buf, max, "%s", s);
151 (void)strnfmt(buf, max, ".%s", s);
159 * @brief OSの差異を吸収しつつ、絶対パスを生成する.
160 * @param path file 引数があるディレクトリ
161 * @param file ファイル名またはディレクトリ名
163 std::filesystem::path path_build(const std::filesystem::path &path, std::string_view file)
165 if ((file[0] == '~') || (prefix(file, PATH_SEP)) || path.empty()) {
169 auto parsed_path = path_parse(path);
170 const auto &path_ret = parsed_path.append(file);
171 constexpr auto max_path_length = 1024;
172 const auto path_str = path_ret.string();
173 if (path_str.length() > max_path_length) {
174 THROW_EXCEPTION(std::runtime_error, format("Path is too long! %s", path_str.data()));
180 static std::string make_file_mode(const FileOpenMode mode, const bool is_binary)
182 std::stringstream ss;
184 case FileOpenMode::READ:
187 case FileOpenMode::WRITE:
190 case FileOpenMode::APPEND:
194 THROW_EXCEPTION(std::logic_error, "Invalid file mode is specified!");
205 * @brief OSごとの差異を吸収してファイルを開く
206 * @param path ファイルの相対パスまたは絶対パス
207 * @param mode ファイルを開くモード
208 * @param is_binary バイナリモードか否か (無指定の場合false:テキストモード)
211 FILE *angband_fopen(const std::filesystem::path &path, const FileOpenMode mode, const bool is_binary)
213 const auto &parsed_path = path_parse(path);
214 const auto &open_mode = make_file_mode(mode, is_binary);
215 return fopen(parsed_path.string().data(), open_mode.data());
219 * Hack -- replacement for "fclose()"
221 errr angband_fclose(FILE *fff)
226 if (fclose(fff) == EOF) {
233 FILE *angband_fopen_temp(char *buf, int max)
235 strncpy(buf, "/tmp/anXXXXXX", max);
236 int fd = mkstemp(buf);
241 return fdopen(fd, "w");
243 #else /* HAVE_MKSTEMP */
244 FILE *angband_fopen_temp(char *buf, int max)
246 if (path_temp(buf, max)) {
249 return angband_fopen(buf, FileOpenMode::WRITE);
251 #endif /* HAVE_MKSTEMP */
254 * @brief ファイルから改行かEOFまでの文字列を読み取り、システムのエンコーディングに変換した結果を返す
257 * @return 読み取った文字列。1バイトも読み取らずファイルの終端に達した場合はstd::nullopt
259 static std::optional<std::string> read_line(FILE *fp)
261 std::string line_buf;
264 while (fgets(buf, sizeof(buf), fp) != nullptr) {
265 std::string_view sv(buf);
267 line_buf.append(sv.begin(), sv.end());
268 if (sv.back() == '\n') {
273 if (line_buf.empty()) {
277 if (line_buf.back() == '\n') {
282 const int len = guess_convert_to_system_encoding(line_buf.data(), line_buf.size());
290 * @brief ファイルから1行読み込む
292 * ファイルから1行読み込み、読み込んだ文字列に以下の処理を行い最大n-1バイトまでの文字列を返す。
294 * - プリント可能文字以外を'?'に変換する
298 * - 文字コードをシステムの文字コードに変換する
299 * - マルチバイト文字の前半のみが残らないようにする
301 * 読み込んだ行で上記の変換を行いn-1バイトを超えた分は読み捨てられる。
303 * @param fp 読み込むファイルポインタ
304 * @param n 読み込む文字列の最大サイズ。
305 * 呼び出し側で終端文字を扱う可能性を考慮し、文字列長としては最大n-1バイトまでの文字列を返す。
306 * デフォルト引数(std::string::npos)の場合は巨大な値であるため実質的にサイズ制限なし。
307 * @return 読み取った文字列。1バイトも読み取らずファイルの終端に達した場合はstd::nullopt
309 std::optional<std::string> angband_fgets(FILE *fp, size_t n)
311 // Reserve for null termination
314 const auto line = read_line(fp);
321 for (const auto *s = line->data(); *s; s++) {
323 constexpr auto tab_width = 8;
324 if (str.length() + tab_width >= n) {
328 const auto space_count = tab_width - (str.length() % tab_width);
329 str.append(space_count, ' ');
332 else if (iskanji(*s)) {
333 if (str.length() + 1 >= n || s[1] == '\0') {
338 } else if (iskana(*s)) {
343 else if (isprint((unsigned char)*s)) {
349 if (str.length() >= n) {
358 * Hack -- replacement for "fputs()"
359 * Dump a string, plus a newline, to a file
360 * Process internal weirdness?
362 errr angband_fputs(FILE *fff, concptr buf, ulong n)
365 (void)fprintf(fff, "%s\n", buf);
370 * Several systems have no "O_BINARY" flag
374 #endif /* O_BINARY */
377 * @brief OSごとの差異を吸収してファイルを削除する
378 * @param file ファイルの相対パスまたは絶対パス
380 void fd_kill(const std::filesystem::path &path)
382 const auto &parsed_path = path_parse(path);
385 std::filesystem::remove(parsed_path, ec);
389 * @brief OSごとの差異を吸収してファイルを移動する
390 * @param path_from 移動元のファイルの相対パスまたは絶対パス
391 * @param path_to 移動先のファイルの相対パスまたは絶対パス
393 void fd_move(const std::filesystem::path &path_from, const std::filesystem::path &path_to)
395 const auto &abs_path_from = path_parse(path_from);
396 const auto &abs_path_to = path_parse(path_to);
399 std::filesystem::rename(abs_path_from, abs_path_to, ec);
403 * @brief OSごとの差異を吸収してファイルを作成する
404 * @param path 作成先ファイルの相対パスまたは絶対パス
405 * @param can_write_group グループに書き込みを許可するか否か
407 int fd_make(const std::filesystem::path &path, bool can_write_group)
409 const auto permission = can_write_group ? 0644 : 0664;
410 const auto &parsed_path = path_parse(path);
411 return open(parsed_path.string().data(), O_CREAT | O_EXCL | O_WRONLY | O_BINARY, permission);
415 * @brief OSごとの差異を吸収してファイルを開く
416 * @param path ファイルの相対パスまたは絶対パス
417 * @param mode ファイルのオープンモード (読み書き、Append/Trunc等)
419 int fd_open(const std::filesystem::path &path, int mode)
421 const auto &path_abs = path_parse(path);
422 return open(path_abs.string().data(), mode | O_BINARY, 0);
426 * Hack -- attempt to lock a file descriptor
428 * Legal lock types -- F_UNLCK, F_RDLCK, F_WRLCK
430 errr fd_lock(int fd, int what)
432 what = what ? what : 0;
437 #if defined(SET_UID) && defined(LOCK_UN) && defined(LOCK_EX)
438 if (what == F_UNLCK) {
439 (void)flock(fd, LOCK_UN);
441 if (flock(fd, LOCK_EX) != 0) {
451 * Hack -- attempt to seek on a file descriptor
453 errr fd_seek(int fd, ulong n)
459 ulong p = lseek(fd, n, SEEK_SET);
468 * Hack -- attempt to read data from a file descriptor
470 errr fd_read(int fd, char *buf, ulong n)
477 if (read(fd, buf, 16384) != 16384) {
486 if (read(fd, buf, n) != (int)n) {
494 * Hack -- Attempt to write data to a file descriptor
496 errr fd_write(int fd, concptr buf, ulong n)
504 if (write(fd, buf, 16384) != 16384) {
513 if (write(fd, buf, n) != (int)n) {
521 * Hack -- attempt to close a file descriptor
523 errr fd_close(int fd)