1 //===-- File.cpp ------------------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "lldb/Host/File.h" 11 12 #include <errno.h> 13 #include <fcntl.h> 14 #include <limits.h> 15 #include <stdarg.h> 16 #include <stdio.h> 17 #include <sys/stat.h> 18 19 #ifdef _WIN32 20 #include "lldb/Host/windows/windows.h" 21 #else 22 #include <sys/ioctl.h> 23 #endif 24 25 #include "lldb/Core/DataBufferHeap.h" 26 #include "lldb/Core/Error.h" 27 #include "lldb/Core/Log.h" 28 #include "lldb/Host/Config.h" 29 #include "lldb/Host/FileSpec.h" 30 31 using namespace lldb; 32 using namespace lldb_private; 33 34 static const char * 35 GetStreamOpenModeFromOptions (uint32_t options) 36 { 37 if (options & File::eOpenOptionAppend) 38 { 39 if (options & File::eOpenOptionRead) 40 { 41 if (options & File::eOpenOptionCanCreateNewOnly) 42 return "a+x"; 43 else 44 return "a+"; 45 } 46 else if (options & File::eOpenOptionWrite) 47 { 48 if (options & File::eOpenOptionCanCreateNewOnly) 49 return "ax"; 50 else 51 return "a"; 52 } 53 } 54 else if (options & File::eOpenOptionRead && options & File::eOpenOptionWrite) 55 { 56 if (options & File::eOpenOptionCanCreate) 57 { 58 if (options & File::eOpenOptionCanCreateNewOnly) 59 return "w+x"; 60 else 61 return "w+"; 62 } 63 else 64 return "r+"; 65 } 66 else if (options & File::eOpenOptionRead) 67 { 68 return "r"; 69 } 70 else if (options & File::eOpenOptionWrite) 71 { 72 return "w"; 73 } 74 return NULL; 75 } 76 77 int File::kInvalidDescriptor = -1; 78 FILE * File::kInvalidStream = NULL; 79 80 File::File(const char *path, uint32_t options, uint32_t permissions) : 81 IOObject(eFDTypeFile, false), 82 m_descriptor (kInvalidDescriptor), 83 m_stream (kInvalidStream), 84 m_options (), 85 m_own_stream (false), 86 m_is_interactive (eLazyBoolCalculate), 87 m_is_real_terminal (eLazyBoolCalculate) 88 { 89 Open (path, options, permissions); 90 } 91 92 File::File (const FileSpec& filespec, 93 uint32_t options, 94 uint32_t permissions) : 95 IOObject(eFDTypeFile, false), 96 m_descriptor (kInvalidDescriptor), 97 m_stream (kInvalidStream), 98 m_options (0), 99 m_own_stream (false), 100 m_is_interactive (eLazyBoolCalculate), 101 m_is_real_terminal (eLazyBoolCalculate) 102 103 { 104 if (filespec) 105 { 106 Open (filespec.GetPath().c_str(), options, permissions); 107 } 108 } 109 110 File::File (const File &rhs) : 111 IOObject(eFDTypeFile, false), 112 m_descriptor (kInvalidDescriptor), 113 m_stream (kInvalidStream), 114 m_options (0), 115 m_own_stream (false), 116 m_is_interactive (eLazyBoolCalculate), 117 m_is_real_terminal (eLazyBoolCalculate) 118 { 119 Duplicate (rhs); 120 } 121 122 123 File & 124 File::operator = (const File &rhs) 125 { 126 if (this != &rhs) 127 Duplicate (rhs); 128 return *this; 129 } 130 131 File::~File() 132 { 133 Close (); 134 } 135 136 137 int 138 File::GetDescriptor() const 139 { 140 if (DescriptorIsValid()) 141 return m_descriptor; 142 143 // Don't open the file descriptor if we don't need to, just get it from the 144 // stream if we have one. 145 if (StreamIsValid()) 146 return fileno (m_stream); 147 148 // Invalid descriptor and invalid stream, return invalid descriptor. 149 return kInvalidDescriptor; 150 } 151 152 IOObject::WaitableHandle 153 File::GetWaitableHandle() 154 { 155 return m_descriptor; 156 } 157 158 159 void 160 File::SetDescriptor (int fd, bool transfer_ownership) 161 { 162 if (IsValid()) 163 Close(); 164 m_descriptor = fd; 165 m_should_close_fd = transfer_ownership; 166 } 167 168 169 FILE * 170 File::GetStream () 171 { 172 if (!StreamIsValid()) 173 { 174 if (DescriptorIsValid()) 175 { 176 const char *mode = GetStreamOpenModeFromOptions (m_options); 177 if (mode) 178 { 179 if (!m_should_close_fd) 180 { 181 // We must duplicate the file descriptor if we don't own it because 182 // when you call fdopen, the stream will own the fd 183 #ifdef _WIN32 184 m_descriptor = ::_dup(GetDescriptor()); 185 #else 186 m_descriptor = ::fcntl(GetDescriptor(), F_DUPFD); 187 #endif 188 m_should_close_fd = true; 189 } 190 191 do 192 { 193 m_stream = ::fdopen (m_descriptor, mode); 194 } while (m_stream == NULL && errno == EINTR); 195 196 // If we got a stream, then we own the stream and should no 197 // longer own the descriptor because fclose() will close it for us 198 199 if (m_stream) 200 { 201 m_own_stream = true; 202 m_should_close_fd = false; 203 } 204 } 205 } 206 } 207 return m_stream; 208 } 209 210 211 void 212 File::SetStream (FILE *fh, bool transfer_ownership) 213 { 214 if (IsValid()) 215 Close(); 216 m_stream = fh; 217 m_own_stream = transfer_ownership; 218 } 219 220 Error 221 File::Duplicate (const File &rhs) 222 { 223 Error error; 224 if (IsValid ()) 225 Close(); 226 227 if (rhs.DescriptorIsValid()) 228 { 229 #ifdef _WIN32 230 m_descriptor = ::_dup(rhs.GetDescriptor()); 231 #else 232 m_descriptor = ::fcntl(rhs.GetDescriptor(), F_DUPFD); 233 #endif 234 if (!DescriptorIsValid()) 235 error.SetErrorToErrno(); 236 else 237 { 238 m_options = rhs.m_options; 239 m_should_close_fd = true; 240 } 241 } 242 else 243 { 244 error.SetErrorString ("invalid file to duplicate"); 245 } 246 return error; 247 } 248 249 Error 250 File::Open (const char *path, uint32_t options, uint32_t permissions) 251 { 252 Error error; 253 if (IsValid()) 254 Close (); 255 256 int oflag = 0; 257 const bool read = options & eOpenOptionRead; 258 const bool write = options & eOpenOptionWrite; 259 if (write) 260 { 261 if (read) 262 oflag |= O_RDWR; 263 else 264 oflag |= O_WRONLY; 265 266 if (options & eOpenOptionAppend) 267 oflag |= O_APPEND; 268 269 if (options & eOpenOptionTruncate) 270 oflag |= O_TRUNC; 271 272 if (options & eOpenOptionCanCreate) 273 oflag |= O_CREAT; 274 275 if (options & eOpenOptionCanCreateNewOnly) 276 oflag |= O_CREAT | O_EXCL; 277 } 278 else if (read) 279 { 280 oflag |= O_RDONLY; 281 282 #ifndef _WIN32 283 if (options & eOpenoptionDontFollowSymlinks) 284 oflag |= O_NOFOLLOW; 285 #endif 286 } 287 288 #ifndef _WIN32 289 if (options & eOpenOptionNonBlocking) 290 oflag |= O_NONBLOCK; 291 #else 292 oflag |= O_BINARY; 293 #endif 294 295 mode_t mode = 0; 296 if (oflag & O_CREAT) 297 { 298 if (permissions & lldb::eFilePermissionsUserRead) mode |= S_IRUSR; 299 if (permissions & lldb::eFilePermissionsUserWrite) mode |= S_IWUSR; 300 if (permissions & lldb::eFilePermissionsUserExecute) mode |= S_IXUSR; 301 if (permissions & lldb::eFilePermissionsGroupRead) mode |= S_IRGRP; 302 if (permissions & lldb::eFilePermissionsGroupWrite) mode |= S_IWGRP; 303 if (permissions & lldb::eFilePermissionsGroupExecute) mode |= S_IXGRP; 304 if (permissions & lldb::eFilePermissionsWorldRead) mode |= S_IROTH; 305 if (permissions & lldb::eFilePermissionsWorldWrite) mode |= S_IWOTH; 306 if (permissions & lldb::eFilePermissionsWorldExecute) mode |= S_IXOTH; 307 } 308 309 do 310 { 311 m_descriptor = ::open(path, oflag, mode); 312 } while (m_descriptor < 0 && errno == EINTR); 313 314 if (!DescriptorIsValid()) 315 error.SetErrorToErrno(); 316 else 317 { 318 m_should_close_fd = true; 319 m_options = options; 320 } 321 322 return error; 323 } 324 325 uint32_t 326 File::GetPermissions (const char *path, Error &error) 327 { 328 if (path && path[0]) 329 { 330 struct stat file_stats; 331 if (::stat (path, &file_stats) == -1) 332 error.SetErrorToErrno(); 333 else 334 { 335 error.Clear(); 336 return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); 337 } 338 } 339 else 340 { 341 if (path) 342 error.SetErrorString ("invalid path"); 343 else 344 error.SetErrorString ("empty path"); 345 } 346 return 0; 347 } 348 349 uint32_t 350 File::GetPermissions(Error &error) const 351 { 352 int fd = GetDescriptor(); 353 if (fd != kInvalidDescriptor) 354 { 355 struct stat file_stats; 356 if (::fstat (fd, &file_stats) == -1) 357 error.SetErrorToErrno(); 358 else 359 { 360 error.Clear(); 361 return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); 362 } 363 } 364 else 365 { 366 error.SetErrorString ("invalid file descriptor"); 367 } 368 return 0; 369 } 370 371 372 Error 373 File::Close () 374 { 375 Error error; 376 if (StreamIsValid() && m_own_stream) 377 { 378 if (::fclose (m_stream) == EOF) 379 error.SetErrorToErrno(); 380 } 381 382 if (DescriptorIsValid() && m_should_close_fd) 383 { 384 if (::close (m_descriptor) != 0) 385 error.SetErrorToErrno(); 386 } 387 m_descriptor = kInvalidDescriptor; 388 m_stream = kInvalidStream; 389 m_options = 0; 390 m_own_stream = false; 391 m_should_close_fd = false; 392 m_is_interactive = eLazyBoolCalculate; 393 m_is_real_terminal = eLazyBoolCalculate; 394 return error; 395 } 396 397 398 Error 399 File::GetFileSpec (FileSpec &file_spec) const 400 { 401 Error error; 402 #ifdef LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED 403 if (IsValid ()) 404 { 405 char path[PATH_MAX]; 406 if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1) 407 error.SetErrorToErrno(); 408 else 409 file_spec.SetFile (path, false); 410 } 411 else 412 { 413 error.SetErrorString("invalid file handle"); 414 } 415 #elif defined(__linux__) 416 char proc[64]; 417 char path[PATH_MAX]; 418 if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0) 419 error.SetErrorString ("cannot resolve file descriptor"); 420 else 421 { 422 ssize_t len; 423 if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1) 424 error.SetErrorToErrno(); 425 else 426 { 427 path[len] = '\0'; 428 file_spec.SetFile (path, false); 429 } 430 } 431 #else 432 error.SetErrorString ("File::GetFileSpec is not supported on this platform"); 433 #endif 434 435 if (error.Fail()) 436 file_spec.Clear(); 437 return error; 438 } 439 440 off_t 441 File::SeekFromStart (off_t offset, Error *error_ptr) 442 { 443 off_t result = 0; 444 if (DescriptorIsValid()) 445 { 446 result = ::lseek (m_descriptor, offset, SEEK_SET); 447 448 if (error_ptr) 449 { 450 if (result == -1) 451 error_ptr->SetErrorToErrno(); 452 else 453 error_ptr->Clear(); 454 } 455 } 456 else if (StreamIsValid ()) 457 { 458 result = ::fseek(m_stream, offset, SEEK_SET); 459 460 if (error_ptr) 461 { 462 if (result == -1) 463 error_ptr->SetErrorToErrno(); 464 else 465 error_ptr->Clear(); 466 } 467 } 468 else if (error_ptr) 469 { 470 error_ptr->SetErrorString("invalid file handle"); 471 } 472 return result; 473 } 474 475 off_t 476 File::SeekFromCurrent (off_t offset, Error *error_ptr) 477 { 478 off_t result = -1; 479 if (DescriptorIsValid()) 480 { 481 result = ::lseek (m_descriptor, offset, SEEK_CUR); 482 483 if (error_ptr) 484 { 485 if (result == -1) 486 error_ptr->SetErrorToErrno(); 487 else 488 error_ptr->Clear(); 489 } 490 } 491 else if (StreamIsValid ()) 492 { 493 result = ::fseek(m_stream, offset, SEEK_CUR); 494 495 if (error_ptr) 496 { 497 if (result == -1) 498 error_ptr->SetErrorToErrno(); 499 else 500 error_ptr->Clear(); 501 } 502 } 503 else if (error_ptr) 504 { 505 error_ptr->SetErrorString("invalid file handle"); 506 } 507 return result; 508 } 509 510 off_t 511 File::SeekFromEnd (off_t offset, Error *error_ptr) 512 { 513 off_t result = -1; 514 if (DescriptorIsValid()) 515 { 516 result = ::lseek (m_descriptor, offset, SEEK_END); 517 518 if (error_ptr) 519 { 520 if (result == -1) 521 error_ptr->SetErrorToErrno(); 522 else 523 error_ptr->Clear(); 524 } 525 } 526 else if (StreamIsValid ()) 527 { 528 result = ::fseek(m_stream, offset, SEEK_END); 529 530 if (error_ptr) 531 { 532 if (result == -1) 533 error_ptr->SetErrorToErrno(); 534 else 535 error_ptr->Clear(); 536 } 537 } 538 else if (error_ptr) 539 { 540 error_ptr->SetErrorString("invalid file handle"); 541 } 542 return result; 543 } 544 545 Error 546 File::Flush () 547 { 548 Error error; 549 if (StreamIsValid()) 550 { 551 int err = 0; 552 do 553 { 554 err = ::fflush (m_stream); 555 } while (err == EOF && errno == EINTR); 556 557 if (err == EOF) 558 error.SetErrorToErrno(); 559 } 560 else if (!DescriptorIsValid()) 561 { 562 error.SetErrorString("invalid file handle"); 563 } 564 return error; 565 } 566 567 568 Error 569 File::Sync () 570 { 571 Error error; 572 if (DescriptorIsValid()) 573 { 574 #ifdef _WIN32 575 int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor)); 576 if (err == 0) 577 error.SetErrorToGenericError(); 578 #else 579 int err = 0; 580 do 581 { 582 err = ::fsync (m_descriptor); 583 } while (err == -1 && errno == EINTR); 584 585 if (err == -1) 586 error.SetErrorToErrno(); 587 #endif 588 } 589 else 590 { 591 error.SetErrorString("invalid file handle"); 592 } 593 return error; 594 } 595 596 Error 597 File::Read (void *buf, size_t &num_bytes) 598 { 599 Error error; 600 ssize_t bytes_read = -1; 601 if (DescriptorIsValid()) 602 { 603 do 604 { 605 bytes_read = ::read (m_descriptor, buf, num_bytes); 606 } while (bytes_read < 0 && errno == EINTR); 607 608 if (bytes_read == -1) 609 { 610 error.SetErrorToErrno(); 611 num_bytes = 0; 612 } 613 else 614 num_bytes = bytes_read; 615 } 616 else if (StreamIsValid()) 617 { 618 bytes_read = ::fread (buf, 1, num_bytes, m_stream); 619 620 if (bytes_read == 0) 621 { 622 if (::feof(m_stream)) 623 error.SetErrorString ("feof"); 624 else if (::ferror (m_stream)) 625 error.SetErrorString ("ferror"); 626 num_bytes = 0; 627 } 628 else 629 num_bytes = bytes_read; 630 } 631 else 632 { 633 num_bytes = 0; 634 error.SetErrorString("invalid file handle"); 635 } 636 return error; 637 } 638 639 Error 640 File::Write (const void *buf, size_t &num_bytes) 641 { 642 Error error; 643 ssize_t bytes_written = -1; 644 if (DescriptorIsValid()) 645 { 646 do 647 { 648 bytes_written = ::write (m_descriptor, buf, num_bytes); 649 } while (bytes_written < 0 && errno == EINTR); 650 651 if (bytes_written == -1) 652 { 653 error.SetErrorToErrno(); 654 num_bytes = 0; 655 } 656 else 657 num_bytes = bytes_written; 658 } 659 else if (StreamIsValid()) 660 { 661 bytes_written = ::fwrite (buf, 1, num_bytes, m_stream); 662 663 if (bytes_written == 0) 664 { 665 if (::feof(m_stream)) 666 error.SetErrorString ("feof"); 667 else if (::ferror (m_stream)) 668 error.SetErrorString ("ferror"); 669 num_bytes = 0; 670 } 671 else 672 num_bytes = bytes_written; 673 674 } 675 else 676 { 677 num_bytes = 0; 678 error.SetErrorString("invalid file handle"); 679 } 680 681 return error; 682 } 683 684 685 Error 686 File::Read (void *buf, size_t &num_bytes, off_t &offset) 687 { 688 #ifndef _WIN32 689 Error error; 690 int fd = GetDescriptor(); 691 if (fd != kInvalidDescriptor) 692 { 693 ssize_t bytes_read = -1; 694 do 695 { 696 bytes_read = ::pread (fd, buf, num_bytes, offset); 697 } while (bytes_read < 0 && errno == EINTR); 698 699 if (bytes_read < 0) 700 { 701 num_bytes = 0; 702 error.SetErrorToErrno(); 703 } 704 else 705 { 706 offset += bytes_read; 707 num_bytes = bytes_read; 708 } 709 } 710 else 711 { 712 num_bytes = 0; 713 error.SetErrorString("invalid file handle"); 714 } 715 return error; 716 #else 717 long cur = ::lseek(m_descriptor, 0, SEEK_CUR); 718 SeekFromStart(offset); 719 Error error = Read(buf, num_bytes); 720 if (!error.Fail()) 721 SeekFromStart(cur); 722 return error; 723 #endif 724 } 725 726 Error 727 File::Read (size_t &num_bytes, off_t &offset, bool null_terminate, DataBufferSP &data_buffer_sp) 728 { 729 Error error; 730 731 if (num_bytes > 0) 732 { 733 int fd = GetDescriptor(); 734 if (fd != kInvalidDescriptor) 735 { 736 struct stat file_stats; 737 if (::fstat (fd, &file_stats) == 0) 738 { 739 if (file_stats.st_size > offset) 740 { 741 const size_t bytes_left = file_stats.st_size - offset; 742 if (num_bytes > bytes_left) 743 num_bytes = bytes_left; 744 745 size_t num_bytes_plus_nul_char = num_bytes + (null_terminate ? 1 : 0); 746 std::unique_ptr<DataBufferHeap> data_heap_ap; 747 data_heap_ap.reset(new DataBufferHeap(num_bytes_plus_nul_char, '\0')); 748 749 if (data_heap_ap.get()) 750 { 751 error = Read (data_heap_ap->GetBytes(), num_bytes, offset); 752 if (error.Success()) 753 { 754 // Make sure we read exactly what we asked for and if we got 755 // less, adjust the array 756 if (num_bytes_plus_nul_char < data_heap_ap->GetByteSize()) 757 data_heap_ap->SetByteSize(num_bytes_plus_nul_char); 758 data_buffer_sp.reset(data_heap_ap.release()); 759 return error; 760 } 761 } 762 } 763 else 764 error.SetErrorString("file is empty"); 765 } 766 else 767 error.SetErrorToErrno(); 768 } 769 else 770 error.SetErrorString("invalid file handle"); 771 } 772 else 773 error.SetErrorString("invalid file handle"); 774 775 num_bytes = 0; 776 data_buffer_sp.reset(); 777 return error; 778 } 779 780 Error 781 File::Write (const void *buf, size_t &num_bytes, off_t &offset) 782 { 783 Error error; 784 int fd = GetDescriptor(); 785 if (fd != kInvalidDescriptor) 786 { 787 #ifndef _WIN32 788 ssize_t bytes_written = -1; 789 do 790 { 791 bytes_written = ::pwrite (m_descriptor, buf, num_bytes, offset); 792 } while (bytes_written < 0 && errno == EINTR); 793 794 if (bytes_written < 0) 795 { 796 num_bytes = 0; 797 error.SetErrorToErrno(); 798 } 799 else 800 { 801 offset += bytes_written; 802 num_bytes = bytes_written; 803 } 804 #else 805 long cur = ::lseek(m_descriptor, 0, SEEK_CUR); 806 error = Write(buf, num_bytes); 807 long after = ::lseek(m_descriptor, 0, SEEK_CUR); 808 809 if (!error.Fail()) 810 SeekFromStart(cur); 811 812 ssize_t bytes_written = after - cur; 813 offset = after; 814 #endif 815 } 816 else 817 { 818 num_bytes = 0; 819 error.SetErrorString("invalid file handle"); 820 } 821 return error; 822 } 823 824 //------------------------------------------------------------------ 825 // Print some formatted output to the stream. 826 //------------------------------------------------------------------ 827 size_t 828 File::Printf (const char *format, ...) 829 { 830 va_list args; 831 va_start (args, format); 832 size_t result = PrintfVarArg (format, args); 833 va_end (args); 834 return result; 835 } 836 837 //------------------------------------------------------------------ 838 // Print some formatted output to the stream. 839 //------------------------------------------------------------------ 840 size_t 841 File::PrintfVarArg (const char *format, va_list args) 842 { 843 size_t result = 0; 844 if (DescriptorIsValid()) 845 { 846 char *s = NULL; 847 result = vasprintf(&s, format, args); 848 if (s != NULL) 849 { 850 if (result > 0) 851 { 852 size_t s_len = result; 853 Write (s, s_len); 854 result = s_len; 855 } 856 free (s); 857 } 858 } 859 else if (StreamIsValid()) 860 { 861 result = ::vfprintf (m_stream, format, args); 862 } 863 return result; 864 } 865 866 mode_t 867 File::ConvertOpenOptionsForPOSIXOpen (uint32_t open_options) 868 { 869 mode_t mode = 0; 870 if (open_options & eOpenOptionRead && open_options & eOpenOptionWrite) 871 mode |= O_RDWR; 872 else if (open_options & eOpenOptionWrite) 873 mode |= O_WRONLY; 874 875 if (open_options & eOpenOptionAppend) 876 mode |= O_APPEND; 877 878 if (open_options & eOpenOptionTruncate) 879 mode |= O_TRUNC; 880 881 if (open_options & eOpenOptionNonBlocking) 882 mode |= O_NONBLOCK; 883 884 if (open_options & eOpenOptionCanCreateNewOnly) 885 mode |= O_CREAT | O_EXCL; 886 else if (open_options & eOpenOptionCanCreate) 887 mode |= O_CREAT; 888 889 return mode; 890 } 891 892 void 893 File::CalculateInteractiveAndTerminal () 894 { 895 const int fd = GetDescriptor(); 896 if (fd >= 0) 897 { 898 m_is_interactive = eLazyBoolNo; 899 m_is_real_terminal = eLazyBoolNo; 900 #if (defined(_WIN32) || defined(__ANDROID_NDK__)) 901 if (_isatty(fd)) 902 { 903 m_is_interactive = eLazyBoolYes; 904 m_is_real_terminal = eLazyBoolYes; 905 } 906 #else 907 if (isatty(fd)) 908 { 909 m_is_interactive = eLazyBoolYes; 910 struct winsize window_size; 911 if (::ioctl (fd, TIOCGWINSZ, &window_size) == 0) 912 { 913 if (window_size.ws_col > 0) 914 m_is_real_terminal = eLazyBoolYes; 915 } 916 } 917 #endif 918 } 919 } 920 921 bool 922 File::GetIsInteractive () 923 { 924 if (m_is_interactive == eLazyBoolCalculate) 925 CalculateInteractiveAndTerminal (); 926 return m_is_interactive == eLazyBoolYes; 927 } 928 929 bool 930 File::GetIsRealTerminal () 931 { 932 if (m_is_real_terminal == eLazyBoolCalculate) 933 CalculateInteractiveAndTerminal(); 934 return m_is_real_terminal == eLazyBoolYes; 935 } 936 937