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> 33using namespace std; 34string ElideMiddle(const string& str, size_t width) { 35 const int kMargin = 3; // Space for "...". 36 string result = str; 37 if (result.size() + kMargin > width) { 38 size_t elide_size = (width - kMargin) / 2; 39 result = result.substr(0, elide_size) 40 + "..." 41 + result.substr(result.size() - elide_size, elide_size); 42 } 43 return result; 44} 45 46LinePrinter::LinePrinter() : have_blank_line_(true) { 47#ifndef _WIN32 48 const char* term = getenv("TERM"); 49 smart_terminal_ = unix_isatty(1) && term && string(term) != "dumb"; 50#else 51 // Disable output buffer. It'd be nice to use line buffering but 52 // MSDN says: "For some systems, [_IOLBF] provides line 53 // buffering. However, for Win32, the behavior is the same as _IOFBF 54 // - Full Buffering." 55 setvbuf(stdout, NULL, _IONBF, 0); 56 console_ = GetStdHandle(STD_OUTPUT_HANDLE); 57 CONSOLE_SCREEN_BUFFER_INFO csbi; 58 smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi); 59#endif 60} 61 62static void Out(const std::string& s) { 63 // Avoid printf and C strings, since the actual output might contain null 64 // bytes like UTF-16 does (yuck). 65 fwrite(s.data(), 1, s.size(), stdout); 66} 67 68void LinePrinter::Print(string to_print, LineType type) { 69 if (!smart_terminal_) { 70 Out(to_print + "\n"); 71 return; 72 } 73 74 // Print over previous line, if any. 75 // On Windows, calling a C library function writing to stdout also handles 76 // pausing the executable when the "Pause" key or Ctrl-S is pressed. 77 printf("\r"); 78 79 if (type == INFO) { 80#ifdef _WIN32 81 CONSOLE_SCREEN_BUFFER_INFO csbi; 82 GetConsoleScreenBufferInfo(console_, &csbi); 83 84 // TODO: std::wstring to_print_wide; if (!android::base::UTF8ToWide(to_print, &to_print_wide)... 85 // TODO: wstring ElideMiddle. 86 to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X)); 87 // We don't want to have the cursor spamming back and forth, so instead of 88 // printf use WriteConsoleOutput which updates the contents of the buffer, 89 // but doesn't move the cursor position. 90 COORD buf_size = { csbi.dwSize.X, 1 }; 91 COORD zero_zero = { 0, 0 }; 92 SMALL_RECT target = { 93 csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y, 94 static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1), 95 csbi.dwCursorPosition.Y 96 }; 97 vector<CHAR_INFO> char_data(csbi.dwSize.X); 98 for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) { 99 // TODO: UnicodeChar instead of AsciiChar, to_print_wide[i]. 100 char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' '; 101 char_data[i].Attributes = csbi.wAttributes; 102 } 103 // TODO: WriteConsoleOutputW. 104 WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target); 105#else 106 // Limit output to width of the terminal if provided so we don't cause 107 // line-wrapping. 108 winsize size; 109 if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) { 110 to_print = ElideMiddle(to_print, size.ws_col); 111 } 112 Out(to_print); 113 printf("\x1B[K"); // Clear to end of line. 114 fflush(stdout); 115#endif 116 117 have_blank_line_ = false; 118 } else { 119 Out(to_print); 120 Out("\n"); 121 have_blank_line_ = true; 122 } 123} 124 125void LinePrinter::KeepInfoLine() { 126 if (!have_blank_line_) Out("\n"); 127 have_blank_line_ = true; 128} 129