├── .gitignore ├── COPYRIGHT ├── ChangeLog ├── Makefile ├── NCURSES_COPYRIGHT ├── README.md ├── doc └── logtop.1 ├── examples ├── example1.c └── example1.py ├── logtop.i ├── setup.py ├── src ├── avl.c ├── curses.c ├── history.c ├── libavl │ ├── avl.c │ └── avl.h ├── logtop.c ├── logtop.h ├── main.c ├── main.h └── stdout.c └── tests └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | examples/example1 2 | liblogtop.* 3 | logtop 4 | src/*.o 5 | src/libavl/*.o -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | logtop is distributed under the following terms: 2 | 3 | Copyright (c) 2010 Palard Julien. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2013-08-10 18:04:37 +0200 Julien Palard 2 | 3 | * Documentation and example of the Python API 4 | 5 | 2013-08-10 17:45:24 +0200 Julien Palard 6 | 7 | * Documentation and example of the C API 8 | 9 | 2013-08-10 17:44:12 +0200 Julien Palard 10 | 11 | * FIX: Style 12 | 13 | 2013-08-10 17:44:01 +0200 Julien Palard 14 | 15 | * NEW: delete_logtop_state 16 | 17 | 2013-08-10 17:43:32 +0200 Julien Palard 18 | 19 | * FIX: Makefile to build liblogtop. 20 | 21 | 2013-07-26 10:07:37 +0200 Julien Palard 22 | 23 | * Version from Makefile 24 | 25 | 2013-07-26 09:57:55 +0200 Julien Palard 26 | 27 | * FIX: Memory leak in python binding 28 | 29 | 2013-04-12 11:23:33 +0200 Julien Palard 30 | 31 | * Adding some doc about python module dependencies 32 | 33 | 2013-03-20 11:36:33 +0100 Julien Palard 34 | 35 | * NEW: Give also global frequency to python module 36 | 37 | 2013-03-20 11:30:08 +0100 Julien Palard 38 | 39 | * NEW: Give frequency of every lines to python module 40 | 41 | 2013-03-20 10:01:36 +0100 Julien Palard 42 | 43 | * FIX: Continue to increment time when no new lines are fed 44 | 45 | 2013-03-20 09:54:29 +0100 Julien Palard 46 | 47 | * Cleaning .pyc files 48 | 49 | 2013-03-20 09:54:21 +0100 Julien Palard 50 | 51 | * Adding a test file for python module 52 | 53 | 2013-03-19 18:43:40 +0100 Julien Palard 54 | 55 | * Do not give simplified representation of the string to python, seems useless. 56 | 57 | 2013-03-14 00:28:52 +0100 Julien Palard 58 | 59 | * Adding python module via swig 60 | 61 | 2013-03-14 00:25:40 +0100 Julien Palard 62 | 63 | * Simplier to use a struct for swig 64 | 65 | 2013-03-13 23:37:58 +0100 Julien Palard 66 | 67 | * Adding a logtop_get function 68 | 69 | 2013-03-13 23:11:18 +0100 Julien Palard 70 | 71 | * Provide a logtop_delete function, renames for consistency 72 | 73 | 2013-03-13 19:11:18 +0100 Julien Palard 74 | 75 | * Renaming frequency -> logtop, more consistent 76 | 77 | 2013-03-13 19:06:24 +0100 Julien Palard 78 | 79 | * Renaming logtop -> main (see next commit) 80 | 81 | 2013-03-13 18:34:34 +0100 Julien Palard 82 | 83 | * Splitting code, first step to provide a lib_logtop. 84 | 85 | 2012-11-17 23:48:35 +0100 Julien Palard 86 | 87 | * UPDATE: Display improvment 88 | 89 | 2012-11-17 17:28:44 +0100 Julien Palard 90 | 91 | * ADD: check-syntax in Makefile for Flymake 92 | 93 | 2012-11-17 17:00:33 +0100 Julien Palard 94 | 95 | * FIX: Diplay consistency in curses mode 96 | 97 | 2012-11-17 16:59:57 +0100 Julien Palard 98 | 99 | * FIX: Update display every second even in the absence of new lines 100 | 101 | 2012-11-17 15:56:27 +0100 Julien Palard 102 | 103 | * FIX: ChangeLog readability 104 | 105 | 2012-11-17 15:08:14 +0100 Julien Palard 106 | 107 | * FIX: Dead lines at the bottom on the list. 108 | 109 | 2012-11-06 18:24:22 +0100 Julien Palard 110 | 111 | * FIX: Usage 112 | 113 | 2012-05-28 15:26:45 +0200 Julien Palard 114 | 115 | * FIX: Support LDFLAGS in Makefile 116 | 117 | 2012-05-21 15:01:50 +0200 Julien Palard 118 | 119 | * FIX: Forgot a debug. 120 | 121 | 2012-05-20 21:58:59 +0200 Julien Palard 122 | 123 | * Upgrading documentation and manpage. 124 | 125 | 2012-05-20 21:25:57 +0200 Julien Palard 126 | 127 | * Prit usage if stdin is a TTY. 128 | 129 | 2012-05-20 21:22:16 +0200 Julien Palard 130 | 131 | * Renaming without_curses in quiet. 132 | 133 | 2012-05-20 21:09:23 +0200 Julien Palard 134 | 135 | * NEW: Line by line reporting. 136 | 137 | 2012-05-20 19:40:49 +0200 Julien Palard 138 | 139 | * NEW: -q option to disable curses 140 | 141 | 2012-05-20 19:28:11 +0200 Julien Palard 142 | 143 | * Enhancing ccurses view. 144 | 145 | 2012-05-20 17:56:02 +0200 Julien Palard 146 | 147 | * Code uniformisation and -Wstrict-prototypes 148 | 149 | 2012-05-20 17:51:38 +0200 Julien Palard 150 | 151 | * NEW: Only print the top 10 elements at exit. 152 | 153 | 2012-05-20 16:33:48 +0200 Julien Palard 154 | 155 | * NEW: Handling SIGINT. 156 | 157 | 2011-05-31 18:43:43 +0200 Julien 158 | 159 | * Adding a missing build dependency in the README file (uthash-dev) 160 | 161 | 2011-05-31 18:31:07 +0200 Julien 162 | 163 | * Addin an 'About libavl' in the README file 164 | 165 | 2011-05-31 18:24:29 +0200 Julien 166 | 167 | * Updating README to reflect the actual implementation, and fixing a typo 168 | 169 | 2011-05-03 23:47:50 +0200 Julien 170 | 171 | * Replacing strings storage, from an AVL tree to an hashtable, little gain in performance (~20%) But keeping an AVL tree for the rank (as it needs to be sorted) 172 | 173 | 2011-04-16 12:30:42 +0200 Julien 174 | 175 | * FIX: If a line does not end with \n (the last?) it was reported as an empty string. NEW: Compatibility with lines ending with CR+LF, LF+CR, LF, or CR (and any other combinations !) 176 | 177 | 2011-04-16 12:17:22 +0200 Julien 178 | 179 | * Some corretion to the manpage 180 | 181 | 2011-04-16 12:07:46 +0200 Julien 182 | 183 | * Merge branch 'master' of github.com:JulienPalard/logtop 184 | 185 | 2011-04-16 12:06:25 +0200 Julien 186 | 187 | * Rewriting the README to remove livavl dependencies 188 | 189 | 2011-03-16 22:47:48 +0100 Julien Palard 190 | 191 | * FIX: Forgotten .o in libavl folder while make clean 192 | 193 | 2011-03-16 08:51:45 +0100 Julien Palard 194 | 195 | * Fixed a bug that makes some entry to appear more than once in the result. 196 | 197 | 2011-03-15 23:07:01 +0100 Julien Palard 198 | 199 | * Fixing display bug 200 | 201 | 2011-03-15 23:06:24 +0100 Julien Palard 202 | 203 | * Removing a useless strlen 204 | 205 | 2011-03-15 23:01:58 +0100 Julien Palard 206 | 207 | * traverse_log_lines can yield NULL, so we have to check it 208 | 209 | 2011-03-15 22:51:07 +0100 Julien Palard 210 | 211 | * Simple refactoring of writers 212 | 213 | 2011-03-15 20:41:29 +0100 Julien Palard 214 | 215 | * Removing one line in the stdout output module to keep the first line of result, previously dropped due to the appearing shell line of the user 216 | 217 | 2011-03-15 20:39:29 +0100 Julien Palard 218 | 219 | * Removing the unused UNUSED define and protecting STRINGIFY 220 | 221 | 2011-03-15 20:35:26 +0100 Julien Palard 222 | 223 | * Dropping dependency to Debian's libavl in favor of the pfaff's one, statically build 224 | 225 | 2011-01-16 20:32:26 +0100 Julien 226 | 227 | * Adding Debian changelog 228 | 229 | 2010-12-28 10:15:17 +0100 mandark 230 | 231 | * Refactorig for readeability 232 | 233 | 2010-12-28 09:46:32 +0100 mandark 234 | 235 | * Moving history structure to history.h 236 | 237 | 2010-12-28 09:42:26 +0100 mandark 238 | 239 | * Updating licences 240 | 241 | 2010-12-28 09:20:37 +0100 mandark 242 | 243 | * Refactoring for readeability 244 | 245 | 2010-12-28 08:51:29 +0100 mandark 246 | 247 | * Splitting history managment in history.c 248 | 249 | 2010-12-27 08:41:17 +0100 Julien 250 | 251 | * Fixing manpage 252 | 253 | 2010-12-12 20:57:50 +0100 mandark 254 | 255 | * FIX: More verbose help statement FIX: Removing deprecated initialisation of value while parsing arguments 256 | 257 | 2010-12-12 20:52:37 +0100 mandark 258 | 259 | * FIX: Fixing help to remove deprecated -c parameter 260 | 261 | 2010-12-12 20:51:31 +0100 mandark 262 | 263 | * ADD: Compile dependencies in README file 264 | 265 | 2010-12-12 00:18:40 +0100 Julien 266 | 267 | * NEW: Use ioctl to determine display height. 268 | 269 | 2010-12-05 20:13:58 +0100 Julien 270 | 271 | * Adding make install and a man 272 | 273 | 2010-12-05 20:13:02 +0100 Julien 274 | 275 | * Adding -v to get version and -h to get some help 276 | 277 | 2010-12-05 19:25:56 +0100 Julien 278 | 279 | * Fine tuning visualisation 280 | 281 | 2010-12-03 09:57:41 +0100 Julien 282 | 283 | * FIX: Replace specials chars like \r to dots to ensure graphical concistency 284 | 285 | 2010-11-14 22:22:03 +0100 Julien 286 | 287 | * FIX: display_height was non used in stdout display 288 | 289 | 2010-11-14 15:30:46 +0100 Julien 290 | 291 | * Style improvment and renaming 292 | 293 | 2010-11-13 21:45:03 +0100 Julien 294 | 295 | * Improving a bit the UI adding the number of log lines read by second ... 296 | 297 | 2010-11-13 21:27:32 +0100 Julien 298 | 299 | * Oops just forgotten -lefence in da Makefile :p 300 | 301 | 2010-11-13 21:24:51 +0100 Julien 302 | 303 | * Fix a bug in "get_newest_element_in_history" 304 | 305 | 2010-11-13 21:09:49 +0100 Julien 306 | 307 | * Fixing headers 308 | 309 | 2010-11-13 17:32:20 +0100 Julien 310 | 311 | * NEW: Now displaying loglines by seconds as : 917 25/s foobar 312 | 313 | 2010-11-13 15:33:54 +0100 Julien 314 | 315 | * ADD: Each history entity is now dated, i'll can add some feature like a rotation by duration. 316 | 317 | 2010-11-13 15:06:13 +0100 Julien 318 | 319 | * FIX: Bug during optparse 320 | 321 | 2010-11-13 15:00:27 +0100 Julien 322 | 323 | * Adding two new files after splitting source 324 | 325 | 2010-11-13 14:59:28 +0100 Julien 326 | 327 | * ADD: Dump last view on stdout leaving FIX: Restore curses leaving 328 | 329 | 2010-07-01 19:17:10 +0200 Julien 330 | 331 | * Add usage sample to the README 332 | 333 | 2010-07-01 19:09:12 +0200 Julien 334 | 335 | * Updating README : Adding dependencies 336 | 337 | 2010-07-01 19:05:05 +0200 Julien 338 | 339 | * EHLO World 340 | 341 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## 2 | ## Makefile for logtop 3 | ## 4 | ## Made by julien palard 5 | ## Login 6 | ## 7 | 8 | VERSION = 0.7.0 9 | MINOR = 0 10 | RELEASE = 0 11 | 12 | NAME = logtop 13 | UNAME=$(shell uname -s) 14 | ifeq ($(UNAME),Darwin) 15 | LINKERNAME = lib$(NAME).dylib 16 | else 17 | LINKERNAME = lib$(NAME).so 18 | endif 19 | SONAME=$(LINKERNAME).$(VERSION) 20 | REALNAME=$(SONAME).$(MINOR).$(RELEASE) 21 | 22 | LIB_SRC = src/avl.c src/history.c \ 23 | src/logtop.c src/libavl/avl.c 24 | SRC = $(LIB_SRC) src/curses.c src/stdout.c src/main.c 25 | 26 | LIB_OBJ = $(LIB_SRC:.c=.o) 27 | OBJ = $(SRC:.c=.o) 28 | 29 | CC = gcc 30 | override INCLUDE += . 31 | LIB = $(shell pkg-config --libs ncursesw) #-lefence 32 | CFLAGS += -O3 -Wall -fPIC -Wextra -pedantic -Wstrict-prototypes -I$(INCLUDE) $(shell pkg-config --cflags ncursesw) 33 | RM = rm -fr 34 | LDFLAGS = 35 | 36 | $(NAME): $(OBJ) 37 | $(CC) -o $(NAME) $(OBJ) $(LIB) $(LDFLAGS) 38 | 39 | lib$(NAME): $(LIB_OBJ) 40 | $(CC) $(CFLAGS) --shared -o $(LINKERNAME) $(OBJ) $(LIB) $(LDFLAGS) 41 | 42 | install: $(NAME) 43 | mkdir -p $(DESTDIR)/usr/bin/ 44 | cp $(NAME) $(DESTDIR)/usr/bin/ 45 | 46 | python-module: 47 | swig -python *.i 48 | python ./setup.py build_ext --inplace 49 | 50 | python3-module: 51 | swig -python *.i 52 | python3 ./setup.py build_ext --inplace 53 | 54 | all: 55 | @make $(NAME) 56 | @make lib$(NAME) 57 | 58 | .c.o: 59 | $(CC) -c $(CFLAGS) $< -o $(<:.c=.o) 60 | 61 | clean: 62 | $(RM) $(NAME) src/*~ src/#*# src/*.o src/*.core \ 63 | src/libavl/*.o _logtop.* liblogtop.* \ 64 | logtop.py *.pyc build/ logtop_wrap.c 65 | 66 | re: clean all 67 | 68 | check-syntax: 69 | gcc -Isrc -Wall -Wextra -ansi -pedantic -o /dev/null -S ${CHK_SOURCES} 70 | -------------------------------------------------------------------------------- /NCURSES_COPYRIGHT: -------------------------------------------------------------------------------- 1 | This is the Debian prepackaged version of the ncurses 2 | library and terminfo utilities. ncurses/terminfo was originally written 3 | by Pavel Curtis and Zeyd M. Ben-Halim , and is 4 | currently held by the Free Software Foundation. 5 | 6 | This package was put together by Vaidhyanathan G Mayilrangam 7 | and Joel Klecker , using sources 8 | obtained from ftp://ftp.gnu.org/gnu/ncurses/ncurses-5.0.tar.gz. 9 | 10 | It is based somewhat on work done by Bruce Perens , 11 | David Engel . Michael Alan Dorman 12 | , Richard Braakman , James Troup 13 | , J.H.M. Dassen (Ray) 14 | , and Galen Hazelwood 15 | over various years. 16 | 17 | 18 | Changes: 19 | * added Debian package maintenance system files 20 | * changed Makefile.in's to compile with -D_REENTRANT 21 | * changed configure.in to support proper compilation of debugging libraries 22 | * Fixed tget{flag,num,str} in lib_termcap ("long" strings work now) 23 | 24 | Changes to terminfo.src: 25 | * Removed ich(1) from linux, screen, vt320 26 | 27 | /**************************************************************************** 28 | * Copyright (c) 1998 Free Software Foundation, Inc. * 29 | * * 30 | * Permission is hereby granted, free of charge, to any person obtaining a * 31 | * copy of this software and associated documentation files (the * 32 | * "Software"), to deal in the Software without restriction, including * 33 | * without limitation the rights to use, copy, modify, merge, publish, * 34 | * distribute, distribute with modifications, sublicense, and/or sell * 35 | * copies of the Software, and to permit persons to whom the Software is * 36 | * furnished to do so, subject to the following conditions: * 37 | * * 38 | * The above copyright notice and this permission notice shall be included * 39 | * in all copies or substantial portions of the Software. * 40 | * * 41 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 42 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 43 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 44 | * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 45 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 46 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 47 | * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 48 | * * 49 | * Except as contained in this notice, the name(s) of the above copyright * 50 | * holders shall not be used in advertising or otherwise to promote the * 51 | * sale, use or other dealings in this Software without prior written * 52 | * authorization. * 53 | ****************************************************************************/ 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Install 2 | 3 | ## Debian / Debian based 4 | 5 | ``` 6 | apt install logtop 7 | ``` 8 | 9 | 10 | ## Void Linux 11 | 12 | ``` 13 | xbps-install -S logtop 14 | ``` 15 | 16 | 17 | ## From sources 18 | 19 | Compile dependencies: `libncursesw5-dev`, `uthash-dev`. 20 | 21 | So on a Debian: 22 | 23 | ```bash session 24 | $ sudo apt install libncursesw5-dev uthash-dev 25 | Reading package lists... Done 26 | [...skipped for readability...] 27 | Processing triggers for man-db (2.8.2-1) ... 28 | $ make 29 | gcc -c -O3 -DVERSION=0.7.0 -Wall -fPIC -Wextra -ansi -pedantic -Wstrict-prototypes -I. src/avl.c -o src/avl.o 30 | [...skipped for readability...] 31 | $ make install 32 | ``` 33 | 34 | # Usage 35 | 36 | `logtop` displays a real-time count of strings received from stdin. 37 | It can be useful in some cases, like getting the IP flooding your server: 38 | 39 | ```bash session 40 | $ tail -f /var/log/apache2/access.log | cut -d' ' -f1 | logtop 41 | ``` 42 | 43 | Or the top buzzing article of your blog: 44 | 45 | ```bash session 46 | $ tail -f /var/log/apache2/access.log | cut -d' ' -f7 | grep article | logtop 47 | ``` 48 | 49 | 50 | # Python bindings 51 | 52 | Dependencies: `python-dev`, `swig`. 53 | 54 | ```bash session 55 | $ sudo apt install python-dev swig 56 | Reading package lists... Done 57 | [...] 58 | $ make python-module 59 | [...] 60 | $ python setup.py install 61 | ``` 62 | 63 | 64 | # Development 65 | 66 | I use a hashtable to store strings and an AVL tree to store frequencies, 67 | so I can fetch by string or fetch ordered by frequency to display the 68 | top-strings. 69 | 70 | 71 | # C API 72 | 73 | Logtop can be used by your C programs, you may to compile against 74 | logtop's sources (`src/{avl.c,history.c,logtop.c,libavl/avl.c}`) or 75 | against `liblogtop`, obtained using `make liblogtop`. 76 | 77 | C API is described in `logtop.h`, you need: 78 | 79 | ```C 80 | struct logtop *new_logtop(size_t history_size); 81 | void delete_logtop(struct logtop *this); 82 | void logtop_feed(struct logtop *this, char *line); 83 | struct logtop_state *logtop_get(struct logtop *this, size_t qte); 84 | double logtop_timespan(struct logtop *this); 85 | unsigned int logtop_qte_of_elements(struct logtop *this); 86 | ``` 87 | 88 | You can find an example of using the C API in `examples/example1.c`. 89 | 90 | 91 | # Python API 92 | 93 | `logtop` module exposes a logtop class containing: 94 | 95 | ```Python 96 | logtop.__init__(history_size) # to build a new logtop keeping 97 | # at most history_size lines. 98 | logtop.feed(line) # to feed a new line in logtop. 99 | logtop.get(qte_of_elements) # to get the top qte_of_elements lines. 100 | logtop.qte_of_elements() # to get the current total number of lines. 101 | logtop.timespan() # to get the duration from the oldest line to now. 102 | ``` 103 | 104 | timespan may be less than the runtime, as logtop drop old lines, 105 | to keep, at most, `history_size` lines, given in the constructor of 106 | the logtop class. 107 | 108 | 109 | # About libavl 110 | 111 | The libavl used here is the Ben Pfaff's one, statically build with logtop, as 112 | Ben want it to be (see INSTALL file and here: 113 | http://lists.debian.org/debian-devel/2001/07/msg01303.html) 114 | So this libavl is NOT packaged as a library for Debian, the libavl you'll 115 | found in Debian repositories is the Wessel Dankers's one. 116 | 117 | 118 | # About the project 119 | 120 | For copyright information, please see the file COPYRIGHT in this 121 | directory or in the files of the source tree. 122 | 123 | This project was initiated on 2010-06-29 by Palard Julien 124 | See http://julien.palard.fr or ask me questions at : 125 | julien at palard dot fr. 126 | -------------------------------------------------------------------------------- /doc/logtop.1: -------------------------------------------------------------------------------- 1 | .\" Hey, EMACS: -*- nroff -*- 2 | .\" First parameter, NAME, should be all caps 3 | .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection 4 | .\" other parameters are allowed: see man(7), man(1) 5 | .TH LOGTOP 1 "April 16, 2011" 6 | .\" Please adjust this date whenever revising the manpage. 7 | .SH "NAME" 8 | logtop \- Realtime log line rate analyser 9 | .SH "SYNOPSIS" 10 | .B logtop 11 | .RI [ OPTIONS ] 12 | .SH "DESCRIPTION" 13 | \fBlogtop\fP is a System Administrator tool analyzing line rate on stdin. 14 | It reads on stdin and print a constantly updated result 15 | displaying, in columns: 16 | Line number, count, frequency, and the actual line. 17 | 18 | $ tail \-f FILE | \fBlogtop\fP 19 | is the friendly version of: 20 | $ watch 'tail FILE | sort | uniq \-c | sort \-gr' 21 | .PP 22 | .SH "OPTIONS" 23 | .TP 24 | .B \-s, \-\-size=K 25 | Only keep K lines in memory, instead of 10000. 26 | .TP 27 | .B \-q, \-\-quiet 28 | Do not display a live view of the data, only display a top at exit. 29 | .TP 30 | .B \-l, \-\-line-by-line=K 31 | Print result line by line, in a machine friendly format, K is the 32 | number of result to print per line. 33 | 34 | Line by line format is : [%d %f %s\\t]*\\n 35 | %d : Number of occurences 36 | %f : Frequency of apparition 37 | %s : String (Control chars replaced by dots. 38 | .TP 39 | .B \-i, \-\-interval=K 40 | Interval between graphical updates, in seconds. Defaults to 1. 41 | .TP 42 | .B \-h, \-\-help 43 | Show summary of options. 44 | .TP 45 | .B \-v, \-\-version 46 | Show version of program. 47 | 48 | .SH "EXAMPLES" 49 | .PP 50 | Here are some \fBlogtop\fP usage examples. 51 | .PP 52 | .PP 53 | \fBtail \-f cache.log | grep \-o "HIT\\|MISS" | logtop\fR 54 | .PP 55 | Realtime hit / miss ratio on some caching software log file. 56 | .PP 57 | \fBtail \-f access.log | cut \-d' ' \-f1 | logtop \-s 10000\fR 58 | .PP 59 | Realtime most querying IPs on your server, as long as log lines in access.log 60 | starts with the client IP. 61 | .PP 62 | \fBtail \-f access.log | cut \-d' ' \-f7 | logtop \-s 10000\fR 63 | .PP 64 | Realtime most requested web pages in a NCSA like log file. 65 | .PP 66 | \fBcat auth.log | grep \-v "CRON" | grep \-o ": .*" | logtop \-q \-s 100000\fR 67 | .PP 68 | Display a one\-shot simple analyse of your auth.log. 69 | .SH "SEE ALSO" 70 | .BR watch (1) 71 | .br 72 | .SH "AUTHOR" 73 | logtop was written by Julien Palard. 74 | .PP 75 | This manual page was written by Julien Palard , 76 | for the Debian project (and may be used by others). 77 | -------------------------------------------------------------------------------- /examples/example1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Julien Palard. All rights reserved. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | /* 27 | 28 | This example is a basic demo of logtop C API. 29 | You may compile it using : 30 | $ cd examples 31 | $ cc example1.c ../src/{avl.c,history.c,logtop.c,libavl/avl.c} -o example1 32 | 33 | or 34 | 35 | $ make liblogtop 36 | $ cd examples 37 | $ cc example1.c -llogtop -L.. -o example1 38 | $ LD_LIBRARY_PATH=.. ./example1 39 | 40 | */ 41 | 42 | #include 43 | #include 44 | #include "../src/logtop.h" 45 | 46 | int main() 47 | { 48 | struct logtop *logtop; 49 | struct logtop_state *state; 50 | unsigned int i; 51 | 52 | logtop = new_logtop(10000); /* Don't keep more than 10k elements */ 53 | 54 | logtop_feed(logtop, "foo"); 55 | logtop_feed(logtop, "bar"); 56 | logtop_feed(logtop, "foo"); 57 | logtop_feed(logtop, "bar"); 58 | 59 | logtop_feed(logtop, "baz"); 60 | logtop_feed(logtop, "baz"); 61 | logtop_feed(logtop, "baz"); 62 | 63 | state = logtop_get(logtop, 10); /* Get the top 10 */ 64 | 65 | printf("%d lines seen during %.2fs (%.2f lines/s):\n", 66 | state->count, 67 | state->timespan, 68 | state->frequency); 69 | i = 0; 70 | while (state->lines[i] != NULL) 71 | { 72 | printf(" \"%s\" seen %d times (%.2f lines/s)\n", 73 | state->lines[i]->string, 74 | state->lines[i]->count, 75 | state->lines[i]->frequency); 76 | i += 1; 77 | } 78 | delete_logtop_state(state); 79 | delete_logtop(logtop); 80 | return EXIT_SUCCESS; 81 | } 82 | -------------------------------------------------------------------------------- /examples/example1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pprint 4 | import sys 5 | from logtop import logtop 6 | 7 | 8 | """ 9 | This example show a complete usage of the python API, 10 | It can be used like this: 11 | 12 | $ make python-module 13 | $ python3 -m venv logtop_venv 14 | $ . logtop_venv/bin/activate 15 | $ python3 -m pip install . 16 | $ 17 | $ cat /etc/passwd | cut -d: -f7 | python3 examples/example1.py 18 | 19 | """ 20 | 21 | l = logtop(10000) 22 | for line in sys.stdin: 23 | l.feed(line) 24 | 25 | pprint.pprint(l.get(10)) 26 | -------------------------------------------------------------------------------- /logtop.i: -------------------------------------------------------------------------------- 1 | %module logtop 2 | 3 | %{ 4 | #include "src/logtop.h" 5 | %} 6 | 7 | %typemap(out) struct logtop_state * 8 | { 9 | int i; 10 | PyObject *log_line; 11 | PyObject *lines; 12 | PyObject *tmp; 13 | 14 | $result = PyDict_New(); 15 | lines = PyList_New(0); 16 | while (result->lines[i] != NULL) 17 | { 18 | log_line = PyTuple_New(3); 19 | PyTuple_SetItem(log_line, 0, PyInt_FromLong(result->lines[i]->count)); 20 | PyTuple_SetItem(log_line, 1, PyFloat_FromDouble(result->lines[i]->frequency)); 21 | PyTuple_SetItem(log_line, 2, PyString_FromString(result->lines[i]->string)); 22 | PyList_Append(lines, log_line); 23 | Py_DECREF(log_line); 24 | i++; 25 | } 26 | PyDict_SetItemString($result, "lines", lines); 27 | Py_DECREF(lines); 28 | tmp = PyInt_FromLong(result->count); 29 | PyDict_SetItemString($result, "count", tmp); 30 | Py_DECREF(tmp); 31 | tmp = PyFloat_FromDouble(result->timespan); 32 | PyDict_SetItemString($result, "timespan", tmp); 33 | Py_DECREF(tmp); 34 | tmp = PyFloat_FromDouble(result->frequency); 35 | PyDict_SetItemString($result, "frequency", tmp); 36 | Py_DECREF(tmp); 37 | delete_logtop_state(result); 38 | } 39 | 40 | struct logtop 41 | { 42 | %extend { 43 | logtop(size_t history_size); 44 | ~logtop(); 45 | void feed(char *line); 46 | struct logtop_state *get(size_t qte); 47 | double timespan(); 48 | unsigned int qte_of_elements(); 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | setup.py file for logtop 5 | """ 6 | 7 | from distutils.core import setup, Extension 8 | 9 | 10 | logtop_module = Extension('_logtop', 11 | sources=['logtop_wrap.c', 12 | 'src/logtop.c', 13 | 'src/avl.c', 14 | 'src/history.c', 15 | 'src/libavl/avl.c']) 16 | 17 | setup(name='logtop', 18 | version='0.2', 19 | author="Julien Palard", 20 | author_email="julien@palard.fr", 21 | description="""Live line frequency analyzer""", 22 | long_description="""logtop is a System Administrator tool analyzing line 23 | rate on stdin. It reads on stdin and print a constantly updated result 24 | displaying, in columns: Line number, count, frequency, 25 | and the actual line.""", 26 | keywords=['log', 'top', 'lines', 'analyzer', 'nginx', 'apache', 27 | 'varnish', 'tail'], 28 | url='https://github.com/JulienPalard/logtop', 29 | classifiers=['Development Status :: 5 - Production/Stable', 30 | 'Intended Audience :: System Administrators', 31 | 'License :: OSI Approved :: BSD License'], 32 | ext_modules=[logtop_module], 33 | py_modules=["logtop"]) 34 | -------------------------------------------------------------------------------- /src/avl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Julien Palard. All rights reserved. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #define _GNU_SOURCE 27 | #include 28 | #include 29 | #include 30 | #include "logtop.h" 31 | 32 | 33 | static int compare_log_lines_count(const void *log_line1, 34 | const void *log_line2, 35 | void *avl_param) 36 | { 37 | (void) avl_param; 38 | if (((log_line_t*)log_line1)->count != ((log_line_t*)log_line2)->count) 39 | { 40 | if (((log_line_t*)log_line2)->count > ((log_line_t*)log_line1)->count) 41 | return 1; 42 | else 43 | return -1; 44 | } 45 | return (strcmp(((log_line_t*)log_line1)->string, 46 | ((log_line_t*)log_line2)->string)); 47 | 48 | } 49 | 50 | static void die(void) 51 | { 52 | fputs("Ran out of memory, commit suicide for important tasks to live !", 53 | stderr); 54 | exit(EXIT_FAILURE); 55 | } 56 | 57 | static char *repr(const char *str) 58 | { 59 | char *clean; 60 | int i; 61 | 62 | clean = strdup(str); 63 | if (clean == NULL) 64 | return NULL; 65 | for (i = 0; clean[i] != '\0'; ++i) 66 | if (clean[i] > 0 && clean[i] < ' ') 67 | clean[i] = '.'; 68 | return clean; 69 | } 70 | 71 | static log_line_t *create_log_entry(struct logtop *this, char *string) 72 | { 73 | log_line_t *entry; 74 | 75 | entry = (log_line_t*)malloc(sizeof(*entry)); 76 | if (entry == NULL) 77 | die(); 78 | entry->count = 0; 79 | entry->string = strdup(string); 80 | if (entry->string == NULL) 81 | die(); 82 | entry->repr = repr(string); 83 | if (entry->repr == NULL) 84 | die(); 85 | HASH_ADD_KEYPTR(hh, this->strings, entry->string, strlen(entry->string), 86 | entry); 87 | avl_insert(this->top, entry); 88 | return entry; 89 | } 90 | 91 | static void delete_log_entry(struct logtop *this, log_line_t *log_entry) 92 | { 93 | HASH_DEL(this->strings, log_entry); 94 | free(log_entry->string); 95 | free(log_entry->repr); 96 | free(log_entry); 97 | } 98 | 99 | struct avl_table *new_avl(struct logtop *this) 100 | { 101 | return avl_create(compare_log_lines_count, this, NULL); 102 | } 103 | 104 | static void free_avl_element(void *avl_item, void *avl_param) 105 | { 106 | delete_log_entry((struct logtop*)avl_param, (log_line_t *)avl_item); 107 | } 108 | 109 | void delete_avl(struct logtop *this) 110 | { 111 | avl_destroy(this->top, free_avl_element); 112 | } 113 | 114 | log_line_t *avl_get(struct logtop *this, char *string) 115 | { 116 | log_line_t *node; 117 | 118 | HASH_FIND_STR(this->strings, string, node); 119 | if (node != NULL) 120 | return node; 121 | else 122 | return create_log_entry(this, string); 123 | } 124 | 125 | void avl_increment(struct logtop *this, log_line_t *log_entry) 126 | { 127 | avl_delete(this->top, log_entry); 128 | log_entry->count += 1; 129 | avl_insert(this->top, log_entry); 130 | } 131 | 132 | void avl_decrement(struct logtop *this, log_line_t *log_entry) 133 | { 134 | avl_delete(this->top, log_entry); 135 | log_entry->count -= 1; 136 | if (log_entry->count != 0) 137 | avl_insert(this->top, log_entry); 138 | else 139 | delete_log_entry(this, log_entry); 140 | } 141 | 142 | void avl_traverse(struct logtop*logtop, unsigned int length, 143 | void (*visitor)(void *data, int index, void *user_data), 144 | void *user_data) 145 | { 146 | struct avl_traverser trav; 147 | void *node; 148 | unsigned int last; 149 | struct avl_table *tree; 150 | 151 | tree = logtop->top; 152 | last = length; 153 | node = avl_t_first(&trav, tree); 154 | while (length-- > 0 && node != NULL) 155 | { 156 | visitor(node, last - length, user_data); 157 | node = avl_t_next(&trav); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/curses.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Julien Palard. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #define _GNU_SOURCE 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "main.h" 34 | 35 | static WINDOW *window; 36 | 37 | static void update_winsize(void) 38 | { 39 | struct winsize ws; 40 | 41 | if (ioctl(1, TIOCGWINSZ, &ws) == -1) 42 | { 43 | gl_env.display_height = 24; 44 | gl_env.display_width = 80; 45 | } 46 | else 47 | { 48 | gl_env.display_height = ws.ws_row; 49 | gl_env.display_width = ws.ws_col; 50 | } 51 | } 52 | 53 | static void on_sigwinch(int sig __attribute__((unused))) 54 | { 55 | update_winsize(); 56 | endwin(); 57 | delwin(window); 58 | window = newwin(gl_env.display_height, gl_env.display_width, 0, 0); 59 | curses_update(); 60 | } 61 | 62 | void curses_setup() 63 | { 64 | update_winsize(); 65 | setup_sighandler(SIGWINCH, SA_RESTART, on_sigwinch); 66 | initscr(); 67 | window = newwin(gl_env.display_height, gl_env.display_width, 0, 0); 68 | } 69 | 70 | void curses_release() 71 | { 72 | delwin(window); 73 | endwin(); 74 | } 75 | 76 | struct display_data 77 | { 78 | double duration; 79 | unsigned int qte_of_elements; 80 | }; 81 | 82 | static void display_line(void *data, int index, void *display_data) 83 | { 84 | log_line_t *line; 85 | 86 | line = (log_line_t *)data; 87 | mvwprintw(window, index + 1, 0, "%4d %6d %8.2f %-*s", 88 | index, 89 | line->count, 90 | line->count / ((struct display_data*)display_data)->duration, 91 | gl_env.display_width - 21, line->repr); 92 | } 93 | 94 | static void display_header(struct display_data *display_data) 95 | { 96 | mvwprintw(window, 0, 0, 97 | "%u lines, %.2f lines/s", 98 | display_data->qte_of_elements, 99 | display_data->qte_of_elements / display_data->duration); 100 | mvwprintw(window, 1, 0, 101 | "RANK CNT LINE/S LINE"); 102 | mvwchgat(window, 1, 0, -1, A_REVERSE, 0, NULL); 103 | } 104 | 105 | void curses_update() 106 | { 107 | struct display_data display_data; 108 | 109 | display_data.duration = logtop_timespan(gl_env.logtop); 110 | display_data.qte_of_elements = logtop_qte_of_elements(gl_env.logtop); 111 | if (display_data.duration == 0) 112 | display_data.duration = 1; 113 | werase(window); 114 | display_header(&display_data); 115 | avl_traverse(gl_env.logtop, gl_env.display_height - 2, 116 | display_line, &display_data); 117 | wrefresh(window); 118 | } 119 | -------------------------------------------------------------------------------- /src/history.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Julien Palard. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #include 27 | #include "logtop.h" 28 | 29 | /* 30 | ** If the element under history_start is null 31 | ** then the history is not full. 32 | */ 33 | unsigned int history_length(struct logtop *this) 34 | { 35 | if (this->history[this->history_start].log_entry == NULL) 36 | return this->history_start; 37 | else 38 | return this->history_size; 39 | } 40 | 41 | history_element_t *history_oldest_element(struct logtop *this) 42 | { 43 | if (this->history[this->history_start].log_entry != NULL) 44 | { 45 | return &(this->history[this->history_start]); 46 | } 47 | else 48 | { 49 | if (this->history_start == 0) 50 | return NULL; 51 | return &(this->history[0]); 52 | } 53 | } 54 | 55 | history_element_t *history_newest_element(struct logtop *this) 56 | { 57 | int newest_item_index; 58 | 59 | newest_item_index = this->history_start == 0 60 | ? this->history_size - 1 61 | : this->history_start - 1; 62 | if (this->history[newest_item_index].log_entry == NULL) 63 | return NULL; 64 | else 65 | return &(this->history[newest_item_index]); 66 | } 67 | 68 | void history_update(struct logtop *this, log_line_t *element) 69 | { 70 | history_element_t *history_element; 71 | log_line_t *log_entry; 72 | 73 | history_element = &(this->history[this->history_start]); 74 | log_entry = history_element->log_entry; 75 | if (log_entry != NULL) 76 | avl_decrement(this, log_entry); 77 | this->history[this->history_start].log_entry = element; 78 | this->history[this->history_start].time = time(NULL); 79 | this->history_start += 1; 80 | if (this->history_start >= this->history_size) 81 | this->history_start = 0; 82 | } 83 | 84 | history_element_t *new_history(struct logtop *this) 85 | { 86 | return calloc(sizeof(history_element_t), this->history_size); 87 | } 88 | 89 | void delete_history(struct logtop *this) 90 | { 91 | free(this->history); 92 | } 93 | -------------------------------------------------------------------------------- /src/libavl/avl.c: -------------------------------------------------------------------------------- 1 | /* Produced by texiweb from libavl.w. */ 2 | 3 | /* libavl - library for manipulation of binary trees. 4 | Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc. 5 | 6 | This program is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License as 8 | published by the Free Software Foundation; either version 2 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | See the GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 19 | 02111-1307, USA. 20 | 21 | The author may be contacted at on the Internet, or 22 | write to Ben Pfaff, Stanford University, Computer Science Dept., 353 23 | Serra Mall, Stanford CA 94305, USA. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "avl.h" 31 | 32 | /* Creates and returns a new table 33 | with comparison function |compare| using parameter |param| 34 | and memory allocator |allocator|. 35 | Returns |NULL| if memory allocation failed. */ 36 | struct avl_table * 37 | avl_create (avl_comparison_func *compare, void *param, 38 | struct libavl_allocator *allocator) 39 | { 40 | struct avl_table *tree; 41 | 42 | assert (compare != NULL); 43 | 44 | if (allocator == NULL) 45 | allocator = &avl_allocator_default; 46 | 47 | tree = allocator->libavl_malloc (allocator, sizeof *tree); 48 | if (tree == NULL) 49 | return NULL; 50 | 51 | tree->avl_root = NULL; 52 | tree->avl_compare = compare; 53 | tree->avl_param = param; 54 | tree->avl_alloc = allocator; 55 | tree->avl_count = 0; 56 | tree->avl_generation = 0; 57 | 58 | return tree; 59 | } 60 | 61 | /* Search |tree| for an item matching |item|, and return it if found. 62 | Otherwise return |NULL|. */ 63 | void * 64 | avl_find (const struct avl_table *tree, const void *item) 65 | { 66 | const struct avl_node *p; 67 | 68 | assert (tree != NULL && item != NULL); 69 | for (p = tree->avl_root; p != NULL; ) 70 | { 71 | int cmp = tree->avl_compare (item, p->avl_data, tree->avl_param); 72 | 73 | if (cmp < 0) 74 | p = p->avl_link[0]; 75 | else if (cmp > 0) 76 | p = p->avl_link[1]; 77 | else /* |cmp == 0| */ 78 | return p->avl_data; 79 | } 80 | 81 | return NULL; 82 | } 83 | 84 | /* Inserts |item| into |tree| and returns a pointer to |item|'s address. 85 | If a duplicate item is found in the tree, 86 | returns a pointer to the duplicate without inserting |item|. 87 | Returns |NULL| in case of memory allocation failure. */ 88 | void ** 89 | avl_probe (struct avl_table *tree, void *item) 90 | { 91 | struct avl_node *y, *z; /* Top node to update balance factor, and parent. */ 92 | struct avl_node *p, *q; /* Iterator, and parent. */ 93 | struct avl_node *n; /* Newly inserted node. */ 94 | struct avl_node *w; /* New root of rebalanced subtree. */ 95 | int dir; /* Direction to descend. */ 96 | 97 | unsigned char da[AVL_MAX_HEIGHT]; /* Cached comparison results. */ 98 | int k = 0; /* Number of cached results. */ 99 | 100 | assert (tree != NULL && item != NULL); 101 | 102 | z = (struct avl_node *) &tree->avl_root; 103 | y = tree->avl_root; 104 | dir = 0; 105 | for (q = z, p = y; p != NULL; q = p, p = p->avl_link[dir]) 106 | { 107 | int cmp = tree->avl_compare (item, p->avl_data, tree->avl_param); 108 | if (cmp == 0) 109 | return &p->avl_data; 110 | 111 | if (p->avl_balance != 0) 112 | z = q, y = p, k = 0; 113 | da[k++] = dir = cmp > 0; 114 | } 115 | 116 | n = q->avl_link[dir] = 117 | tree->avl_alloc->libavl_malloc (tree->avl_alloc, sizeof *n); 118 | if (n == NULL) 119 | return NULL; 120 | 121 | tree->avl_count++; 122 | n->avl_data = item; 123 | n->avl_link[0] = n->avl_link[1] = NULL; 124 | n->avl_balance = 0; 125 | if (y == NULL) 126 | return &n->avl_data; 127 | 128 | for (p = y, k = 0; p != n; p = p->avl_link[da[k]], k++) 129 | if (da[k] == 0) 130 | p->avl_balance--; 131 | else 132 | p->avl_balance++; 133 | 134 | if (y->avl_balance == -2) 135 | { 136 | struct avl_node *x = y->avl_link[0]; 137 | if (x->avl_balance == -1) 138 | { 139 | w = x; 140 | y->avl_link[0] = x->avl_link[1]; 141 | x->avl_link[1] = y; 142 | x->avl_balance = y->avl_balance = 0; 143 | } 144 | else 145 | { 146 | assert (x->avl_balance == +1); 147 | w = x->avl_link[1]; 148 | x->avl_link[1] = w->avl_link[0]; 149 | w->avl_link[0] = x; 150 | y->avl_link[0] = w->avl_link[1]; 151 | w->avl_link[1] = y; 152 | if (w->avl_balance == -1) 153 | x->avl_balance = 0, y->avl_balance = +1; 154 | else if (w->avl_balance == 0) 155 | x->avl_balance = y->avl_balance = 0; 156 | else /* |w->avl_balance == +1| */ 157 | x->avl_balance = -1, y->avl_balance = 0; 158 | w->avl_balance = 0; 159 | } 160 | } 161 | else if (y->avl_balance == +2) 162 | { 163 | struct avl_node *x = y->avl_link[1]; 164 | if (x->avl_balance == +1) 165 | { 166 | w = x; 167 | y->avl_link[1] = x->avl_link[0]; 168 | x->avl_link[0] = y; 169 | x->avl_balance = y->avl_balance = 0; 170 | } 171 | else 172 | { 173 | assert (x->avl_balance == -1); 174 | w = x->avl_link[0]; 175 | x->avl_link[0] = w->avl_link[1]; 176 | w->avl_link[1] = x; 177 | y->avl_link[1] = w->avl_link[0]; 178 | w->avl_link[0] = y; 179 | if (w->avl_balance == +1) 180 | x->avl_balance = 0, y->avl_balance = -1; 181 | else if (w->avl_balance == 0) 182 | x->avl_balance = y->avl_balance = 0; 183 | else /* |w->avl_balance == -1| */ 184 | x->avl_balance = +1, y->avl_balance = 0; 185 | w->avl_balance = 0; 186 | } 187 | } 188 | else 189 | return &n->avl_data; 190 | z->avl_link[y != z->avl_link[0]] = w; 191 | 192 | tree->avl_generation++; 193 | return &n->avl_data; 194 | } 195 | 196 | /* Inserts |item| into |table|. 197 | Returns |NULL| if |item| was successfully inserted 198 | or if a memory allocation error occurred. 199 | Otherwise, returns the duplicate item. */ 200 | void * 201 | avl_insert (struct avl_table *table, void *item) 202 | { 203 | void **p = avl_probe (table, item); 204 | return p == NULL || *p == item ? NULL : *p; 205 | } 206 | 207 | /* Inserts |item| into |table|, replacing any duplicate item. 208 | Returns |NULL| if |item| was inserted without replacing a duplicate, 209 | or if a memory allocation error occurred. 210 | Otherwise, returns the item that was replaced. */ 211 | void * 212 | avl_replace (struct avl_table *table, void *item) 213 | { 214 | void **p = avl_probe (table, item); 215 | if (p == NULL || *p == item) 216 | return NULL; 217 | else 218 | { 219 | void *r = *p; 220 | *p = item; 221 | return r; 222 | } 223 | } 224 | 225 | /* Deletes from |tree| and returns an item matching |item|. 226 | Returns a null pointer if no matching item found. */ 227 | void * 228 | avl_delete (struct avl_table *tree, const void *item) 229 | { 230 | /* Stack of nodes. */ 231 | struct avl_node *pa[AVL_MAX_HEIGHT]; /* Nodes. */ 232 | unsigned char da[AVL_MAX_HEIGHT]; /* |avl_link[]| indexes. */ 233 | int k; /* Stack pointer. */ 234 | 235 | struct avl_node *p; /* Traverses tree to find node to delete. */ 236 | int cmp; /* Result of comparison between |item| and |p|. */ 237 | 238 | assert (tree != NULL && item != NULL); 239 | 240 | k = 0; 241 | p = (struct avl_node *) &tree->avl_root; 242 | for (cmp = -1; cmp != 0; 243 | cmp = tree->avl_compare (item, p->avl_data, tree->avl_param)) 244 | { 245 | int dir = cmp > 0; 246 | 247 | pa[k] = p; 248 | da[k++] = dir; 249 | 250 | p = p->avl_link[dir]; 251 | if (p == NULL) 252 | return NULL; 253 | } 254 | item = p->avl_data; 255 | 256 | if (p->avl_link[1] == NULL) 257 | pa[k - 1]->avl_link[da[k - 1]] = p->avl_link[0]; 258 | else 259 | { 260 | struct avl_node *r = p->avl_link[1]; 261 | if (r->avl_link[0] == NULL) 262 | { 263 | r->avl_link[0] = p->avl_link[0]; 264 | r->avl_balance = p->avl_balance; 265 | pa[k - 1]->avl_link[da[k - 1]] = r; 266 | da[k] = 1; 267 | pa[k++] = r; 268 | } 269 | else 270 | { 271 | struct avl_node *s; 272 | int j = k++; 273 | 274 | for (;;) 275 | { 276 | da[k] = 0; 277 | pa[k++] = r; 278 | s = r->avl_link[0]; 279 | if (s->avl_link[0] == NULL) 280 | break; 281 | 282 | r = s; 283 | } 284 | 285 | s->avl_link[0] = p->avl_link[0]; 286 | r->avl_link[0] = s->avl_link[1]; 287 | s->avl_link[1] = p->avl_link[1]; 288 | s->avl_balance = p->avl_balance; 289 | 290 | pa[j - 1]->avl_link[da[j - 1]] = s; 291 | da[j] = 1; 292 | pa[j] = s; 293 | } 294 | } 295 | 296 | tree->avl_alloc->libavl_free (tree->avl_alloc, p); 297 | 298 | assert (k > 0); 299 | while (--k > 0) 300 | { 301 | struct avl_node *y = pa[k]; 302 | 303 | if (da[k] == 0) 304 | { 305 | y->avl_balance++; 306 | if (y->avl_balance == +1) 307 | break; 308 | else if (y->avl_balance == +2) 309 | { 310 | struct avl_node *x = y->avl_link[1]; 311 | if (x->avl_balance == -1) 312 | { 313 | struct avl_node *w; 314 | assert (x->avl_balance == -1); 315 | w = x->avl_link[0]; 316 | x->avl_link[0] = w->avl_link[1]; 317 | w->avl_link[1] = x; 318 | y->avl_link[1] = w->avl_link[0]; 319 | w->avl_link[0] = y; 320 | if (w->avl_balance == +1) 321 | x->avl_balance = 0, y->avl_balance = -1; 322 | else if (w->avl_balance == 0) 323 | x->avl_balance = y->avl_balance = 0; 324 | else /* |w->avl_balance == -1| */ 325 | x->avl_balance = +1, y->avl_balance = 0; 326 | w->avl_balance = 0; 327 | pa[k - 1]->avl_link[da[k - 1]] = w; 328 | } 329 | else 330 | { 331 | y->avl_link[1] = x->avl_link[0]; 332 | x->avl_link[0] = y; 333 | pa[k - 1]->avl_link[da[k - 1]] = x; 334 | if (x->avl_balance == 0) 335 | { 336 | x->avl_balance = -1; 337 | y->avl_balance = +1; 338 | break; 339 | } 340 | else 341 | x->avl_balance = y->avl_balance = 0; 342 | } 343 | } 344 | } 345 | else 346 | { 347 | y->avl_balance--; 348 | if (y->avl_balance == -1) 349 | break; 350 | else if (y->avl_balance == -2) 351 | { 352 | struct avl_node *x = y->avl_link[0]; 353 | if (x->avl_balance == +1) 354 | { 355 | struct avl_node *w; 356 | assert (x->avl_balance == +1); 357 | w = x->avl_link[1]; 358 | x->avl_link[1] = w->avl_link[0]; 359 | w->avl_link[0] = x; 360 | y->avl_link[0] = w->avl_link[1]; 361 | w->avl_link[1] = y; 362 | if (w->avl_balance == -1) 363 | x->avl_balance = 0, y->avl_balance = +1; 364 | else if (w->avl_balance == 0) 365 | x->avl_balance = y->avl_balance = 0; 366 | else /* |w->avl_balance == +1| */ 367 | x->avl_balance = -1, y->avl_balance = 0; 368 | w->avl_balance = 0; 369 | pa[k - 1]->avl_link[da[k - 1]] = w; 370 | } 371 | else 372 | { 373 | y->avl_link[0] = x->avl_link[1]; 374 | x->avl_link[1] = y; 375 | pa[k - 1]->avl_link[da[k - 1]] = x; 376 | if (x->avl_balance == 0) 377 | { 378 | x->avl_balance = +1; 379 | y->avl_balance = -1; 380 | break; 381 | } 382 | else 383 | x->avl_balance = y->avl_balance = 0; 384 | } 385 | } 386 | } 387 | } 388 | 389 | tree->avl_count--; 390 | tree->avl_generation++; 391 | return (void *) item; 392 | } 393 | 394 | /* Refreshes the stack of parent pointers in |trav| 395 | and updates its generation number. */ 396 | static void 397 | trav_refresh (struct avl_traverser *trav) 398 | { 399 | assert (trav != NULL); 400 | 401 | trav->avl_generation = trav->avl_table->avl_generation; 402 | 403 | if (trav->avl_node != NULL) 404 | { 405 | avl_comparison_func *cmp = trav->avl_table->avl_compare; 406 | void *param = trav->avl_table->avl_param; 407 | struct avl_node *node = trav->avl_node; 408 | struct avl_node *i; 409 | 410 | trav->avl_height = 0; 411 | for (i = trav->avl_table->avl_root; i != node; ) 412 | { 413 | assert (trav->avl_height < AVL_MAX_HEIGHT); 414 | assert (i != NULL); 415 | 416 | trav->avl_stack[trav->avl_height++] = i; 417 | i = i->avl_link[cmp (node->avl_data, i->avl_data, param) > 0]; 418 | } 419 | } 420 | } 421 | 422 | /* Initializes |trav| for use with |tree| 423 | and selects the null node. */ 424 | void 425 | avl_t_init (struct avl_traverser *trav, struct avl_table *tree) 426 | { 427 | trav->avl_table = tree; 428 | trav->avl_node = NULL; 429 | trav->avl_height = 0; 430 | trav->avl_generation = tree->avl_generation; 431 | } 432 | 433 | /* Initializes |trav| for |tree| 434 | and selects and returns a pointer to its least-valued item. 435 | Returns |NULL| if |tree| contains no nodes. */ 436 | void * 437 | avl_t_first (struct avl_traverser *trav, struct avl_table *tree) 438 | { 439 | struct avl_node *x; 440 | 441 | assert (tree != NULL && trav != NULL); 442 | 443 | trav->avl_table = tree; 444 | trav->avl_height = 0; 445 | trav->avl_generation = tree->avl_generation; 446 | 447 | x = tree->avl_root; 448 | if (x != NULL) 449 | while (x->avl_link[0] != NULL) 450 | { 451 | assert (trav->avl_height < AVL_MAX_HEIGHT); 452 | trav->avl_stack[trav->avl_height++] = x; 453 | x = x->avl_link[0]; 454 | } 455 | trav->avl_node = x; 456 | 457 | return x != NULL ? x->avl_data : NULL; 458 | } 459 | 460 | /* Initializes |trav| for |tree| 461 | and selects and returns a pointer to its greatest-valued item. 462 | Returns |NULL| if |tree| contains no nodes. */ 463 | void * 464 | avl_t_last (struct avl_traverser *trav, struct avl_table *tree) 465 | { 466 | struct avl_node *x; 467 | 468 | assert (tree != NULL && trav != NULL); 469 | 470 | trav->avl_table = tree; 471 | trav->avl_height = 0; 472 | trav->avl_generation = tree->avl_generation; 473 | 474 | x = tree->avl_root; 475 | if (x != NULL) 476 | while (x->avl_link[1] != NULL) 477 | { 478 | assert (trav->avl_height < AVL_MAX_HEIGHT); 479 | trav->avl_stack[trav->avl_height++] = x; 480 | x = x->avl_link[1]; 481 | } 482 | trav->avl_node = x; 483 | 484 | return x != NULL ? x->avl_data : NULL; 485 | } 486 | 487 | /* Searches for |item| in |tree|. 488 | If found, initializes |trav| to the item found and returns the item 489 | as well. 490 | If there is no matching item, initializes |trav| to the null item 491 | and returns |NULL|. */ 492 | void * 493 | avl_t_find (struct avl_traverser *trav, struct avl_table *tree, void *item) 494 | { 495 | struct avl_node *p, *q; 496 | 497 | assert (trav != NULL && tree != NULL && item != NULL); 498 | trav->avl_table = tree; 499 | trav->avl_height = 0; 500 | trav->avl_generation = tree->avl_generation; 501 | for (p = tree->avl_root; p != NULL; p = q) 502 | { 503 | int cmp = tree->avl_compare (item, p->avl_data, tree->avl_param); 504 | 505 | if (cmp < 0) 506 | q = p->avl_link[0]; 507 | else if (cmp > 0) 508 | q = p->avl_link[1]; 509 | else /* |cmp == 0| */ 510 | { 511 | trav->avl_node = p; 512 | return p->avl_data; 513 | } 514 | 515 | assert (trav->avl_height < AVL_MAX_HEIGHT); 516 | trav->avl_stack[trav->avl_height++] = p; 517 | } 518 | 519 | trav->avl_height = 0; 520 | trav->avl_node = NULL; 521 | return NULL; 522 | } 523 | 524 | /* Attempts to insert |item| into |tree|. 525 | If |item| is inserted successfully, it is returned and |trav| is 526 | initialized to its location. 527 | If a duplicate is found, it is returned and |trav| is initialized to 528 | its location. No replacement of the item occurs. 529 | If a memory allocation failure occurs, |NULL| is returned and |trav| 530 | is initialized to the null item. */ 531 | void * 532 | avl_t_insert (struct avl_traverser *trav, struct avl_table *tree, void *item) 533 | { 534 | void **p; 535 | 536 | assert (trav != NULL && tree != NULL && item != NULL); 537 | 538 | p = avl_probe (tree, item); 539 | if (p != NULL) 540 | { 541 | trav->avl_table = tree; 542 | trav->avl_node = 543 | ((struct avl_node *) 544 | ((char *) p - offsetof (struct avl_node, avl_data))); 545 | trav->avl_generation = tree->avl_generation - 1; 546 | return *p; 547 | } 548 | else 549 | { 550 | avl_t_init (trav, tree); 551 | return NULL; 552 | } 553 | } 554 | 555 | /* Initializes |trav| to have the same current node as |src|. */ 556 | void * 557 | avl_t_copy (struct avl_traverser *trav, const struct avl_traverser *src) 558 | { 559 | assert (trav != NULL && src != NULL); 560 | 561 | if (trav != src) 562 | { 563 | trav->avl_table = src->avl_table; 564 | trav->avl_node = src->avl_node; 565 | trav->avl_generation = src->avl_generation; 566 | if (trav->avl_generation == trav->avl_table->avl_generation) 567 | { 568 | trav->avl_height = src->avl_height; 569 | memcpy (trav->avl_stack, (const void *) src->avl_stack, 570 | sizeof *trav->avl_stack * trav->avl_height); 571 | } 572 | } 573 | 574 | return trav->avl_node != NULL ? trav->avl_node->avl_data : NULL; 575 | } 576 | 577 | /* Returns the next data item in inorder 578 | within the tree being traversed with |trav|, 579 | or if there are no more data items returns |NULL|. */ 580 | void * 581 | avl_t_next (struct avl_traverser *trav) 582 | { 583 | struct avl_node *x; 584 | 585 | assert (trav != NULL); 586 | 587 | if (trav->avl_generation != trav->avl_table->avl_generation) 588 | trav_refresh (trav); 589 | 590 | x = trav->avl_node; 591 | if (x == NULL) 592 | { 593 | return avl_t_first (trav, trav->avl_table); 594 | } 595 | else if (x->avl_link[1] != NULL) 596 | { 597 | assert (trav->avl_height < AVL_MAX_HEIGHT); 598 | trav->avl_stack[trav->avl_height++] = x; 599 | x = x->avl_link[1]; 600 | 601 | while (x->avl_link[0] != NULL) 602 | { 603 | assert (trav->avl_height < AVL_MAX_HEIGHT); 604 | trav->avl_stack[trav->avl_height++] = x; 605 | x = x->avl_link[0]; 606 | } 607 | } 608 | else 609 | { 610 | struct avl_node *y; 611 | 612 | do 613 | { 614 | if (trav->avl_height == 0) 615 | { 616 | trav->avl_node = NULL; 617 | return NULL; 618 | } 619 | 620 | y = x; 621 | x = trav->avl_stack[--trav->avl_height]; 622 | } 623 | while (y == x->avl_link[1]); 624 | } 625 | trav->avl_node = x; 626 | 627 | return x->avl_data; 628 | } 629 | 630 | /* Returns the previous data item in inorder 631 | within the tree being traversed with |trav|, 632 | or if there are no more data items returns |NULL|. */ 633 | void * 634 | avl_t_prev (struct avl_traverser *trav) 635 | { 636 | struct avl_node *x; 637 | 638 | assert (trav != NULL); 639 | 640 | if (trav->avl_generation != trav->avl_table->avl_generation) 641 | trav_refresh (trav); 642 | 643 | x = trav->avl_node; 644 | if (x == NULL) 645 | { 646 | return avl_t_last (trav, trav->avl_table); 647 | } 648 | else if (x->avl_link[0] != NULL) 649 | { 650 | assert (trav->avl_height < AVL_MAX_HEIGHT); 651 | trav->avl_stack[trav->avl_height++] = x; 652 | x = x->avl_link[0]; 653 | 654 | while (x->avl_link[1] != NULL) 655 | { 656 | assert (trav->avl_height < AVL_MAX_HEIGHT); 657 | trav->avl_stack[trav->avl_height++] = x; 658 | x = x->avl_link[1]; 659 | } 660 | } 661 | else 662 | { 663 | struct avl_node *y; 664 | 665 | do 666 | { 667 | if (trav->avl_height == 0) 668 | { 669 | trav->avl_node = NULL; 670 | return NULL; 671 | } 672 | 673 | y = x; 674 | x = trav->avl_stack[--trav->avl_height]; 675 | } 676 | while (y == x->avl_link[0]); 677 | } 678 | trav->avl_node = x; 679 | 680 | return x->avl_data; 681 | } 682 | 683 | /* Returns |trav|'s current item. */ 684 | void * 685 | avl_t_cur (struct avl_traverser *trav) 686 | { 687 | assert (trav != NULL); 688 | 689 | return trav->avl_node != NULL ? trav->avl_node->avl_data : NULL; 690 | } 691 | 692 | /* Replaces the current item in |trav| by |new| and returns the item replaced. 693 | |trav| must not have the null item selected. 694 | The new item must not upset the ordering of the tree. */ 695 | void * 696 | avl_t_replace (struct avl_traverser *trav, void *new) 697 | { 698 | void *old; 699 | 700 | assert (trav != NULL && trav->avl_node != NULL && new != NULL); 701 | old = trav->avl_node->avl_data; 702 | trav->avl_node->avl_data = new; 703 | return old; 704 | } 705 | 706 | /* Destroys |new| with |avl_destroy (new, destroy)|, 707 | first setting right links of nodes in |stack| within |new| 708 | to null pointers to avoid touching uninitialized data. */ 709 | static void 710 | copy_error_recovery (struct avl_node **stack, int height, 711 | struct avl_table *new, avl_item_func *destroy) 712 | { 713 | assert (stack != NULL && height >= 0 && new != NULL); 714 | 715 | for (; height > 2; height -= 2) 716 | stack[height - 1]->avl_link[1] = NULL; 717 | avl_destroy (new, destroy); 718 | } 719 | 720 | /* Copies |org| to a newly created tree, which is returned. 721 | If |copy != NULL|, each data item in |org| is first passed to |copy|, 722 | and the return values are inserted into the tree, 723 | with |NULL| return values taken as indications of failure. 724 | On failure, destroys the partially created new tree, 725 | applying |destroy|, if non-null, to each item in the new tree so far, 726 | and returns |NULL|. 727 | If |allocator != NULL|, it is used for allocation in the new tree. 728 | Otherwise, the same allocator used for |org| is used. */ 729 | struct avl_table * 730 | avl_copy (const struct avl_table *org, avl_copy_func *copy, 731 | avl_item_func *destroy, struct libavl_allocator *allocator) 732 | { 733 | struct avl_node *stack[2 * (AVL_MAX_HEIGHT + 1)]; 734 | int height = 0; 735 | 736 | struct avl_table *new; 737 | const struct avl_node *x; 738 | struct avl_node *y; 739 | 740 | assert (org != NULL); 741 | new = avl_create (org->avl_compare, org->avl_param, 742 | allocator != NULL ? allocator : org->avl_alloc); 743 | if (new == NULL) 744 | return NULL; 745 | new->avl_count = org->avl_count; 746 | if (new->avl_count == 0) 747 | return new; 748 | 749 | x = (const struct avl_node *) &org->avl_root; 750 | y = (struct avl_node *) &new->avl_root; 751 | for (;;) 752 | { 753 | while (x->avl_link[0] != NULL) 754 | { 755 | assert (height < 2 * (AVL_MAX_HEIGHT + 1)); 756 | 757 | y->avl_link[0] = 758 | new->avl_alloc->libavl_malloc (new->avl_alloc, 759 | sizeof *y->avl_link[0]); 760 | if (y->avl_link[0] == NULL) 761 | { 762 | if (y != (struct avl_node *) &new->avl_root) 763 | { 764 | y->avl_data = NULL; 765 | y->avl_link[1] = NULL; 766 | } 767 | 768 | copy_error_recovery (stack, height, new, destroy); 769 | return NULL; 770 | } 771 | 772 | stack[height++] = (struct avl_node *) x; 773 | stack[height++] = y; 774 | x = x->avl_link[0]; 775 | y = y->avl_link[0]; 776 | } 777 | y->avl_link[0] = NULL; 778 | 779 | for (;;) 780 | { 781 | y->avl_balance = x->avl_balance; 782 | if (copy == NULL) 783 | y->avl_data = x->avl_data; 784 | else 785 | { 786 | y->avl_data = copy (x->avl_data, org->avl_param); 787 | if (y->avl_data == NULL) 788 | { 789 | y->avl_link[1] = NULL; 790 | copy_error_recovery (stack, height, new, destroy); 791 | return NULL; 792 | } 793 | } 794 | 795 | if (x->avl_link[1] != NULL) 796 | { 797 | y->avl_link[1] = 798 | new->avl_alloc->libavl_malloc (new->avl_alloc, 799 | sizeof *y->avl_link[1]); 800 | if (y->avl_link[1] == NULL) 801 | { 802 | copy_error_recovery (stack, height, new, destroy); 803 | return NULL; 804 | } 805 | 806 | x = x->avl_link[1]; 807 | y = y->avl_link[1]; 808 | break; 809 | } 810 | else 811 | y->avl_link[1] = NULL; 812 | 813 | if (height <= 2) 814 | return new; 815 | 816 | y = stack[--height]; 817 | x = stack[--height]; 818 | } 819 | } 820 | } 821 | 822 | /* Frees storage allocated for |tree|. 823 | If |destroy != NULL|, applies it to each data item in inorder. */ 824 | void 825 | avl_destroy (struct avl_table *tree, avl_item_func *destroy) 826 | { 827 | struct avl_node *p, *q; 828 | 829 | assert (tree != NULL); 830 | 831 | for (p = tree->avl_root; p != NULL; p = q) 832 | if (p->avl_link[0] == NULL) 833 | { 834 | q = p->avl_link[1]; 835 | if (destroy != NULL && p->avl_data != NULL) 836 | destroy (p->avl_data, tree->avl_param); 837 | tree->avl_alloc->libavl_free (tree->avl_alloc, p); 838 | } 839 | else 840 | { 841 | q = p->avl_link[0]; 842 | p->avl_link[0] = q->avl_link[1]; 843 | q->avl_link[1] = p; 844 | } 845 | 846 | tree->avl_alloc->libavl_free (tree->avl_alloc, tree); 847 | } 848 | 849 | /* Allocates |size| bytes of space using |malloc()|. 850 | Returns a null pointer if allocation fails. */ 851 | void * 852 | avl_malloc (struct libavl_allocator *allocator, size_t size) 853 | { 854 | assert (allocator != NULL && size > 0); 855 | return malloc (size); 856 | } 857 | 858 | /* Frees |block|. */ 859 | void 860 | avl_free (struct libavl_allocator *allocator, void *block) 861 | { 862 | assert (allocator != NULL && block != NULL); 863 | free (block); 864 | } 865 | 866 | /* Default memory allocator that uses |malloc()| and |free()|. */ 867 | struct libavl_allocator avl_allocator_default = 868 | { 869 | avl_malloc, 870 | avl_free 871 | }; 872 | 873 | #undef NDEBUG 874 | #include 875 | 876 | /* Asserts that |avl_insert()| succeeds at inserting |item| into |table|. */ 877 | void 878 | (avl_assert_insert) (struct avl_table *table, void *item) 879 | { 880 | void **p = avl_probe (table, item); 881 | assert (p != NULL && *p == item); 882 | } 883 | 884 | /* Asserts that |avl_delete()| really removes |item| from |table|, 885 | and returns the removed item. */ 886 | void * 887 | (avl_assert_delete) (struct avl_table *table, void *item) 888 | { 889 | void *p = avl_delete (table, item); 890 | assert (p != NULL); 891 | return p; 892 | } 893 | 894 | -------------------------------------------------------------------------------- /src/libavl/avl.h: -------------------------------------------------------------------------------- 1 | /* Produced by texiweb from libavl.w. */ 2 | 3 | /* libavl - library for manipulation of binary trees. 4 | Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc. 5 | 6 | This program is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License as 8 | published by the Free Software Foundation; either version 2 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | See the GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 19 | 02111-1307, USA. 20 | 21 | The author may be contacted at on the Internet, or 22 | write to Ben Pfaff, Stanford University, Computer Science Dept., 353 23 | Serra Mall, Stanford CA 94305, USA. 24 | */ 25 | 26 | #ifndef AVL_H 27 | #define AVL_H 1 28 | 29 | #include 30 | 31 | /* Function types. */ 32 | typedef int avl_comparison_func (const void *avl_a, const void *avl_b, 33 | void *avl_param); 34 | typedef void avl_item_func (void *avl_item, void *avl_param); 35 | typedef void *avl_copy_func (void *avl_item, void *avl_param); 36 | 37 | #ifndef LIBAVL_ALLOCATOR 38 | #define LIBAVL_ALLOCATOR 39 | /* Memory allocator. */ 40 | struct libavl_allocator 41 | { 42 | void *(*libavl_malloc) (struct libavl_allocator *, size_t libavl_size); 43 | void (*libavl_free) (struct libavl_allocator *, void *libavl_block); 44 | }; 45 | #endif 46 | 47 | /* Default memory allocator. */ 48 | extern struct libavl_allocator avl_allocator_default; 49 | void *avl_malloc (struct libavl_allocator *, size_t); 50 | void avl_free (struct libavl_allocator *, void *); 51 | 52 | /* Maximum AVL height. */ 53 | #ifndef AVL_MAX_HEIGHT 54 | #define AVL_MAX_HEIGHT 32 55 | #endif 56 | 57 | /* Tree data structure. */ 58 | struct avl_table 59 | { 60 | struct avl_node *avl_root; /* Tree's root. */ 61 | avl_comparison_func *avl_compare; /* Comparison function. */ 62 | void *avl_param; /* Extra argument to |avl_compare|. */ 63 | struct libavl_allocator *avl_alloc; /* Memory allocator. */ 64 | size_t avl_count; /* Number of items in tree. */ 65 | unsigned long avl_generation; /* Generation number. */ 66 | }; 67 | 68 | /* An AVL tree node. */ 69 | struct avl_node 70 | { 71 | struct avl_node *avl_link[2]; /* Subtrees. */ 72 | void *avl_data; /* Pointer to data. */ 73 | signed char avl_balance; /* Balance factor. */ 74 | }; 75 | 76 | /* AVL traverser structure. */ 77 | struct avl_traverser 78 | { 79 | struct avl_table *avl_table; /* Tree being traversed. */ 80 | struct avl_node *avl_node; /* Current node in tree. */ 81 | struct avl_node *avl_stack[AVL_MAX_HEIGHT]; 82 | /* All the nodes above |avl_node|. */ 83 | size_t avl_height; /* Number of nodes in |avl_parent|. */ 84 | unsigned long avl_generation; /* Generation number. */ 85 | }; 86 | 87 | /* Table functions. */ 88 | struct avl_table *avl_create (avl_comparison_func *, void *, 89 | struct libavl_allocator *); 90 | struct avl_table *avl_copy (const struct avl_table *, avl_copy_func *, 91 | avl_item_func *, struct libavl_allocator *); 92 | void avl_destroy (struct avl_table *, avl_item_func *); 93 | void **avl_probe (struct avl_table *, void *); 94 | void *avl_insert (struct avl_table *, void *); 95 | void *avl_replace (struct avl_table *, void *); 96 | void *avl_delete (struct avl_table *, const void *); 97 | void *avl_find (const struct avl_table *, const void *); 98 | void avl_assert_insert (struct avl_table *, void *); 99 | void *avl_assert_delete (struct avl_table *, void *); 100 | 101 | #define avl_count(table) ((size_t) (table)->avl_count) 102 | 103 | /* Table traverser functions. */ 104 | void avl_t_init (struct avl_traverser *, struct avl_table *); 105 | void *avl_t_first (struct avl_traverser *, struct avl_table *); 106 | void *avl_t_last (struct avl_traverser *, struct avl_table *); 107 | void *avl_t_find (struct avl_traverser *, struct avl_table *, void *); 108 | void *avl_t_insert (struct avl_traverser *, struct avl_table *, void *); 109 | void *avl_t_copy (struct avl_traverser *, const struct avl_traverser *); 110 | void *avl_t_next (struct avl_traverser *); 111 | void *avl_t_prev (struct avl_traverser *); 112 | void *avl_t_cur (struct avl_traverser *); 113 | void *avl_t_replace (struct avl_traverser *, void *); 114 | 115 | #endif /* avl.h */ 116 | -------------------------------------------------------------------------------- /src/logtop.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Julien Palard. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include "logtop.h" 29 | 30 | struct logtop *new_logtop(size_t history_size) 31 | { 32 | struct logtop *this; 33 | 34 | this = malloc(sizeof(*this)); 35 | if (this == NULL) 36 | return NULL; 37 | this->history_start = 0; 38 | this->history_size = history_size; 39 | this->strings = NULL; 40 | this->history = new_history(this); 41 | this->top = new_avl(this); 42 | return this; 43 | } 44 | 45 | void delete_logtop(struct logtop *this) 46 | { 47 | delete_history(this); 48 | delete_avl(this); 49 | free(this); 50 | } 51 | 52 | void logtop_feed(struct logtop *this, char *string) 53 | { 54 | log_line_t *element; 55 | 56 | element = avl_get(this, string); 57 | avl_increment(this, element); 58 | history_update(this, element); 59 | } 60 | 61 | static void logtop_get_fill(log_line_t *line, int index, 62 | struct logtop_state *state) 63 | { 64 | state->lines[index - 1] = line; 65 | if (state->timespan != 0) 66 | line->frequency = line->count / state->timespan; 67 | else 68 | line->frequency = HUGE_VAL; 69 | } 70 | 71 | struct logtop_state *logtop_get(struct logtop *this, size_t qte) 72 | { 73 | struct logtop_state *state; 74 | 75 | state = malloc(sizeof(*state)); 76 | if (state == NULL) 77 | goto fail_state; 78 | state->lines = calloc(qte + 1, sizeof(*state->lines)); 79 | if (state->lines == NULL) 80 | goto fail_lines; 81 | state->timespan = logtop_timespan(this); 82 | state->count = history_length(this); 83 | state->frequency = (state->timespan != 0) 84 | ? state->count / state->timespan 85 | : HUGE_VAL; 86 | avl_traverse(this, qte, (void (*)(void*, int, void*))logtop_get_fill, 87 | state); 88 | return state; 89 | fail_lines: 90 | free(state); 91 | fail_state: 92 | return NULL; 93 | } 94 | 95 | void delete_logtop_state(struct logtop_state *this) 96 | { 97 | free(this->lines); 98 | free(this); 99 | } 100 | 101 | double logtop_timespan(struct logtop *this) 102 | { 103 | history_element_t *oldest_element; 104 | 105 | oldest_element = history_oldest_element(this); 106 | if (oldest_element != NULL) 107 | return difftime(time(NULL), oldest_element->time); 108 | else 109 | return 0; 110 | } 111 | 112 | unsigned int logtop_qte_of_elements(struct logtop *this) 113 | { 114 | return history_length(this); 115 | } 116 | -------------------------------------------------------------------------------- /src/logtop.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Julien Palard. All rights reserved. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __FREQUENCY_H__ 27 | #define __FREQUENCY_H__ 28 | 29 | #include 30 | #include 31 | 32 | #include "libavl/avl.h" 33 | #include "logtop.h" 34 | 35 | typedef struct s_log_line 36 | { 37 | char *string; 38 | char *repr; 39 | unsigned int count; 40 | double frequency; /* Not live, updated lazily in logto_get */ 41 | UT_hash_handle hh; 42 | } log_line_t; 43 | 44 | struct logtop_state 45 | { 46 | double timespan; 47 | unsigned int count; 48 | double frequency; 49 | log_line_t **lines; 50 | }; 51 | 52 | typedef struct s_history_element 53 | { 54 | log_line_t *log_entry; 55 | time_t time; 56 | } history_element_t; 57 | 58 | struct logtop 59 | { 60 | log_line_t *strings; 61 | struct avl_table *top; 62 | history_element_t *history; 63 | unsigned int history_start; 64 | unsigned int history_size; 65 | }; 66 | 67 | history_element_t *new_history(struct logtop *this); 68 | void delete_history(struct logtop *this); 69 | history_element_t *history_oldest_element(struct logtop *this); 70 | history_element_t *history_newest_element(struct logtop *this); 71 | unsigned int history_length(struct logtop *this); 72 | void history_update(struct logtop *this, log_line_t *element); 73 | 74 | struct avl_table *new_avl(struct logtop *this); 75 | void delete_avl(struct logtop *this); 76 | log_line_t *avl_get(struct logtop *this, char *); 77 | void avl_increment(struct logtop *this, log_line_t *); 78 | void avl_decrement(struct logtop *this, log_line_t *); 79 | void avl_traverse(struct logtop *this, unsigned int length, 80 | void (*visitor)(void *data, int index, void *user_data), 81 | void *user_data); 82 | 83 | struct logtop *new_logtop(size_t history_size); 84 | void delete_logtop(struct logtop *this); 85 | void logtop_feed(struct logtop *this, char *line); 86 | struct logtop_state *logtop_get(struct logtop *this, size_t qte); 87 | void delete_logtop_state(struct logtop_state *this); 88 | double logtop_timespan(struct logtop *this); 89 | unsigned int logtop_qte_of_elements(struct logtop *this); 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Julien Palard. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #define _GNU_SOURCE 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "main.h" 34 | 35 | env_t gl_env; 36 | 37 | /** 38 | * Basic sig handling using sigaction. 39 | * Reset action to SIG_DFL if act is NULL. 40 | */ 41 | void setup_sighandler(int signum, int flags, void (*act)(int)) 42 | { 43 | struct sigaction sa; 44 | 45 | sigemptyset(&sa.sa_mask); 46 | if (act != NULL) 47 | sa.sa_handler = act; 48 | else 49 | sa.sa_handler = SIG_DFL; 50 | sa.sa_flags = flags; 51 | sigaction(signum, &sa, NULL); 52 | } 53 | 54 | static void update_display(int sig __attribute__((unused))) 55 | { 56 | time_t current_time; 57 | 58 | if (gl_env.quiet) 59 | return ; 60 | current_time = time(NULL); 61 | if (current_time < gl_env.last_update_time + gl_env.interval) 62 | return ; 63 | gl_env.last_update_time = current_time; 64 | if (gl_env.line_by_line) 65 | stdout_update(gl_env.line_by_line, 1); 66 | else 67 | curses_update(); 68 | alarm(1); 69 | } 70 | 71 | static void run(struct logtop *logtop) 72 | { 73 | char *string; 74 | size_t size; 75 | ssize_t str_length; 76 | 77 | string = NULL; 78 | size = 0; 79 | while ((str_length = getline(&string, &size, stdin)) != -1) 80 | { 81 | while (str_length > 0 && (string[str_length - 1] == '\n' 82 | || string[str_length - 1] == '\r')) 83 | { 84 | string[str_length - 1] = '\0'; 85 | str_length -= 1; 86 | } 87 | logtop_feed(logtop, string); 88 | } 89 | if (string != NULL) 90 | free(string); 91 | } 92 | 93 | static void usage_and_exit(int exit_code) 94 | { 95 | fprintf(exit_code == EXIT_SUCCESS ? stdout : stderr, 96 | "Usage: tail -f something | logtop [OPTIONS]\n" 97 | " -s, --size=NUM Number of log line to keep in memory\n" 98 | " Defaults to : " 99 | STRINGIFY(DEFAULT_HISTORY_SIZE) "\n" 100 | " -q, --quiet Quiet, only display a top 10 at exit.\n" 101 | " -l, --line-by-line=NUM Print result line by line\n" 102 | " in a machine friendly format,\n" 103 | " NUM: quantity of result by line.\n"); 104 | fprintf(exit_code == EXIT_SUCCESS ? stdout : stderr, 105 | " -i, --interval=NUM Interval between graphical updates,\n" 106 | " in seconds. Defaults to 1.\n" 107 | "\n" 108 | " Line by line format is : [%%d %%f %%s\\t]*\\n\n" 109 | " %%d : Number of occurences\n" 110 | " %%f : Frequency of apparition\n" 111 | " %%s : String (Control chars replaced by dots).\n" 112 | "\n"); 113 | exit(exit_code); 114 | } 115 | 116 | static void version_and_exit(void) 117 | { 118 | #define stringify(v) #v 119 | #define concat_version(v) "logtop v" stringify(v) "\n" 120 | fprintf(stdout, concat_version(VERSION)); 121 | exit(EXIT_SUCCESS); 122 | } 123 | 124 | static void parse_args(int ac, char **av) 125 | { 126 | int c; 127 | 128 | gl_env.history_size = 0; 129 | gl_env.quiet = 0; 130 | gl_env.last_update_time = 0; 131 | gl_env.line_by_line = 0; 132 | gl_env.interval = 1; 133 | while (1) 134 | { 135 | int option_index = 0; 136 | static struct option long_options[] = { 137 | {"size", 1, 0, 's'}, 138 | {"quiet", 0, 0, 'q'}, 139 | {"help", 0, 0, 'h'}, 140 | {"version", 0, 0, 'v'}, 141 | {"line-by-line", 1, 0, 'l'}, 142 | {"interval", 1, 0, 'i'}, 143 | {0, 0, 0, 0} 144 | }; 145 | c = getopt_long(ac, av, "qhvl:s:i:", 146 | long_options, &option_index); 147 | if (c == -1) 148 | break; 149 | switch (c) 150 | { 151 | case 'l': 152 | gl_env.line_by_line = atoi(optarg); 153 | break; 154 | case 'q': 155 | gl_env.quiet = 1; 156 | break; 157 | case 's': 158 | gl_env.history_size = atoi(optarg); 159 | break; 160 | case 'i': 161 | gl_env.interval = atoi(optarg); 162 | break; 163 | case 'v': 164 | version_and_exit(); 165 | case 'h': 166 | usage_and_exit(EXIT_SUCCESS); 167 | default: 168 | usage_and_exit(EXIT_FAILURE); 169 | } 170 | } 171 | if (isatty(fileno(stdin))) 172 | usage_and_exit(EXIT_FAILURE); 173 | if (gl_env.history_size == 0) 174 | gl_env.history_size = DEFAULT_HISTORY_SIZE; 175 | } 176 | 177 | static void at_exit(void) 178 | { 179 | if (!gl_env.quiet && !gl_env.line_by_line) 180 | curses_release(); 181 | if (gl_env.line_by_line) 182 | stdout_update(gl_env.line_by_line, 1); 183 | else 184 | stdout_update(10, 0); 185 | delete_logtop(gl_env.logtop); 186 | fflush(NULL); 187 | } 188 | 189 | static void on_sigint(int sig) 190 | { 191 | setup_sighandler(SIGINT, 0, NULL); 192 | at_exit(); 193 | kill(getpid(), sig); 194 | } 195 | 196 | int main(int ac, char **av) 197 | { 198 | setlocale(LC_ALL, ""); 199 | parse_args(ac, av); 200 | setup_sighandler(SIGINT, 0, on_sigint); 201 | setup_sighandler(SIGALRM, SA_RESTART, update_display); 202 | alarm(1); 203 | gl_env.last_update_time = time(NULL); 204 | gl_env.logtop = new_logtop(gl_env.history_size); 205 | if (!gl_env.quiet && !gl_env.line_by_line) 206 | curses_setup(); 207 | run(gl_env.logtop); 208 | at_exit(); 209 | return EXIT_SUCCESS; 210 | } 211 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Julien Palard. All rights reserved. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __LOGTOP_H__ 27 | #define __LOGTOP_H__ 28 | 29 | #include 30 | 31 | #include "logtop.h" 32 | 33 | #ifndef STRINGIFY 34 | # define __LOGTOP_STRINGIFY(x) #x 35 | # define STRINGIFY(x) __LOGTOP_STRINGIFY(x) 36 | #endif 37 | 38 | #define DEFAULT_HISTORY_SIZE 10000 39 | 40 | typedef struct s_env 41 | { 42 | int quiet; 43 | int line_by_line; 44 | unsigned int history_size; 45 | unsigned int display_width; 46 | unsigned int display_height; 47 | time_t last_update_time; 48 | time_t interval; 49 | struct logtop *logtop; 50 | } env_t; 51 | 52 | extern env_t gl_env; 53 | 54 | void curses_setup(void); 55 | void curses_release(void); 56 | void curses_update(void); 57 | void stdout_update(int nb_lines, int line_by_line); 58 | void setup_sighandler(int signum, int flags, void (*act)(int)); 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/stdout.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Julien Palard. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #include 27 | #include "main.h" 28 | 29 | struct display_data 30 | { 31 | double duration; 32 | unsigned int qte_of_elements; 33 | }; 34 | 35 | typedef void (*result_printer)(void *data, int index, void *metadata); 36 | 37 | static void display_line(void *data, int index, void *metadata) 38 | { 39 | log_line_t *line; 40 | 41 | line = (log_line_t *)data; 42 | printf("%4d %6d %8.2f %-*s\n", 43 | index, 44 | line->count, 45 | line->count / ((struct display_data *)metadata)->duration, 46 | gl_env.display_width - 21, line->repr); 47 | } 48 | 49 | static void display_result(void *data, 50 | int index __attribute__((unused)), 51 | void *metadata) 52 | { 53 | log_line_t *line; 54 | 55 | line = (log_line_t *)data; 56 | printf("%d %.2f %s\t", 57 | line->count, 58 | line->count / ((struct display_data *)metadata)->duration, 59 | line->repr); 60 | } 61 | 62 | static void display_header(struct display_data *display_data) 63 | { 64 | printf("%d lines, %.2f lines/s\n", 65 | display_data->qte_of_elements, 66 | display_data->qte_of_elements / (double)display_data->duration); 67 | printf("%s\n", "RANK CNT LINE/S LINE"); 68 | } 69 | 70 | static const result_printer printers[2] = {display_line, 71 | display_result}; 72 | 73 | 74 | void stdout_update(int nb_results, int line_by_line) 75 | { 76 | struct display_data display_data; 77 | 78 | display_data.duration = logtop_timespan(gl_env.logtop); 79 | display_data.qte_of_elements = logtop_qte_of_elements(gl_env.logtop); 80 | if (display_data.duration == 0) 81 | display_data.duration = 1; 82 | if (line_by_line) 83 | { 84 | avl_traverse(gl_env.logtop, nb_results, 85 | display_result, 86 | &display_data); 87 | printf("\n"); 88 | } 89 | else 90 | { 91 | display_header(&display_data); 92 | avl_traverse(gl_env.logtop, nb_results, 93 | display_line, 94 | &display_data); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import os.path as path 5 | sys.path.append(path.realpath(__file__ + '/../../')) 6 | 7 | from threading import Thread, Lock 8 | import time 9 | from logtop import logtop # use make python-module to build it 10 | 11 | 12 | def build_lines(): 13 | while True: 14 | for i in range(10): 15 | yield str(i % 3) 16 | time.sleep(1) 17 | time.sleep(10) 18 | 19 | 20 | class LogtopPrinter(Thread): 21 | def __init__(self): 22 | self.lock = Lock() 23 | self.logtop = logtop(10000) 24 | Thread.__init__(self) 25 | self.daemon = True 26 | 27 | def run(self): 28 | while True: 29 | self.lock.acquire() 30 | data = self.logtop.get(6) 31 | count = self.logtop.qte_of_elements() 32 | timespan = self.logtop.timespan() 33 | self.lock.release() 34 | if timespan == 0: 35 | timespan = 1 36 | print "%.2f l/s (%d lines, %.2f seconds): %s" % (count / timespan, 37 | count, 38 | timespan, 39 | repr(data)) 40 | time.sleep(1) 41 | 42 | def feed(self, line): 43 | self.lock.acquire() 44 | self.logtop.feed(line) 45 | self.lock.release() 46 | 47 | 48 | printer = LogtopPrinter() 49 | printer.start() 50 | 51 | for line in build_lines(): 52 | printer.feed(line) 53 | --------------------------------------------------------------------------------