Ninja
line_printer.cc
Go to the documentation of this file.
00001 // Copyright 2013 Google Inc. All Rights Reserved.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //     http://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
00014 
00015 #include "line_printer.h"
00016 
00017 #include <stdio.h>
00018 #include <stdlib.h>
00019 #ifdef _WIN32
00020 #include <windows.h>
00021 #else
00022 #include <unistd.h>
00023 #include <sys/ioctl.h>
00024 #include <sys/time.h>
00025 #endif
00026 
00027 #include "util.h"
00028 
00029 LinePrinter::LinePrinter() : have_blank_line_(true) {
00030 #ifndef _WIN32
00031   const char* term = getenv("TERM");
00032   smart_terminal_ = isatty(1) && term && string(term) != "dumb";
00033 #else
00034   // Disable output buffer.  It'd be nice to use line buffering but
00035   // MSDN says: "For some systems, [_IOLBF] provides line
00036   // buffering. However, for Win32, the behavior is the same as _IOFBF
00037   // - Full Buffering."
00038   setvbuf(stdout, NULL, _IONBF, 0);
00039   console_ = GetStdHandle(STD_OUTPUT_HANDLE);
00040   CONSOLE_SCREEN_BUFFER_INFO csbi;
00041   smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
00042 #endif
00043 }
00044 
00045 void LinePrinter::Print(string to_print, LineType type) {
00046 #ifdef _WIN32
00047   CONSOLE_SCREEN_BUFFER_INFO csbi;
00048   GetConsoleScreenBufferInfo(console_, &csbi);
00049 #endif
00050 
00051   if (smart_terminal_) {
00052 #ifndef _WIN32
00053     printf("\r");  // Print over previous line, if any.
00054 #else
00055     csbi.dwCursorPosition.X = 0;
00056     SetConsoleCursorPosition(console_, csbi.dwCursorPosition);
00057 #endif
00058   }
00059 
00060   if (smart_terminal_ && type == ELIDE) {
00061 #ifdef _WIN32
00062     // Don't use the full width or console will move to next line.
00063     size_t width = static_cast<size_t>(csbi.dwSize.X) - 1;
00064     to_print = ElideMiddle(to_print, width);
00065     // We don't want to have the cursor spamming back and forth, so
00066     // use WriteConsoleOutput instead which updates the contents of
00067     // the buffer, but doesn't move the cursor position.
00068     GetConsoleScreenBufferInfo(console_, &csbi);
00069     COORD buf_size = { csbi.dwSize.X, 1 };
00070     COORD zero_zero = { 0, 0 };
00071     SMALL_RECT target = {
00072       csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,
00073       static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1),
00074       csbi.dwCursorPosition.Y
00075     };
00076     CHAR_INFO* char_data = new CHAR_INFO[csbi.dwSize.X];
00077     memset(char_data, 0, sizeof(CHAR_INFO) * csbi.dwSize.X);
00078     for (int i = 0; i < csbi.dwSize.X; ++i) {
00079       char_data[i].Char.AsciiChar = ' ';
00080       char_data[i].Attributes = csbi.wAttributes;
00081     }
00082     for (size_t i = 0; i < to_print.size(); ++i)
00083       char_data[i].Char.AsciiChar = to_print[i];
00084     WriteConsoleOutput(console_, char_data, buf_size, zero_zero, &target);
00085     delete[] char_data;
00086 #else
00087     // Limit output to width of the terminal if provided so we don't cause
00088     // line-wrapping.
00089     winsize size;
00090     if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) {
00091       to_print = ElideMiddle(to_print, size.ws_col);
00092     }
00093     printf("%s", to_print.c_str());
00094     printf("\x1B[K");  // Clear to end of line.
00095     fflush(stdout);
00096 #endif
00097 
00098     have_blank_line_ = false;
00099   } else {
00100     printf("%s\n", to_print.c_str());
00101   }
00102 }
00103 
00104 void LinePrinter::PrintOnNewLine(const string& to_print) {
00105   if (!have_blank_line_)
00106     printf("\n");
00107   printf("%s", to_print.c_str());
00108   have_blank_line_ = to_print.empty() || *to_print.rbegin() == '\n';
00109 }