C++ Utils  0.1
utils/progress.h
Go to the documentation of this file.
00001 
00036 #ifndef UTILS_PROGRESS_H
00037 #define UTILS_PROGRESS_H
00038 
00039 #include "utils/env.h"
00040 #include "utils/logger.h"
00041 #include "utils/stringutils.h"
00042 
00043 #include <fstream>
00044 #include <iomanip>
00045 #include <iostream>
00046 #include <string>
00047 #include <unistd.h>
00048 #include <sys/ioctl.h>
00049 
00050 namespace utils
00051 {
00052 
00053 // TODO replace this with constexpr in C++11
00054 #define ROTATION_IND "-\\|/"
00055 
00056 class Progress
00057 {
00058 private:
00059         enum OutputType
00060         {
00061                 DISABLED,
00062                 TTY
00063                 // TODO Support file output
00064         };
00065 
00066 private:
00068         std::ostream *m_output;
00069 
00070         OutputType m_type;
00071 
00073         unsigned long m_total;
00074 
00076         unsigned long m_current;
00077 
00079         unsigned long m_barSize;
00080 
00082         unsigned char m_rotPosition;
00083 
00085         std::ofstream m_tty;
00086 
00087 public:
00088         Progress(unsigned long total = 100)
00089                 : m_current(0), m_barSize(80) /* Default size */, m_rotPosition(0)
00090         {
00091                 std::string envOutput = Env::get<std::string>("UTILS_PROGRESS_OUTPUT", "STDERR");
00092 
00093                 StringUtils::toUpper(envOutput);
00094 
00095                 if (envOutput == "STDOUT") {
00096                         m_output = &std::cout;
00097                         m_type = TTY;
00098 
00099                         setSize(isatty(fileno(stdout)));
00100                 } else if (envOutput == "STDERR") {
00101                         m_output = &std::cerr;
00102                         m_type = TTY;
00103 
00104                         setSize(isatty(fileno(stderr)));
00105                 } else if (envOutput == "TTY") {
00106                         m_tty.open("/dev/tty"); // try unix
00107                         if (!m_tty)
00108                                 m_tty.open("CON:"); // try windows
00109 
00110                         if (m_tty) {
00111                                 m_output = &m_tty;
00112                                 m_type = TTY;
00113                                 setSize();
00114                         } else {
00115                                 logWarning() << "Could not open terminal. Disabling progress bar.";
00116                                 m_type = DISABLED;
00117                         }
00118                 } else {
00119                         m_type = DISABLED;
00120                 }
00121         }
00122 
00127         void setTotal(unsigned long total)
00128         {
00129                 m_total = total;
00130         }
00131 
00135         void set(unsigned long current)
00136         {
00137                 m_current = std::min(current, m_total);
00138         }
00139 
00146         void update(unsigned long current)
00147         {
00148                 set(current);
00149 
00150                 if (m_type == DISABLED)
00151                         return;
00152 
00153                 // Calculuate the ratio of complete-to-incomplete.
00154                 const float ratio = m_current/static_cast<float>(m_total);
00155 
00156                 // Show the percentage complete
00157                 (*m_output) << std::setw(3) << (int)(ratio*100) << "% [";
00158 
00159                 // real width (without additional chars)
00160                 const unsigned long realSize = m_barSize - 9;
00161 
00162                 const unsigned long comChars = realSize * ratio;
00163 
00164                 // Show the load bar
00165                 for (unsigned int i = 0; i < comChars; i++)
00166                         (*m_output) << '=';
00167 
00168                 for (unsigned int i = comChars; i < realSize; i++)
00169                         (*m_output) << ' ';
00170 
00171                 (*m_output) << "] ";
00172 
00173                 // Print rotation indicator
00174                 (*m_output) << ROTATION_IND[m_rotPosition];
00175                 m_rotPosition = (m_rotPosition+1) % 4;
00176 
00177                 // go to the beginning of the line
00178                 (*m_output) << '\r' << std::flush;
00179         }
00180 
00184         void update()
00185         {
00186                 update(m_current);
00187         }
00188 
00192         void increment()
00193         {
00194                 update(m_current + 1);
00195         }
00196 
00200         void clear()
00201         {
00202                 if (m_type == DISABLED)
00203                         return;
00204 
00205                 for (unsigned int i = 0; i < m_barSize; i++)
00206                         (*m_output) << ' ';
00207                 (*m_output) << '\r' << std::flush;
00208         }
00209 
00210 private:
00214         void setSize(bool automatic = true)
00215         {
00216                 // Check if size is set in env
00217                 unsigned long size = Env::get<unsigned long>("UTILS_PROGRESS_SIZE", 0);
00218 
00219                 if (size > 0) {
00220                         m_barSize = size;
00221                         return;
00222                 }
00223 
00224                 if (!automatic)
00225                         // No automatic size detection (e.g. for stdout)
00226                         return;
00227 
00228                 // Try to get terminal size
00229 #ifdef TIOCGSIZE
00230                 struct ttysize ts;
00231                 ioctl(STDIN_FILENO, TIOCGSIZE, &ts);
00232                 m_barSize = ts.ts_cols;
00233 #elif defined(TIOCGWINSZ)
00234                 struct winsize ts;
00235                 ioctl(STDIN_FILENO, TIOCGWINSZ, &ts);
00236                 m_barSize = ts.ws_col;
00237 #else
00238                 logWarning() << "Could not get terminal size, using default";
00239 #endif
00240         }
00241 };
00242 
00243 }
00244 
00245 #endif // UTILS_PROGRESS_H
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Defines