/* colortail -- output last part of file(s) in color. Copyright (C) 1999 Joakim Andersson This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "TailFile.h" TailFile::TailFile() { m_filename = NULL; m_file = NULL; m_colorizer = NULL; m_position = 0; m_follow_buffer = NULL; } TailFile::~TailFile() { if (m_filename) { delete m_filename; } if (m_file) { fclose(m_file); } if (m_colorizer) { delete m_colorizer; } if (m_follow_buffer) { delete m_follow_buffer; } } int TailFile::open(char *filename, Colorizer *colorizer) { // Opens the file to tail. And sets the colorizer // Prints error message if failed to open file. // returns: 0 - success // 1 - failure if (filename == NULL) { cout << "colortail: Filename was NULL" << endl; return 1; } else { // save filename m_filename = new char[strlen(filename) + 1]; strcpy(m_filename, filename); // tries to open the file m_file = fopen(filename, "r"); if (m_file == NULL) { // open failed cout << "colortail: Failed to open file: " << filename << endl; return 1; } // set the colorizer m_colorizer = colorizer; // set the saved stream position used to see if the file has // changed size to the end of the file m_position = end_of_file_position(); } return 0; } void TailFile::print(int n) { // print and colorize last n rows of the file // is a file open if (m_file == NULL) { // no file open return; } find_position(n); // const bufSize = 1024; char buf[MAX_CHARS_READ]; // print file int loop = 1; while (loop) { // empty buffer // not nessesary?? for (int i = 0 ; i < MAX_CHARS_READ ; i++) { buf[i] = '\0'; } // read line char *ret = fgets(buf, MAX_CHARS_READ, m_file); // read more than zero chars if (ret != NULL) { // print the line print_to_stdout(buf); } else { // eof // stop looping loop = 0; } } } void TailFile::printFilename() { // prints the filename to stdout cout << "==> " << m_filename << " <==" << endl; } char* TailFile::get_filename() { // returns the filename return m_filename; } void TailFile::printAll() { // print the complete file, for debugging // if (!m_inFile) // { // return; // } const int bufSize = 1024; char buf[bufSize]; // while (m_inFile >> setw(bufSize) >> buf) // { // cout << buf << endl; // } } void TailFile::find_position(int n) { // sets the file to the position after the n:th return // from the end of the file // check if file is not open if (m_file == NULL) { // no file open return; } // check if zero rows if (n == 0) { // zero rows // set position to end of file fseek(m_file, 0, SEEK_END); return; } const int bufSize (2048); // buffer for chars char buf[bufSize]; // bytes to read int bytesToRead = bufSize; // position in file from beginning long pos = 0; // go to end of file fseek(m_file, 0, SEEK_END); // set pos to last position pos = ftell(m_file); while (pos != 0) { // sub bufSize-1 from position pos -= (bufSize-1); // check if pos < 0 if (pos < 0) { // pos below zero // calc new bytesToRead (pos is < 0) bytesToRead += pos; // pos below zero, set pos to zero pos = 0; } // goto that position fseek(m_file, pos, SEEK_SET); // read bytesToRead bytes from the file // note: use fgetc, because fgets stops at newlines for (int j = 0 ; j < bytesToRead-1 ; j++) { buf[j] = fgetc(m_file); } // null terminate the string buf[bytesToRead-1] = '\0'; // search through the character buffer for (int i = bytesToRead-1 ; i >= 0 ; i--) { // is it a CR if (buf[i] == '\n') { // found return // decrease n n--; // enough returns if (n < 0) { // goto position after the return fseek(m_file, pos + i + 1, SEEK_SET); return; } } } } // if execution gets here, then all the returns wasn't found // set position to beginning of file fseek(m_file, 0, SEEK_SET); } long TailFile::end_of_file_position() { // returns the position for the end of the current file ie // the size of the file. // doesn't modify the current position // check if file isn't open if (m_file == NULL) { // no file open, return 0 return 0; } /* // save the current position long current = ftell(m_file); // move to end of file fseek(m_file, 0, SEEK_END); // save this position long end = ftell(m_file); // set the saved position fseek(m_file, current, SEEK_SET); */ struct stat file_stats; // get the stats for the current file int ret = fstat(fileno(m_file), &file_stats); if (ret != 0) { // some error, return 0 return 0; } // DEBUG //cout << "File size: " << file_stats.st_size << endl; // ENDDEBUG // return the size return file_stats.st_size; } int TailFile::more_to_read() { // returns the number of bytes that can be read from the file // or 0 if nothing has been added to the file // updates the saved position if the file has been truncated. // RETURNS: number of bytes or 0 // check if no file open if (m_file == NULL) { // no file open, return 0 return 0; } // get the end of file position long end = end_of_file_position(); // DEBUG // cout << "size: " << end << endl; // cout << "current pos: " << m_position << endl; // ENDDEBUG // check if file is truncated if (end < m_position) { // yes, truncated // set the saved position to the new (lesser) stream position m_position = end; // nothing added to the file, just removed return 0; } // return the differance between the saved position and the end position //return end - m_position; // TEST // if ((end - m_position) == 0) // { // return 0; // } // else // { // return end - m_position - 1; // } return end - m_position; } void TailFile::follow_print(int n, int verbose, char *last_filename) { // Reads n characters from the file and search from the beginning after a // '\n'. If it is found that line is colorized and printed, and the // current stream position is uppdated. If a line is found and the // verbose flag is set. The filename is printed in ==> <== :s if it // isn't the same file as the last line was printed from. // If it isn't found, nothing is printed and the stream position // isn't changed. // check if a file isn't open if (m_file == NULL) { // no file open // just return return; } // check if there isn't a follow buffer if (m_follow_buffer == NULL) { m_follow_buffer = new std::ostrstream(); } // make buffer //char *buf = new char[n + 1]; // DEBUG //for (int b = 0 ; b < (n + 1) ; b++) //{ // buf[b] = '\0'; //} // ENDDEBUG // check allocation //assert (buf != NULL); // read n characters // for (int j = 0 ; j < n ; j++) // { // // DEBUG // cout << "current position: " << ftell(m_file) << endl; // // ENDDEBUG // buf[j] = fgetc(m_file); // // DEBUG // cout << "read[" << j << "]: " << (int)buf[j] << endl; // // ENDDEBUG // } // // null terminate the string // buf[n] = '\0'; // DEBUG //clearerr(m_file); // ENDDEBUG // read n characters //char *ret = fgets(buf, (n + 1), m_file); for (int i = 0 ; i < (n-1) ; i++) { int ch = fgetc(m_file); // add the character to the string if (ch == EOF) clearerr(m_file); // required by POSIX and ANSI else m_follow_buffer->put(ch); // check if return if (ch == '\n') { // a return // DEBUG // cout << "found a return" << endl; // ENDDEBUG // chack if the verbose flag is set if (verbose) { // check if last_filename is NULL if (last_filename == NULL) { // print the name of this file printFilename(); } else { // last_filename isn't NULL // check if same filename if (strcmp(last_filename, m_filename) != 0) { // not same file // print the name of this file printFilename(); } } } // null terminate the string m_follow_buffer->put('\0'); // get the string char *str = m_follow_buffer->str(); // print the line print_to_stdout(str); // free the mem delete str; delete m_follow_buffer; m_follow_buffer = NULL; // update the saved stream position with the no of characters // until and including the '\n' //m_position += i; m_position = ftell(m_file); // break the loop break; } } // // DEBUG // cout << "Return value: " << ret << endl; // cout << "string read: " << buf << endl; // // ENDDEBUG // // // did we read anything? // if (0) //// if (buf[0] != '\0') // { // // search from the beginning of the string for a '\n' // int i = 0, notfound = 1; // while (notfound && i < n) // { // if (buf[i] == '\n') // { // // found a return, we've got a line to print // // // DEBUG // cout << "Found a return at " << i << endl; // // // ENDDEBUG // // // include the return in the string // i++; // // // chack if the verbose flag is set // if (verbose) // { // // check if last_filename is NULL // if (last_filename == NULL) // { // // print the name of this file // printFilename(); // } // else // { // // last_filename isn't NULL // // check if same filename // if (strcmp(last_filename, m_filename) != 0) // { // // not same file // // print the name of this file // printFilename(); // } // } // } // // // make temporary buffer // char *buf2 = new char[i+1]; // strncpy(buf2, buf, i); // // null-terminate the string // buf2[i] = '\0'; // // // print the line // print_to_stdout(buf2); // // // free the mem // delete buf2; // // // update the saved stream position with the no of characters // // until and including the '\n' // m_position += i; // // set the file to this position // fseek(m_file, m_position, SEEK_SET); // // // break the loop // notfound = 0; // } // i++; // } // // no '\n' found? // if (notfound) // { // // reset the file's position to the saved position // fseek(m_file, m_position, SEEK_SET); // } // } // // free mem // delete buf; } void TailFile::print_to_stdout(char *str) { // checks if there is a colorizer. If so the string is colorized // before it's printed. If not the string isn't colorized. // check that str is something if (str == NULL) { return; } // check if we have a colorizer if (m_colorizer) { // colorize the string char *buf = m_colorizer->colorize(str); // print the new colorized string cout << buf; // free the mem delete buf; } else { // don't colorize // print the line cout << str; } }