1 /**
2  * Copyright (c) 2007-2012, Timothy Stack
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * * Redistributions of source code must retain the above copyright notice, this
10  * list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  * * Neither the name of Timothy Stack nor the names of its contributors
15  * may be used to endorse or promote products derived from this software
16  * without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  * @file logfile_sub_source.hh
30  */
31 
32 #ifndef logfile_sub_source_hh
33 #define logfile_sub_source_hh
34 
35 #include <limits.h>
36 
37 #include <map>
38 #include <list>
39 #include <array>
40 #include <sstream>
41 #include <utility>
42 #include <vector>
43 
44 #include "base/lnav_log.hh"
45 #include "base/time_util.hh"
46 #include "log_accel.hh"
47 #include "strong_int.hh"
48 #include "logfile.hh"
49 #include "bookmarks.hh"
50 #include "big_array.hh"
51 #include "textview_curses.hh"
52 #include "filter_observer.hh"
53 #include "log_format.hh"
54 
55 STRONG_INT_TYPE(uint64_t, content_line);
56 
57 struct sqlite3_stmt;
58 extern "C" {
59 int sqlite3_finalize(sqlite3_stmt *pStmt);
60 }
61 
62 class logfile_sub_source;
63 
64 class index_delegate {
65 public:
66     virtual ~index_delegate() = default;
67 
index_start(logfile_sub_source & lss)68     virtual void index_start(logfile_sub_source &lss) {
69 
70     };
71 
index_line(logfile_sub_source & lss,logfile * lf,logfile::iterator ll)72     virtual void index_line(logfile_sub_source &lss, logfile *lf, logfile::iterator ll) {
73 
74     };
75 
index_complete(logfile_sub_source & lss)76     virtual void index_complete(logfile_sub_source &lss) {
77 
78     };
79 };
80 
81 class pcre_filter
82     : public text_filter {
83 public:
pcre_filter(type_t type,const std::string & id,size_t index,pcre * code)84     pcre_filter(type_t type, const std::string& id, size_t index, pcre *code)
85         : text_filter(type, filter_lang_t::REGEX, id, index),
86           pf_pcre(code) { };
87 
88     ~pcre_filter() override = default;
89 
matches(const logfile & lf,logfile::const_iterator ll,shared_buffer_ref & line)90     bool matches(const logfile &lf, logfile::const_iterator ll, shared_buffer_ref &line) override {
91         pcre_context_static<30> pc;
92         pcre_input pi(line.get_data(), 0, line.length());
93 
94         return this->pf_pcre.match(pc, pi);
95     };
96 
to_command()97     std::string to_command() override {
98         return (this->lf_type == text_filter::INCLUDE ?
99                 "filter-in " : "filter-out ") +
100                this->lf_id;
101     };
102 
103 protected:
104     pcrepp pf_pcre;
105 };
106 
107 class sql_filter : public text_filter {
108 public:
sql_filter(logfile_sub_source & lss,std::string stmt_str,sqlite3_stmt * stmt)109     sql_filter(logfile_sub_source& lss, std::string stmt_str, sqlite3_stmt *stmt)
110         : text_filter(EXCLUDE, filter_lang_t::SQL, std::move(stmt_str), 0),
111           sf_log_source(lss) {
112         this->sf_filter_stmt = stmt;
113     }
114 
115     bool matches(const logfile &lf, logfile::const_iterator ll, shared_buffer_ref &line) override;
116 
117     std::string to_command() override;
118 
119     auto_mem<sqlite3_stmt> sf_filter_stmt{sqlite3_finalize};
120     logfile_sub_source& sf_log_source;
121 };
122 
123 class log_location_history : public location_history {
124 public:
log_location_history(logfile_sub_source & lss)125     explicit log_location_history(logfile_sub_source &lss)
126         : llh_history(std::begin(this->llh_backing),
127                       std::end(this->llh_backing)),
128           llh_log_source(lss) {
129     }
130 
131     ~log_location_history() override = default;
132 
133     void loc_history_append(vis_line_t top) override;
134 
135     nonstd::optional<vis_line_t>
136     loc_history_back(vis_line_t current_top) override;
137 
138     nonstd::optional<vis_line_t>
139     loc_history_forward(vis_line_t current_top) override;
140 
141 private:
142     nonstd::ring_span<content_line_t> llh_history;
143     logfile_sub_source &llh_log_source;
144     content_line_t llh_backing[MAX_SIZE];
145 };
146 
147 /**
148  * Delegate class that merges the contents of multiple log files into a single
149  * source of data for a text view.
150  */
151 class logfile_sub_source
152     : public text_sub_source,
153       public text_time_translator,
154       public list_input_delegate {
155 public:
156 
157     static bookmark_type_t BM_ERRORS;
158     static bookmark_type_t BM_WARNINGS;
159     static bookmark_type_t BM_FILES;
160 
161     virtual void text_filters_changed();
162 
163     logfile_sub_source();
164 
toggle_time_offset()165     void toggle_time_offset() {
166         this->lss_flags ^= F_TIME_OFFSET;
167         this->clear_line_size_cache();
168     };
169 
increase_line_context()170     void increase_line_context() {
171         auto old_flags = this->lss_flags;
172 
173         if (this->lss_flags & F_FILENAME) {
174             // Nothing to do
175         } else if (this->lss_flags & F_BASENAME) {
176             this->lss_flags &= ~F_NAME_MASK;
177             this->lss_flags |= F_FILENAME;
178         } else {
179             this->lss_flags |= F_BASENAME;
180         }
181         if (old_flags != this->lss_flags) {
182             this->clear_line_size_cache();
183         }
184     };
185 
decrease_line_context()186     bool decrease_line_context() {
187         auto old_flags = this->lss_flags;
188 
189         if (this->lss_flags & F_FILENAME) {
190             this->lss_flags &= ~F_NAME_MASK;
191             this->lss_flags |= F_BASENAME;
192         } else if (this->lss_flags & F_BASENAME) {
193             this->lss_flags &= ~F_NAME_MASK;
194         }
195         if (old_flags != this->lss_flags) {
196             this->clear_line_size_cache();
197 
198             return true;
199         }
200 
201         return false;
202     };
203 
get_filename_offset() const204     size_t get_filename_offset() const {
205         if (this->lss_flags & F_FILENAME) {
206             return this->lss_filename_width;
207         } else if (this->lss_flags & F_BASENAME) {
208             return this->lss_basename_width;
209         }
210 
211         return 0;
212     }
213 
set_time_offset(bool enabled)214     void set_time_offset(bool enabled) {
215         if (enabled)
216             this->lss_flags |= F_TIME_OFFSET;
217         else
218             this->lss_flags &= ~F_TIME_OFFSET;
219         this->clear_line_size_cache();
220     };
221 
is_time_offset_enabled() const222     bool is_time_offset_enabled() const {
223         return (bool) (this->lss_flags & F_TIME_OFFSET);
224     };
225 
is_filename_enabled() const226     bool is_filename_enabled() const {
227         return (bool) (this->lss_flags & F_FILENAME);
228     };
229 
is_basename_enabled() const230     bool is_basename_enabled() const {
231         return (bool) (this->lss_flags & F_BASENAME);
232     };
233 
get_min_log_level() const234     log_level_t get_min_log_level() const {
235         return this->lss_min_log_level;
236     };
237 
set_force_rebuild()238     void set_force_rebuild() {
239         this->lss_force_rebuild = true;
240     }
241 
set_min_log_level(log_level_t level)242     void set_min_log_level(log_level_t level) {
243         if (this->lss_min_log_level != level) {
244             this->lss_min_log_level = level;
245             this->text_filters_changed();
246         }
247     };
248 
get_min_log_time(struct timeval & tv_out) const249     bool get_min_log_time(struct timeval &tv_out) const {
250         tv_out = this->lss_min_log_time;
251         return (this->lss_min_log_time.tv_sec != 0 ||
252                 this->lss_min_log_time.tv_usec != 0);
253     };
254 
set_min_log_time(const struct timeval & tv)255     void set_min_log_time(const struct timeval &tv) {
256         if (this->lss_min_log_time != tv) {
257             this->lss_min_log_time = tv;
258             this->text_filters_changed();
259         }
260     };
261 
get_max_log_time(struct timeval & tv_out) const262     bool get_max_log_time(struct timeval &tv_out) const {
263         tv_out = this->lss_max_log_time;
264         return (this->lss_max_log_time.tv_sec != std::numeric_limits<time_t>::max() ||
265                 this->lss_max_log_time.tv_usec != 0);
266     };
267 
set_max_log_time(struct timeval & tv)268     void set_max_log_time(struct timeval &tv) {
269         if (this->lss_max_log_time != tv) {
270             this->lss_max_log_time = tv;
271             this->text_filters_changed();
272         }
273     };
274 
clear_min_max_log_times()275     void clear_min_max_log_times() {
276         if (this->lss_min_log_time.tv_sec != 0 ||
277             this->lss_min_log_time.tv_usec != 0 ||
278             this->lss_max_log_time.tv_sec != std::numeric_limits<time_t>::max() ||
279             this->lss_max_log_time.tv_usec != 0) {
280             memset(&this->lss_min_log_time, 0, sizeof(this->lss_min_log_time));
281             this->lss_max_log_time.tv_sec = std::numeric_limits<time_t>::max();
282             this->lss_max_log_time.tv_usec = 0;
283             this->text_filters_changed();
284         }
285     };
286 
287     bool list_input_handle_key(listview_curses &lv, int ch);
288 
set_marked_only(bool val)289     void set_marked_only(bool val) {
290         if (this->lss_marked_only != val) {
291             this->lss_marked_only = val;
292             this->text_filters_changed();
293         }
294     };
295 
get_marked_only()296     bool get_marked_only() {
297         return this->lss_marked_only;
298     };
299 
text_line_count()300     size_t text_line_count()
301     {
302         return this->lss_filtered_index.size();
303     };
304 
text_line_width(textview_curses & curses)305     size_t text_line_width(textview_curses &curses) {
306         return this->lss_longest_line;
307     };
308 
file_count() const309     size_t file_count() const {
310         size_t retval = 0;
311         const_iterator iter;
312 
313         for (iter = this->cbegin(); iter != this->cend(); ++iter) {
314             if (*iter != NULL && (*iter)->get_file() != NULL) {
315                 retval += 1;
316             }
317         }
318 
319         return retval;
320     }
321 
empty() const322     bool empty() const { return this->lss_filtered_index.empty(); };
323 
324     void text_value_for_line(textview_curses &tc,
325                              int row,
326                              std::string &value_out,
327                              line_flags_t flags);
328 
329     void text_attrs_for_line(textview_curses &tc,
330                              int row,
331                              string_attrs_t &value_out);
332 
text_size_for_line(textview_curses & tc,int row,line_flags_t flags)333     size_t text_size_for_line(textview_curses &tc, int row, line_flags_t flags) {
334         size_t index = row % LINE_SIZE_CACHE_SIZE;
335 
336         if (this->lss_line_size_cache[index].first != row) {
337             std::string value;
338 
339             this->text_value_for_line(tc, row, value, flags);
340             this->lss_line_size_cache[index].second = value.size();
341             this->lss_line_size_cache[index].first = row;
342         }
343         return this->lss_line_size_cache[index].second;
344     };
345 
text_mark(bookmark_type_t * bm,vis_line_t line,bool added)346     void text_mark(bookmark_type_t *bm, vis_line_t line, bool added)
347     {
348         if (line >= (int) this->lss_index.size()) {
349             return;
350         }
351 
352         content_line_t cl = this->at(line);
353         std::vector<content_line_t>::iterator lb;
354 
355         if (bm == &textview_curses::BM_USER) {
356             logline *ll = this->find_line(cl);
357 
358             ll->set_mark(added);
359         }
360         lb = std::lower_bound(this->lss_user_marks[bm].begin(),
361                               this->lss_user_marks[bm].end(),
362                               cl);
363         if (added) {
364             if (lb == this->lss_user_marks[bm].end() || *lb != cl) {
365                 this->lss_user_marks[bm].insert(lb, cl);
366             }
367         }
368         else if (lb != this->lss_user_marks[bm].end() && *lb == cl) {
369             require(lb != this->lss_user_marks[bm].end());
370 
371             this->lss_user_marks[bm].erase(lb);
372         }
373         if (bm == &textview_curses::BM_META &&
374             this->lss_meta_grepper.gps_proc != nullptr) {
375             this->tss_view->search_range(line, line + 1_vl);
376             this->tss_view->search_new_data();
377         }
378     };
379 
text_clear_marks(bookmark_type_t * bm)380     void text_clear_marks(bookmark_type_t *bm)
381     {
382         std::vector<content_line_t>::iterator iter;
383 
384         if (bm == &textview_curses::BM_USER) {
385             for (iter = this->lss_user_marks[bm].begin();
386                  iter != this->lss_user_marks[bm].end();) {
387                 auto bm_iter = this->lss_user_mark_metadata.find(*iter);
388                 if (bm_iter != this->lss_user_mark_metadata.end()) {
389                     ++iter;
390                     continue;
391                 }
392                 this->find_line(*iter)->set_mark(false);
393                 iter = this->lss_user_marks[bm].erase(iter);
394             }
395         } else {
396             this->lss_user_marks[bm].clear();
397         }
398     };
399 
400     bool insert_file(const std::shared_ptr<logfile>& lf);
401 
remove_file(std::shared_ptr<logfile> lf)402     void remove_file(std::shared_ptr<logfile> lf)
403     {
404         iterator iter;
405 
406         iter = std::find_if(this->lss_files.begin(),
407                             this->lss_files.end(),
408                             logfile_data_eq(lf));
409         if (iter != this->lss_files.end()) {
410             bookmarks<content_line_t>::type::iterator mark_iter;
411             int file_index = iter - this->lss_files.begin();
412 
413             (*iter)->clear();
414             for (mark_iter = this->lss_user_marks.begin();
415                  mark_iter != this->lss_user_marks.end();
416                  ++mark_iter) {
417                 content_line_t mark_curr = content_line_t(
418                     file_index * MAX_LINES_PER_FILE);
419                 content_line_t mark_end = content_line_t(
420                     (file_index + 1) * MAX_LINES_PER_FILE);
421                 bookmark_vector<content_line_t>::iterator bv_iter;
422                 bookmark_vector<content_line_t> &         bv =
423                     mark_iter->second;
424 
425                 while ((bv_iter =
426                             std::lower_bound(bv.begin(), bv.end(),
427                                              mark_curr)) != bv.end()) {
428                     if (*bv_iter >= mark_end) {
429                         break;
430                     }
431                     mark_iter->second.erase(bv_iter);
432                 }
433             }
434 
435             this->lss_force_rebuild = true;
436         }
437     };
438 
439     enum class rebuild_result {
440         rr_no_change,
441         rr_appended_lines,
442         rr_partial_rebuild,
443         rr_full_rebuild,
444     };
445 
446     rebuild_result rebuild_index(nonstd::optional<ui_clock::time_point> deadline = nonstd::nullopt);
447 
448     void text_update_marks(vis_bookmarks &bm);
449 
set_user_mark(bookmark_type_t * bm,content_line_t cl)450     void set_user_mark(bookmark_type_t *bm, content_line_t cl)
451     {
452         this->lss_user_marks[bm].insert_once(cl);
453     };
454 
get_user_bookmarks()455     bookmarks<content_line_t>::type &get_user_bookmarks()
456     {
457         return this->lss_user_marks;
458     };
459 
get_user_bookmark_metadata()460     std::map<content_line_t, bookmark_metadata> &get_user_bookmark_metadata() {
461         return this->lss_user_mark_metadata;
462     };
463 
get_filtered_count() const464     int get_filtered_count() const {
465         return this->lss_index.size() - this->lss_filtered_index.size();
466     };
467 
get_filtered_count_for(size_t filter_index) const468     int get_filtered_count_for(size_t filter_index) const {
469         int retval = 0;
470 
471         for (auto& ld : this->lss_files) {
472             retval += ld->ld_filter_state.lfo_filter_state.tfs_filter_hits[filter_index];
473         }
474 
475         return retval;
476     }
477 
478     Result<void, std::string> set_sql_filter(std::string stmt_str, sqlite3_stmt *stmt);
479 
480     Result<void, std::string> set_sql_marker(std::string stmt_str, sqlite3_stmt *stmt);
481 
482     Result<void, std::string> set_preview_sql_filter(sqlite3_stmt *stmt);
483 
get_sql_filter_text()484     std::string get_sql_filter_text() {
485         auto filt = this->get_sql_filter();
486 
487         if (filt) {
488             return filt.value()->get_id();
489         }
490         return "";
491     }
492 
get_sql_marker_text() const493     std::string get_sql_marker_text() const {
494         return this->lss_marker_stmt_text;
495     }
496 
497     std::shared_ptr<logfile> find(const char *fn, content_line_t &line_base);
498 
find(content_line_t & line) const499     std::shared_ptr<logfile> find(content_line_t &line) const
500     {
501         std::shared_ptr<logfile> retval;
502 
503         retval = this->lss_files[line / MAX_LINES_PER_FILE]->get_file();
504         line   = content_line_t(line % MAX_LINES_PER_FILE);
505 
506         return retval;
507     };
508 
find_file_ptr(content_line_t & line)509     logfile *find_file_ptr(content_line_t &line)
510     {
511         auto retval = this->lss_files[line / MAX_LINES_PER_FILE]->get_file_ptr();
512         line = content_line_t(line % MAX_LINES_PER_FILE);
513 
514         return retval;
515     };
516 
find_line(content_line_t line) const517     logline *find_line(content_line_t line) const
518     {
519         logline *retval = nullptr;
520         std::shared_ptr<logfile> lf = this->find(line);
521 
522         if (lf != nullptr) {
523             auto ll_iter = lf->begin() + line;
524 
525             retval = &(*ll_iter);
526         }
527 
528         return retval;
529     };
530 
531     nonstd::optional<vis_line_t> find_from_time(const struct timeval &start) const;
532 
find_from_time(time_t start) const533     nonstd::optional<vis_line_t> find_from_time(time_t start) const {
534         struct timeval tv = { start, 0 };
535 
536         return this->find_from_time(tv);
537     };
538 
find_from_time(const exttm & etm) const539     nonstd::optional<vis_line_t> find_from_time(const exttm &etm) const {
540         return this->find_from_time(etm.to_timeval());
541     };
542 
find_from_content(content_line_t cl)543     nonstd::optional<vis_line_t> find_from_content(content_line_t cl) {
544         content_line_t line = cl;
545         std::shared_ptr<logfile> lf = this->find(line);
546 
547         if (lf != nullptr) {
548             auto ll_iter = lf->begin() + line;
549             auto &ll = *ll_iter;
550             auto vis_start_opt = this->find_from_time(ll.get_timeval());
551 
552             if (!vis_start_opt) {
553                 return nonstd::nullopt;
554             }
555 
556             auto vis_start = *vis_start_opt;
557 
558             while (vis_start < vis_line_t(this->text_line_count())) {
559                 content_line_t guess_cl = this->at(vis_start);
560 
561                 if (cl == guess_cl) {
562                     return vis_start;
563                 }
564 
565                 auto guess_line = this->find_line(guess_cl);
566 
567                 if (!guess_line || ll < *guess_line) {
568                     return nonstd::nullopt;
569                 }
570 
571                 ++vis_start;
572             }
573         }
574 
575         return nonstd::nullopt;
576     }
577 
time_for_row(vis_line_t row)578     nonstd::optional<struct timeval> time_for_row(vis_line_t row) {
579         if (row < (ssize_t) this->text_line_count()) {
580             return this->find_line(this->at(row))->get_timeval();
581         }
582         return nonstd::nullopt;
583     };
584 
row_for_time(struct timeval time_bucket)585     nonstd::optional<vis_line_t> row_for_time(struct timeval time_bucket) {
586         return this->find_from_time(time_bucket);
587     };
588 
at(vis_line_t vl)589     content_line_t at(vis_line_t vl) {
590         return this->lss_index[this->lss_filtered_index[vl]];
591     };
592 
at_base(vis_line_t vl)593     content_line_t at_base(vis_line_t vl) {
594         while (this->find_line(this->at(vl))->get_sub_offset() != 0) {
595             --vl;
596         }
597 
598         return this->at(vl);
599     };
600 
601     log_accel::direction_t get_line_accel_direction(vis_line_t vl);
602 
603     /**
604      * Container for logfile references that keeps of how many lines in the
605      * logfile have been indexed.
606      */
607     struct logfile_data {
logfile_datalogfile_sub_source::logfile_data608         logfile_data(size_t index, filter_stack &fs, const std::shared_ptr<logfile> &lf)
609             : ld_file_index(index),
610               ld_filter_state(fs, lf),
611               ld_visible(lf->is_indexing()) {
612             lf->set_logline_observer(&this->ld_filter_state);
613         };
614 
clearlogfile_sub_source::logfile_data615         void clear()
616         {
617             this->ld_filter_state.lfo_filter_state.clear();
618         };
619 
set_filelogfile_sub_source::logfile_data620         void set_file(const std::shared_ptr<logfile> &lf) {
621             this->ld_filter_state.lfo_filter_state.tfs_logfile = lf;
622             lf->set_logline_observer(&this->ld_filter_state);
623         };
624 
get_filelogfile_sub_source::logfile_data625         std::shared_ptr<logfile> get_file() const {
626             return this->ld_filter_state.lfo_filter_state.tfs_logfile;
627         };
628 
get_file_ptrlogfile_sub_source::logfile_data629         logfile *get_file_ptr() const {
630             return this->ld_filter_state.lfo_filter_state.tfs_logfile.get();
631         };
632 
is_visiblelogfile_sub_source::logfile_data633         bool is_visible() const {
634             return this->ld_visible;
635         }
636 
set_visibilitylogfile_sub_source::logfile_data637         void set_visibility(bool vis) {
638             this->ld_visible = vis;
639         }
640 
641         size_t ld_file_index;
642         line_filter_observer ld_filter_state;
643         size_t ld_lines_indexed{0};
644         bool ld_visible;
645     };
646 
647     typedef std::vector<std::unique_ptr<logfile_data>>::iterator iterator;
648     typedef std::vector<std::unique_ptr<logfile_data>>::const_iterator const_iterator;
649 
begin()650     iterator begin()
651     {
652         return this->lss_files.begin();
653     };
654 
end()655     iterator end()
656     {
657         return this->lss_files.end();
658     };
659 
cbegin() const660     const_iterator cbegin() const
661     {
662         return this->lss_files.begin();
663     };
664 
cend() const665     const_iterator cend() const
666     {
667         return this->lss_files.end();
668     };
669 
find_data(content_line_t & line)670     iterator find_data(content_line_t &line)
671     {
672         auto retval = this->lss_files.begin();
673         std::advance(retval, line / MAX_LINES_PER_FILE);
674         line = content_line_t(line % MAX_LINES_PER_FILE);
675 
676         return retval;
677     };
678 
find_data(content_line_t line,uint64_t & offset_out)679     iterator find_data(content_line_t line, uint64_t &offset_out)
680     {
681         auto retval = this->lss_files.begin();
682         std::advance(retval, line / MAX_LINES_PER_FILE);
683         offset_out = line % MAX_LINES_PER_FILE;
684 
685         return retval;
686     };
687 
find_data(const std::shared_ptr<logfile> & lf)688     nonstd::optional<logfile_data *> find_data(const std::shared_ptr<logfile>& lf) {
689         for (auto& ld : *this) {
690             if (ld->ld_filter_state.lfo_filter_state.tfs_logfile == lf) {
691                 return ld.get();
692             }
693         }
694         return nonstd::nullopt;
695     }
696 
find_data_i(const std::shared_ptr<const logfile> & lf)697     iterator find_data_i(const std::shared_ptr<const logfile>& lf) {
698         for (auto iter = this->begin(); iter != this->end(); ++iter) {
699             if ((*iter)->ld_filter_state.lfo_filter_state.tfs_logfile == lf) {
700                 return iter;
701             }
702         }
703 
704         return this->end();
705     }
706 
get_file_base_content_line(iterator iter)707     content_line_t get_file_base_content_line(iterator iter) {
708         ssize_t index = std::distance(this->begin(), iter);
709 
710         return content_line_t(index * MAX_LINES_PER_FILE);
711     };
712 
set_index_delegate(index_delegate * id)713     void set_index_delegate(index_delegate *id) {
714         if (id != this->lss_index_delegate) {
715             this->lss_index_delegate = id;
716             this->reload_index_delegate();
717         }
718     };
719 
get_index_delegate() const720     index_delegate *get_index_delegate() const {
721         return this->lss_index_delegate;
722     };
723 
reload_index_delegate()724     void reload_index_delegate() {
725         if (this->lss_index_delegate == nullptr) {
726             return;
727         }
728 
729         this->lss_index_delegate->index_start(*this);
730         for (unsigned int index : this->lss_filtered_index) {
731             content_line_t cl = (content_line_t) this->lss_index[index];
732             uint64_t line_number;
733             auto ld = this->find_data(cl, line_number);
734             std::shared_ptr<logfile> lf = (*ld)->get_file();
735 
736             this->lss_index_delegate->index_line(*this, lf.get(), lf->begin() + line_number);
737         }
738         this->lss_index_delegate->index_complete(*this);
739     };
740 
741     class meta_grepper
742         : public grep_proc_source<vis_line_t>,
743           public grep_proc_sink<vis_line_t> {
744     public:
meta_grepper(logfile_sub_source & source)745         meta_grepper(logfile_sub_source &source)
746             : lmg_source(source) {
747         };
748 
grep_value_for_line(vis_line_t line,std::string & value_out)749         bool grep_value_for_line(vis_line_t line, std::string &value_out) override {
750             content_line_t cl = this->lmg_source.at(vis_line_t(line));
751             std::map<content_line_t, bookmark_metadata> &user_mark_meta =
752                 lmg_source.get_user_bookmark_metadata();
753             auto meta_iter = user_mark_meta.find(cl);
754 
755             if (meta_iter == user_mark_meta.end()) {
756                 value_out.clear();
757             } else {
758                 bookmark_metadata &bm = meta_iter->second;
759 
760                 value_out.append(bm.bm_comment);
761                 for (const auto &tag : bm.bm_tags) {
762                     value_out.append(tag);
763                 }
764             }
765 
766             return !this->lmg_done;
767         };
768 
grep_initial_line(vis_line_t start,vis_line_t highest)769         vis_line_t grep_initial_line(vis_line_t start, vis_line_t highest) override {
770             vis_bookmarks &bm = this->lmg_source.tss_view->get_bookmarks();
771             bookmark_vector<vis_line_t> &bv = bm[&textview_curses::BM_META];
772 
773             if (bv.empty()) {
774                 return -1_vl;
775             }
776             return *bv.begin();
777         };
778 
grep_next_line(vis_line_t & line)779         void grep_next_line(vis_line_t &line) override {
780             vis_bookmarks &bm = this->lmg_source.tss_view->get_bookmarks();
781             bookmark_vector<vis_line_t> &bv = bm[&textview_curses::BM_META];
782 
783             line = bv.next(vis_line_t(line));
784             if (line == -1) {
785                 this->lmg_done = true;
786             }
787         };
788 
grep_begin(grep_proc<vis_line_t> & gp,vis_line_t start,vis_line_t stop)789         void grep_begin(grep_proc<vis_line_t> &gp, vis_line_t start, vis_line_t stop) override {
790             this->lmg_source.tss_view->grep_begin(gp, start, stop);
791         };
792 
grep_end(grep_proc<vis_line_t> & gp)793         void grep_end(grep_proc<vis_line_t> &gp) override {
794             this->lmg_source.tss_view->grep_end(gp);
795         };
796 
grep_match(grep_proc<vis_line_t> & gp,vis_line_t line,int start,int end)797         void grep_match(grep_proc<vis_line_t> &gp,
798                         vis_line_t line,
799                         int start,
800                         int end) override {
801             this->lmg_source.tss_view->grep_match(gp, line, start, end);
802         };
803 
804         logfile_sub_source &lmg_source;
805         bool lmg_done{false};
806     };
807 
808     nonstd::optional<std::pair<grep_proc_source<vis_line_t> *, grep_proc_sink<vis_line_t> *>>
809     get_grepper();
810 
get_location_history()811     nonstd::optional<location_history *> get_location_history() {
812         return &this->lss_location_history;
813     };
814 
815     Result<bool, std::string> eval_sql_filter(
816         sqlite3_stmt *stmt, iterator ld, logfile::const_iterator ll);
817 
818     void invalidate_sql_filter();
819 
set_line_meta_changed()820     void set_line_meta_changed() {
821         this->lss_line_meta_changed = true;
822     }
823 
is_line_meta_changed() const824     bool is_line_meta_changed() const {
825         return this->lss_line_meta_changed;
826     }
827 
828     static const uint64_t MAX_CONTENT_LINES = (1ULL << 40) - 1;
829     static const uint64_t MAX_LINES_PER_FILE = 256 * 1024 * 1024;
830     static const uint64_t MAX_FILES          = (
831         MAX_CONTENT_LINES / MAX_LINES_PER_FILE);
832 
833     std::function<void(logfile_sub_source&, file_off_t, file_size_t)> lss_sorting_observer;
834 
835 private:
836     static const size_t LINE_SIZE_CACHE_SIZE = 512;
837 
838     enum {
839         B_SCRUB,
840         B_TIME_OFFSET,
841         B_FILENAME,
842         B_BASENAME,
843     };
844 
845     enum {
846         F_SCRUB       = (1UL << B_SCRUB),
847         F_TIME_OFFSET = (1UL << B_TIME_OFFSET),
848         F_FILENAME    = (1UL << B_FILENAME),
849         F_BASENAME    = (1UL << B_BASENAME),
850 
851         F_NAME_MASK   = (F_FILENAME | F_BASENAME),
852     };
853 
854     struct __attribute__((__packed__)) indexed_content {
indexed_contentlogfile_sub_source::indexed_content855         indexed_content() {
856 
857         };
858 
indexed_contentlogfile_sub_source::indexed_content859         indexed_content(content_line_t cl) : ic_value(cl) {
860 
861         };
862 
operator content_line_tlogfile_sub_source::indexed_content863         operator content_line_t () const {
864             return content_line_t(this->ic_value);
865         };
866 
867         uint64_t ic_value : 40;
868     };
869 
870     struct logline_cmp {
logline_cmplogfile_sub_source::logline_cmp871         logline_cmp(logfile_sub_source & lc)
872             : llss_controller(lc) { };
operator ()logfile_sub_source::logline_cmp873         bool operator()(const content_line_t &lhs, const content_line_t &rhs) const
874         {
875             logline *ll_lhs = this->llss_controller.find_line(lhs);
876             logline *ll_rhs = this->llss_controller.find_line(rhs);
877 
878             return (*ll_lhs) < (*ll_rhs);
879         };
880 
operator ()logfile_sub_source::logline_cmp881         bool operator()(const uint32_t &lhs, const uint32_t &rhs) const
882         {
883             content_line_t cl_lhs = (content_line_t)
884                     llss_controller.lss_index[lhs];
885             content_line_t cl_rhs = (content_line_t)
886                     llss_controller.lss_index[rhs];
887             logline *ll_lhs = this->llss_controller.find_line(cl_lhs);
888             logline *ll_rhs = this->llss_controller.find_line(cl_rhs);
889 
890             return (*ll_lhs) < (*ll_rhs);
891         };
892 #if 0
893         bool operator()(const indexed_content &lhs, const indexed_content &rhs)
894         {
895             logline *ll_lhs = this->llss_controller.find_line(lhs.ic_value);
896             logline *ll_rhs = this->llss_controller.find_line(rhs.ic_value);
897 
898             return (*ll_lhs) < (*ll_rhs);
899         };
900 #endif
operator ()logfile_sub_source::logline_cmp901         bool operator()(const content_line_t &lhs, const time_t &rhs) const
902         {
903             logline *ll_lhs = this->llss_controller.find_line(lhs);
904 
905             return *ll_lhs < rhs;
906         };
operator ()logfile_sub_source::logline_cmp907         bool operator()(const content_line_t &lhs, const struct timeval &rhs) const
908         {
909             logline *ll_lhs = this->llss_controller.find_line(lhs);
910 
911             return *ll_lhs < rhs;
912         };
913 
914         logfile_sub_source & llss_controller;
915     };
916 
917     struct filtered_logline_cmp {
filtered_logline_cmplogfile_sub_source::filtered_logline_cmp918         filtered_logline_cmp(const logfile_sub_source & lc)
919                 : llss_controller(lc) { };
operator ()logfile_sub_source::filtered_logline_cmp920         bool operator()(const uint32_t &lhs, const uint32_t &rhs) const
921         {
922             content_line_t cl_lhs = (content_line_t)
923                     llss_controller.lss_index[lhs];
924             content_line_t cl_rhs = (content_line_t)
925                     llss_controller.lss_index[rhs];
926             logline *ll_lhs = this->llss_controller.find_line(cl_lhs);
927             logline *ll_rhs = this->llss_controller.find_line(cl_rhs);
928 
929             return (*ll_lhs) < (*ll_rhs);
930         };
931 
operator ()logfile_sub_source::filtered_logline_cmp932         bool operator()(const uint32_t &lhs, const struct timeval &rhs) const
933         {
934             content_line_t cl_lhs = (content_line_t)
935                     llss_controller.lss_index[lhs];
936             logline *ll_lhs = this->llss_controller.find_line(cl_lhs);
937 
938             return (*ll_lhs) < rhs;
939         };
940 
941         const logfile_sub_source & llss_controller;
942     };
943 
944     /**
945      * Functor for comparing the ld_file field of the logfile_data struct.
946      */
947     struct logfile_data_eq {
logfile_data_eqlogfile_sub_source::logfile_data_eq948         explicit logfile_data_eq(std::shared_ptr<logfile> lf) : lde_file(std::move(lf)) { };
949 
operator ()logfile_sub_source::logfile_data_eq950         bool operator()(const std::unique_ptr<logfile_data>& ld) const
951         {
952             return this->lde_file == ld->get_file();
953         }
954 
955         std::shared_ptr<logfile> lde_file;
956     };
957 
clear_line_size_cache()958     void clear_line_size_cache() {
959         this->lss_line_size_cache.fill(std::make_pair(0, 0));
960         this->lss_line_size_cache[0].first = -1;
961     };
962 
get_sql_filter()963     nonstd::optional<std::shared_ptr<text_filter>> get_sql_filter() {
964         auto iter = std::find_if(this->tss_filters.begin(),
965                                  this->tss_filters.end(),
966                                  [](const auto& filt) {
967                                      return filt->get_index() == 0;
968                                  });
969 
970         if (iter != this->tss_filters.end()) {
971             return *iter;
972         }
973         return nonstd::nullopt;
974     }
975 
976     bool check_extra_filters(iterator ld, logfile::iterator ll);
977 
978     size_t                    lss_basename_width = 0;
979     size_t                    lss_filename_width = 0;
980     unsigned long             lss_flags{0};
981     bool lss_force_rebuild{false};
982     std::vector<std::unique_ptr<logfile_data>> lss_files;
983 
984     big_array<indexed_content> lss_index;
985     std::vector<uint32_t> lss_filtered_index;
986     auto_mem<sqlite3_stmt> lss_preview_filter_stmt{sqlite3_finalize};
987 
988     bookmarks<content_line_t>::type lss_user_marks;
989     std::map<content_line_t, bookmark_metadata> lss_user_mark_metadata;
990     auto_mem<sqlite3_stmt> lss_marker_stmt{sqlite3_finalize};
991     std::string lss_marker_stmt_text;
992 
993     line_flags_t lss_token_flags{0};
994     iterator lss_token_file_data;
995     std::shared_ptr<logfile> lss_token_file;
996     std::string       lss_token_value;
997     string_attrs_t    lss_token_attrs;
998     std::vector<logline_value> lss_token_values;
999     int lss_token_shift_start{0};
1000     int lss_token_shift_size{0};
1001     shared_buffer     lss_share_manager;
1002     logfile::iterator lss_token_line;
1003     std::array<std::pair<int, size_t>, LINE_SIZE_CACHE_SIZE> lss_line_size_cache;
1004     log_level_t  lss_min_log_level{LEVEL_UNKNOWN};
1005     struct timeval    lss_min_log_time{0, 0};
1006     struct timeval    lss_max_log_time{std::numeric_limits<time_t>::max(), 0};
1007     bool lss_marked_only{false};
1008     index_delegate    *lss_index_delegate{nullptr};
1009     size_t            lss_longest_line{0};
1010     meta_grepper lss_meta_grepper;
1011     log_location_history lss_location_history;
1012 
1013     bool lss_in_value_for_line{false};
1014     bool lss_line_meta_changed{false};
1015 };
1016 
1017 #endif
1018