1 // Copyright 2013 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "line_printer.h" 16 17 #include <stdio.h> 18 #include <stdlib.h> 19 #ifdef _WIN32 20 #include <windows.h> 21 #else 22 #include <unistd.h> 23 #include <sys/ioctl.h> 24 #include <termios.h> 25 #include <sys/time.h> 26 #endif 27 28 // Make sure printf is really adb_printf which works for UTF-8 on Windows. 29 #include <sysdeps.h> 30 31 // Stuff from ninja's util.h that's needed below. 32 #include <vector> 33 using namespace std; 34 // This does not account for multiple UTF-8 bytes corresponding to a single Unicode code point, or 35 // multiple code points corresponding to a single grapheme cluster (user-perceived character). 36 string ElideMiddle(const string& str, size_t width) { 37 const int kMargin = 3; // Space for "...". 38 string result = str; 39 if (result.size() + kMargin > width) { 40 size_t elide_size = (width - kMargin) / 2; 41 result = result.substr(0, elide_size) 42 + "..." 43 + result.substr(result.size() - elide_size, elide_size); 44 } 45 return result; 46 } 47 48 LinePrinter::LinePrinter() : have_blank_line_(true) { 49 #ifndef _WIN32 50 const char* term = getenv("TERM"); 51 smart_terminal_ = unix_isatty(1) && term && string(term) != "dumb"; 52 #else 53 // Disable output buffer. It'd be nice to use line buffering but 54 // MSDN says: "For some systems, [_IOLBF] provides line 55 // buffering. However, for Win32, the behavior is the same as _IOFBF 56 // - Full Buffering." 57 setvbuf(stdout, nullptr, _IONBF, 0); 58 console_ = GetStdHandle(STD_OUTPUT_HANDLE); 59 CONSOLE_SCREEN_BUFFER_INFO csbi; 60 smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi); 61 #endif 62 } 63 64 static void Out(const std::string& s) { 65 // Avoid printf and C strings, since the actual output might contain null 66 // bytes like UTF-16 does (yuck). 67 fwrite(s.data(), 1, s.size(), stdout); 68 } 69 70 void LinePrinter::Print(string to_print, LineType type) { 71 if (!smart_terminal_) { 72 if (type == LineType::INFO) { 73 info_line_ = to_print + "\n"; 74 } else { 75 Out(to_print + "\n"); 76 } 77 return; 78 } 79 80 // Print over previous line, if any. 81 // On Windows, calling a C library function writing to stdout also handles 82 // pausing the executable when the "Pause" key or Ctrl-S is pressed. 83 printf("\r"); 84 85 if (type == INFO) { 86 #ifdef _WIN32 87 CONSOLE_SCREEN_BUFFER_INFO csbi; 88 GetConsoleScreenBufferInfo(console_, &csbi); 89 90 to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X)); 91 std::wstring to_print_wide; 92 // ElideMiddle may create invalid UTF-8, so ignore conversion errors. 93 (void)android::base::UTF8ToWide(to_print, &to_print_wide); 94 // We don't want to have the cursor spamming back and forth, so instead of 95 // printf use WriteConsoleOutput which updates the contents of the buffer, 96 // but doesn't move the cursor position. 97 COORD buf_size = { csbi.dwSize.X, 1 }; 98 COORD zero_zero = { 0, 0 }; 99 SMALL_RECT target = { 100 csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y, 101 static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1), 102 csbi.dwCursorPosition.Y 103 }; 104 vector<CHAR_INFO> char_data(csbi.dwSize.X); 105 for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) { 106 char_data[i].Char.UnicodeChar = i < to_print_wide.size() ? to_print_wide[i] : L' '; 107 char_data[i].Attributes = csbi.wAttributes; 108 } 109 WriteConsoleOutputW(console_, &char_data[0], buf_size, zero_zero, &target); 110 #else 111 // Limit output to width of the terminal if provided so we don't cause 112 // line-wrapping. 113 winsize size; 114 if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) { 115 to_print = ElideMiddle(to_print, size.ws_col); 116 } 117 Out(to_print); 118 printf("\x1B[K"); // Clear to end of line. 119 fflush(stdout); 120 #endif 121 122 have_blank_line_ = false; 123 } else { 124 Out(to_print); 125 Out("\n"); 126 have_blank_line_ = true; 127 } 128 } 129 130 void LinePrinter::KeepInfoLine() { 131 if (smart_terminal_) { 132 if (!have_blank_line_) Out("\n"); 133 have_blank_line_ = true; 134 } else { 135 Out(info_line_); 136 info_line_.clear(); 137 } 138 } 139