Ninja
line_printer.cc
Go to the documentation of this file.
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 <sys/time.h>
25 #endif
26 
27 #include "util.h"
28 
29 LinePrinter::LinePrinter() : have_blank_line_(true) {
30 #ifndef _WIN32
31  const char* term = getenv("TERM");
32  smart_terminal_ = isatty(1) && term && string(term) != "dumb";
33 #else
34  // Disable output buffer. It'd be nice to use line buffering but
35  // MSDN says: "For some systems, [_IOLBF] provides line
36  // buffering. However, for Win32, the behavior is the same as _IOFBF
37  // - Full Buffering."
38  setvbuf(stdout, NULL, _IONBF, 0);
39  console_ = GetStdHandle(STD_OUTPUT_HANDLE);
40  CONSOLE_SCREEN_BUFFER_INFO csbi;
41  smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
42 #endif
43 }
44 
45 void LinePrinter::Print(string to_print, LineType type) {
46 #ifdef _WIN32
47  CONSOLE_SCREEN_BUFFER_INFO csbi;
48  GetConsoleScreenBufferInfo(console_, &csbi);
49 #endif
50 
51  if (smart_terminal_) {
52 #ifndef _WIN32
53  printf("\r"); // Print over previous line, if any.
54 #else
55  csbi.dwCursorPosition.X = 0;
56  SetConsoleCursorPosition(console_, csbi.dwCursorPosition);
57 #endif
58  }
59 
60  if (smart_terminal_ && type == ELIDE) {
61 #ifdef _WIN32
62  // Don't use the full width or console will move to next line.
63  size_t width = static_cast<size_t>(csbi.dwSize.X) - 1;
64  to_print = ElideMiddle(to_print, width);
65  // We don't want to have the cursor spamming back and forth, so
66  // use WriteConsoleOutput instead which updates the contents of
67  // the buffer, but doesn't move the cursor position.
68  GetConsoleScreenBufferInfo(console_, &csbi);
69  COORD buf_size = { csbi.dwSize.X, 1 };
70  COORD zero_zero = { 0, 0 };
71  SMALL_RECT target = {
72  csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,
73  static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1),
74  csbi.dwCursorPosition.Y
75  };
76  CHAR_INFO* char_data = new CHAR_INFO[csbi.dwSize.X];
77  memset(char_data, 0, sizeof(CHAR_INFO) * csbi.dwSize.X);
78  for (int i = 0; i < csbi.dwSize.X; ++i) {
79  char_data[i].Char.AsciiChar = ' ';
80  char_data[i].Attributes = csbi.wAttributes;
81  }
82  for (size_t i = 0; i < to_print.size(); ++i)
83  char_data[i].Char.AsciiChar = to_print[i];
84  WriteConsoleOutput(console_, char_data, buf_size, zero_zero, &target);
85  delete[] char_data;
86 #else
87  // Limit output to width of the terminal if provided so we don't cause
88  // line-wrapping.
89  winsize size;
90  if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) {
91  to_print = ElideMiddle(to_print, size.ws_col);
92  }
93  printf("%s", to_print.c_str());
94  printf("\x1B[K"); // Clear to end of line.
95  fflush(stdout);
96 #endif
97 
98  have_blank_line_ = false;
99  } else {
100  printf("%s\n", to_print.c_str());
101  }
102 }
103 
104 void LinePrinter::PrintOnNewLine(const string& to_print) {
105  if (!have_blank_line_)
106  printf("\n");
107  printf("%s", to_print.c_str());
108  have_blank_line_ = to_print.empty() || *to_print.rbegin() == '\n';
109 }
bool have_blank_line_
Whether the caret is at the beginning of a blank line.
Definition: line_printer.h:45
void PrintOnNewLine(const string &to_print)
Prints a string on a new line, not overprinting previous output.
void Print(string to_print, LineType type)
Overprints the current line.
Definition: line_printer.cc:45
bool smart_terminal_
Whether we can do fancy terminal control codes.
Definition: line_printer.h:42
string ElideMiddle(const string &str, size_t width)
Elide the given string str with '...' in the middle if the length exceeds width.
Definition: util.cc:351