multitail-6.0/0000755000175000017500000000000012245202637013264 5ustar folkertfolkertmultitail-6.0/multitail.conf0000644000175000017500000012516712245202637016153 0ustar folkertfolkert# Format of this file: # # include:configfile # Also pars 'configfile'. # # defaultcscheme: # Selects the default color scheme to use. If this one is set, you # no longer need -cS/-CS. # # colorscheme: # This name can be given for the commandline-parameter -cS. That # way, one can select what colorscheme to use for the next # logfile. # # cs_re:: # This defines a regular expression to find a particular string. # # color: [fg],[bg],[attribute[/otherattribute]][|other colorpair+attribute] # e.g.: red,,bold|red would give bold red for line 1 and just red for line 2, etc. # Possible colors: red, green, yellow, blue, magenta, cyan and white. # # # cs_re_s:: # Like cs_re but only the substrings are used(!). E.g.: # ^....(...)...(...) # In the example above only what matches between '(' and ')' is # assigned a color. See the 'acctail' colorscheme for an example. # # cs_re_val_less::: # cs_re_val_bigger::: # cs_re_val_equal::: # Like cs_re_s(!) but here the value of the sub(!)-string is # compared against the given value. e.g.: # cs_re_val_less: if "" less then the value matched with # the regular expression, then use the selected color. Please # note that you have to select the value in the string with a # substring match; put '(' and ')' around it. # # mcsre:color:regexp # mcsre_s:color:regexp # mcsre_val_less/bigger/equal:color:regexp # These work like their cs_re* sisters only they merge their # attributes (colors, bold, bright, etd) with the previous # merging one. # # scheme:: # This defines what colorscheme to use when the filename matches # the given regular expression. This way, one can automatically # use the correct colorscheme for certain files. # # check_mail: # How often MultiTail should check for new e-mail. You can switch # mail-checking off by setting this value to 0. # # tab_stop: # Specifies the width of TAB characters. Default-value is 4. # # bind:key:program # Binds a key to a external command. For example: # bind:^j:/bin/bash - binds ^j (control + j) to bash. While the # selected program is running, MultiTail is suspended. MultiTail # will automatically come back when the external command ends. # # titlebar: # what to put in the titlebar of the xterm in which MultiTail is # running. see below for details # # tail:tail_program # path to the tail-program (in case you want to use turbotail or # so instead of the regular tail) # # bright:(1|0) # use bright colors (1) or not (0) # # abbreviate_filesize # wether to abbreviate filesizes to xKB/MB/GB (1) or not (0) # # show_subwindow_id # when set to 1 and merging multiple inputs (e.g. logfiles) the # output show is prepended with a number indicating the input # # markerline_color # with this one you can set how a markerline looks. e.g. yellow on # red or so # # markerline_timestamp # sets wether to put a timestamp in the markerline (1) or not (0) # # ts_format: # format of timestamps. see the man-page of 'strftime' for a list # of the fields that can be used # # shell: # shell to use when invoking programs. default is /bin/sh # # umask: # umask used when creating files # #defaultcscheme:syslog # # /var/log/messages & .../syslog colorscheme:syslog:kernel and unsorted messages # segfaults cs_re_s:red,,bold:kernel: ([^:]*): segfault at [^ ]* rip [^ ]* rsp [^ ]* error cs_re:yellow:kernel: .*: segfault at [^ ]* rip [^ ]* rsp [^ ]* error # misc cs_re:red,,inverse/underline:kernel cs_re:green:Detected.*$ cs_re:green:\[ cs_re:green:\] cs_re:cyan:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re_s:,,bold:^... .. ..:..:.. [^ ]* ([A-z0-9]*) cs_re:red:scanlogd.* cs_re:yellow:Did.not # # ssh colorscheme:ssh:www.openssh.org cs_re:yellow:error: Could not get shadow information for.* cs_re:yellow:fatal: Timeout before authentication for.* cs_re_s:red,,bold:error: PAM: Authentication failure for(.*) cs_re:red:error: PAM: Authentication failure for cs_re:red,,blink:error: Bind to port [0-9]* on [^ ]* failed: Address already in use. cs_re_s:red,,bold:error: PAM: Authentication failure for ([^ ]*) from (.*) cs_re:red:error: PAM: Authentication failure for ([^ ]*) from (.*) cs_re_s:green,,bold:Accepted [^ ]* for ([^ ]*) from ([^ ]*) port ([0-9]*) ssh2 cs_re:green:Accepted [^ ]* for [^ ]* from [^ ]* port.* cs_re:red:PAM session setup failed\[[0-9]*\]:.* cs_re_s:yellow,,bold:Failed ([^ ]*) for ([^ ]*) from ([^ ]*) port ([0-9]*).* cs_re:yellow:Failed [^ ]* for [^ ]* from [^ ]* port [0-9]* .* cs_re:red:Disconnecting: Too many authentication failures for.* # # PowerDNS colorscheme:powerdns:www.powerdns.com cs_re_s:green:Remote\ (.*)\ wants cs_re:blue:'.*' cs_re:red:MISS cs_re:green,,bold:HIT # # ADB logcat colorscheme:logcat cs_re_s:blue,,bold:^./(dalvikvm)\( cs_re_s:blue,,bold:^./(Process)\( cs_re_s:cyan:^./(ActivityManager)\( cs_re_s:cyan:^./(ActivityThread)\( cs_re_s:white,,bold:^./([^\(]*)\( cs_re_s:green:^[^\(]*(\()[^\)]*(\)) cs_re:red,,inverse:[Pp]ermission [Dd]eni[ae][dl] cs_re:red,,inverse:Caused by: cs_re:cyan:: #cs_re:red,,inverse:^F #cs_re:red,,bold:^E #cs_re:yellow,,bold:^W #cs_re:cyan,,bold:^I #cs_re:green,,bold:^V #cs_re:white:^D cs_re_s:red,,inverse:^(F)/[^:]*: (.*)$ cs_re_s:red:^(E)/[^:]*: (.*)$ cs_re_s:yellow:^(W).[^:]*: (.*)$ #cs_re_s:green:^(I).[^:]*: (.*)$ cs_re_s:green:^(V)/[^:]*: (.*)$ cs_re_s:black,,bold:^(D)/[^:]*: (.*)$ # # linux iptables firewall colorscheme:liniptfw:Linux IPtables (2.6.x kernel) cs_re:cyan:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re_s:red:kernel: .*(DPT=[0-9]*) cs_re_s:yellow:kernel: (IN=[^ ]*) cs_re_s:cyan:kernel: .*(SRC=[^ ]*) *(DST=[^ ]*) cs_re_s:green:kernel: .*(PROTO=[^ ]*) # # postfix log colorscheme:postfix:www.postfix.org cs_re:cyan:: cs_re:yellow:status=sent cs_re:magenta:queue.active cs_re:green:from=.*> cs_re:red:to=.*> cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re:green:\[ cs_re:green:\] # # apache colorscheme:apache:default Apache logging (webserver) cs_re:red: 404 cs_re:cyan:: cs_re:green:\[ cs_re:green:\] # ip-adresses in the format x.x.x.x cs_re:yellow:^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\} # hostnames cs_re:yellow:^[^ ]* ### Apache errorlog colorscheme:apache_error:default Apache error logging cs_re:yellow:\[client .*\] cs_re:red: [^ ]*$ cs_re:blue:^\[... ... .. ..:..:.. ....\] # # rsstail (http://www.vanheusden.com/rsstail/) colorscheme:rsstail:RSSTail output (RSS feed reader) cs_re:cyan:: cs_re:cyan:/ cs_re:blue:^.......... ..:..:.. cs_re:green:Title:.* cs_re:red:^Link:.* cs_re:yellow:^Description: # # acctail (http://www.vanheusden.com/acctail/) colorscheme:acctail:(BSD-) process accounting reader cs_re:green:^................ cs_re_s:red:^.................(....) cs_re_s:cyan:^......................(........) cs_re_s:yellow:^...............................(........) cs_re:blue:\. # # wtmptail (http://www.vanheusden.com/wtmptail/) colorscheme:wtmptail:www.vanheusden.com/wtmptail/ cs_re:blue:\. cs_re:blue:: cs_re:yellow:^USER.* cs_re:green:^........ cs_re:cyan:[0-9]*:[0-9]*[ap]m cs_re:red:^.................................. cs_re:black,red,blink:BOOT cs_re:black,yellow:DEAD # # squid colorscheme:squid:http proxy server cs_re:blue:^[^ ]* cs_re_s:yellow:^[^ ]* *[0-9]* *([^ ]*) cs_re_s:green:^[^ ]* *[0-9]* *[^ ]* ([^ ]*) cs_re_s:cyan:^[^ ]* *[0-9]* *[^ ]* [^ ]* *[0-9]* *[^ ]* *([^ ]*) # # asterisk colorscheme:asterisk:software PBX cs_re:blue:: cs_re:cyan:^............... cs_re:red:ERROR cs_re:yellow:WARNING cs_re:green:NOTICE cs_re:magenta:DEBUG cs_re:magenta:VERBOSE cs_re:red,black,underline:Unable to.* # # sendmail colorscheme:sendmail cs_re:blue|blue,,bold:^............... cs_re:cyan:to=<[^>]*> cs_re:yellow:stat=Deferred.* cs_re:red:stat=User unknown cs_re:green:stat=Sent.* cs_re:magenta:relay=[^ ]* cs_re:cyan:: cs_re:red:verify=FAIL # # MailScanner colorscheme:mailscanner:wrapper around sendmail/clamav/spamassassin cs_re:red:Detected HTML-specific exploits in.* cs_re:magenta:Delivered [0-9]* warnings to virus senders cs_re:yellow:Spam Checks: Found [0-9]* spam messages cs_re:yellow:Content Checks: Detected and will disarm HTML message in.* cs_re:green:Uninfected: Delivered [0-9]* messages cs_re:red:Infected message [^ ]* came from.* cs_re:yellow:Saved infected "[^"]*" to.* cs_re:blue|blue,,bold:^............... cs_re:cyan:: # # SpamAssassin colorscheme:spamassassin cs_re:magenta:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re:yellow:autolearn=failed cs_re:red,,blink:server killed by [^,]*, shutting down cs_re_s:yellow,,bold:identified spam ([^ ]*) for ([^ ]*) in ([^ ]*) seconds, ([^ ]*) bytes. cs_re:yellow:identified spam [^ ]* for [^ ]* in [^ ]* seconds, [^ ]* bytes. cs_re_s:green,,bold:server successfully spawned child process, pid (.*) cs_re:green:server successfully spawned child process, pid # # ClamAV colorscheme:clamav:clamd logging cs_re:magenta:: cs_re:blue:^... ... [0-9]* ..:..:.. .... cs_re_s:red,,bold:ERROR:(.*) cs_re:red:ERROR: cs_re_s:green,,bold:Protecting against ([0-9]*) viruses. cs_re:green:Protecting against [0-9]* viruses. cs_re:red,black,inverse:Exiting.* cs_re_s:yellow,,bold:^(.*): Unable to open file or directory ERROR cs_re:yellow:^.*: Unable to open file or directory ERROR cs_re:red,black,inverse:LOGGING DISABLED.* # # samba colorscheme:samba cs_re_s:blue:^.([0-9]*/[0-9]*/[0-9]* [0-9]*:[0-9]*:[0-9]*, [0-9]*) cs_re:blue:\. cs_re_s:green:^ *([^ ]*).*(connect to service folkert initially as user [^ ]*) cs_re:yellow:closed connection to service.* cs_re:red:Error =.* cs_re:red:ERRNO =.* cs_re:red:^.*does not exist or is not a directory, when connecting to.* cs_re:red:Ignoring unknown parameter.* cs_re:green,,bold:smbd version.*started. cs_re:green,,bold:Netbios nameserver version.*started. cs_re:green:Samba name server.*is now a local master browser for workgroup.*on subnet.* cs_re:yellow:Attempting to become domain master browser on workgroup.*, subnet.* cs_re:green:Samba is now a logon server for workgroup.*on subnet.* cs_re:yellow,,bold:Server.*at IP.*is announcing itself as a local master browser for workgroup.*and we think we are master. Forcing election. # # audit.log colorscheme:audit cs_re:cyan:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re:red:Illegal user.* cs_re:green:session opened for user.* cs_re:green:Accepted publickey for cs_re:yellow:Did not receive identification string from.* # # exim colorscheme:exim cs_re:cyan:: cs_re:blue|blue,,bold:^....-..-.. ..:..:.. cs_re:magenta,,bold:<= cs_re:magenta,,bold:=> cs_re:green,,bold:=> *[^ ]* cs_re:green:<= *[^ ]* cs_re:green,,bold:=> *[^ ]* <[^>]*> cs_re:green:<= *[^ ]* <[^>]*> cs_re:yellow:H=[^ ]* cs_re:red:verify failed for SMTP recipient.* cs_re:red: *[^ ]* \[[0-9\.:]*\]: Connection refused cs_re:red:SMTP.*timeout.* cs_re:yellow:Spool file is locked (another process is handling this message) # # httping colorscheme:httping:ping for HTTP cs_re:green:^PING .*: cs_re:magenta:time=[0-9\.]* ms cs_re:yellow:seq=[0-9]* cs_re:green:^---.* cs_re_s:green:^([0-9]*) connects, ([0-9]*) ok, ([^%]*) cs_re_s:green:^round-trip.*= ([0-9\.]*)/([0-9\.]*)/([0-9\.]*) cs_re:red:404 Not Found cs_re:blue:[0-9]*KB/s cs_re:red:could not connect # # netstat (use for example with multitail -R 1 -l "netstat") colorscheme:netstat:see www.vanheusden.com/multitail/examples.html cs_re:green:ESTABLISHED cs_re:yellow:SYN_SENT cs_re:magenta:CLOSE_WAIT cs_re:blue:FIN_WAIT1 cs_re:blue,,underline:FIN_WAIT2 cs_re_s:red::([^ ]*) cs_re:cyan:^[^ ]* # # tcpdump colorscheme:tcpdump cs_re:blue:^[^ ]* cs_re:magenta:: cs_re_s:red:^[^ ]* [^ ]* *[^ ]* > [^:]*: *([^ ]*) cs_re_s:green:^[^ ]* [^ ]* *[^ ]*\.([^ ]*) > [^ ]*\.([^:]*): cs_re:magenta:\. cs_re_s:yellow|yellow,,bold: IP (.*) > .*: cs_re_s:yellow,,bold|yellow: IP [^ ]* > ([^:]*): #17:44:07.363010 IP muur.intranet.vanheusden.com.49584 > keetweej.intranet.vanheusden.com.ssh: # # dhcpd colorscheme:dhcpd cs_re:magenta:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re:green,,bold:DHCPACK cs_re_s:green:DHCPACK on ([^ ]*) to ([^ ]*) to (.*) cs_re_s:red,,bold:DHCPINFORM from ([^ ]*) via cs_re:red:DHCPINFORM from.*via.*not authoritative for subnet.* cs_re_s:yellow,,bold:DHCPDECLINE of ([^ ]*) from ([^ ]*) via cs_re:yellow:DHCPDECLINE of.*from.*via.* cs_re:cyan:DHCPNAK # # bind colorscheme:bind cs_re:magenta:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re_s:yellow,,bold:lame server resolving ([^ ]*).*in ([^?]*).*: (.*$) cs_re:yellow:lame server resolving [^ ]* .in [^:]*..: cs_re_s:red,,bold:client ([^#]*)#[0-9]*: update forwarding denied cs_re:red:client [^:]*: update forwarding denied cs_re_s:cyan,,bold:received notify for zone (.*) cs_re:cyan:received notify for zone cs_re:cyan:zone [^:]*: transferred serial.* cs_re:cyan:zone [^:]*: sending notifies .serial [0-9]*. cs_re_s:green,,bold:transfer of [^ ]* from ([^#]*)#[0-9]*: end of transfer cs_re:green:transfer of [^ ]* from [^:]*: end of transfer cs_re:yellow:loading configuration from.* cs_re:yellow:no IPv6 interfaces found cs_re:red:logging channel [^ ]* file [^:]*: permission denied cs_re:red:isc_log_open [^ ]* failed: permission denied cs_re:red:zone [^:]*: loading master file [^:]*: file not found cs_re:green,,bold:named reload succeeded # # smartd colorscheme:smartd cs_re_s:red,,bold:Device: [^,]*, SMART Usage Attribute: [0-9]* ([^ ]*) changed from [0-9]* to (.*) cs_re:red:Device: [^,]*, SMART Usage Attribute: [0-9]* [^ ]* changed from [0-9]* to.* # # kerberos colorscheme:kerberos cs_re:magenta:: cs_re:blue:^... [0-9]* ..:..:.. cs_re_s:yellow,,bold:klogind.*: Authentication failed from ([^:]*): Software caused connection abort cs_re:yellow:klogind.*: Authentication failed from [^:]*: Software caused connection abort cs_re:red:klogind.*: Kerberos authentication failed cs_re:red,,bold:klogind.*: User ([^ ]*) is not authorized to login to account(.*) cs_re:red:klogind.*: User [^ ]* is not authorized to login to account.* cs_re_s:red,,bold:ksu.*:.*authentication failed for ([^ ]*) on cs_re:red:ksu.*:.*authentication failed for [^ ]* on.* cs_re:green:commencing operation cs_re_s:,,bold:AS_REQ [^}]*}, ([^ ]*) for (.*) cs_re:magenta:Ticket expired cs_re:red:Client not found in Kerberos database # # Oracle colorscheme:oracle cs_re:red,,bold:^ORA-0*600.* cs_re:red,,bold:^ORA-0*7445.* # ORA-07745:? cs_re_s:yellow:^ORA-([^:]*): # # ntpd colorscheme:ntpd cs_re:magenta:: cs_re:blue:^... .. ..:..:.. cs_re_s:red,,bold:configure: keyword "([^"]*)" unknown, line ignored cs_re:red:configure: keyword "([^"]*)" unknown, line ignored cs_re:yellow,,blink:ntpd\[.*\]: ntpd exiting on signal.* cs_re:green,,bold:ntpd\[.*\]: ntpd [^e].* cs_re_s:green,,bold:synchronized to ([^,]*) cs_re:green:synchronized to.* cs_re:red:check receiver configuration / cableling # # nagtail colorscheme:nagtail:www.nagios.org status viewer cs_re:magenta:: cs_re:magenta:/ cs_re:blue:^..../../.. ..:.. cs_re_s:red,,bold:^................ (CRIT) cs_re_s:yellow,,bold:^................ (WARN) cs_re_s:green:^................ ( OK ) cs_re_s:white,,bold:^................ ( \?\? ) cs_re_s:green:^..../../.. ..:.. ..... *([^ ]*) cs_re_s:yellow:^..../../.. ..:.. ..... *[^ ]* *(.*) # # WebSphere errorlog colorscheme:websphere:WebSphere error-log cs_re:magenta:: cs_re:magenta:/ cs_re:blue,,bold:\. cs_re:blue:^.[0-9]*/[0-9]*/[0-9]* *[0-9]*:..:..:[0-9]* [^ ]* cs_re_s:yellow:^.*\(([^:\)]*) cs_re:red:Reason:.* cs_re:red,,bold:Unable to cs_re_s:red:Unable to(.*) cs_re:red,,bold:Failed to cs_re_s:red:Failed to(.*) cs_re_s:green:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *([^ ]*) cs_re_s:red,,bold:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( F ) cs_re_s:red:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( E ) cs_re_s:yellow,,bold:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( W ) cs_re_s:yellow:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( A ) cs_re_s:green,,bold:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *(I) cs_re_s:green:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( C ) cs_re_s:magenta,,bold:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( R ) cs_re_s:magenta:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( O ) cs_re_s:red,,bold:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( Z ) cs_re:yellow:Next Linked Exception: cs_re:magenta,,bold:Queue manager security exit rejected connection with error code [0-9]* cs_re:red,,bold:com.ibm.mq.MQException: [^:]*: An MQException occurred: Completion Code [0-9]*, Reason [0-9]* cs_re:yellow:Begin backtrace for nested exception cs_re:yellow:Socket connection attempt refused cs_re:yellow:Other data: cs_re:yellow:Exception data follows: cs_re:green:Target name: .* # # NNTPcache colorscheme:nntpcache cs_re:magenta:: cs_re:magenta:/ cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re:yellow:nntpcache-expire.*: clean shutdown cs_re:green:nntpcache-expire.*: expire task awakening cs_re_s:red,,bold:Connection timed out: could.*t connect to ([^ ]*) as (.*) cs_re:red:Connection timed out: could.*t connect to [^ ]* as cs_re:yellow:^.*dropped connection during rebuild of.*$ cs_re:red,,bold:'501.* cs_re:red,,bold:'480.* # # Veritas Netbackup restore log colorscheme:vnetbr:Veritas Netbackup backup/restore logs cs_re:magenta:: cs_re:magenta:\( cs_re:magenta:\) cs_re:blue:^..:..:.. .[0-9]*.[^\)]*. cs_re_s:green:Restore job id ([0-9]*) will require ([0-9]*) image cs_re:green:Restore job id [0-9]* will require [0-9]* image.* cs_re_s:yellow,,bold:Media id ([^ ]*) is needed for the restore. cs_re:yellow:Media id ([^ ]*) is needed for the restore. cs_re:green:INF - Beginning restore from server [^ ]* to client [^ ]*. cs_re_s:yellow,,bold:Changed ([^ ]*) to ([^ ]*) cs_re:yellow:Changed [^ ]* to [^ ]* cs_re_s:red,,bold:Directory ([^ ]*) already exists. cs_re:red:Directory [^ ]* already exists. cs_re_s:green,,bold:Added ([^ ]*) permission to directory (.*) cs_re:green:Added [^ ]* permission to directory.* cs_re_s:yellow,,blink:INF - Media id ([^ ]*) is not in a robotic library; administrative interaction may be required to satisfy a mount request. cs_re:yellow:INF - Media id [^ ]* is not in a robotic library; administrative interaction may be required to satisfy a mount request. cs_re:red,black,inverse:INF - Status = termination requested by administrator. cs_re:red,,blink:media read error cs_re:red,,bold:Status of restore from image created.*media read error cs_re:red:INF - Status = the restore failed to recover the requested files. cs_re:green,,bold:INF - Status = the requested operation was successfully completed. # # procmail colorscheme:procmail cs_re:magenta:: cs_re:magenta:/ cs_re_s:blue,,bold:^procmail: \[[0-9]*\] ([^ ]* [^ ]* *[^ ]* ..:..:.. [^ ]*) cs_re:blue:^procmail: \[[0-9]*\] [^ ]* [^ ]* *[^ ]* ..:..:.. [^ ]* cs_re_s:green,,bold:^procmail: Match on "(.*)" cs_re:green:^procmail: Match on.* cs_re:red:^procmail: Executing.* cs_re:magenta,,bold:^procmail: Assigning cs_re:yellow:warning:.* cs_re:yellow:Couldn't determine implicit lockfile from.* # # checkpoint colorscheme:checkpoint:Checkpoint Firewall-1 cs_re:magenta:: cs_re:blue|blue,,bold:^[0-9]*:[0-9]*:[0-9]* cs_re:red,,bold:^..:..:.. *drop cs_re_s:red,,bold:^..:..:.. *drop.*(service: *[^;]*) cs_re:green:^..:..:.. *accept cs_re:yellow,,bold:^..:..:.. *reject cs_re_s:yellow,,bold:^..:..:.. *reject.*(service: *[^;]*) cs_re:green,,bold:rule: *[^;]* cs_re:yellow:service: *[^;]* cs_re:magenta:src: *[^;]* cs_re:magenta,,bold:dst: *[^;]* cs_re:blue,,bold:>[^ ]* # # pppd colorscheme:pppd:PPP daemon cs_re:magenta:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re_s:red,,bold:Hangup(.*) cs_re:red:Hangup cs_re_s:yellow,,bold:Terminating on signal(.*) cs_re:yellow:Terminating on signal.* cs_re_s:green,,bold:Connect time ([^ ]*) minutes. cs_re:green:Connect time [^ ]* minutes. cs_re_s:magenta,,bold:Sent ([0-9]*) bytes, received ([0-9]*) bytes. cs_re:magenta:Sent [0-9]* bytes, received [0-9]* bytes. cs_re:green:pppd [^ ]* started by [^,]*, uid [0-9]* cs_re_s:blue,,bold:Using interface (.*) cs_re:blue:Using interface.* cs_re_s:green,,bold:local *IP address (.*) cs_re:green:local *IP address.* cs_re:red,black,inverse:Couldn't detach (fork failed:.*) cs_re_s:yellow,,bold:Unsupported protocol (.*) received cs_re:yellow,black,inverse:Unsupported protocol .* received cs_re:yellow,,blink:Peer not responding cs_re:,,inverse:DNS address.* # # INN colorscheme:inn cs_re:magenta:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re:yellow:nnrpd.*: .* timeout # # Netscape Directory server (LDAP) colorscheme:netscapeldap:Netscape Directory server (LDAP) cs_re:magenta:: cs_re:magenta:/ cs_re:blue|blue,,bold:^.../.../....:..:..:.. ...... cs_re:green:Netscape-Directory.*starting up cs_re:yellow:All database threads now stopped cs_re:green:Backing up file.* cs_re:red:Detected Disorderly Shutdown last time Directory Server was running, recovering database. cs_re_s:yellow,,bold:Entry ([^ ]*) unknown object class ([^ ]*) cs_re:yellow:Entry [^ ]* unknown object class [^ ]* cs_re:red,,blink:Shutting down due to possible conflicts with other slapd processes cs_re:red,,bold:Unable to start slapd because it is already running as process ([0-9]*) cs_re:red,,blink:Unable to start slapd because it is already running as process [0-9]* cs_re:yellow:slapd got [^ ]* signal # # vmstat colorscheme:vmstat:vmstat is part of sysstat cs_re_s:magenta:^(procs) cs_re_s:red:^procs *(-*memory-*) cs_re_s:green:^procs *-*memory-* *(-*swap-*) cs_re_s:yellow:^procs *-*memory-* *-*swap-* *(-*io-*) cs_re_s:blue:^procs *-*memory-* *-*swap-* *-*io-* *(-*system-*) cs_re_s:magenta,,bold:^ *(r *b) cs_re_s:red,,bold:^ *r *b *(swpd *free *buff *cache) cs_re_s:green,,bold:^ *r *b *swpd *free *buff *cache *(si *so) cs_re_s:yellow,,bold:^ *r *b *swpd *free *buff *cache *si *so *(bi *bo) cs_re_s:blue,,bold:^ *r *b *swpd *free *buff *cache *si *so *bi *bo *(in *cs) cs_re_s:,,bold:^ *r *b *swpd *free *buff *cache *si *so *bi *bo *in *cs *(us *sy *id *wa) cs_re_val_bigger:yellow:0:([0-9]{1,}) # # mpstat colorscheme:mpstat:mpstat is part of systat cs_re:magenta:: cs_re:blue:^..:..:.. .. cs_re:blue,,bold:CPU.* cs_re_val_bigger:yellow:0:([0-9]{1,}\.[0-9]{1,}) # # log4j colorscheme:log4j cs_re:magenta:: cs_re:magenta:/ cs_re:blue:^[0-9]*-[0-9]*-[0-9]* [0-9]*:[0-9]*:[0-9]*,[0-9]* cs_re_s:blue,,bold:^[^ ]* *[^,]*,[^ ]* *[0-9]* *(DEBUG) *[^ ]* [^ ]* *(.*)$ cs_re_s:green:^[^ ]* *[^,]*,[0-9]* *[0-9]* *(INFO) *[^ ]* [^ ]* *(.*)$ cs_re_s:yellow:^[^ ]* *[^,]*,[0-9]* *[0-9]* *(WARN) *[^ ]* [^ ]* *(.*)$ cs_re_s:red:^[^ ]* *[^,]*,[0-9]* *[0-9]* *(ERROR) *[^ ]* [^ ]* *(.*)$ cs_re_s:red,,bold:^[^ ]* *[^,]*,[0-9]* *[0-9]* *(FATAL) *[^ ]* [^ ]* *(.*)$ cs_re_s:white,,bold:^[^ ]* *[^,]*,[0-9]* *[0-9]* *[A-Z]* *(.*) # # LambdaMOO colorscheme:lambdamoo:MUD/MOO server http://www.moo.mud.org/ cs_re:magenta:: cs_re:blue:^... [0-9]* ..:..:..: cs_re:green:STARTING: Version [^ ]* of the LambdaMOO server cs_re:yellow:DUMPING:.* cs_re:red:Can't create initial connection point cs_re_s:red,,bold/blink:Trying to dump database:(.*) cs_re:red,,bold:Trying to dump database: # # BOINCTail colorscheme:boinctail:BOINCTail http://www.vanheusden.com/boinctail/ cs_re:magenta:: cs_re_s:blue:^---> (... ... .. ..:..:.. ....) cs_re_s:blue,,bold:^---> ........................ .([0-9]* seconds.*) cs_re:yellow:report deadline cs_re_s:red:^................ ([^,]*) cs_re_s:green:, (estimated.*) cs_re:green,,bold:workunit is ready cs_re:red,,bold:not cs_re_s:white,,bold:^name of workunit: (.*) # # p0f colorscheme:p0f:p0f http://lcamtuf.coredump.cx/p0f.shtml cs_re:magenta:: cs_re:blue,,bold:^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} cs_re_s:blue:^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:([0-9]*) cs_re_s:yellow:^[^ ]* - ([^(]*) cs_re:green:\(older, [0-9]*\) cs_re:green,,bold:\(newer, [0-9]*\) cs_re_val_bigger:cyan,,bold:501:\(up: ([0-9]*).*\) cs_re:red:\(up: .*\) cs_re:red,,bold:distance [0-9]* cs_re_s:cyan:^ -> [^:]*:([0-9]*) cs_re_s:white:^>> Masquerade at ([^:]*): cs_re:white,,bold:^>> Masquerade at [^:]*: cs_re_val_bigger:green,,bold:50:indicators at ([0-9]*)% # # portsentry colorscheme:portsentry:http://sourceforge.net/projects/sentrytools/ cs_re:magenta:: cs_re_s:red,,bold:attackalert: ([^/]*)/Normal scan from host: ([^/]*)/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) to TCP port: ([0-9]*) cs_re:red:attackalert: [^/]*/Normal scan from host: [^/]*/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} to TCP port: [0-9]* cs_re:cyan:attackalert: ERROR: cannot open ignore file. Blocking host anyway. cs_re_s:yellow,,bold:attackalert: Host: ([^/]*)/([^ ]*) is already blocked Ignoring cs_re:yellow:attackalert: Host: [^ ]* is already blocked Ignoring # # strace colorscheme:strace:strace is the truss of Linux # comments cs_re:blue:/\*.*\*/ # call cs_re:yellow:^[a-z_]*[0-9]* # parenthesis around parameters cs_re_s:yellow:^[a-z_]*[0-9]*(\()[^)]*(\)) # errno details cs_re_s:blue:^[a-z]*\([^)]*\).*(\(.*\)) # return value cs_re_s:green:^.*(= *-*[a-fx0-9]*)[^=]*$ # errno cs_re_s:cyan:^.*= *-*[a-fx0-9]* *([A-Z]*)[^=]*$ # escapes cs_re:magenta:\\[a-z] cs_re:magenta:\\[0-9]* # parameters (1) cs_re:red:"[^"]*" # names cs_re:white:[a-z]*= cs_re:white:[a-z0-9_]*: # parameters (2) cs_re:red:[0-9a-fx]* cs_re:cyan:[A-Z_]* # {}[] cs_re:yellow:[\[\]]* # # Argus colorscheme:argus:Argus http://qosient.com/argus/ mcsre_s:,,bold:[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\.([0-9]+).*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\.([0-9]+) mcsre_val_bigger:red,,bold:20000:([0-9]+)[ ]+([0-9]+)[ ]+[A-Z][A-Z][A-Z] mcsre_s:magenta,,bold: (<->) mcsre_s:cyan,,bold: (<[-?]) mcsre_s:yellow,,bold: ([-?]>) mcsre:,blue:.*tcp.* mcsre:,green:.*udp.* mcsre:,cyan:.*icmp.* mcsre:,,inverse:.*man.* # # ii - irc client colorscheme:ii:ii IRC client http://www.suckless.org/wiki/tools/irc cs_re_s:cyan:(^....-..-.. ..:..) cs_re_s:magenta:^....-..-.. ..:.. <([[:alnum:]_\^\|`-]+)> cs_re_s:green:^....-..-.. ..:.. <([[:alnum:]_\^\|`-]+)> (nion[ ,:].*)$ cs_re_s:yellow:(((http|https|ftp|gopher)|mailto):(//)?[^ <>\"[:blank:]]*|(www|ftp)[0-9]?\.[-a-z0-9.]+) cs_re:blue:....-..-.. ..:...*has joined \#.* cs_re:blue:....-..-.. ..:.. .*changed mode.* # # Snort colorscheme:snort:Intrusion detector cs_re:green:.*Priority: 3.* cs_re:yellow:.*Priority: 2.* cs_re:red:.*Priority: 1.* # # Motion colorscheme:motion:Security camera software cs_re:red:Unable to start external command cs_re:red:[^ ]* query failed cs_re:yellow:Failed to put image into video pipe cs_re:red:Could not create symbolic link cs_re:red:ffopen_open error creating [^ ]* file cs_re:red:Memory error while allocating output media context cs_re:red:av_new_stream - could not alloc stream cs_re:red:avcodec_alloc_frame - could not alloc frame cs_re:red:url_fopen - error opening file %s cs_re:red:Error opening file %s cs_re:red:Error while writing video frame cs_re:red:Could not alloc frame cs_re:green:Motion detected - starting event [0-9]* cs_re:red:Could not fetch initial image from network camera cs_re:red:Error capturing first image cs_re:red:Failed to open video loopback cs_re:red:MySQL error was cs_re:red,,bold:Video device fatal error - terminating camera thread cs_re:red:Video signal lost - Adding grey image cs_re:red,,bold:Somebody stole the video device, lets hope we got his picture cs_re:red,,blink:Could not allocate [0-9]* bytes of memory! cs_re:red:Problem creating directory cs_re:red:Error opening file [^ ]* with mode cs_re:yellow:No response from camera cs_re:red:Can't write picture to file cs_re:red,,bold:Thread is going to finish due to this fatal error cs_re:magenta:[^ ]* error in proc %d cs_re:magenta:mmap failed cs_re:yellow,,bold:Motion Exits. cs_re:yellow,,bold:httpd quitting # # errpt - IBM AIX error report # example usage: multitail -R 10 -cS errpt -l errpt colorscheme:errpt:AIX error reporting tool cs_re_s:blue|blue,,bold:^[^ ]* *(....)....(..) cs_re_s:blue,,bold|blue:^[^ ]* *....(....).. cs_re:magenta: I [A-Z] .* cs_re:yellow: T [A-Z] .* cs_re:red: [A-Z] H .* # # MySQL error log colorscheme:mysql:MySQL error log cs_re:magenta:: cs_re_s:blue|blue,,bold:^..(..).. ..:..:.. cs_re_s:blue,,bold|blue:^(..)..(..) ..:..:.. cs_re:blue:^...... ..:..:.. cs_re:red,,bold:^...... ..:..:.. \[ERROR\] cs_re:red:^...... ..:..:.. \[ERROR\].* cs_re:yellow,,bold:^...... ..:..:.. \[Warning\] cs_re:yellow:^...... ..:..:.. \[Warning\].* cs_re:green,,bold:^...... ..:..:.. \[Note\] cs_re:green:^...... ..:..:.. \[Note\].* cs_re:magenta:mysqld ended cs_re:red:Can't start server.* # # BOINC # execute boinc_client with -redirectio, it'll then create # stdoutdae.txt and stderrdae.txt files colorscheme:boinc:BOINC http://boinc.berkeley.edu/ cs_re:magenta:: cs_re:magenta:- cs_re:blue|blue,,bold:....-..-.. ..:..:.. cs_re_s:green:^....-..-.. ..:..:.. ([^]]*]) cs_re:yellow:Received signal cs_re:yellow,,blink:This computer is not attached to any projects cs_re:red:gethostbyname failed cs_re:green,,bold:Exit requested by user cs_re:cyan:Rescheduling CPU: application exited cs_re:cyan,,bold:Scheduler list download succeeded cs_re:yellow,,bold:Throughput [0-9]* bytes/sec cs_re:yellow,,underline:Finished upload of file.* cs_re:yellow,,bold/underline:Computation for task.*finished cs_re:red:Project communication failed: cs_re:yellow:Access to reference site succeeded - project servers may be temporarily down. cs_re_s:green,,bold:Requesting ([0-9]*) seconds of new work cs_re:green:Requesting [0-9]* seconds of new work cs_re:white,,bold:Deferring scheduler requests for.* cs_re:white,,bold:Deferring communication for.* cs_re:blue,,bold: [^ ]* download of file.* cs_re:red:Project is down # # acpitail # http://www.vanheusden.com/acpitail/ colorscheme:acpitail:Show temperature/battery/etc info cs_re:magenta:: cs_re:magenta:- cs_re:blue|blue,,bold:... ... [0-9]* ..:..:.. 2... cs_re_val_less:red,,bold:5:^.* remaining capacity: .* .([0-9]*) minutes cs_re_val_less:yellow:10:^.*remaining capacity: .* .([0-9]*) minutes cs_re:red:error.* cs_re_val_bigger:yellow:55:temperature.*: ([0-9]*) cs_re_val_bigger:red,,bold:65:temperature.*: ([0-9]*) # # QMT: clamd colorscheme:qmt-clamd cs_re:blue,,bold:^....-..-.. ..:..:.. cs_re:red:ERROR: cs_re_s:green,,bold:Protecting against ([0-9]*) viruses. cs_re:green:Protecting against [0-9]* viruses. cs_re:red,black,inverse:Exiting.* cs_re_s:red,,bold:^(.*): Unable to open file or directory ERROR cs_re:red:^.*: Unable to open file or directory ERROR cs_re:red,black,inverse:LOGGING DISABLED.* #cs_re:cyan:/var/qmail/simscan/.*(: OK) cs_re:cyan:(: OK) cs_re:magenta:/var/qmail/simscan/.*(: [^ ]* FOUND) cs_re:yellow: LibClamAV Warning.* # # QMT: qmail-smtp colorscheme:qmt-smtp cs_re:blue,,bold:^....-..-.. ..:..:.. cs_re:red:verify failed for SMTP recipient.* cs_re:red: *[^ ]* \[[0-9\.:]*\]: Connection refused cs_re:red,,bold: *[^ ]*policy_check: policy_load failed cs_re:white,,bold: *[^ ]*CLEAN ([^ ]*).* cs_re:white,,bold: *[^ ]*policy_check: policy allows([^ ]*).* cs_re:white,,bold: *[^ ]*RELAYCLIENT:([^ ]*).* cs_re:magenta: *[^ ]*policy_check: policy forbid([^ ]*).* cs_re:magenta:rblsmtpd: ([0-9\.]*).* cs_re:magenta:qmail-smtpd.* cs_re:magenta:spf-reject.* cs_re:magenta: *[^ ]*CHKUSER rejected intrusion: ([^ ]*).* cs_re:magenta: *[^ ]*CHKUSER rejected relaying: ([^ ]*).* cs_re:magenta: *[^ ]*CHKUSER rejected rcpt: ([^ ]*).* cs_re:magenta,,bold: *[^ ]*SPAM REJECT ([^ ]*).* cs_re:magenta,,bold: *[^ ]*:VIRUS:.* cs_re:magenta,,bold: *[^ ]*:ATTACH:.* # # QMT: qmail-send colorscheme:qmt-send cs_re:blue:^....-..-.. ..:..:.. cs_re:white,,bold:starting delivery ([0-9]*) cs_re:white,,bold:delivery *[^ ]*: success:.* cs_re:red,,bold:delivery *[^ ]*: failure:.* cs_re:yellow,,bold:delivery *[^ ]*: deferral:.* # # QMT: SpamAssassin colorscheme:qmt-spamassassin cs_re:blue,,bold:^....-..-.. ..:..:.. #cs_re:cyan,,bold:info: spamd: clean message .* cs_re_s:cyan,,bold:clean message ([^ ]*) for ([^ ]*) in ([^ ]*) seconds, ([^ ]*) bytes. cs_re:magenta,,bold:info: spamd: identified spam.* #cs_re:magenta,,bold:identified spam ([^ ]*) for ([^ ]*) in ([^ ]*) seconds, ([^ ]*) bytes. cs_re_s:magenta,,bold:info: spamd: result: Y ([^ ]*) -.* cs_re:yellow:autolearn=failed cs_re:red,,blink:server killed by [^,]*, shutting down cs_re_s:green,,bold:server successfully spawned child process, pid (.*) cs_re:green:server successfully spawned child process, pid cs_re:yellow: warn.* cs_re:white,,bold:info: spamd: processing message.* # # QMT: sophie colorscheme:qmt-sophie cs_re:blue,,bold:^....-..-.. ..:..:.. cs_re:white,,bold:NOTICE *[^ ]*:.* cs_re:yellow,,bold:Virus present.* cs_re:magenta,,bold:WARNING *[^ ]*: Scan result =.* cs_re:red,,bold:WARNING *[^ ]*: error:.* # # colorscript: colorscripts are external scripts that decide what colors to use # for input they receive the line that needs colors # as a result they emit: start,end,foreground color,background color,attributes\n # ...\n # \n <- an empty line (only a linefeed) indicates # end of parameters for this line # start offset: what position these colors/attributes start # end offset: the position AFTER the last character for which the attributes are # valid # do NOT use spaces in each line! colorscript:cscriptexample:/etc/multitail/colors-example.pl:this is a barely functional example script # # # default colorschemes: scheme:postfix:/var/log/mail/ scheme:sendmail:/var/log/mail/ scheme:exim:/var/log/mail/ scheme:apache:/var/log/apache/.*access scheme:apache:/var/log/lighttpd/.*access scheme:apache_error:/var/log/apache/.*error scheme:asterisk:/var/log/asterisk/messages scheme:samba:/var/log/samba/ scheme:squid:/var/log/squid/ scheme:syslog,ssh:/var/log/ scheme:vnetbr:bplog.rest scheme:procmail:procmail.log scheme:inn:/var/log/news/ scheme:snort:/var/log/snort/alert scheme:boinc:/var/lib/boinc-client/std...dae.txt scheme:qmt-send:/var/log/qmail/send/current scheme:qmt-smtp:/var/log/qmail/smtp/current scheme:qmt-smtp:/var/log/qmail/submission/current scheme:qmt-clamd:/var/log/qmail/clamd/current scheme:qmt-spamassassin:/var/log/qmail/spam/current scheme:qmt-sophie:/var/log/qmail/sophie/current # the following line is to show you that colorscripts can be used the same way as colorschemes scheme:cscriptexample:/dev/null # # default number of lines to buffer FOR THESE PATTERNS: #default_nlines:500:/var/log/apache/*.access #default_bytes:100kb:/var/log/ # # default number of lines to buffer globally # one can set only 1 of these two #global_default_nlines:500 global_default_nkb:1MB # # how often to check for mail # set to 0 to disable # default is every 5 seconds check_mail:0 # # width of a TAB-character. in the VI editor this is, for # example, 8. default in multitail is 4 tab_stop:8 # # what program to start when the user presses a key # ...:x:... just that key # ...:^x:... that key with control bind:g:/usr/bin/pine bind:^k:/bin/bash bind:^l:/usr/bin/telnet bind:^s:/bin/su # # enable things with the xterm title bar # %f changed file # %h hostname # %l system load # %m "New mail" or nothing # %u username # %t date + time titlebar:%m %u@%h %f (%t) [%l] # # how timestrings will look when using '-ts' line_ts_format:%Y/%m/%d %H:%M:%S # # where to find tail. this is used when you don't use the default tail (using # turbotail for example) # or your tail is located somewhere else #tail:/usr/bin/tail # wether this tail only understands posix commands (=yes) posix_tail:no # # use bright colors #bright:1 # # wether to abbreviate filesizes to xKB/MB/GB abbreviate_filesize:on # # show number of subwindow? show_subwindow_id:off # # attributes for the marker-line markerline_color:red,black,reverse # # show timestamp in markerline? markerline_timestamp:on # whot character to print in the markerline markerline_char:- # # attributes for the line that is printed when multitail switches subwindow changeline_color:blue,white,bold/reverse changeline_char:- # # line printed when nothing happens in a window for a while idleline_color:yellow,black,bold/reverse idleline_char:- # # line printed for multitail messages (regexp errors, file truncated, etc) msgline_color:magenta,black,bold/reverse msgline_char:- # # when converting to a timestring, use this format: # (also for markerline) ts_format:%b %d %H:%M:%S # # timestring format for conversions cnv_ts_format:%b %d %H:%M:%S %Y # # timestring format for statusline statusline_ts_format:%Y/%m/%d %H:%M:%S # # put statusline above the data instead below it? statusline_above_data:no # # statusline attributes statusline_attrs:white,black,reverse # # conversions # note: the part you want to have replaced must be between '(' and ')' # possible conversions: # ip4tohost: an ip-address in 4 byte dotted format to a hostname # epochtodate: a value representing the number of seconds since 1970 to # a time-string. format of the string can be set with # "ts_format". # errnotostr: an errno-value to the error message it represents # hextodec: a hex-value into its decimal version # dectohex: a decimal value into hexadecimal # tai64todate: converts a TAI64 string to a datestring (see http://cr.yp.to/libtai/tai64.html#tai64n for details) # script this is a special case: it requires an extra parameter (before the regular expression): this parameter # selects a script (perl, bash, as long as it is executable) which loops forever and then reads a line # ending with \n, processes it and then emits the converted output also ending with \n (in 1 write!) # Please note: when using perl, disable i/o buffering! # abbrtok: abbreviates a value to KB/MB/GB # signrtostring: signal number to descriptive name convert:apache:ip4tohost:^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) convert:squid:epochtodate:^([0-9]*) convert:squid:ip4tohost:^[^ ]* *[0-9]* *([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) convert:asterisk:epochtodate:^([0-9]*).([0-9]*) convert:nagios.log:epochtodate:^.([0-9]*). convert:qmailtimestr:tai64todate:^(@[^ ]*) convert:geoip:script:/etc/multitail/convert-geoip.pl:([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) # # shell to invoke shell:/bin/sh # # default scrollback linewrap mode # default is yes (do wrap) dsblwm:yes # # should a popup box be displayed when a window closes by itself? warn_closed:yes # # allow 8 bit ascii? (e.g. umlauts etc.) allow_8bit:yes # # beep, flash, popup or none when error? beep_method:flash # in case of beep_method:popup; how long to display the popup before it # automatically disappears (in seconds) beep_popup_length:0.1 # # ^x instead of an inverse . caret_notation:yes # # what umask to use when creating files umask:0022 # # how often to refresh popups (in case applicable) popup_refresh_interval:5 # # print a markerline when one merges multiple logfiles and tail changes # from one logfile to an other global_mark_change:no # default settings for files selected with a regular expression(!) default_mark_change:yes:/var/log/apache/.* # replace_by_markerline:-- MARK -- # # what to buffer by default 'a'll or what went through the 'f'ilter default_bufferwhat:f # # should searches be case insentive? press 'I' in the main menu to toggle at run-time searches_case_insensitive:no # # default linewrap mode # a: all # l: left # r: right # s: syslog # S: syslog w/o procname # o: offset # w: wordwrap default_linewrap:a # # follow filename instead of descriptor? follow_filename:yes # # filters (complete lines) filterscheme:syslog:removes '----mark----' and such rule:ev:---- MARK ---- # on what file(s) to use the filter by default usefilterscheme:syslog:/var/log/messages # # edits (part of lines) # the type (e.g. 'ke') is like the commandswitch -ke/-kS/-kr etc. editscheme:syslog:removes '----' editrule:ke:---- editrule:ke:make # on what file(s) to use the filter by default useeditscheme:syslog:/var/log/messages # # close windows when the end-of-file was reached? close_closed_windows:yes # # should we skip empty lines? (the scrollback window always displays them) suppress_empty_lines:yes # # how the splitline should look like splitline_attrs:white,black,reverse # can be attr (use attributes defined with splitline_attrs), regular (use statusline attributes), none (display none) splitline:attributes # # what attributes to use when displaying things in reverse (-eC/-ec, highlight in main menu) inverse:bold/reverse # # what key must the user press to abort an action? e.g. a menu or so # this parameter expects an ascii-value # e.g. control + a = 1 # escape = 27 - please note that you would have to press it twice # ^g = 7 abort_key:7 # what key to press to exit multitail # ^c = 3 exit_key:3 # default_convert:apache:/var/log/apache/.*access default_convert:apache:/var/log/lighttpd/.*access default_convert:asterisk:/var/log/asterisk/messages default_convert:squid:/var/log/squid/ default_convert:qmailtimestr:/var/log/qmail/qmail.smtpd.log # # when a buffer (for scrollback) gets too full, some lines must be freed (unless one sets the buffersize to unlimited) # with this parameter one can set the minimum lines to free. this parameter is implemented for efficiency as with a # 1MB buffer buffer management starts to use quit a bit of processortime min_shrink:10 # # when scrolling or searching through the logging, show (sub-)window-nrs? (toggle with ^t) # press 't' to see a list of window-nrs and what's displayed in them scrollback_show_winnrs:no # # for word-wrap: what is the max length of a word which should still be wrapped wordwrapmaxlength:31 # # for searches, multitail can remeber a history which can be retrieved with ^r or cursorkey down in places where # you can enter searchstrings # set 'history_size' to 0 to disable this feature searchhistory_file:~/.multitail.searchhistory # how many strings to remember searchhistory_size:15 # # like searchhistory only for filenames, pathnames and commands cmdfile_history_file:~/.multitail.cmdfilehistory cmdfile_history_size:30 # # default background color # comment this line to use the default terminal color #default_background_color:black # # in search-fields: give an empty edit-field or the previously used search string reuse_searchstring:no # # how many initial lines to tail at least initially (if available) min_n_bufferlines:50 # # what characters to use for the borders around popups and such # comment-out to use defaults #box_bottom_left_hand_corner:+ #box_bottom_right_hand_corner:+ #box_bottom_side:- #box_left_side:| #box_right_side:| #box_top_left_hand_corner:+ #box_top_right_hand_corner:+ #box_top_side:- # # text to put in front of line with window-number window_number:[%02d] subwindow_number:[%02d] # # parameters for --limit / --Limit # format of timestamp in logging syslog_ts_format:%Y/%m/%d %H:%M:%S # show ip addresses or hostnames? resolv_ip_addresses:yes # show severity/facility? not shown in regular syslogd show_severity_facility:yes # # suppress colors in the scollback window? this speeds up scrolling a little scrollback_no_colors:no # # when you search in the scrollback: open new window with found strings (= on) # or jump to the next found (= off) scrollback_search_new_window:yes multitail-6.0/makefile.cygwin0000644000175000017500000000362312245202637016267 0ustar folkertfolkertinclude version CONFIG_FILE=/etc/multitail.conf DEBUG=#-g -D_DEBUG -W -pedantic #-pg #-fprofile-arcs LDFLAGS=-lpanel -lncurses -lm $(DEBUG) CFLAGS=-D__CYGWIN__ -O2 -Wall -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o all: multitail multitail: $(OBJS) $(CC) -Wall -W $(OBJS) $(LDFLAGS) -o multitail install: multitail mkdir -p $(DESTDIR)/usr/bin mkdir -p $(DESTDIR)/usr/share/man/man1 cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # mkdir -p $(DESTDIR)/etc mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual*.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp multitail.conf $(DESTDIR)/etc/multitail.conf.new rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz gzip -9 $(DESTDIR)/usr/share/man/man1/multitail.1 # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz rm -f $(DESTDIR)/etc/multitail.conf rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core gmon.out *.da thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | email -s "multitail $(VERSION)" folkert@vanheusden.com multitail-6.0/my_pty.h0000644000175000017500000000006512245202637014757 0ustar folkertfolkertint get_pty_and_fork(int *fd_master, int *fd_slave); multitail-6.0/doassert.h0000644000175000017500000000011612245202637015257 0ustar folkertfolkert#ifdef _DEBUG #define NDEBUG #else #undef NDEBUG #endif #include multitail-6.0/diff.c0000644000175000017500000000271112245202637014341 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include "mt.h" #include "mem.h" #include "utils.h" #include "globals.h" void store_for_diff(diff_t *diff, char *string) { diff -> bcur = (char **)myrealloc(diff -> bcur, sizeof(char *) * (diff -> ncur + 1)); (diff -> bcur)[diff -> ncur] = mystrdup(string); diff -> ncur++; } void generate_diff(int winnr, proginfo *cur) { int loop_cur; int search_offset = 0; /* calculate & print difference */ for(loop_cur=0; loop_cur < cur -> restart.diff.ncur; loop_cur++) { int loop_prev; char found = 0; for(loop_prev=0; loop_prev < cur -> restart.diff.nprev; loop_prev++) { if (strcmp((cur -> restart.diff.bprev)[(search_offset + loop_prev) % cur -> restart.diff.nprev], (cur -> restart.diff.bcur)[loop_cur]) == 0) { found = 1; break; } } if (!found) { /* output cur[loop] */ emit_to_buffer_and_term(winnr, cur, cur -> restart.diff.bcur[loop_cur]); search_offset = 0; } else { search_offset = loop_prev + 1; } } update_panels(); /* free up previous */ delete_array(cur -> restart.diff.bprev, cur -> restart.diff.nprev); /* remember current */ cur -> restart.diff.bprev = cur -> restart.diff.bcur; cur -> restart.diff.nprev = cur -> restart.diff.ncur; cur -> restart.diff.bcur = NULL; cur -> restart.diff.ncur = 0; } multitail-6.0/colors-example.sh0000755000175000017500000000013612245202637016555 0ustar folkertfolkert#!/bin/sh while read line ; do echo 2,4,red,yellow,bold echo 6,7,green,white echo done multitail-6.0/error.h0000644000175000017500000000027012245202637014565 0ustar folkertfolkertvoid error_exit_(char *file, const char *function, int line, char *format, ...); #define error_exit(fmt, ...) error_exit_(__FILE__, __PRETTY_FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) multitail-6.0/exec.h0000644000175000017500000000052712245202637014365 0ustar folkertfolkertint start_tail(char *filename, char retry_open, char follow_filename, int initial_tail, int *pipefd); int start_proc(proginfo *cur, int initial_tail); int execute_program(char *execute, char bg); void init_children_reaper(void); pid_t exec_with_pipe(char *command, int pipe_to_proc[], int pipe_from_proc[]); void exec_script(script *pscript); multitail-6.0/makefile.macosx0000644000175000017500000000451212245202637016257 0ustar folkertfolkertinclude version CONFIG_FILE=$(DESTDIR)/etc/multitail.conf DEBUG=#-g -D_DEBUG #-pg #-fprofile-arcs LDFLAGS=-lpanel -lncurses -lm $(DEBUG) CFLAGS=-O2 -D$(shell uname) -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o all: multitail multitail: $(OBJS) $(CC) -Wall -W $(OBJS) $(LDFLAGS) -o multitail install: multitail cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # cp multitail.conf $(DESTDIR)/etc/multitail.conf.new mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/share/man/man1/multitail.1 rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core macosxbinpackage: multitail # as it is rather tricky to install something in /etc on MacOS X, # we're skipping it here rm -rf usr mkdir -p usr/bin mkdir -p usr/share/man/man1 mkdir -p usr/share/doc/multitail-$(VERSION) cp multitail usr/bin cp multitail.1 usr/share/man/man1 cp *.txt INSTALL manual.html usr/share/doc/multitail-$(VERSION) tar cvzf multitail-$(VERSION)-macosx.tgz usr rm -rf usr package: clean # source package rm -rf multitail-$(VERSION)* mkdir multitail-$(VERSION) cp *.c *.h multitail.1 manual.html Makefile makefile.* Changes INSTALL license.txt readme.txt multitail-$(VERSION) tar czf multitail-$(VERSION).tgz multitail-$(VERSION) rm -rf multitail-$(VERSION) thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-6.0/help.c0000644000175000017500000011056012245202637014363 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include "mt.h" #include "mem.h" #include "help.h" #include "term.h" #include "utils.h" #include "globals.h" char *help_main_menu[] = { "MultiTail lets you create one or more windows in", "one terminal. In each window one can monitor one", "or more logfiles and(!)/or the output of external", "programs: when using multiple inputs, they get", "automatically merged.", "", "In the main menu one can do add/delete windows,", "move them, swap them, set regular expressions, set", "color(-schemes) etc.", "", "One can press the following keys:", " ^q^ quit the program", " ^F1^ this help", " ^/^ search in all windows", " ^shift^ + ^/^ highlight in all windows", " ^b^ scroll back", " ^B^ scroll back in ALL windows merged into", " one window", " ^I^ toggle case sensitivity for searches", " ^a^ add a new window or add another file/", " program to an existing window", " ^d^ delete a window (or a file/program from a", " window)", " ^s^ swap the location of 2 windows", " ^e^ enter/edit regular expressions", " ^f^ enter/edit in line filter regexps", " ^c^ set/change colors (only works if your", " terminal supports colors)", " ^C^ edit the r/g/b components of a color", " ^b^ scrollback in the buffer of a window", " ^v^ toggle vertical split", " ^0^...^9^ set a mark in a window so that you can", " easily see what has changed since the last", " time you looked", " ^R^ restart a window", " ^ctrl^ + ^v^ select conversion scheme(s)", " ^ctrl^ + ^t^ toggle subwindow before lines", " ^Y^ send a signal to a window (for commands)", " ^y^ set linewrap", " ^o^ wipe (clear) a window", " ^O^ wipe all windows", " ^T^ truncate a logfile", " ^g^ make screendump", " ^r^ redraw screen", " ^I^ toggle case sensitivity for searches", " ^l^ list keybindings", " ^i^ info", " ^t^ statistics", " ^j^ set window sizes", " ^z^ hide/unhide a window", " ^u^ hide all windows but the one selected", " ^U^ unhide all windows", " ^w^ write a script which starts multitail", " with the current settings (the files that", " are monitored, regular expressions, etc.)", " ^n^ clear the buffer of a window", " ^N^ clear all buffers of all windows", " ^m^ set the size of a buffer", " ^k^ enter a (limited) terminal-mode", " ^p^ pause all windows", " ^P^ (un-)pause one window", "", "", "", "^MultiTail^ " VERSION " was written by", "_folkert@vanheusden.com_.", "Check: _http://www.vanheusden.com/multitail/_", "for new versions.", NULL }; char *help_add_file_or_cmd[] = { "MultiTail lets you display logfiles and the", "output of commands. At this point you can select", "wether you want to add to the new window a file", "or the output of a command.", " ^f^ selects a file", " ^c^ selects a commands", " ^ctrl+g^ abort", "", "Commands are executed in their own virtual tty to", "force line-buffered output. They are executed by", "starting up */bin/sh -c* with as parameter the", "command you'll enter here.", NULL }; char *help_list_keybindings[] = { "This screen lists the keybindings you defined in", "the MultiTail configurationfile which is located", "in "CONFIG_FILE". The keybindings only", "work in the main screen.", "In this screen, you can press the followking", "keys:", " ^UP^ (cursor key UP) scroll up", " ^DOWN^ (cursor key DOWN) scroll down", " ^q^ abort", NULL }; char *help_enter_filename_to_monitor[] = { "Please enter the filename of the file you wish to", "monitor. If the file is not in the directory you", "started MultiTail from, please enter the complete", "pathname. As with bash, you can press the *TAB*-", "key for filename completion: if more then one file", "exists beginning with what you typed at that", "point, you'll get a list of files that match. With", "the cursor keys and the enter key you can then", "select the filename to use.", NULL }; char *help_enter_cmd_to_monitor[] = { "Please enter the command that you want to monitor", "with MultiTail. If the command is not in the", "directory you started MultiTail from, please enter", "the complete pathname. As with bash, you can press", "the *TAB* key for filename completion: if more", "then one file exists beginning with what you typed", "at that point, you'll get a list of files that", "match. With the cursor keys and the enter key you", "can then select the filename to use.", "", "Commands are executed in their own virtual tty to", "force line-buffered output. They are executed by", "starting up */bin/sh -c* with as parameter the", "command you'll enter here.", NULL }; char *help_add_window_merge_subwin[] = { "At this point there are already one or more", "windows on the screen. You can now indicate if", "you want the new file/command to be *merged* in an", "existing window. Lines from the logfiles/commands", "in a window are then interleaved as they are", "generated", "Enter ^y^ or ^n^ here or ^ctrl+g^ to abort.", NULL }; char *help_add_window_select_merge_window[] = { "At this point there are already one or more", "windows on the screen. You can now indicate if", "you want the new file/command to be *merged* in an", "existing window. Lines from the logfiles/commands", "in a window are then interleaved as they are", "generated", "Here, select the window to merge with.", "Press ^ctrl+g^ to abort.", NULL }; char *help_add_file_follow_filename[] = { "Here you can select wether to follow the filename", "(^y^) or the filedescriptor (^n^). When the file that", "you want to monitor gets, for example, \"rotated\"", "by *logrotate* every night, you want to select to", "follow the filename here. What this means is: if", "the filename gets renamed (or deleted) and a new", "file with the original filename is created, that", "new file will automatically be monitored instead", "of the original file.", NULL, }; char *help_ask_colors[] = { "Now you can select how MultiTail colors the text", "it displays.", " ^n^ do *not* use any coloring at all", " ^s^ this option expects that you're monitoring", " a file in the syslog format, e.g: date", " hostname, program, pid, message. It uses", " the name of the program which logged the", " message to select a color", " ^m^ this option evaluates the complete logged", " line to select a color", " ^S^ this option lets you select a pre-defined", " color scheme. That color scheme should be", " defined in *multitail.conf* which can be", " stored in your home directory or in */etc*", " See the example file for details.", " ^f^ when selecting this option, you'll be", " asked to enter the number of the field to", " use for selecting a color. You'll also be", " asked to enter the character that seperates", " each field in a line", " ^ctrl+g^ abort", "", "Using colors only works if your terminal supports", "this.", NULL, }; char *help_colors_field_nr[] = { "Selecting a field works like the *cut* command:", "you select a delimiter and a field-nummer. The", "delimiter specifies where a field ends and where", "the next field starts. There's one important", "difference between MultiTail and *cut* field-", "delimiters can be more then one character in size.", "", "Here, please enter the number of the field which", "you want to use for the color-selection. The first", "field on a line has number 0.", NULL }; char *help_colors_field_delimiter[] = { "Selecting a field works like the *cut* command:", "you select a delimiter and a field-nummer. The", "delimiter specifies where a field ends and where", "the next field starts. There's one important", "difference between MultiTail and *cut* field-", "delimiters can be more then one character in size.", "", "Here, please enter the string that delimits each", "field. This delimiter must be at least one", "character in size.", NULL }; char *help_ask_colors_select_scheme[] = { "Here you can select a predefined color scheme.", "These color schemes are defined in *multitail.conf*", "which can be located in your homedirectory and in", "*/etc*.", "In this configuration file you select using", "regular expressions what color to use for what", "patterns.", "See the example *multitail.conf* which should've", "been included with the MultiTail when you", "installed it.", "Press ^ctrl+g^ to abort.", NULL }; char *help_add_window_repeat_interval[] = { "In the case that the external command you're", "monitoring with MultiTail exits, the window gets", "automatically closed. Press enter to indicate that", "that is ok with you. If you want the command to be", "automatically repeated instead, enter here the", "delay before restarting that command. The minimum", "is 0: in that case the program gets repeated", "immediately (that is: when MultiTail notices that", "it died!).", "Press enter on an empty line to abort.", NULL }; char *help_add_file_display_diff[] = { "In some situations (for example: when monitoring", "the output of '*netstat -t tcp*') you may only", "want to see the difference between each run. In", "that case press ^y^ here. To see all output every", "time, press ^n^.", "Press ^ctrl+g^ to abort.", "", "On the commandline you can select this behaviour", "with the ^-R^ switch.", NULL }; #define help_merge_another_window help_add_window_select_merge_window char *help_enter_regexp_select_window[] = { "In this menu one can enter/edit/delete regular", "expressions which are used to filter what is", "displayed in a window." "First you need to select the main-window. If more", "then one file/command is displayed in a window,", "you'll get another selection box after this one", "where you can select a \"sub-window\".", "Press ^ctrl+g^ to abort.", NULL }; char *help_enter_regexp_select_subwindow[] = { "The window you selected contains the output of", "more then one file/command. You now have to", "select the \"sub-window\" to edit the regular", "expressions for.", "Press ^ctrl+g^ to abort.", NULL }; char *help_regexp_menu[] = { "This menu has a couple of options for editing", "regexps ('regular expressions'):", " ^a^ add a new regexp (one can have a maximum", " of 10 per file/command)", " ^e^ edit an existing regexp", " ^d^ delete one of the existing regexps", " ^D^ move an entry down, this also changes", " priority of this reg.exp.", " ^U^ move an entry up", " ^r^ reset counter", " ^q^/^x^ leave this menu", NULL }; char *help_enter_regexp[] = { "Enter a regular expression. This expression will", "be used for filtering.", "A regular expression is a method for defining", "text-patterns. See wikipedia for more info:", "_http://en.wikipedia.org/wiki/Regular__expression_", "or read my (Dutch!) article:", "_http://www.vanheusden.com/misc/regexp.html_", "Press enter on an empty line to abort.", NULL }; char *help_negate_regexp[] = { "Here you select how to handle the outcome of the", "regular expression processing: should it match", "when the regexp matches, or should it match when", "the regexp does *not* match?", "Press ^ctrl+g^ to abort.", NULL }; char *help_regexp_usage[] = { "Here you select what to do when a regexp matches.", " ^m^ show the line if the regexp matches. If", " selected to \"negate\" the outcome of the", " regexp, ^m^ tells MultiTail to only display", " the line if the regexp does *not* match.", " ^c^ if the regexp matches (see also ^m^)," " the line is displayed and colored", " ^C^ the lines are always printed but if the", " regexp matches, colors are used", " ^B^ if a regexp matches for a line, a bell-", " sound is produced", " ^b^ combination of ^C^ and ^B^", " ^x^ if a regexp matches, a command is", " executed", " ^ctrl+g^ abort", NULL }; char *help_enter_cmd[] = { "Enter the command which should be executed when a", "line matches a regular expression.", "The command is executed in the background and its", "output is redirected to */dev/null*.", "The command is started as a parameter of */bin/sh -c*", "Press enter on an empty line to abort.", NULL }; char *help_compile_regexp_failed[] = { "Compilation (\"pre-processing\") of the regular", "expression failed.", "Please check the wikipedia page for the correct", "syntax:", "_http://en.wikipedia.org/wiki/Regular__expression_", "or read my (Dutch!) article:", "_http://www.vanheusden.com/misc/regexp.html_", NULL }; char *help_delete_select_window[] = { "You've indicated that you want to delete a window.", "Select the window that you want to remove.", "If a window consists of more then one logfile or", "command, you'll be asked next if you want to", "remove the complete window including all", "\"subwindows\" or just the first one (the first", "file/command).", "Press ^ctrl+g^ to abort.", NULL }; char *help_delete_window_delete_all_subwin[] = { "This window contains more then one logfile /", "command. Here you select if you want to remove all", "\"subwindows\" or just the first file/command.", "Press ^ctrl+g^ to abort.", NULL }; char *help_delete_select_subwindow[] = { "This window contains more then one logfile /", "command. Select the file / command you wish to", "remove from this window.", "Press ^ctrl+g^ to abort.", NULL }; char *help_swap_win1[] = { "You have indicated that you want to swap the", "location (on the screen) of 2 windows.", "Please select the first window.", "Press ^ctrl+g^ to abort.", NULL }; char *help_swap_win2[] = { "You have indicated that you want to swap the", "location (on the screen) of 2 windows.", "Please select the second window.", "Press ^ctrl+g^ to abort.", NULL }; char *help_toggle_colors_select_window[] = { "You've indicated that you want to change the", "colors of a window. Please select that window.", "If a window consists of more then one logfile or", "command, you'll be asked to select the file or", "command in that window to change the colors for.", "Press ^ctrl+g^ to abort.", NULL }; char *help_toggle_colors_select_subwindow[] = { "This window contains more then one logfile /", "command. Select the file / command you wish to", "change the colors for.", "Press ^ctrl+g^ to abort.", NULL }; char *help_failed_to_start_tail[] = { "There was a problem starting the '*tail*'-process", "(in case you wanted to monitor a logfile) or", "there was a problem starting the command you", "wanted to monitor.", NULL }; char *help_window_closed[] = { "A window has closed. This happens when you're", "monitoring the output of an external program. That", "program has died and when that happens the", "window is automatically closed.", "On the commandline you can set with the ^-r^ or", "the ^-R^ that a command should be repeated when it", "exits. This behaviour can also be set when you add", "a window by pressing the ^a^-key in the main menu.", NULL }; char *help_write_script[] = { "This function writes a script to disk which will", "start MultiTail in the same state it now is. This", "is usefull in case you did a lot of configuring", "with the menu-interface, you then don't have to", "find out what commandline switches to set.", "Here you have to enter the filename for the script", "to generate.", "Press enter on an empty line to abort.", NULL }; char *help_statistics[] = { "In this menu one can select the window or sub-", "window to display the statistics off.", " ^r^ reset ALL counters for ALL windows", " ^ctrl+g^ exit window", NULL, }; char *help_statistics_popup[] = { "Apart from how long MultiTail has been running, it", "also shows a couple of statistics for each window.", "It also shows some statistics on the interval", "between each show line in each window.", " *average* average interval", " *standard deviation* speaks for itself", "", " ^r^ reset the counters for this window", " ^ctrl+g^ exit window", NULL }; char *help_set_buffering[] = { "In this window you can control the buffering for", "each window. The first question lets you select", "on what window you want to set the buffering", "parameters:" " ^a^ set on all windows", " ^o^ set on one window", "Press ^ctrl+g^ to abort.", NULL }; char *help_set_buffering_store_what[] = { "Here you set what should be buffered.", " ^a^ buffer all lines that are monitored", " ^m^ only buffer the lines that match the", " configured regular expressions. If you have", " not set any regexps, everything is stored.", "Press ^ctrl+g^ to abort.", NULL }; char *help_enter_number_of_lines_to_store[] = { "Set the size of the buffer. The default size is", "100 lines per window.", "Press enter on an empty line to abort.", NULL }; char *help_set_buffering_select_window[] = { "Here you select the window on which you want to", "set the buffer limits.", "Press ^ctrl+g^ to abort.", NULL }; char *help_hide_window[] = { "Select the window that you want to (un-)hide. When", "a window is hidden, it is not displayed: logging", "and filtering continues as usual.", "Press ^ctrl+g^ to abort.", NULL }; char *help_set_windowsizes[] = { "Here you can set the sizes of windows. The width", "can only set for *all* windows, the height can be", "set for each individual window.", " ^m^ manage columns: add a column, set width", " ^h^ set the height for a window", "Press ^ctrl+g^ to abort.", NULL }; char *help_set_window_width[] = { "When setting the window width, you actually set", "the width of the windows on the left. So if you", "set it to 20, the windows on the left are 20", "characters in width. The windows on the right are", "then what is left in width. Beware: one column is", "used for the dividing line.", "This option can only be used when you've switched", "on \"vertical split\" in the main menu (^v^).", "Press enter on an empty line to abort.", NULL }; char *help_set_window_height[] = { "The height of a window must be at least 2 lines.", "That is excluding the statusline. The statusline", "can be switched off using the '^-D^' commandline", "switch.", "One can only set the window height if there are at", "least 2 windows on the screen.", "When you enter '0' MultiTail will automatically", "set the height. The height must be either 0 or", "more then one.", "Press enter on an empty line to abort.", NULL }; char *help_set_window_height_select_window[] = { "You're about to set the height of a window. The", "first step is to select the window which you want", "to resize.", "Press ^ctrl+g^ to abort.", NULL }; char *help_terminal_mode_select_window[] = { "MultiTail features a small terminal. With this", "terminal you can send commando's to a program you", "are monitoring. That way, one can put a telnet-", "session in a window, login and start a tail-", "process on the remote host.", "Warning: this doesn't work for ssh as ssh accesses", "the tty you've been logged on directly.", NULL }; char *help_scrollback_help[] = { "The scrollback menu lets you scrollback in the", "buffer. The default size of the buffer is 100", "lines. This size can be set with the ^-m^ command-", "line parameter or the ^m^-key in the main menu.", " ^c^ set colors", " ^f^/^/^ search for a string in the buffer", " ^n^ find the next occurence", " ^Y^ toggle linewrap. if linewrap is disabled,", " one can scroll left/right with the cursor-", " keys.", " ^ctrl^+^t^ show (sub-)window number in front of", " each line", " ^t^ show a list of window-nrs and what they", " contain. can also display statistics.", " ^s^ save the buffer to a file", "Press ^ctrl+g^ to abort.", NULL }; char *help_scrollback_savefile[] = { "Save a buffer to a file.", " ^a^ write all lines to the file", " ^m^ write only the lines to the file which", " match the regular expression(s) (if any)", NULL }; char *help_scrollback_savefile_enter_filename[] = { "Save a buffer to a file. Enter the name of the", "file to write to.", "Press enter on an empty line to abort.", NULL }; char *help_scrollback_no_mark[] = { "You cannot scrollback in this window as it has no", "buffer. Press ^m^ in the main-menu to set the size", "of the buffer or use the ^-m^ commandline switch.", NULL }; char *help_scrollback_select_window[] = { "Select the window in which you want to scrollback.", "Press ^ctrl+g^ to abort.", NULL }; char *help_scrollback_edit_search_string[] = { "Enter the string to search for in the scrollback-", "buffer. The search-string can be a regular", "expression.", "Press enter on an empty line to abort.", NULL }; char *help_pause_a_window[] = { "Select the window to (un-)pause. When a window is", "paused, it won't be updated until the window is", "un-paused. NOTHING will be discarded.", NULL }; char *help_ask_colors_select_color[] = { "Select the color or attribute to use.", NULL }; char *help_set_vertical_split_n_win[] = { "Enter the number of columns to split the terminal", "in. If you press 'j' in the menu, you can also", "select the number of windows per column.", NULL }; char *help_wipe_window[] = { "Press the number of the window to wipe. The number", "can be found in the status-line of the windows.", "Press 'r' to redraw the windows so that you can", "see their contents again.", NULL }; char *help_select_colorschemes[] = { "Move the cursor with the cursor keys to the a", "colorscheme. Then press space to select and again", "to unselect. Press enter to submit or ^g to abort.", NULL }; char *help_manage_cols[] = { "Press ^a^ to add a column, ^d^ to delete one and press", "^e^ to set the number of windows in a column.", NULL }; char *help_set_linewrap[] = { "In this menu one can set how the wrap the text in", "windows.", " ^l^ start at the left and cut of at the right", " ^a^ try to display everything", " ^r^ cut off at the left", " ^s^ cut right before the syslog process name", " ^S^ cut right after the syslog process name", " ^o^ enter a position where to cut", " Press ctrl+g to abort.", NULL }; char *help_enter_stripper_select_window[] = { "Select window to apply the stripping to.", NULL }; char *help_enter_stripper_select_subwindow[] = { "Select the sub-window to apply the stripping to.", NULL }; char *help_redirect_failed[] = { "Sending the data which was retrieved from a tail-", "process to some other process or file failed.", "Maybe the disk is full or the process died?", NULL }; char *help_stripper_type[] = { " ^e^ filter by using a regular expression", " ^r^ filter by specifying the character-range", " ^c^ specify a column to strip - you'll be asked", " to enter a delimiter as well", NULL }; char *help_stripper_start_offset[] = { "Please enter the offset from where to strip the", "string. The offset starts at 0.", NULL }; char *help_stripper_end_offset[] = { "Please enter the offset upto where the string will", "be filtered. Upto, not including!", NULL }; char *help_stripper_delimiter[] = { "When selecting stripping by column-number you need", "to enter the delimiter for each column. Usually", "you would enter a space here.", NULL }; char *help_stripper_col_nr[] = { "Enter the number of the column here. The first one", "is at offset 0.", NULL }; char *help_error_write_script_create_file[] = { "There was an error writing the scriptfile. Maybe", "the filesystem is full or the media is damaged?", NULL }; char *help_column_width[] = { "Enter the number of characters that will fit in a", "line for this column.", NULL }; char *help_n_win_per_col[] = { "Enter the number of windows that will fit in this", "column (vertically).", NULL }; char *help_send_signal_select_window[] = { "Select the window to send the signal to. Please", "note that sending a signal may cause the process", "(for example the tail-process when monitoring a", "logfile) to exit and thereby closing the window.", NULL }; char *help_send_signal_window_send_to_all_subwin[] = { "Would you like to send the signal to all sub-", "windows?", NULL }; char *help_send_signal_select_subwindow[] = { "Select the subwindow to send the signal to.", NULL }; char *help_send_signal_failed[] = { "Sending the signal to the process failed. This", "should not happen.", NULL }; char *help_select_signal[] = { "Select the signal to send. Please note that some", "signals may cause the process to exit and closing", "the window", NULL }; char *help_screendump_select_file[] = { "Select the file to write the screendump to.", "Existing files will be overwritten.", NULL }; char *help_screendump_select_win[] = { "Select the window to dump to a screendump-file.", "The output file will be plain ascii.", NULL }; char *help_truncate_file_select_window[] = { "Select window. By selecting a window you select", "the file to truncate.", NULL }; char *help_truncate_file_select_subwindow[] = { "As the window you selected contains several sub-", "windows, you need to select now the subwindow to", "truncate.", NULL }; char *help_truncate_areyousure[] = { "Are you really sure you want to truncate this", "file? This cannot be undone!", NULL }; char *help_truncate_failed[] = { "There was an error truncating the file. Please", "check the permissions on the file.", NULL }; char *help_truncate_only_logfiles[] = { "One can only truncate logfiles and not processes.", NULL }; char *help_select_color_and_attributes[] = { "Select the foreground and background color to use.", "You can select one foreground and one background-", "color but you can select multiple attributes.", "Press the space-bar to select or un-select.", NULL }; char *help_cannot_change_color[] = { "Your terminal doesn't support chaning the red,", "green and blue values of a color.", NULL }; char *help_edit_color_edit[] = { "Depending on the range you selected (by pressing", "the cursor up and down key), you can enter a value", "for either red, green or blue in the range of", "0-1000, 0-255 or 00-ff (hex value).", NULL }; char *help_edit_color[] = { "In this menu one can edit the values for the red,", "the green and the blue component of a color.", "Move the '>' with the up and down cursorkeys in", "front of the range in which you want to enter the", "value. The uppermost range being 0 upto 1000, the", "middle 0-255 and the lowest also 0-255 but entered", "in hexadecimal values.", " ^r^ edit the red component", " ^g^ edit the green component", " ^b^ edit the blue component", " ^c^ give the color a name/description", NULL }; char *help_edit_color_change_name[] = { "One can give this specific red, green and blue", "values a name. Like roses-red or so. This name can", "then be used in a colorscheme.", NULL }; char *help_hide_but_window[] = { "Select which window to keep open. All other", "windows will be closed.", NULL }; char *help_search_in_all_windows[] = { "This functionality lets you search in all windows.", "The result will be merged into one new window.", NULL }; char *help_highlight_in_all_windows[] = { "This functionality lets you search in all windows.", "All lines with the search-string will be", "highlighted (displayed in reverse).", NULL }; char *help_select_conversionschemes[] = { "With a conversionscheme one can let MultiTail", "automatically convert e.g. ip-addresses, time-", "stamps, etc. to something more readable. In this", "menu you can select what conversionscheme(s) to", "apply to a (sub-)window.", NULL }; char *help_select_restart_window[] = { "Restarting a window stops- and restarts the tail-", "or command running underneath a (sub-)window.", NULL }; char *help_select_restart_window_all[] = { "This chooses wether to select all sub-windows or", "just one specific.", NULL }; char *help_select_terminal_emulation[] = { "MultiTail can emulate a terminal. That way it can", "display color (escape-)codes that sometimes are", "in (log-)files.", NULL }; char *help_clear_buffer[] = { "Select window for which to clear the scrollback", "buffer.", NULL }; char *help_history[] = { /*2345678901234567890123456789012345678901234567890 */ "Select a string from this list. These strings have", "been used in the past in MultiTail. They are", "stored in a file. Check the 'history_file' entry", "in the configuration-file to see in what file.", NULL }; #if 0 char *help_bla[] = { /*2345678901234567890123456789012345678901234567890 */ "FIXME", /* FIXME */ NULL }; #endif help_t help[] = { { HELP_MAIN_MENU, help_main_menu }, { HELP_ADD_FILE_OR_CMD, help_add_file_or_cmd }, { HELP_LIST_KEYBINDINGS, help_list_keybindings }, { HELP_ENTER_FILENAME_TO_MONITOR, help_enter_filename_to_monitor }, { HELP_ENTER_CMD_TO_MONITOR, help_enter_cmd_to_monitor }, { HELP_ADD_WINDOW_MERGE_SUBWIN, help_add_window_merge_subwin }, { HELP_ADD_WINDOW_SELECT_MERGE_WINDOW, help_add_window_select_merge_window }, { HELP_ADD_FILE_FOLLOW_FILENAME, help_add_file_follow_filename }, { HELP_ASK_COLORS, help_ask_colors }, { HELP_COLORS_FIELD_NR, help_colors_field_nr }, { HELP_COLORS_FIELD_DELIMITER, help_colors_field_delimiter }, { HELP_ASK_COLORS_SELECT_SCHEME, help_ask_colors_select_scheme }, { HELP_ADD_WINDOW_REPEAT_INTERVAL, help_add_window_repeat_interval }, { HELP_ADD_FILE_DISPLAY_DIFF, help_add_file_display_diff }, { HELP_MERGE_ANOTHER_WINDOW, help_merge_another_window }, { HELP_ENTER_REGEXP_SELECT_WINDOW, help_enter_regexp_select_window }, { HELP_ENTER_STRIPPER_SELECT_WINDOW, help_enter_stripper_select_window }, { HELP_ENTER_REGEXP_SELECT_SUBWINDOW, help_enter_regexp_select_subwindow }, { HELP_ENTER_STRIPPER_SELECT_SUBWINDOW, help_enter_stripper_select_subwindow }, { HELP_REGEXP_MENU, help_regexp_menu }, { HELP_ENTER_REGEXP, help_enter_regexp }, { HELP_NEGATE_REGEXP, help_negate_regexp }, { HELP_REGEXP_USAGE, help_regexp_usage }, { HELP_ENTER_CMD, help_enter_cmd }, { HELP_COMPILE_REGEXP_FAILED, help_compile_regexp_failed }, { HELP_DELETE_SELECT_WINDOW, help_delete_select_window }, { HELP_DELETE_SELECT_SUBWINDOW, help_delete_select_subwindow }, { HELP_DELETE_WINDOW_DELETE_ALL_SUBWIN, help_delete_window_delete_all_subwin }, { HELP_SWAP_WIN1, help_swap_win1 }, { HELP_SWAP_WIN2, help_swap_win2 }, { HELP_TOGGLE_COLORS_SELECT_WINDOW, help_toggle_colors_select_window }, { HELP_TOGGLE_COLORS_SELECT_SUBWINDOW, help_toggle_colors_select_subwindow }, { HELP_FAILED_TO_START_TAIL, help_failed_to_start_tail }, { HELP_WINDOW_CLOSED, help_window_closed }, { HELP_WRITE_SCRIPT, help_write_script }, { HELP_STATISTICS, help_statistics }, { HELP_SET_BUFFERING, help_set_buffering }, { HELP_SET_BUFFERING_STORE_WHAT, help_set_buffering_store_what }, { HELP_ENTER_NUMBER_OF_LINES_TO_STORE, help_enter_number_of_lines_to_store }, { HELP_SET_BUFFERING_SELECT_WINDOW, help_set_buffering_select_window }, { HELP_HIDE_WINDOW, help_hide_window }, { HELP_SET_WINDOWSIZES, help_set_windowsizes }, { HELP_SET_WINDOW_WIDTH, help_set_window_width }, { HELP_SET_WINDOW_HEIGHT, help_set_window_height }, { HELP_SET_WINDOW_HEIGHT_SELECT_WINDOW, help_set_window_height_select_window }, { HELP_TERMINAL_MODE_SELECT_WINDOW, help_terminal_mode_select_window }, { HELP_SCROLLBACK_HELP, help_scrollback_help }, { HELP_SCROLLBACK_SAVEFILE, help_scrollback_savefile }, { HELP_SCROLLBACK_NO_MARK, help_scrollback_no_mark }, { HELP_SCROLLBACK_SAVEFILE_ENTER_FILENAME, help_scrollback_savefile_enter_filename }, { HELP_SCROLLBACK_SELECT_WINDOW, help_scrollback_select_window }, { HELP_SCROLLBACK_EDIT_SEARCH_STRING, help_scrollback_edit_search_string }, { HELP_PAUSE_A_WINDOW, help_pause_a_window }, { HELP_ASK_COLORS_SELECT_COLOR, help_ask_colors_select_color }, { HELP_SET_VERTICAL_SPLIT_N_WIN, help_set_vertical_split_n_win }, { HELP_MANAGE_COLS, help_manage_cols }, { HELP_WIPE_WINDOW, help_wipe_window }, { HELP_SELECT_COLORSCHEMES, help_select_colorschemes }, { HELP_SET_LINEWRAP, help_set_linewrap }, { HELP_REDIRECT_FAILED, help_redirect_failed }, { HELP_STRIPPER_TYPE, help_stripper_type }, { HELP_STRIPPER_START_OFFSET, help_stripper_start_offset }, { HELP_STRIPPER_END_OFFSET, help_stripper_end_offset }, { HELP_STRIPPER_DELIMITER, help_stripper_delimiter }, { HELP_STRIPPER_COL_NR, help_stripper_col_nr }, { HELP_ERROR_WRITE_SCRIPT_CREATE_FILE, help_error_write_script_create_file }, { HELP_COLUMN_WIDTH, help_column_width }, { HELP_N_WIN_PER_COL, help_n_win_per_col }, { HELP_SEND_SIGNAL_SELECT_WINDOW, help_send_signal_select_window }, { HELP_SEND_SIGNAL_WINDOW_SEND_TO_ALL_SUBWIN, help_send_signal_window_send_to_all_subwin }, { HELP_SEND_SIGNAL_SELECT_SUBWINDOW, help_send_signal_select_subwindow }, { HELP_SEND_SIGNAL_FAILED, help_send_signal_failed }, { HELP_SELECT_SIGNAL, help_select_signal }, { HELP_SCREENDUMP_SELECT_FILE, help_screendump_select_file }, { HELP_SCREENDUMP_SELECT_WIN, help_screendump_select_win }, { HELP_TRUNCATE_FILE_SELECT_WINDOW, help_truncate_file_select_window }, { HELP_TRUNCATE_FILE_SELECT_SUBWINDOW, help_truncate_file_select_subwindow }, { HELP_TRUNCATE_AREYOUSURE, help_truncate_areyousure }, { HELP_TRUNCATE_FAILED, help_truncate_failed }, { HELP_TRUNCATE_ONLY_LOGFILES, help_truncate_only_logfiles }, { HELP_SELECT_COLOR_AND_ATTRIBUTES, help_select_color_and_attributes }, { HELP_CANNOT_EDIT_COLOR, help_cannot_change_color }, { HELP_EDIT_COLOR_EDIT, help_edit_color_edit }, { HELP_EDIT_COLOR, help_edit_color }, { HELP_EDIT_COLOR_CHANGE_NAME, help_edit_color_change_name }, { HELP_HIDE_BUT_WINDOW, help_hide_but_window }, { HELP_SEARCH_IN_ALL_WINDOWS, help_search_in_all_windows }, { HELP_HIGHLIGHT_IN_ALL_WINDOWS, help_highlight_in_all_windows }, { HELP_SELECT_CONVERSIONSCHEMES, help_select_conversionschemes }, { HELP_SELECT_RESTART_WINDOW, help_select_restart_window }, { HELP_SELECT_RESTART_WINDOW_ALL, help_select_restart_window_all }, { HELP_SELECT_TERMINAL_EMULATION, help_select_terminal_emulation }, { HELP_CLEAR_BUFFER, help_clear_buffer }, { HELP_HISTORY, help_history }, { -1, NULL } }; void show_help(int what_help) { help_t *php = NULL; int index = 0; int line_cnt = 0; int ppos = -1, pos = 0; while(help[index].nr != -1) { if (help[index].nr == what_help) { php = &help[index]; break; } index++; } if (php) { NEWWIN *winb = create_popup(15 + 2, 50 + 4); NEWWIN *win = create_popup(15 , 50 ); while(php -> text[line_cnt]) line_cnt++; wattron(winb -> win, A_STANDOUT); if (line_cnt > 15) mvwprintw(winb -> win, 0, 2, "Use cursor UP/DOWN to scroll, ctrl+g to exit"); else mvwprintw(winb -> win, 0, 2, "Press ctrl+g to exit"); wattroff(winb -> win, A_STANDOUT); mydoupdate(); for(;;) { int c; if (ppos != pos) { int loop; werase(win -> win); for(loop=pos; loop 0) { pos--; } else if ((c == KEY_DOWN || c == 13) && pos < (line_cnt - 1)) { pos++; } else if (c == KEY_PPAGE && pos >= 15) { pos -= 15; } else if ((c == KEY_NPAGE || c == ' ') && (pos + 15) < (line_cnt - 1)) { pos += 15; } else if (c == KEY_HOME && pos > 0) { pos = 0; } else if (tolower(c) == 'q' || c == abort_key) { break; } else if (c == -1) { /* a window got closed */ } else { wrong_key(); } } delete_popup(win); delete_popup(winb); } else { LOG("no help found for: %d\n", what_help); wrong_key(); } } multitail-6.0/mem.h0000644000175000017500000000022012245202637014205 0ustar folkertfolkertvoid * mymalloc(int size); void * myrealloc(void *oldp, int newsize); void myfree(void *p); char * mystrdup(char *in); void clean_memory(void); multitail-6.0/color.h0000644000175000017500000000055212245202637014555 0ustar folkertfolkertvoid add_color_scheme(int_array_t *schemes, int cur_scheme); myattr_t choose_color(char *string, proginfo *cur, color_offset_in_line **cmatches, int *n_cmatches, mybool_t *has_merge_color, char **new_string); int find_colorscheme(char *name); void init_colors(void); color_offset_in_line *realloc_color_offset_in_line(color_offset_in_line *oldp, int n_entries); multitail-6.0/colors-example.pl0000755000175000017500000000025112245202637016554 0ustar folkertfolkert#!/usr/bin/perl # disable I/O buffering (this is essential) $| = 1; while(<>) { # 2 colors print "2,4,red,yellow,bold\n"; print "6,7,green,white\n"; print "\n"; } multitail-6.0/misc.c0000644000175000017500000003440712245202637014373 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mt.h" #include "term.h" #include "utils.h" #include "error.h" #include "mem.h" #include "help.h" #include "globals.h" #include "ui.h" void info(void) { NEWWIN *mywin = create_popup(19, 60); int line = 7; struct utsname uinfo; int proc_u_line; char *term = getenv("TERM"); mvwprintw(mywin -> win, 1, 2, "-=* MultiTail " VERSION " *=-"); mvwprintw(mywin -> win, 3, 2, "Written by folkert@vanheusden.com"); mvwprintw(mywin -> win, 4, 2, "Website: http://www.vanheusden.com/multitail/"); if (!use_colors) mvwprintw(mywin -> win, line++, 2, "Your terminal doesn't support colors"); if (uname(&uinfo) == -1) error_popup("Retrieving system information", -1, "uname() failed\n"); else { line++; mvwprintw(mywin -> win, line++, 2, "Running on:"); #ifdef _GNU_SOURCE mvwprintw(mywin -> win, line++, 2, "%s/%s %s %s", uinfo.nodename, uinfo.sysname, uinfo.machine, uinfo.domainname); #else mvwprintw(mywin -> win, line++, 2, "%s/%s %s", uinfo.nodename, uinfo.sysname, uinfo.machine); #endif mvwprintw(mywin -> win, line++, 2, "%s %s", uinfo.release, uinfo.version); line++; } if (has_colors()) mvwprintw(mywin -> win, line++, 2, "colors: %d, colorpairs: %d (%d), change colors: %s", COLORS, COLOR_PAIRS, cp.n_def, can_change_color()?"yes":"no"); else mvwprintw(mywin -> win, line++, 2, "Terminal does not support colors."); if (term) mvwprintw(mywin -> win, line++, 2, "Terminal size: %dx%d, terminal: %s", max_x, max_y, term); else mvwprintw(mywin -> win, line++, 2, "Terminal size: %dx%d", max_x, max_y); if (beep_interval > 0) mvwprintw(mywin -> win, line++, 2, "Did %d beeps.", did_n_beeps); proc_u_line = line++; escape_print(mywin, 16, 2, "_Press any key to exit this screen_"); #if defined(__FreeBSD__) || defined(linux) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined(sun) || defined(__sun) || defined(__GNU__) || defined(__CYGWIN__) for(;;) { dtime_t run_time = get_ts() - mt_started; #ifndef __CYGWIN__ double v1, v2, v3; get_load_values(&v1, &v2, &v3); mvwprintw(mywin -> win, 6, 2, "Current load of system: %f %f %f", v1, v2, v3); #endif if (run_time) { struct rusage usage; if (getrusage(RUSAGE_SELF, &usage) == -1) error_exit("getrusage() failed\n"); mvwprintw(mywin -> win, proc_u_line, 2, "Runtime: %02d:%02d:%02d, avg.proc.usage: %.2f%% ints/s: %.1f", (int)(run_time / 3600), ((int)run_time / 60) % 60, (int)run_time % 60, ((double)usage.ru_utime.tv_sec + (double)usage.ru_utime.tv_usec / 1000000.0 + (double)usage.ru_stime.tv_sec + (double)usage.ru_stime.tv_usec / 1000000.0) * 100.0 / run_time, (double)total_wakeups / run_time); } mydoupdate(); if (wait_for_keypress(-1, popup_refresh_interval, mywin, 0) != -1) break; } #else mydoupdate(); wait_for_keypress(-1, 0, mywin, 0); #endif delete_popup(mywin); } void reset_counters(statistics_t *ps) { memset(ps, 0x0, sizeof(*ps)); ps -> sccfirst = 1; } void statistics_popup(int f_index, proginfo *cur) { NEWWIN *popup = create_popup(16, 68); const char *title = "Statistics for "; char *abbr_fname = shorten_filename(cur -> filename, 54 - strlen(title)); char buffer[54 + 1]; snprintf(buffer, sizeof(buffer), "%s%s", title, abbr_fname); for(;;) { dtime_t time_running = get_ts() - cur -> statistics.start_ts; time_t start_ts = (time_t)cur -> statistics.start_ts; char *start_ts_str = mystrdup(ctime(&start_ts)); time_t lastevent = (time_t)cur -> statistics.lastevent; char *last_ts_str = mystrdup(ctime(&lastevent)); char *dummy; char *vmsize_str = NULL; char *fsize_str = NULL; char *total_data_processed_str = amount_to_str(cur -> statistics.bytes_processed); char *buffer_kb; off64_t fsize = -1; int c; int total_re = 0; int loop; if (cur -> wt == WT_COMMAND) { vmsize_str = amount_to_str(get_vmsize(cur -> pid)); } else if (cur -> wt == WT_FILE) { (void)file_info(cur -> filename, &fsize, 0, NULL, NULL); fsize_str = amount_to_str(fsize); } dummy = strchr(start_ts_str, '\n'); if (dummy) *dummy = 0x00; dummy = strchr(last_ts_str, '\n'); if (dummy) *dummy = 0x00; werase(popup -> win); win_header(popup, buffer); ui_inverse_on(popup); mvwprintw(popup -> win, 3, 2, "# lines :"); mvwprintw(popup -> win, 3, 27, "#l/s :"); mvwprintw(popup -> win, 3, 44, "Avg len:"); mvwprintw(popup -> win, 4, 2, "Data interval :"); if (cur -> wt == WT_COMMAND) mvwprintw(popup -> win, 5, 2, "VM size :"); else if (cur -> wt == WT_FILE) mvwprintw(popup -> win, 5, 2, "File size :"); mvwprintw(popup -> win, 9, 2, "Data processed:"); mvwprintw(popup -> win, 9, 27, "Bps :"); mvwprintw(popup -> win, 6, 2, "Started at :"); mvwprintw(popup -> win, 7, 2, "Last event :"); mvwprintw(popup -> win, 8, 2, "Next expected@:"); mvwprintw(popup -> win, 10, 2, "# matched r.e.:"); mvwprintw(popup -> win, 11, 2, "Buffered lines:"); mvwprintw(popup -> win, 11, 27, "Bytes:"); mvwprintw(popup -> win, 11, 44, "Limit :"); mvwprintw(popup -> win, 12, 2, "# of beeps: "); if (cur -> wt == WT_COMMAND) { mvwprintw(popup -> win, 13, 2, "Number of runs:"); mvwprintw(popup -> win, 13, 27, "Last rc:"); } ui_inverse_off(popup); mvwprintw(popup -> win, 3, 18, "%d", cur -> statistics.n_events); mvwprintw(popup -> win, 6, 18, "%s", start_ts_str); if (cur -> statistics.lastevent != (dtime_t)0.0) mvwprintw(popup -> win, 7, 18, "%s", last_ts_str); else mvwprintw(popup -> win, 7, 18, "---"); if (cur -> statistics.n_events == 0) { mvwprintw(popup -> win, 4, 18, "Not yet available"); } else { double avg = cur -> statistics.med / (double)cur -> statistics.n_events; double dev = sqrt((cur -> statistics.dev / (double)cur -> statistics.n_events) - pow(avg, 2.0)); /* serial correlation coefficient */ double scct1 = cur -> statistics.scct1 + cur -> statistics.scclast * cur -> statistics.sccu0; double med = cur -> statistics.med * cur -> statistics.med; double scc = (double)cur -> statistics.n_events * cur -> statistics.dev - med; if (scc != 0.0) { scc = ((double)cur -> statistics.n_events * scct1 - med) / scc; mvwprintw(popup -> win, 4, 18, "average: %.2f, std.dev.: %.2f, SCC: %1.6f", avg, dev, scc); } else mvwprintw(popup -> win, 4, 18, "average: %.2f, std.dev.: %.2f, not correlated", avg, dev); if (avg) { double dummy_d = (double)(time(NULL) - cur -> statistics.lastevent) / avg; time_t next_event = cur -> statistics.lastevent + (ceil(dummy_d) * avg); char *ne_str = mystrdup(ctime(&next_event)); char *dummy_str = strchr(ne_str, '\n'); if (dummy_str) *dummy_str = 0x00; mvwprintw(popup -> win, 8, 18, "%s", ne_str); myfree(ne_str); } mvwprintw(popup -> win, 3, 53, "%.1f", (double)cur -> statistics.bytes_processed / (double)cur -> statistics.n_events); } if (cur -> wt == WT_COMMAND) mvwprintw(popup -> win, 5, 18, "%s", vmsize_str); else if (cur -> wt == WT_STDIN || cur -> wt == WT_SOCKET) mvwprintw(popup -> win, 5, 18, "n.a."); else if (cur -> wt == WT_FILE) mvwprintw(popup -> win, 5, 18, "%s", fsize_str); myfree(vmsize_str); myfree(fsize_str); mvwprintw(popup -> win, 9, 18, "%s", total_data_processed_str); myfree(total_data_processed_str); if (time_running > 0) { char *bps_str = amount_to_str((double)cur -> statistics.bytes_processed / (double)time_running); mvwprintw(popup -> win, 9, 34, "%s", bps_str); myfree(bps_str); mvwprintw(popup -> win, 3, 34, "%.4f", (double)cur -> statistics.n_events / (double)time_running); } buffer_kb = amount_to_str(lb[f_index].curbytes); mvwprintw(popup -> win, 11, 18, "%d", lb[f_index].curpos); mvwprintw(popup -> win, 11, 34, "%s", buffer_kb); myfree(buffer_kb); mvwprintw(popup -> win, 12, 18, "%d", cur -> beep.did_n_beeps); escape_print(popup, 14, 2, "Press ^r^ to reset counters, ^q^ to exit"); myfree(start_ts_str); myfree(last_ts_str); for(loop=0; loop n_re; loop++) total_re += (cur -> pre)[loop].match_count; if (cur -> statistics.n_events) mvwprintw(popup -> win, 10, 18, "%d (%.2f%%)", total_re, (total_re * 100.0) / (double)cur -> statistics.n_events); else mvwprintw(popup -> win, 10, 18, "%d", total_re); if (cur -> wt == WT_COMMAND) { mvwprintw(popup -> win, 13, 18, "%d", cur -> n_runs); mvwprintw(popup -> win, 13, 36, "%d", cur -> last_exit_rc); } if (lb[f_index].maxnlines > 0) { mvwprintw(popup -> win, 11, 53, "%d lines", lb[f_index].maxnlines); } else if (lb[f_index].maxbytes > 0) { char *str = amount_to_str(lb[f_index].maxbytes); mvwprintw(popup -> win, 11, 53, "%s", str); myfree(str); } draw_border(popup); mydoupdate(); c = toupper(wait_for_keypress(HELP_STATISTICS_POPUP, popup_refresh_interval, popup, 0)); if (c == 'Q' || c == abort_key) { break; } else if (c == 'R') { reset_counters(&cur -> statistics); } else if (c != -1) { wrong_key(); } } delete_popup(popup); } void statistics_menu(void) { NEWWIN *mywin = create_popup(23, 65); int offset = 0, cur_line = 0; for(;;) { int c; int vmsize = get_vmsize(getpid()); time_t now = time(NULL); struct tm *tmnow = localtime(&now); proginfo **plist = NULL; char *issub = NULL; int *winnr = NULL; int loop, nwin = 0; /* create list of (sub-)windows */ for(loop=0; loop next; } } werase(mywin -> win); win_header(mywin, "Statistics"); for(loop=0; loop<18; loop++) { int cur_index = loop + offset; int is_sub_indent; if (cur_index >= nwin) break; is_sub_indent = issub[cur_index]; if (loop == cur_line) ui_inverse_on(mywin); if (is_sub_indent) mvwprintw(mywin -> win, 2 + loop, 7, "%s", shorten_filename(plist[cur_index] -> filename, 54)); else mvwprintw(mywin -> win, 2 + loop, 2, "[%02d] %s", winnr[cur_index], shorten_filename(plist[cur_index] -> filename, 56)); if (loop == cur_line) ui_inverse_off(mywin); } mvwprintw(mywin -> win, 20, 2, "Run-time: %.2f hours %02d:%02d", (get_ts() - mt_started) / 3600.0, tmnow -> tm_hour, tmnow -> tm_min); if (vmsize != -1) { char *vmsize_str = amount_to_str(vmsize); mvwprintw(mywin -> win, 20, 35, "Memory usage: %s", vmsize_str); myfree(vmsize_str); } escape_print(mywin, 21, 2, "Press ^r^ to reset counters, ^q^ to exit"); draw_border(mywin); mydoupdate(); c = toupper(wait_for_keypress(HELP_STATISTICS, popup_refresh_interval, mywin, 1)); if (c == 'R') { for(loop=0; loop statistics); cur = cur -> next; } } } else if (c == KEY_UP) { if (cur_line) cur_line--; else if (offset) offset--; else wrong_key(); } else if (c == KEY_DOWN) { if ((cur_line + offset) < (nwin - 1)) { if (cur_line < (18 - 1)) cur_line++; else offset++; } else wrong_key(); } else if (c == 13 || c == ' ') { statistics_popup(winnr[cur_line + offset], plist[cur_line + offset]); } else if (c == 'Q' || c == abort_key) { myfree(plist); myfree(issub); myfree(winnr); break; } else if (c != -1) { wrong_key(); } myfree(plist); myfree(issub); myfree(winnr); } delete_popup(mywin); } void heartbeat(void) { time_t now = time(NULL); struct tm *ptm = localtime(&now); static int x = 0, y = 0, dx = 1, dy = 1; static NEWWIN *hb_win = NULL; x += dx; y += dy; if (x >= (max_x - 8)) { dx = -(myrand(1) + 1); x = max_x - (8 + 1); } else if (x < 0) { dx = (myrand(2) + 1); x = 0; } if (y >= max_y) { dy = -(myrand(2) + 1); y = max_y - 1; } else if (y < 0) { dy = (myrand(2) + 1); y = 0; } if (dx == 0 && dy == 0) { dy = 1; dy = -1; } if (!hb_win) { hb_win = create_popup(1, 8); } move_panel(hb_win -> pwin, y, x); ui_inverse_on(hb_win); mvwprintw(hb_win -> win, 0, 0, "%02d:%02d:%02d", ptm -> tm_hour, ptm -> tm_min, ptm -> tm_sec); ui_inverse_off(hb_win); mydoupdate(); } void do_check_for_mail() { if (check_for_mail > 0 && mail_spool_file != NULL) { /* get current filesize */ if (stat64(mail_spool_file, &msf_info) == -1) { if (errno != ENOENT) { check_for_mail = 0; error_popup("Check for new e-mail", -1, "Error doing stat64() on file %s.\ne-Mail check disabled.\n", mail_spool_file); } } /* filesize changed? */ if (msf_info.st_size != msf_prev_size) { /* file became bigger: new mail * if it became less, the file changed because * mail was deleted or so */ if (msf_info.st_size > msf_prev_size) { mail = 1; redraw_statuslines(); if (get_do_refresh() != 2) set_do_refresh(1); } msf_prev_size = msf_info.st_size; } } } void store_statistics(proginfo *cur, dtime_t now) { if (cur -> statistics.lastevent) { dtime_t cur_deltat = now - cur -> statistics.lastevent; if (cur -> statistics.n_events == 1) { cur -> statistics.total_deltat += (cur_deltat - cur -> statistics.prev_deltat); cur -> statistics.prev_deltat = cur_deltat; } cur -> statistics.med += cur_deltat; cur -> statistics.dev += pow(cur_deltat, 2.0); cur -> statistics.n_events++; /* Update calculation of serial correlation coefficient */ /* (also uses cur -> med/dev) */ if (cur -> statistics.sccfirst) { cur -> statistics.sccfirst = 0; cur -> statistics.scclast = 0; cur -> statistics.sccu0 = cur_deltat; } else cur -> statistics.scct1 = cur -> statistics.scct1 + cur -> statistics.scclast * cur_deltat; cur -> statistics.scclast = cur_deltat; } cur -> statistics.lastevent = now; } multitail-6.0/makefile.aix0000644000175000017500000000333312245202637015546 0ustar folkertfolkertinclude version CONFIG_FILE=$(DESTDIR)/etc/multitail.conf DEBUG=#-g -D_DEBUG #-pg #-fprofile-arcs LDFLAGS=-L/usr/local/lib/ -lpanel -lncurses -lm $(DEBUG) CFLAGS=-I/usr/local/include/ -fsigned-char -DAIX -O2 -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail install: multitail cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # cp multitail.conf $(DESTDIR)/etc/multitail.conf.new mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual*.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz rm -f $(DESTDIR)/etc/multitail.conf rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-6.0/manual-nl.html0000644000175000017500000000337312245202637016044 0ustar folkertfolkert Een introductie tot MultiTail

Een introductie tot MultiTail

Introductie

Wat is MultiTail?

Met MultiTail kan men een of meerdere logfiles bekijken netzoals bij tail. Het verschil met het originele tail-programma is dat het meerdere vensters in het console-venster maakt (met ncurses) met in ieder venster een logfile. Verder kan het 2 of meer logfiles samenvoegen in 'n venster. Logfiles kunnen ook met kleur getoond worden (selectie m.b.h.v. reguliere expressies) om zo sneller te herkennen wat belangrijk is en wat niet. Logregels kunnen ook weggefiltert worden, wederom met reguliere expressies. Deze reguliere expressies kunnen via interactieve menu's toegevoegd en verwijderd worden. Ook extra vensters laten tonen kan zonder het programma te verlaten. Niet alleen logfiles kunnen bekeken worden, ook de uitvoer van shellscripts en programma's kunnen worden getoond. MultiTail kan daarbij de functionaliteit van watch nadoen.

Waarom deze tekst?

Als men MultiTail start zonder parameters, dan verschijnt er een leeg scherm met alleen een lijstje van de toetsen die men kan indrukken op dat moment of wanneer het programma loopt. Men kan er ook voor kiezen MultiTail te starten met de '-h' parameter, dat geeft een overzicht van alle mogelijke parameters. Het zijn geen kleine lijsten en dat kan wat intimiderend zijn. Ook is de help in het programma en in de man-page misschien niet voldoende, daarom is deze tekst geschreven. Mocht er dan toch nog wat onduidelijk zijn, schrijf me dan op het volgende e-mail adres:

De Basis

multitail-6.0/my_pty.c0000644000175000017500000001062612245202637014756 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ /* I read somewhere that this is needed on HP-UX */ #define _INCLUDE_HPUX_SOURCE #define _INCLUDE_POSIX_SOURCE #define _INCLUDE_XOPEN_SOURCE #define _INCLUDE_XOPEN_SOURCE_EXTENDED #define _INCLUDE_AES_SOURCE #include #include #include #include #include #include #include #include #include #include #include "mt.h" #ifdef __APPLE__ #include #endif #ifdef __OpenBSD__ #include #endif #if defined(linux) || defined(__CYGWIN__) || defined(__GNU__) || defined(__GLIBC__) #include #endif #if defined(__FreeBSD__) || defined(__minix) #include #endif #if defined(sun) || defined(__sun) #include #include #endif #if defined(IRIX) || defined(IRIX64) #endif #if defined(AIX) #endif #if defined(_HPUX_SOURCE) #include #include #include #include #include #include #include #include #endif #include #include "error.h" /* the following code was mostly taken from: */ /* $NetBSD: sshpty.c,v 1.8 2002/10/15 15:33:04 manu Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Allocating a pseudo-terminal, and making it the controlling tty. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ /* additional code for *BSD/Linux/Apple, AIX and IRIX by folkert@vanheusden.com */ int get_pty_and_fork(int *fd_master, int *fd_slave) { #if defined(__FreeBSD__) || defined(linux) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__GNU__) || defined(__GLIBC__) || defined(__minix) if (openpty(fd_master, fd_slave, NULL, NULL, NULL) == -1) { error_exit("openpty() failed.\n"); return -1; } return fork(); #elif defined(sun) || defined(__sun) || defined(AIX) || defined(_HPUX_SOURCE) || defined(OSF1) || defined(scoos) /* * This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 * also has bsd-style ptys, but they simply do not work.) */ int ptm; char *pts; pid_t pid; #if defined(AIX) char *multiplexer = "/dev/ptc"; #else char *multiplexer = "/dev/ptmx"; #endif ptm = myopen(multiplexer, O_RDWR | O_NOCTTY); if (ptm < 0) { error_exit("Error opening %s.\n", multiplexer); return -1; } *fd_master = ptm; pid = fork(); if (pid == 0) { if (grantpt(ptm) < 0) error_exit("grantpt() failed.\n"); if (unlockpt(ptm) < 0) error_exit("unlockpt() failed.\n"); setsid(); /* lose old controlling terminal (FvH) */ pts = ptsname(ptm); if (pts == NULL) error_exit("Slave pty side name could not be obtained.\n"); /* Open the slave side. */ *fd_slave = myopen(pts, O_RDWR | O_NOCTTY); if (*fd_slave < 0) error_exit("Problem opening slave-side of pseudo tty (file '%s').\n", pts); #if !defined(AIX) && !defined(scoos) /* Push the appropriate streams modules, as described in Solaris pts(7). */ if (ioctl(*fd_slave, I_PUSH, "ptem") < 0) error_exit("ioctl I_PUSH ptem failed.\n"); if (ioctl(*fd_slave, I_PUSH, "ldterm") < 0) error_exit("ioctl I_PUSH ldterm failed.\n"); (void)ioctl(*fd_slave, I_PUSH, "ttcompat"); /* not on HP-UX? */ #endif } return pid; #elif defined(IRIX) || defined(IRIX64) char *line = _getpty(fd_master, O_RDWR | O_NDELAY, 0600, 0); if (line == NULL) error_exit("_getpy() failed.\n"); *fd_slave = myopen(line, O_RDWR); if (*fd_slave < 0) error_exit("Error while openening file %s.\n", line); return fork(); #else #error I'm sorry, but I don't know what kind of system this is. #error Because of that I do not know how to do openpty() on your #error system. Please contact me at folkert@vanheusden.com and #error tell me what kind of system you have and please give me #error the output of #error man openpty #error strings `which gcc` #error strings `which cc` #error ls -lR /usr/include #error Thank you in advance and sorry for all the hassle! #endif } multitail-6.0/Makefile0000644000175000017500000000613312245202637014727 0ustar folkertfolkertinclude version UTF8_SUPPORT=yes DESTDIR=/ CONFIG_FILE=$(DESTDIR)/etc/multitail.conf CC=gcc DEBUG=-g -D_FORTIFY_SOURCE=2 # -D_DEBUG # -pg # -D_DEBUG #-pg -W -pedantic # -pg #-fprofile-arcs ifeq ($(UTF8_SUPPORT),yes) LDFLAGS+=-lpanelw -lncursesw -lutil -lm $(DEBUG) -rdynamic CFLAGS+=-funsigned-char -D`uname` -O2 -Wall -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" -DUTF8_SUPPORT else LDFLAGS+=-lpanel -lncurses -lutil -lm $(DEBUG) -rdynamic CFLAGS+=-funsigned-char -D`uname` -O2 -Wall -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" endif OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o all: multitail multitail: $(OBJS) $(CC) -Wall -W $(OBJS) $(LDFLAGS) -o multitail multitail_ccmalloc: $(OBJS) ccmalloc --no-wrapper $(CC) -Wall -W $(OBJS) $(LDFLAGS) -o ccmultitail install: multitail cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual*.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # cp multitail.conf $(CONFIG_FILE).new mkdir -p $(DESTDIR)/etc/multitail/ cp convert-* colors-* $(DESTDIR)/etc/multitail/ rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz gzip -9 $(DESTDIR)/usr/share/man/man1/multitail.1 # # There's a mailinglist! # Send an e-mail to minimalist@vanheusden.com with in the subject # 'subscribe multitail' to subscribe. # # you might want to run 'make thanks' now :-) # http://www.vanheusden.com/wishlist.php # # How do YOU use multitail? Please send me an e-mail so that I can # update the examples page. uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz rm -f $(CONFIG_FILE) rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core gmon.out *.da ccmultitail package: clean # source package rm -rf multitail-$(VERSION)* mkdir multitail-$(VERSION) cp conversion-scripts/* *.conf *.c *.h multitail.1 manual*.html Makefile makefile.* Changes INSTALL license.txt readme.txt thanks.txt version multitail-$(VERSION) tar czf multitail-$(VERSION).tgz multitail-$(VERSION) rm -rf multitail-$(VERSION) thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com echo Is your company using MultiTail and you would like to be echo mentioned on http://www.vanheusden.com/multitail/usedby.html ? echo Then please send me a logo -not too big- and a link and I will echo add it to that page. echo echo Oh, blatant plug: http://keetweej.vanheusden.com/wishlist.html check: cppcheck -v --force -j 3 --enable=all --inconclusive -I. . 2> err.txt # make clean scan-build make coverity: make clean rm -rf cov-int CC=gcc cov-build --dir cov-int make all tar vczf ~/site/coverity/multitail.tgz README cov-int/ putsite -q /home/folkert/.coverity-mt.sh multitail-6.0/mt.c0000644000175000017500000024250412245202637014057 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #if !defined(__APPLE__) && !defined(__CYGWIN__) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifndef AIX #include /* needed on Solaris 8 */ #endif #include #include #include #if defined(__GLIBC__) && ( __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2)) #include #endif /* syslog receive */ #include #include #include #include #include #ifdef UTF8_SUPPORT #include #include #endif #include "mt.h" #include "globals.h" #include "error.h" #include "mem.h" #include "utils.h" #include "scrollback.h" #include "help.h" #include "term.h" #include "history.h" #include "cv.h" #include "selbox.h" #include "stripstring.h" #include "color.h" #include "misc.h" #include "ui.h" #include "exec.h" #include "diff.h" #include "config.h" #include "cmdline.h" /* #define KEYB_DEBUG */ void LOG(char *s, ...) { #ifdef _DEBUG va_list ap; FILE *fh = fopen("log.log", "a+"); if (!fh) { endwin(); error_exit("error logging\n"); } va_start(ap, s); vfprintf(fh, s, ap); va_end(ap); fclose(fh); #endif } typedef void (*sh_t)(int); void set_signal(int sig, sh_t func, char *signame) { if (SIG_ERR == signal(sig, func)) error_exit("Setting of handler for signal %s failed.\n", signame); } void free_subentry(proginfo *entry) { int loop; /* free up filters */ for(loop=0; loop n_strip; loop++) myfree((entry -> pstrip)[loop].del); myfree(entry -> pstrip); /* free all those allocated memory blocks */ myfree(entry -> filename); myfree(entry -> cdef.field_del); if (entry -> status) delete_popup(entry -> status); if (entry -> data) delete_popup(entry -> data); /* free buffers for diff (if any) */ if (entry -> restart.diff.bcur) delete_array(entry -> restart.diff.bcur, entry -> restart.diff.ncur); if (entry -> restart.diff.bprev) delete_array(entry -> restart.diff.bprev, entry -> restart.diff.nprev); /* delete regular expressions */ for(loop=0; loop n_re; loop++) free_re(&(entry -> pre)[loop]); myfree(entry -> pre); /* stop process */ stop_process(entry -> pid); /* close pipe to (tail) process */ myclose(entry -> fd); if (entry -> wfd != entry -> fd) myclose(entry -> wfd); } void buffer_replace_pi_pointers(int f_index, proginfo *org, proginfo *new) { int loop; for(loop=0; loop curpos; loop++) myfree((pb -> be)[loop].Bline); myfree(pb -> be); pb -> be = NULL; pb -> curpos = 0; } char delete_entry(int f_index, proginfo *sub) { char delete_all = 0; /* no children? then sub must be pointing to current */ if (pi[f_index].next == NULL) { sub = NULL; } /* stop the process(es) we're watching ('tail' most of the time) */ if (sub == NULL) /* delete all? */ { proginfo *cur = &pi[f_index]; do { free_subentry(cur); cur = cur -> next; } while(cur); /* free the subwindows (if any) */ cur = pi[f_index].next; while(cur) { proginfo *dummy = cur -> next; myfree(cur); cur = dummy; } delete_all = 1; } else { free_subentry(sub); } /* remove entry from array */ if (sub == NULL) /* delete entry in the main array */ { int n_to_move = (nfd - f_index) - 1; /* free buffers */ delete_be_in_buffer(&lb[f_index]); if (n_to_move > 0) { int loop; /* update buffer proginfo-pointers */ for(loop=f_index + 1; loop pi[loop] met pi[loop-1] */ buffer_replace_pi_pointers(loop, &pi[loop], &pi[loop - 1]); } /* prog info */ memmove(&pi[f_index], &pi[f_index+1], sizeof(proginfo) * n_to_move); /* buffers */ memmove(&lb[f_index], &lb[f_index+1], sizeof(buffer) * n_to_move); } nfd--; /* shrink array */ if (nfd == 0) /* empty? */ { myfree(pi); pi = NULL; myfree(lb); lb = NULL; } else /* not empty, just shrink */ { pi = (proginfo *)myrealloc(pi, nfd * sizeof(proginfo)); lb = (buffer *) myrealloc(lb, nfd * sizeof(buffer)); } } else /* delete sub */ { if (sub != &pi[f_index]) /* not in main array? */ { proginfo *parent = &pi[f_index]; /* find parent of 'sub' */ while (parent -> next != sub) parent = parent -> next; parent -> next = sub -> next; buffer_replace_pi_pointers(f_index, sub, NULL); myfree(sub); } else /* main array, find next */ { proginfo *oldnext = pi[f_index].next; /* first, delete the entries of that entry (which is in the array) */ buffer_replace_pi_pointers(f_index, &pi[f_index], NULL); /* then, 'rename' the pointers (original address -> address in array)... */ buffer_replace_pi_pointers(f_index, oldnext, &pi[f_index]); /* and move the object to the array */ memmove(&pi[f_index], oldnext, sizeof(proginfo)); /* free the obsolete entry */ myfree(oldnext); } } return delete_all; } /** do_exit * - in: int sig * - returns: nothing (doesn't return!) * this function is called when MultiTail receives the TERM-signal. it is also * called by, for example, wait_for_keypress when ^c is pressed. it stops all * child-processes, ends the curses-library and exits with SUCCESS status */ void do_exit(void) { proginfo *cur; int loop; set_signal(SIGCHLD, SIG_IGN, "do_exit"); /* kill tail processes */ for(loop=0; loop n_redirect; r_i++) { if ((cur -> predir)[r_i].type != 0) { myclose((cur -> predir)[r_i].fd); if ((cur -> predir)[r_i].type == REDIRECTTO_PIPE_FILTERED || (cur -> predir)[r_i].type == REDIRECTTO_PIPE) stop_process((cur -> predir)[r_i].pid); } } myclose(cur -> fd); if (cur -> wfd != -1 && cur -> wfd != cur -> fd) myclose(cur -> wfd); if (cur -> pid != -1) stop_process(cur -> pid); cur = cur -> next; } while(cur); } /* kill convesion scripts */ for(loop=0; loop 16) *prt_start = find_char_offset(&string[16], ' '); else *prt_start = -1; if (*prt_start == -1) *prt_start = 0; *disp_end = nbytes - *prt_start; } else if (mode == 'o') { *prt_start = min(wrap_offset, nbytes); *disp_end = nbytes - *prt_start; } } int draw_tab(NEWWIN *win) { if (tab_width) { int curx = getcurx(win -> win), loop; int move = (((curx / tab_width) + 1) * tab_width) - curx; for(loop=0; loop win, ' '); return move; } return 0; } char find_highlight_matches(regmatch_t *matches, char use_regex, int offset) { int match_offset; for(match_offset=0; match_offset= matches[match_offset].rm_so && offset < matches[match_offset].rm_eo; if ((use_regex == 'c' && matching) || (use_regex == 'C' && !matching)) { return 1; } } return 0; } myattr_t * find_cmatches_index(color_offset_in_line *cmatches, int n_cmatches, mybool_t has_merge_colors, int offset) { static myattr_t final_color; int cmatches_index = 0; int fg_composed = -1, bg_composed = -1; int attrs = -1; char first_set = 1; for(cmatches_index=0; cmatches_index= cmatches[cmatches_index].start && offset < cmatches[cmatches_index].end) { if (has_merge_colors == MY_FALSE) { return &cmatches[cmatches_index].attrs; } if (cmatches[cmatches_index].merge_color == MY_TRUE) { /* merge these colors */ if (cmatches[cmatches_index].attrs.colorpair_index != -1) { int fg_fc, bg_fc; fg_fc = cp.fg_color[cmatches[cmatches_index].attrs.colorpair_index]; bg_fc = cp.bg_color[cmatches[cmatches_index].attrs.colorpair_index]; if (fg_fc != -1 && fg_composed == -1) fg_composed = fg_fc; if (bg_fc != -1 && bg_composed == -1) bg_composed = bg_fc; } if (cmatches[cmatches_index].attrs.attrs != -1) { if (attrs == -1) attrs = cmatches[cmatches_index].attrs.attrs; else attrs |= cmatches[cmatches_index].attrs.attrs; } } else if (first_set) { first_set = 0; fg_composed = cp.fg_color[cmatches[cmatches_index].attrs.colorpair_index]; bg_composed = cp.bg_color[cmatches[cmatches_index].attrs.colorpair_index]; attrs = cmatches[cmatches_index].attrs.attrs; } } } if (fg_composed != -1 || bg_composed != -1 || final_color.attrs != -1) { final_color.attrs = attrs; final_color.colorpair_index = find_or_init_colorpair(fg_composed, bg_composed, 1); return &final_color; } return NULL; } void gen_wordwrap_offsets(char *string, int start_offset, int end_offset, int win_width, int **offsets) { int check_x = start_offset; int n_ww = 0; for(;;) { int max_len = wordwrapmaxlength < win_width ? wordwrapmaxlength : win_width - 2; check_x += win_width - 1; if (check_x >= end_offset) break; while(max_len >= 0 && (!isspace(string[check_x])) && check_x > 0) { check_x--; max_len--; } if (max_len >= 0) { *offsets = (int *)myrealloc(*offsets, (n_ww + 1) * sizeof(int)); (*offsets)[n_ww++] = check_x; } else { check_x += wordwrapmaxlength; } } *offsets = (int *)myrealloc(*offsets, (n_ww + 1) * sizeof(int)); (*offsets)[n_ww] = -1; } int count_utf_bytes(int c) { if ((c & 0xe0) == 0xc0) return 2; else if ((c & 0xf0) == 0xe0) return 3; else if ((c & 0xf8) == 0xf0) return 4; return 1; } void do_color_print(proginfo *cur, char *use_string, int prt_start, int prt_end, int disp_end, color_offset_in_line *cmatches, int n_cmatches, mybool_t has_merge_colors, char start_reverse, regmatch_t *matches, int matching_regex, char use_regex, NEWWIN *win) { int offset; myattr_t cdev = { -1, -1 }; char default_reverse_state = 0; int disp_offset = 0; char highlight_part = toupper(use_regex) == 'C'; char use_colorschemes = cur -> cdef.colorize == 'S' || cur -> cdef.colorize == 'T' || highlight_part; int *ww = NULL; int ww_offset = 0; if (global_highlight_str) { if (regexec(&global_highlight_re, use_string, 0, NULL, 0) == 0) default_reverse_state = 1; } if (cur -> line_wrap == 'w') { gen_wordwrap_offsets(use_string, prt_start, prt_end, getmaxx(win -> win), &ww); } /* print text */ for(offset=prt_start; offset win, "\n"); ww_offset++; #ifdef UTF8_SUPPORT if (iswspace(wcur)) #else if (isspace(wcur)) #endif { offset++; continue; } } /* find things to colorize */ if (use_colorschemes) { if (highlight_part && find_highlight_matches(matches, use_regex, offset)) { re_inv = 1; } else { /* if there's a list of offsets where colors should be displayed, check if the * current offset in the string (the one we're going to display) is somewhere * in that list off offsets */ myattr_t *pa = find_cmatches_index(cmatches, n_cmatches, has_merge_colors, offset); if (pa != NULL) new_cdev = *pa; } } /* control-characters will be displayed as an inverse '.' */ #ifdef UTF8_SUPPORT if (iswcntrl(wcur)) #else if (iscntrl(wcur)) #endif { is_control_or_extended_ascii = 1; #ifdef UTF8_SUPPORT if (!iswspace(wcur)) #else if (!isspace(wcur)) #endif re_inv = 1; } if (re_inv) new_cdev.attrs = inverse_attrs; /* new color selected? then switch off previous color */ if (cdev.colorpair_index != new_cdev.colorpair_index || cdev.attrs != new_cdev.attrs) { myattr_off(win, cdev); myattr_on(win, new_cdev); cdev = new_cdev; } if (!is_control_or_extended_ascii) { #ifdef UTF8_SUPPORT waddnwstr(win -> win, &wcur, 1); #else wprintw(win -> win, "%c", wcur); #endif disp_offset++; } else { /* error_exit("> 126 %d: %02x %02x %02x", wcur, use_string[offset+0], use_string[offset+1], use_string[offset+2]); */ if (wcur == 9 && tab_width > 0) /* TAB? */ { disp_offset += draw_tab(win); } /* 13 (CR) is just silently ignored */ else if (wcur != 13) { if (caret_notation) { wprintw(win -> win, "^%c", 'a' + wcur - 1); disp_offset++; } else waddch(win -> win, '.'); disp_offset++; } } if (disp_offset >= disp_end && disp_end != -1) break; offset += count_utf_bytes(use_string[offset]); } if (prt_start == prt_end) /* scrolled out line */ wprintw(win -> win, "\n"); if (cdev.attrs != -1) myattr_off(win, cdev); myfree(ww); } void color_print(int f_index, NEWWIN *win, proginfo *cur, char *string, regmatch_t *matches, int matching_regex, mybool_t force_to_winwidth, int start_at_offset, int end_at_offset, double ts, char show_window_nr) { char reverse = 0; myattr_t cdev = { -1, -1 }; int prt_start = 0, prt_end = 0, disp_end = 0; int mx = -1; char use_regex = 0; color_offset_in_line *cmatches = NULL; int n_cmatches = 0; mybool_t has_merge_colors = MY_FALSE; char *use_string = NULL; int x; /* stop if there's no window tou output too */ if (!win) return; /* check if the cursor is not at the beginning of the line * if it is not and it is also not at the most right position, move it * to the left (when it is already at the right most position, it'll move * to the left itself when emitting text) */ mx = getmaxx(win -> win); x = getcurx(win -> win); if ((x != 0 && x != mx) || !suppress_empty_lines) wprintw(win -> win, "\n"); /* cur == NULL? Markerline! */ if (IS_MARKERLINE(cur)) { draw_marker_line(win, string, cur); return; } if (f_index >= 0) { /* add a window-id [xx] in front of each line */ if (show_window_nr) { mx -= wprintw(win -> win, window_number, f_index); } /* add a subwindow-ID: [xx] in front of each line */ if (show_subwindow_id || (show_window_nr && pi[f_index].next != NULL)) { proginfo *ppi = &pi[f_index]; int subwin_nr = 0; while(ppi != cur && ppi -> next != NULL) { subwin_nr++; ppi = ppi -> next; } mx -= wprintw(win -> win, subwindow_number, subwin_nr); } } /* add a timestamp in front of each line */ if (cur -> add_timestamp) { char timestamp[1024]; double_ts_to_str(ts, line_ts_format, timestamp, sizeof(timestamp)); mx -= wprintw(win -> win, "%s ", timestamp); } /* add a label */ LOG("Label\n"); if (cur -> label != NULL && (cur -> label)[0]) { mx -= wprintw(win -> win, "%s", cur -> label); } if (mx <= 0) mx = 4; /* had a matching regexp? */ if (matching_regex != -1) use_regex = (cur -> pre)[matching_regex].use_regex; /* select color or generate list of offset for colors */ if (cur -> cdef.colorize) /* no need to do this for b/w terminals */ { /* choose_color not only generates a list of colors but if there are terminal- * codes in that string which set a color those are stripped as well * the stripping part should be moved to a seperate function */ cdev = choose_color(string, cur, &cmatches, &n_cmatches, &has_merge_colors, &use_string); /* if not using colorschemes (which can have more then one color * per line), set the color */ if (cur -> cdef.colorize != 'S' && cur -> cdef.colorize != 'T') { myattr_on(win, cdev); if (cdev.attrs != -1 && cdev.attrs & A_REVERSE) reverse = 1; } } /* select start and end of string to display */ LOG("lwo: %d, mx: %d, ps: %d, pe: %d, de: %d\n", cur -> line_wrap_offset, mx, prt_start, prt_end, disp_end); select_display_start_and_end(USE_IF_SET(use_string, string), force_to_winwidth == MY_TRUE?'l':cur -> line_wrap, cur -> line_wrap_offset, mx, &prt_start, &prt_end, &disp_end); if (prt_start == 0 && start_at_offset > 0) { prt_start = start_at_offset; prt_end = end_at_offset; } /* and display on terminal */ LOG("ps: %d, pe: %d, de: %d\n", prt_start, prt_end, disp_end); LOG("%s\n", USE_IF_SET(use_string, string)); do_color_print(cur, USE_IF_SET(use_string, string), prt_start, prt_end, disp_end, cmatches, n_cmatches, has_merge_colors, reverse, matches, matching_regex, use_regex, win); myattr_off(win, cdev); myfree(use_string); myfree(cmatches); } void check_filter_exec(char *cmd, char *matching_string) { int str_index, par_len = strlen(matching_string); int cmd_len = strlen(cmd); char *command = mymalloc(cmd_len + 1/* cmd + space */ + 1 + (par_len * 2) + 1 + 1); /* "string"\0 */ int loop; memcpy(command, cmd, cmd_len); str_index = cmd_len; command[str_index++] = ' '; command[str_index++] = '\"'; for(loop=0; loop n_re; loop++) { int rc; char cmd = (cur -> pre)[loop].use_regex; char invert = (cur -> pre)[loop].invert_regex; /* SKIP DISABLED REGEXPS */ if (!cmd) continue; /* EXECUTE THE REGEXPS */ if (pmatch) { if (*pmatch == NULL) *pmatch = (regmatch_t *)mymalloc(sizeof(regmatch_t) * MAX_N_RE_MATCHES); rc = regexec(&(cur -> pre)[loop].regex, string, MAX_N_RE_MATCHES, *pmatch, 0); } else { rc = regexec(&(cur -> pre)[loop].regex, string, MAX_N_RE_MATCHES, local_matches, 0); } /* IF ERROR, ABORT EVERYTHING */ if (rc != 0 && rc != REG_NOMATCH) { *error = convert_regexp_error(rc, &(cur -> pre)[loop].regex); re_exec_ok = 0; break; } /* MATCHED? */ if (rc == 0) { if (!invert) { *matching_regex = loop; if (cmd == 'm') { *display = 1; (cur -> pre)[loop].match_count++; return 1; } else if (cmd == 'v') { *display = 0; (cur -> pre)[loop].match_count++; return 1; } else if (cmd == 'x' && do_re) { check_filter_exec((cur -> pre)[loop].cmd, string); (cur -> pre)[loop].match_count++; } else if (cmd == 'X' && do_re) { regmatch_t *usep = USE_IF_SET(*pmatch, local_matches); int len = usep[1].rm_eo - usep[1].rm_so; char *dummy = (char *)mymalloc(len + 1); memcpy(dummy, &string[usep[1].rm_so], len); dummy[len] = 0x00; check_filter_exec((cur -> pre)[loop].cmd, dummy); (cur -> pre)[loop].match_count++; myfree(dummy); } else if (toupper(cmd) == 'B' && do_re) { beep(); (cur -> pre)[loop].match_count++; } } else { if (cmd == 'm') { *display = 0; } } } else { myfree(*pmatch); *pmatch = NULL; if (invert) { *matching_regex = loop; if (cmd == 'm') { *display = 1; (cur -> pre)[loop].match_count++; return 1; } else if (cmd == 'v') { *display = 0; (cur -> pre)[loop].match_count++; return 1; } else if (cmd == 'x' && do_re) { check_filter_exec((cur -> pre)[loop].cmd, string); (cur -> pre)[loop].match_count++; } else if (cmd == 'X' && do_re) { int len = *pmatch ? (*pmatch)[1].rm_eo - (*pmatch)[1].rm_so : local_matches[1].rm_eo - local_matches[1].rm_so; char *dummy = (char *)mymalloc(len + 1); memcpy(dummy, &string[local_matches[1].rm_so], len); dummy[len] = 0x00; check_filter_exec((cur -> pre)[loop].cmd, dummy); (cur -> pre)[loop].match_count++; myfree(dummy); } else if (toupper(cmd) == 'B' && do_re) { beep(); (cur -> pre)[loop].match_count++; } } else { if (cmd == 'm') { *display = 0; } } } } return re_exec_ok; } void do_print(int f_index, proginfo *cur, char *string, regmatch_t *matches, int matching_regex, double ts) { /* check filter */ /* if cur == NULL, then marker line: don't check against filter */ if (IS_MARKERLINE(cur)) { color_print(f_index, pi[f_index].data, cur, string, NULL, -1, MY_FALSE, 0, 0, ts, 0); } else { color_print(f_index, pi[f_index].data, cur, string, matches, matching_regex, MY_FALSE, 0, 0, ts, 0); } } char is_buffer_too_full(buffer *lb, int n_bytes_to_add) { return (lb -> curpos >= lb -> maxnlines && lb -> maxnlines > 0) || ((lb -> curbytes + n_bytes_to_add) >= lb -> maxbytes && lb -> maxbytes > 0 && n_bytes_to_add < lb -> maxbytes); } void do_buffer(int f_index, proginfo *cur, char *string, char filter_match, dtime_t now) { /* remember string */ if (lb[f_index].bufferwhat == 'a' || filter_match == 1) { int cur_line; int line_len = 0; int move_index = 0, old_curpos = lb[f_index].curpos; int min_shrink = 0; if (string) line_len = strlen(string); if (is_buffer_too_full(&lb[f_index], line_len)) min_shrink = default_min_shrink; /* remove enough lines untill there's room */ if (lb[f_index].curpos > 0) { while(is_buffer_too_full(&lb[f_index], line_len) || min_shrink > 0) { /* delete oldest */ if (lb[f_index].be[move_index].Bline) { lb[f_index].curbytes -= strlen(lb[f_index].be[move_index].Bline); myfree(lb[f_index].be[move_index].Bline); } move_index++; lb[f_index].curpos--; if (lb[f_index].curpos == 0) break; min_shrink--; } } if (move_index > 0) { /* move entries over the deleted one */ memmove(&lb[f_index].be[0], &lb[f_index].be[move_index], (old_curpos - move_index) * sizeof(buffered_entry)); } else { /* grow array */ lb[f_index].be = (buffered_entry *)myrealloc(lb[f_index].be, sizeof(buffered_entry) * (lb[f_index].curpos + 1)); } cur_line = lb[f_index].curpos++; /* add the logline itself */ if (string) { lb[f_index].be[cur_line].Bline = mystrdup(string); lb[f_index].curbytes += line_len; } else lb[f_index].be[cur_line].Bline = NULL; /* remember pointer to subwindow (required for setting colors etc.) */ lb[f_index].be[cur_line].pi = cur; /* remember when this string was buffered */ lb[f_index].be[cur_line].ts = now; } } void do_redirect(redirect_t *predir, char *buffer, int nbytes, char add_lf) { int failed = 0; if (predir -> type == REDIRECTTO_SOCKET_FILTERED || predir -> type == REDIRECTTO_SOCKET) { int msg_len = nbytes + 6; char *msg = (char *)mymalloc(msg_len + 1);; snprintf(msg, msg_len, "<%d>%s\n", predir -> prio_fac, buffer); if (sendto(predir -> fd, msg, msg_len, 0, (const struct sockaddr *)&(predir -> sai), sizeof(predir -> sai)) == -1) failed = 1; myfree(msg); } else { if (WRITE(predir -> fd, buffer, nbytes, "redirect") != nbytes) failed = 1; if (add_lf == MY_TRUE && WRITE(predir -> fd, "\n", 1, "redirect") != 1) failed = 1; } if (failed) { error_popup("Error redirecting output", HELP_REDIRECT_FAILED, "Redirecting the output failed: %s\nRedirection stopped.", strerror(errno)); /* stop redirecting */ myclose(predir -> fd); predir -> fd = -1; predir -> type = REDIRECTTO_NONE; } } void redirect(proginfo *cur, char *data, int n_bytes, mybool_t is_filtered) { int r_i; for(r_i=0; r_i < cur -> n_redirect; r_i++) { if (is_filtered == MY_TRUE && ((cur -> predir)[r_i].type == REDIRECTTO_FILE_FILTERED || (cur -> predir)[r_i].type == REDIRECTTO_PIPE_FILTERED || (cur -> predir)[r_i].type == REDIRECTTO_SOCKET_FILTERED)) { do_redirect(&(cur -> predir)[r_i], data, n_bytes, 1); } else if (is_filtered == MY_FALSE && ((cur -> predir)[r_i].type == REDIRECTTO_FILE || (cur -> predir)[r_i].type == REDIRECTTO_PIPE || (cur -> predir)[r_i].type == REDIRECTTO_SOCKET)) { do_redirect(&(cur -> predir)[r_i], data, n_bytes, 0); } } } void delete_all_markerlines(void) { int index; for(index=0; index conversions, line); (void)check_filter(cur, new_line, &pmatch, &error, &matching_regex, 1, &display); /* error happened while processing regexp? */ if (error) { if (!marker_added) { marker_added = 1; add_marker_if_changed(f_index, cur); } add_markerline(f_index, cur, MARKER_MSG, error); do_print_and_buffer = 1; something_got_displayed = 1; myfree(error); } if (cur -> repeat.suppress_repeating_lines) { if ((!cur -> repeat.last_line && !new_line) || (cur -> repeat.last_line != NULL && new_line != NULL && strcmp(cur -> repeat.last_line, new_line) == 0)) { cur -> repeat.n_times_repeated++; } else { do_print_and_buffer = 1; if (new_line) cur -> repeat.last_line = mystrdup(new_line); else cur -> repeat.last_line = NULL; } } else do_print_and_buffer = 1; if (do_print_and_buffer) { if ((cur -> repeat.n_times_repeated > 0 || display) && !marker_added) { marker_added = 1; something_got_displayed |= add_marker_if_changed(f_index, cur); } if (cur -> repeat.n_times_repeated) { char message[128]; snprintf(message, sizeof(message), "Last message repeated %d times", cur -> repeat.n_times_repeated); do_print(f_index, cur, message, NULL, -1, now); do_buffer(f_index, cur, message, 1, now); something_got_displayed = 1; cur -> repeat.n_times_repeated = 0; myfree(cur -> repeat.last_line); cur -> repeat.last_line = NULL; } if (display) { char *stripped = do_strip(cur, new_line); /* output new text */ do_print(f_index, cur, USE_IF_SET(stripped, new_line), pmatch, matching_regex, now); myfree(stripped); something_got_displayed = 1; redirect(cur, new_line, strlen(new_line), MY_TRUE); } do_buffer(f_index, cur, new_line, display, now); } if (pmatch) myfree(pmatch); if (new_line != line) myfree(new_line); return something_got_displayed; } void update_statusline(NEWWIN *status, int win_nr, proginfo *cur) { if (mode_statusline > 0 && status != NULL && cur != NULL) { int dx; myattr_t attrs = statusline_attrs; int statusline_len = 0; off64_t fsize = (off64_t)-1; time_t ts = time(NULL); char show_f1 = 0; int help_str_offset = 0; int total_info_len = 0; int win_width; char *fname = cur -> filename; char timestamp[TIMESTAMP_EXTEND_BUFFER]; if (win_nr == terminal_main_index) attrs.colorpair_index = find_colorpair(COLOR_RED, -1, 0); else if (mail) attrs.colorpair_index = find_colorpair(COLOR_GREEN, -1, 0); if ((ts - mt_started) < 5) show_f1 = 1; myattr_on(status, attrs); draw_line(status, LINE_BOTTOM); if (filename_only) { char *dummy = strrchr(cur -> filename, '/'); if (dummy) fname = dummy + 1; } win_width = getmaxx(status -> win); mvwprintw(status -> win, 0, 0, "%02d] %s", win_nr, shorten_filename(USE_IF_SET(cur -> win_title, fname), win_width - 4)); if (cur -> wt == WT_FILE) (void)file_info(cur -> filename, &fsize, TT_MTIME, &ts, NULL); get_now_ts(statusline_ts_format, timestamp, sizeof(timestamp)); help_str_offset = 4 + strlen(USE_IF_SET(cur -> win_title, fname)); /* 4: '%02d] '!! (window nr.) */ statusline_len = help_str_offset + strlen(timestamp) + 1; /* 4: '%02d] '!! (window nr.) */ if (win_nr == terminal_main_index) wprintw(status -> win, ", press +, to exit"); else if (mail) wprintw(status -> win, " You've got mail!"); dx = getcurx(status -> win); if (dx >= (statusline_len + 13)) { if (cur -> paused) { color_on(status, find_colorpair(COLOR_YELLOW, -1, 0)); mvwprintw(status -> win, 0, dx - 10, " Paused "); color_off(status, find_colorpair(COLOR_YELLOW, -1, 0)); } else if (cur -> wt == WT_COMMAND) { int vmsize = get_vmsize(cur -> pid); total_info_len = statusline_len + 12; if (vmsize != -1 && dx >= (statusline_len + 30)) { int str_x = dx - strlen(timestamp) - 30; char *vmsize_str = amount_to_str(vmsize); mvwprintw(status -> win, 0, str_x, "%6s (VMsize) %5d (PID) - %s", vmsize_str, cur -> pid, timestamp); myfree(vmsize_str); total_info_len = statusline_len + 30; } else { if (cur -> last_exit_rc != 0) { mvwprintw(status -> win, 0, dx - strlen(timestamp) - 26, "Last rc: %d, %5d (PID) - %s", WEXITSTATUS(cur -> last_exit_rc), cur -> pid, timestamp); } else mvwprintw(status -> win, 0, dx - strlen(timestamp) - 12, "%5d (PID) - %s", cur -> pid, timestamp); } } else if (fsize == -1) { if (cur -> wt == WT_STDIN || cur -> wt == WT_SOCKET) mvwprintw(status -> win, 0, dx - strlen(timestamp), "%s", timestamp); else { mvwprintw(status -> win, 0, dx - strlen(timestamp) - 6, "??? - %s", timestamp); total_info_len = statusline_len + 6; } } else { int cur_len = 0; if (fsize < cur -> last_size) add_markerline(win_nr, cur, MARKER_MSG, " file got truncated"); cur -> last_size = fsize; if (afs) { char *filesize = amount_to_str(fsize); cur_len = 3 + strlen(filesize); mvwprintw(status -> win, 0, dx - (strlen(timestamp) + cur_len), "%s - %s", filesize, timestamp); myfree(filesize); } else { cur_len = 13; /* is this trick still neccessary as I moved from off_t to off64_t? */ #if 0 /* this trick is because on MacOS X 'off_t' is specified as a 64 bit integer */ #endif if (sizeof(off64_t) == 8) mvwprintw(status -> win, 0, dx - (strlen(timestamp) + cur_len), "%10lld - %s", fsize, timestamp); else mvwprintw(status -> win, 0, dx - (strlen(timestamp) + cur_len), "%10ld - %s", fsize, timestamp); } total_info_len = statusline_len + cur_len; } } if (show_f1) { if (use_colors) color_on(status, find_colorpair(COLOR_YELLOW, -1, 0)); if (dx >= (total_info_len + 32)) mvwprintw(status -> win, 0, help_str_offset, " *Press F1/+ for help* "); else if (dx >= (total_info_len + 21)) mvwprintw(status -> win, 0, help_str_offset, " F1/+: help "); else if (dx >= (total_info_len + 13)) mvwprintw(status -> win, 0, help_str_offset, " F1/^h: help "); if (use_colors) color_off(status, find_colorpair(COLOR_YELLOW, -1, 0)); } myattr_off(status, attrs); update_panels(); } } void create_window_set(int startx, int width, int nwindows, int indexoffset) { int loop; int term_offset = 0; int n_not_hidden = 0; int n_height_not_set = 0; int n_lines_requested = 0, n_with_nlines_request = 0; int n_lines_available = 0; int n_lines_available_per_win = 0; int reserved_lines = mode_statusline >= 0?1:0; int *lines_per_win = (int *)mymalloc(nwindows * sizeof(int)); set_signal(SIGWINCH, SIG_IGN, "SIGWINCH"); memset(lines_per_win, 0x00, sizeof(int) * nwindows); /* count the number of windows that are not hidden */ for(loop=0; loop= nfd) break; if (pi[cur_win_index].hidden == 0) n_not_hidden++; } if (n_not_hidden == 0) { free(lines_per_win); return; } /* count the number of lines needed by the windows for which * the height is explicitly set * also count the number of windows with this request set */ for(loop=0; loop= nfd) break; if (pi[cur_win_index].hidden == 0 && pi[cur_win_index].win_height != -1) { n_lines_requested += (pi[cur_win_index].win_height + reserved_lines); n_with_nlines_request++; } } /* fill in the array with heights */ /* not enough lines? simple fill-in */ n_lines_available = max_y; n_height_not_set = n_not_hidden - n_with_nlines_request; if (n_height_not_set > 0) n_lines_available_per_win = (max_y - n_lines_requested) / n_height_not_set; if (n_height_not_set > 0 && n_lines_available_per_win < 3) { int lost_lines; int n_windows_that_will_fit; n_lines_available_per_win = max(3, max_y / n_not_hidden); n_windows_that_will_fit = max_y / n_lines_available_per_win; lost_lines = max_y - (n_lines_available_per_win * n_windows_that_will_fit); for(loop=0; loop= nfd) break; if (pi[cur_win_index].hidden == 0) { lines_per_win[loop] = n_lines_available_per_win - reserved_lines; if (lost_lines) { lines_per_win[loop]++; lost_lines--; } n_lines_available -= n_lines_available_per_win; if (n_lines_available < n_lines_available_per_win) break; } } } else /* enough lines */ { int lost_lines = (max_y - n_lines_requested) - (n_lines_available_per_win * (n_not_hidden - n_with_nlines_request)); /* not enough lines available? then ignore all requests */ for(loop=0; loop= nfd) break; if (!pi[cur_win_index].hidden) { if (pi[cur_win_index].win_height != -1) { lines_per_win[loop] = pi[cur_win_index].win_height; n_lines_available -= (pi[cur_win_index].win_height + reserved_lines); } else { lines_per_win[loop] = n_lines_available_per_win - reserved_lines; if (lost_lines) { lines_per_win[loop]++; lost_lines--; } n_lines_available -= (n_lines_available_per_win + reserved_lines); } } } } /* re-create windows */ for(loop=0; loop= nfd) break; if (pi[cur_index].hidden) continue; if (lines_per_win[loop] == 0) continue; /* create data window, clear en set scroll ok */ if (statusline_above_data) pi[cur_index].data = mynewwin(lines_per_win[loop], width, term_offset + 1, startx); else pi[cur_index].data = mynewwin(lines_per_win[loop], width, term_offset, startx); bottom_panel(pi[cur_index].data -> pwin); werase(pi[cur_index].data -> win); scrollok(pi[cur_index].data -> win, TRUE);/* supposed to always return OK, according to the manpage */ /* create status window */ if (mode_statusline >= 0) { if (statusline_above_data) pi[cur_index].status = mynewwin(1, width, term_offset, startx); else pi[cur_index].status = mynewwin(1, width, term_offset + lines_per_win[loop], startx); bottom_panel(pi[cur_index].status -> pwin); werase(pi[cur_index].status -> win); /* create status-line */ update_statusline(pi[cur_index].status, cur_index, &pi[cur_index]); } term_offset += lines_per_win[loop] + (mode_statusline >= 0?1:0); /* display old strings */ for(redraw_loop=max(0, lb[cur_index].curpos - lines_per_win[loop]); redraw_loop win); wprintw(menu_win -> win, version_str); wprintw(menu_win -> win, "\n\n"); wprintw(menu_win -> win, "%s\n", F1); } else { if (split > 0 && nfd > 1 && n_not_hidden >= split) { int cols_per_col = max_x / split; int win_per_col = nfd / split; int x = 0, indexoffset = 0; int *vs = (int *)mymalloc(sizeof(int) * split); int *vn = (int *)mymalloc(sizeof(int) * split); if (vertical_split) { int cols_in_use = 0; int cols_set = 0; int other_cols_size; for(loop=0; loop= 4) { vs[loop] = vertical_split[loop]; cols_in_use += vertical_split[loop]; cols_set++; } } /* determine width of other (=not set) columns */ if (cols_set < split) { other_cols_size = (max_x - cols_in_use) / (split - cols_set); /* doesn't fit? */ if (other_cols_size < 4) { /* then use the predetermined size */ for(loop=0; loop 0) { vn[loop] = n_win_per_col[loop]; win_in_use += n_win_per_col[loop]; win_set++; } } if (win_set < split) { other_win_n = (nfd - win_in_use) / (split - win_set); if (other_win_n == 0) { for(loop=0; loop 0) splitlines = (NEWWIN **)mymalloc(sizeof(NEWWIN *) * n_splitlines); } for(loop=0; loop pwin); myattr_on(splitlines[loop], sl_attrs); draw_line(splitlines[loop], LINE_LEFT); myattr_off(splitlines[loop], sl_attrs); } else { create_window_set(x, vs[loop], vn[loop], indexoffset); } x += vs[loop]; indexoffset += vn[loop]; } } myfree(vn); myfree(vs); } else if (nfd != 0) { create_window_set(0, max_x, nfd, 0); } } mydoupdate(); } void do_set_bufferstart(int f_index, char store_what_lines, int maxnlines) { lb[f_index].bufferwhat = store_what_lines; if (maxnlines < lb[f_index].maxnlines) { delete_be_in_buffer(&lb[f_index]); lb[f_index].curpos = 0; } lb[f_index].maxnlines = maxnlines; } char close_window(int winnr, proginfo *cur, mybool_t stop_proc) { if (winnr == terminal_main_index) { terminal_index = NULL; terminal_main_index = -1; } /* make sure it is really gone */ if (stop_proc) stop_process(cur -> pid); /* restart window? */ if (cur -> restart.restart >= 0) { int initial_n_lines_tail; /* close old fds */ if (myclose(cur -> fd) == -1) error_exit("Closing file descriptor of read-end of pipe failed.\n"); if (cur -> fd != cur -> wfd) { if (myclose(cur -> wfd) == -1) error_exit("Closing file descriptor of write-end of pipe failed.\n"); } /* do diff */ if (cur -> restart.do_diff) { generate_diff(winnr, cur); /* update statusline */ update_statusline(pi[winnr].status, winnr, cur); } if (cur -> initial_n_lines_tail == -1) initial_n_lines_tail = max_y / (nfd + 1); else initial_n_lines_tail = cur -> initial_n_lines_tail; /* restart tail-process */ if (start_proc(cur, initial_n_lines_tail) != -1) { cur -> restart.is_restarted = 1; return -1; } /* ...and if that fails, go on with the closing */ } if (warn_closed) { NEWWIN *mywin; int subwinnr = 0; proginfo *dummy = &pi[winnr]; int win_width = 18 + 4; int win_name_len = strlen(cur -> filename); while(dummy -> next) { subwinnr++; dummy = dummy -> next; } if (win_name_len > win_width && (win_name_len + 4) < max_x) win_width = win_name_len; mywin = create_popup(5, win_width); color_on(mywin, 1); mvwprintw(mywin -> win, 1, 2, "Window %d/%d closed", winnr, subwinnr); mvwprintw(mywin -> win, 2, 2, "%s", cur -> filename); mvwprintw(mywin -> win, 3, 2, "Exit code: %d", WEXITSTATUS(cur -> last_exit_rc)); draw_border(mywin); color_off(mywin, 1); wmove(mywin -> win, 1, 2); mydoupdate(); /* wait_for_keypress(HELP_WINDOW_CLOSED, 0); */ (void)getch(); delete_popup(mywin); } if (do_not_close_closed_windows) { cur -> closed = 1; add_markerline(winnr, cur, MARKER_MSG, " end of file reached"); return 127; } else { /* file no longer exists (or so) */ return delete_entry(winnr, cur); } } void do_restart_window(proginfo *cur) { /* stop process */ stop_process(cur -> pid); /* close pipes */ myclose(cur -> fd); myclose(cur -> wfd); /* re-start tail */ if (start_proc(cur, 1) == -1) error_exit("Failed to start process %s.\n", cur -> filename); } char * key_to_keybinding(char what) { static char buffer[3]; buffer[1] = buffer[2] = 0x00; if (what < 32) { buffer[0] = '^'; buffer[1] = 'a' + what - 1; } else { buffer[0] = what; } return buffer; } void write_escape_str(FILE *fh, char *string) { int loop, len = strlen(string); fprintf(fh, "\""); for(loop=0; loop 0) { if (what.colorpair_index == -1) fprintf(fh, ",,"); else fprintf(fh, ","); fprintf(fh, "%s", attr_to_str(what.attrs)); } } void add_pars_per_file(char *re, char *colorscheme, int n_buffer_lines, int buffer_bytes, char change_win_marker, int fs, int es, char *conversion_scheme) { int loop; for(loop=0; loop 0) { chosen = (lb[loop].be)[lb[loop].curpos - 1].pi; /* chosen = lb[loop].pi[lb[loop].curpos - 1]; */ if (IS_MARKERLINE(chosen)) chosen = NULL; } if (!chosen) chosen = &pi[loop]; update_statusline(pi[loop].status, loop, chosen); } } int exec_bind(char key) { int executed = 0; int loop; for(loop=0; loop wt == WT_COMMAND || cur -> wt == WT_STDIN || cur -> wt == WT_SOCKET || cur -> check_interval != 0 || cur -> retry_open) { strncpy(real_fname, cur -> filename, pm); real_fname[pm - 1] = 0x00; } else { if (!realpath(cur -> filename, real_fname)) error_exit("Problem obtaining complete (real) path of file %s.\n", cur -> filename); } /* check if any default parameters are given in the configfile */ for(ppf_index=0; ppf_index pre, &cur -> n_re); } for(es_loop=0; es_loop pstrip, &cur -> n_strip); } /* set default colorscheme if not already given */ if (ppf[ppf_index].n_colorschemes > 0 && (get_iat_size(&cur -> cdef.color_schemes) == 0 && cur -> wt != WT_COMMAND && (cur -> cdef.colorize == 'n' || cur -> cdef.colorize == 0))) { for(c_nr=0; c_nr cdef.color_schemes, cs_index); } cur -> cdef.colorize = 'S'; } for(c_nr=0; c_nr conversions, ppf[ppf_index].conversion_schemes[c_nr]); if (ppf[ppf_index].buffer_maxnlines != -1) maxnlines = ppf[ppf_index].buffer_maxnlines; if (ppf[ppf_index].buffer_bytes != -1) { maxbytes = ppf[ppf_index].buffer_bytes; } } /* we should inform the user of any errors while executing * the regexp! */ else { char *error = convert_regexp_error(rc, &ppf[ppf_index].regex); if (error) error_popup("Set default parameters", -1, "Execution of regular expression failed with error:\n%s\n", error); } } cur = cur -> next; } while(cur); if (pi_index != -1) { if (lb[pi_index].maxnlines == -1 && lb[pi_index].maxbytes == -1) { if (maxbytes) { lb[pi_index].maxbytes = maxbytes; lb[pi_index].maxnlines = 0; } else { lb[pi_index].maxnlines = maxnlines; lb[pi_index].maxbytes = 0; } } else if (lb[pi_index].maxnlines == -1) { lb[pi_index].maxnlines = maxnlines; } else /* if (lb[pi_index].maxbytes == -1) */ { lb[pi_index].maxbytes = maxbytes; } } myfree(real_fname); } void set_default_parameters_if_not_given(void) { int pi_index; for(pi_index=0; pi_index statistics.start_ts = time(NULL); if (cur -> wt == WT_STDIN) { int old_0 = mydup(0); if (old_0 == -1) error_exit("Cannot dup(0).\n"); if (myclose(0) == -1) error_exit("Error closing fd 0.\n"); if (myopen("/dev/tty", O_RDONLY) != 0) error_exit("New fd != 0\n"); cur -> fd = old_0; cur -> wfd = -1; cur -> pid = -1; } else if (cur -> wt == WT_SOCKET) { char *dummy = mystrdup(cur -> filename); char *colon = strchr(dummy, ':'); struct addrinfo hints; struct addrinfo* result; struct addrinfo* rp; int sfd = -1, s; char *host = NULL; char *service = "syslog"; if (colon) { service = colon + 1; *colon = 0x00; if (colon > dummy) host = dummy; } memset(&hints, 0x00, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE; hints.ai_protocol = 0; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; s = getaddrinfo(host, service, &hints, &result); if (s != 0) error_exit("Failed to create socket for receiving syslog data on %s: %s.\n", cur -> filename, gai_strerror(s)); for (rp = result; rp != NULL; rp = rp -> ai_next) { sfd = socket(rp -> ai_family, rp -> ai_socktype, rp -> ai_protocol); if (sfd == -1) continue; if (bind(sfd, rp -> ai_addr, rp -> ai_addrlen) == 0) break; close(sfd); } freeaddrinfo(result); if (rp == NULL) error_exit("Failed to create socket for receiving syslog data on %s.\n", cur -> filename); cur -> wfd = cur -> fd = sfd; cur -> wfd = cur -> fd = socket(AF_INET, SOCK_DGRAM, 0); if (cur -> fd == -1) myfree(dummy); cur -> pid = -1; } else { if (cur -> initial_n_lines_tail == -1) cur -> initial_n_lines_tail = cur_win_size; /* start the tail process for this file/command */ if (start_proc(cur, cur -> initial_n_lines_tail) == -1) error_exit("Failed to start process for %s.\n", cur -> filename); } cur = cur -> next; } while(cur); } } void version(void) { printf("%s", version_str); printf("\n\nThank you for using MultiTail.\n"); printf("If you have any suggestion on how I can improve this program,\n"); printf("do not hesitate to contact me at folkert@vanheusden.com\n"); printf("Website is available at: http://www.vanheusden.com/multitail/\n\n\n"); } void init_check_for_mail(void) { if (check_for_mail > 0 && mail_spool_file != NULL) { if (stat64(mail_spool_file, &msf_info) == -1) { check_for_mail = 0; printf("Could not determine size of file '%s' (which is supposed to be ", mail_spool_file); printf("your mailfile): mail-check is disabled.\n"); printf("You can prevent this message by adding the line 'check_mail:0' "); printf("in "CONFIG_FILE" or in .multitailrc in your home-directory.\n\n"); printf("Press enter to continue..."); fflush(NULL); getchar(); } else { msf_prev_size = msf_info.st_size; msf_last_check = get_ts(); } } } void main_loop(void) { /* create windows */ set_do_refresh(2); for(;;) { int c = wait_for_keypress(HELP_MAIN_MENU, 0, NULL, 0); if (terminal_main_index == -1) { int uc = toupper(c); set_do_refresh(1); if (mail) { mail = 0; redraw_statuslines(); } if (c == -1) { set_do_refresh(2); continue; } else if (uc == 'Q' || uc == 'X') { break; } else if (c == 'Z') /* clear all marker lines */ { delete_all_markerlines(); set_do_refresh(2); continue; } else if (uc == 'A') { if (add_window()) set_do_refresh(2); continue; } else if (uc == 'H' || c == 8) { show_help(HELP_MAIN_MENU); continue; } else if (c == 'r' || c == 12) { set_do_refresh(2); continue; } else if (c == 'R') { restart_window(); continue; } else if (c == 'i') { info(); continue; } else if (c == 't') { statistics_menu(); continue; } else if (c == 'T') { truncate_file(); continue; } else if (uc == 'J') { if (set_window_sizes()) set_do_refresh(2); continue; } else if (uc == 'L') { list_keybindings(); continue; } if (nfd == 0) { wrong_key(); set_do_refresh(0); continue; } else if (uc == 'E' || uc == '\\') { if (edit_regexp()) set_do_refresh(2); } else if (uc == 'F') { if (edit_strippers()) set_do_refresh(2); } else if (uc == 'D') { if (delete_window()) set_do_refresh(2); continue; } else if (uc == 'V') { if (toggle_vertical_split()) set_do_refresh(2); } else if (c == 'c') { if (toggle_colors()) set_do_refresh(2); } else if (c == 'C' && use_colors) { if (can_change_color()) (void)color_management(NULL, NULL); else error_popup("Edit colors", HELP_CANNOT_EDIT_COLOR, "Your terminal doesn't support editing of colors."); } else if (uc == 'S') { if (swap_window()) set_do_refresh(2); continue; } else if (c == 'z') { if (hide_window()) set_do_refresh(2); } else if (c == 'u') { if (hide_all_but()) set_do_refresh(2); } else if (c == 'U') { if (unhide_all_windows()) set_do_refresh(2); } else if (uc == 'G') { screendump(); } else if (uc == 'W') { write_script(); } else if (uc == 'M') { set_buffering(); } else if (c == 'b') { scrollback(); } else if (c == 'B') { merged_scrollback_with_search(NULL, MY_FALSE); } else if (c == 'p') { do_pause(); } else if (c == 'P') /* pause an individual screen */ { selective_pause(); } else if (uc >= '0' && uc <= '9') { int index = uc - '0'; if (index < nfd) add_markerline(index, NULL, MARKER_REGULAR, NULL); else wrong_key(); } else if (uc == 13) { int loop; for(loop=0; loop s_port; cp.n_def = cp.size = 0; cp.fg_color = cp.bg_color = NULL; /* removed because if gives regexp compilation problems for some * LANG=... environment variables settings * setlocale(LC_ALL, ""); */ time(&mt_started); set_signal(SIGTERM, signal_handler, "SIGTERM"); set_signal(SIGINT, signal_handler, "SIGINT" ); set_signal(SIGHUP, signal_handler, "SIGHUP" ); set_signal(SIGCHLD, signal_handler, "SIGCHLD"); shell = mystrdup("/bin/sh"); tail = mystrdup("tail"); ts_format = mystrdup("%b %d %H:%M:%S"); window_number = mystrdup("[%02d]"); cnv_ts_format = mystrdup("%b %d %H:%M:%S %Y"); line_ts_format = mystrdup("%b %d %H:%M:%S %Y "); subwindow_number = mystrdup("[%02d]"); statusline_ts_format = mystrdup("%b %d %H:%M:%S %Y"); syslog_ts_format = mystrdup("%b %d %H:%M:%S %Y"); mail_spool_file = getenv("MAIL"); if (dummy) check_for_mail = atoi(dummy); /* calc. buffer length (at least a complete terminal screen) */ initscr(); min_n_bufferlines = max(MIN_N_BUFFERLINES, LINES); if (has_colors()) { start_color(); use_default_colors(); use_colors = 1; } init_colors(); init_colornames(); /* start curses library */ init_ncurses(); /* verify size of terminal window */ if (LINES < 23 || COLS < 78) error_popup("Terminal too small", -1, "Your terminal(-window) is %dx%d. That is too small\nfor MultiTail: at least 78x23 is required.\nSome menus will look garbled!", COLS, LINES); /* default parameters for the statusline */ statusline_attrs.colorpair_index = find_or_init_colorpair(COLOR_WHITE, COLOR_BLACK, 0); statusline_attrs.attrs = A_REVERSE; endwin(); do_commandline(argc, argv); cmdfile_h.history = search_h.history = NULL; load_configfile(config_file); init_history(&search_h); init_history(&cmdfile_h); if (defaultcscheme) { default_color_scheme = find_colorscheme(defaultcscheme); if (default_color_scheme == -1) error_exit("Default color scheme '%s' is not defined. Please check the MultiTail configuration file %s.\n", defaultcscheme, config_file); } if (markerline_attrs.colorpair_index == -1 && markerline_attrs.attrs == -1) markerline_attrs = find_attr(COLOR_RED, COLOR_BLACK, A_REVERSE); init_children_reaper(); /* initialise mail-stuff */ init_check_for_mail(); /* it seems that on at least FreeBSD ncurses forgets all defined * colorpairs after an endwin() * so here were explicitly re-setting them */ for(loop=0; loop wt == WT_FILE) { int pipefd[2]; stop_process(cur -> pid); if (myclose(cur -> fd) == -1) error_exit("Closing read filedescriptor failed.\n"); if (cur -> fd != cur -> wfd) { if (myclose(cur -> wfd) == -1) error_exit("Closing write filedescriptor failed.\n"); } /* create a pipe, will be to child-process */ if (-1 == pipe(pipefd)) error_exit("Error while creating pipe.\n"); cur -> pid = start_tail(cur -> filename, cur -> retry_open, cur -> follow_filename, min_n_bufferlines, pipefd); cur -> fd = pipefd[0]; cur -> wfd = pipefd[1]; } cur = cur -> next; } while(cur); } } int check_for_died_processes(void) { int a_window_got_closed = 0; for(;;) { int f_index; char found = 0; /* see if any processed stopped */ int exit_code = 0; pid_t died_proc = waitpid(-1, &exit_code, WNOHANG | WUNTRACED); if (died_proc == 0) { break; } else if (died_proc == -1) { if (errno == ECHILD) break; error_exit("waitpid failed.\n"); } /* now check every window for died processes */ for(f_index=0; f_index pid && cur -> wt != WT_STDIN && cur -> wt != WT_SOCKET) { int refresh_info = close_window(f_index, cur, MY_FALSE); found = a_window_got_closed = 1; /* is an entry deleted? (>=0) or restarted? (==-1) */ if (refresh_info >= 0) { set_do_refresh(2); } else if (cur -> restart.do_diff && get_do_refresh() == 0) { if (get_do_refresh() != 2) set_do_refresh(1); } break; } cur = cur -> next; } while(cur); } /* check child processes */ if (!found) { int loop; for(loop=0; loop 0) memmove(&children_list[loop], &children_list[loop + 1], sizeof(pid_t) * n_pids_to_move); n_children--; break; } } } } return a_window_got_closed; } int find_window(char *filename, int *index, proginfo **cur) { int loop; for(loop=0; loop filename, filename) == 0) { if (index) *index = loop; if (cur) *cur = pc; return 0; } pc = pc -> next; } while(pc); } return -1; } void create_new_win(proginfo **cur, int *nr) { int loop; proginfo *newpi = (proginfo *)myrealloc(pi, (nfd + 1) * sizeof(proginfo)); lb = (buffer *)myrealloc(lb, (nfd + 1) * sizeof(buffer)); /* 'lb' has pointers to 'pi'-structures. now since we've realloced the pi- * array, we need to update to pointers to pi in the lb-array */ for(loop=0; loop= cdg[loop].check_interval) { int fi; glob_t files; /* get list of files that match */ if (glob(cdg[loop].glob_str, GLOB_ERR | GLOB_NOSORT, NULL, &files) != 0) continue; /* check each found file */ for(fi=0; fi= cdg[loop].last_check && find_window(files.gl_pathv[fi], NULL, NULL) == -1 && S_ISREG(ftype) && ((cdg[loop].new_only && last_mod >= mt_started) || !cdg[loop].new_only) ) { proginfo *cur = NULL; int win_nr = -1; /* create new structure containing all info and such */ if (!cdg[loop].in_one_window) { create_new_win(&cur, &win_nr); } else if (cdg[loop].window_nr == -1) { create_new_win(&cur, &cdg[loop].window_nr); win_nr = cdg[loop].window_nr; } else { cur = &pi[cdg[loop].window_nr]; /* skip to end of chain */ while (cur -> next) cur = cur -> next; /* allocate new entry */ cur -> next = (proginfo *)mymalloc(sizeof(proginfo)); cur = cur -> next; memset(cur, 0x00, sizeof(proginfo)); } /* fill in */ cur -> filename = mystrdup(files.gl_pathv[fi]); cur -> line_wrap = default_linewrap; cur -> win_height = -1; cur -> statistics.sccfirst = 1; set_default_parameters_if_not_given_do(cur, win_nr); /* start the tail process for this file/command */ if (start_proc(cur, max_y / nfd) == -1) error_exit("Failed to start tail process for %s.\n", cur -> filename); new_wins = 1; } } /* check all matched files */ cdg[loop].last_check = now; } /* time for a check? */ } /* check all search patterns */ return new_wins; } void resize_terminal_do(NEWWIN *popup) { determine_terminal_size(&max_y, &max_x); if (ERR == resizeterm(max_y, max_x)) error_exit("An error occured while resizing terminal(-window).\n"); endwin(); refresh(); /* <- as specified by ncurses faq, was: doupdate(); */ create_windows(); if (popup) { if ((popup -> x_off + popup -> width) > max_x || (popup -> y_off + popup -> height) > max_y) { popup -> x_off = max(max_x - (popup -> width + 1), 0); popup -> y_off = max(max_y - (popup -> height + 1), 0); move_panel(popup -> pwin, popup -> y_off, popup -> x_off); mydoupdate(); } } } int process_global_keys(int what_help, NEWWIN *popup, char cursor_shift) { int c = getch(); if (c == KEY_RESIZE) return -1; redraw_statuslines(); if (c == KEY_F(5)) { resize_terminal_do(popup); set_do_refresh(2); } else if (c == exit_key) /* ^C */ { do_exit(); error_exit("This should not be reached.\n"); } else if (c == 8 || c == KEY_F(1)) /* HELP (^h / F1) */ { show_help(what_help); } else if (popup != NULL && ((cursor_shift == 0 && c == KEY_LEFT) || (cursor_shift == 1 && c == KEY_SLEFT))) { if (popup -> x_off > 0) { popup -> x_off--; move_panel(popup -> pwin, popup -> y_off, popup -> x_off); mydoupdate(); } else wrong_key(); } else if (cursor_shift == 0 && c == KEY_UP) { if (popup != NULL && popup -> y_off > 0) { popup -> y_off--; move_panel(popup -> pwin, popup -> y_off, popup -> x_off); mydoupdate(); } else wrong_key(); } else if (popup != NULL && ((cursor_shift == 0 && c == KEY_RIGHT) || (cursor_shift == 1 && c == KEY_SRIGHT))) { if ((popup -> x_off + popup -> width) < max_x) { popup -> x_off++; move_panel(popup -> pwin, popup -> y_off, popup -> x_off); mydoupdate(); } else wrong_key(); } else if (cursor_shift == 0 && c == KEY_DOWN) { if (popup != NULL && (popup -> y_off + popup -> height) < max_y) { popup -> y_off++; move_panel(popup -> pwin, popup -> y_off, popup -> x_off); mydoupdate(); } else wrong_key(); } return c; } char process_input_data(int win_nr, proginfo *cur, char *data_in, int new_data_offset, int n_bytes_added, double now) { char *pnt = data_in; char statusline_update_needed = 0; /* statistics */ cur -> statistics.bytes_processed += n_bytes_added; data_in[new_data_offset + n_bytes_added] = 0x00; if (strchr(&data_in[new_data_offset], '\n')) { char emitted = 0; if (cur -> cont) /* reconnect lines with '\' */ { char *contsearch = pnt; while((contsearch = strstr(contsearch, "\\\n"))) { memmove(contsearch, contsearch + 2, strlen(contsearch + 2) + 1); } } /* display lines */ for(;*pnt;) { char *end = strchr(pnt, '\n'); if (!end) break; *end = 0x00; /* redirect to some other file/pipe? */ redirect(cur, pnt, (int)(end - pnt), MY_FALSE); /* gen stats */ store_statistics(cur, now); /* see if we need to beep already */ if (beep_interval > 0) { if (++linecounter_for_beep == beep_interval) { linecounter_for_beep = 0; did_n_beeps++; beep(); } } /* beep per (sub-)window */ if (cur -> beep.beep_interval > 0) { if (++cur -> beep.linecounter_for_beep == cur -> beep.beep_interval) { cur -> beep.linecounter_for_beep = 0; cur -> beep.did_n_beeps++; beep(); } } /* is this the output of a program which we should diff and such? */ if (cur -> restart.do_diff) { store_for_diff(&cur -> restart.diff, pnt); } else /* no, just output */ { if (get_do_refresh() == 0) set_do_refresh(1); /* after update interval, update screen */ statusline_update_needed |= emit_to_buffer_and_term(win_nr, cur, pnt); } if (!emitted) { emitted = 1; if (cur -> restart.is_restarted && cur -> restart.restart_clear) { cur -> restart.is_restarted = 0; werase(pi[win_nr].data -> win); } update_panels(); } pnt = end + 1; } } if (*pnt != 0x00) { int line_len = strlen(pnt) + 1; cur -> incomplete_line = mymalloc(line_len); memcpy(cur -> incomplete_line, pnt, line_len); } return statusline_update_needed; } int recv_from_fd(int fd, char **buffer, int new_data_offset, int read_size) { struct sockaddr sa; socklen_t ssa_len = sizeof(sa); char *recv_buffer = mymalloc(read_size + 1); char time_buffer[TIMESTAMP_EXTEND_BUFFER]; char *host = ""; char *facility = ""; char *severity = ""; int added_bytes; int nbytes; get_now_ts(syslog_ts_format, time_buffer, sizeof(time_buffer)); nbytes = recvfrom(fd, recv_buffer, read_size, 0, &sa, &ssa_len); recv_buffer[nbytes] = 0x00; if (resolv_ip_addresses == MY_TRUE) { struct sockaddr_in *sai = (struct sockaddr_in *)&sa; struct hostent *he = gethostbyaddr(&(sai -> sin_addr.s_addr), sizeof(sai -> sin_addr.s_addr), AF_INET); if (he != NULL && he -> h_name != NULL) host = he -> h_name; else host = "?"; } else { host = inet_ntoa(((struct sockaddr_in *)&sa) -> sin_addr); } if (show_severity_facility == MY_TRUE) { char *gt = strchr(recv_buffer, '>'); if (recv_buffer[0] == '<' && (recv_buffer[2] == '>' || recv_buffer[3] == '>' || recv_buffer[4] == '>') && gt != NULL) { int value = atoi(recv_buffer + 1); int severity_nr = value & 7; int facility_nr = value / 8; severity = severities[severity_nr]; if (facility_nr >= 24) facility = "?"; else facility = facilities[facility_nr]; memmove(recv_buffer, gt + 1, strlen(gt)); } } added_bytes = strlen(time_buffer) + 1 + strlen(host) + 1 + strlen(facility) + 1 + strlen(severity) + 1 + 1 + nbytes + 1; *buffer = myrealloc(*buffer, new_data_offset + added_bytes); nbytes = snprintf(&(*buffer)[new_data_offset], added_bytes, "%s%s%s%s%s%s%s%s %s\n", time_buffer, time_buffer[0]?" ":"", host, host[0] ?" ":"", facility, facility[0] ?" ":"", severity, severity[0] ?" ":"", recv_buffer); myfree(recv_buffer); return nbytes; } int wait_for_keypress(int what_help, double max_wait, NEWWIN *popup, char cursor_shift) { int c = -1; dtime_t max_wait_start = get_ts(); for(;;) { int deleted_entry_in_array = -1; int last_fd = 0, rc, loop; fd_set rfds; struct timeval tv; dtime_t now = get_ts(); proginfo *last_changed_window = NULL; char prev_mail_status = mail; static double lastupdate = 0; double sleep = 32767.0; /* need to check any paths? */ if (check_paths()) set_do_refresh(2); sleep = min(sleep, check_for_mail > 0 ? max((msf_last_check + check_for_mail) - now, 0) : 32767); sleep = min(sleep, heartbeat_interval > 0 ? max((heartbeat_t + heartbeat_interval) - now, 0) : 32767); sleep = min(sleep, max_wait > 0.0 ? max((max_wait_start + max_wait) - now, 0) : 32767); for(loop=0; loop paused) continue; if (cur -> closed) continue; do { if (cur -> fd != -1) { FD_SET(cur -> fd, &rfds); last_fd = max(last_fd, cur -> fd); } cur = cur -> next; } while(cur); } /* update screen? */ if (get_do_refresh()) { if (get_do_refresh() == 2) create_windows(); if (now - lastupdate >= update_interval) { set_do_refresh(0); lastupdate = now; mydoupdate(); } } /* wait for any data or key press */ if ((rc = select(last_fd + 1, &rfds, NULL, NULL, tv.tv_sec != 32767 ? &tv : NULL)) == -1) { if (errno != EINTR && errno != EAGAIN) error_exit("select() returned an error."); } now = get_ts(); if (heartbeat_interval > 0 && now - heartbeat_t >= heartbeat_interval) { heartbeat(); heartbeat_t = now; } /* check for mail */ if (now - msf_last_check >= check_for_mail) { do_check_for_mail(now); msf_last_check = now; } total_wakeups++; if (terminal_changed) { terminal_changed = 0; resize_terminal_do(popup); } if (max_wait != 0.0 && now - max_wait_start >= max_wait) break; if (got_sigusr1) { got_sigusr1 = 0; sigusr1_restart_tails(); } if (rc > 0) { /* any key pressed? */ if (FD_ISSET(0, &rfds)) { mail = 0; c = process_global_keys(what_help, popup, cursor_shift); if (c != -1) break; } /* go through all fds */ for(loop=0; loop paused) continue; if (cur -> closed) continue; do { if (cur -> fd != -1 && FD_ISSET(cur -> fd, &rfds)) { char *buffer = NULL; int read_size = min(SSIZE_MAX, 65536); int nbytes, new_data_offset = cur -> incomplete_line ? strlen(cur -> incomplete_line) : 0; buffer = myrealloc(cur -> incomplete_line, new_data_offset + read_size + 1); cur -> incomplete_line = NULL; if (cur -> wt == WT_SOCKET) { nbytes = recv_from_fd(cur -> fd, &buffer, new_data_offset, read_size); } else { nbytes = read(cur -> fd, &buffer[new_data_offset], read_size); } /* read error or EOF? */ if ((nbytes == -1 && errno != EINTR && errno != EAGAIN) || nbytes == 0) { if (nbytes == 0 && cur -> wt == WT_STDIN) { pi[loop].paused = 1; update_statusline(status, loop, cur); set_do_refresh(2); } else { if (last_changed_window == cur) last_changed_window = NULL; deleted_entry_in_array = close_window(loop, cur, MY_TRUE); if (deleted_entry_in_array >= 0) { set_do_refresh(2); free(buffer); goto closed_window; } else if (cur -> restart.do_diff && get_do_refresh() == 0) { if (get_do_refresh() != 2) set_do_refresh(1); } } } else if (nbytes != -1) /* if nbytes == -1 it must be an interrupt while READ */ { /* display statusline? */ if (process_input_data(loop, cur, buffer, new_data_offset, nbytes, now)) { update_statusline(status, loop, cur); } /* remember this window as it might be displayed in the GUI * terminal window */ last_changed_window = cur; } myfree(buffer); } /* close idle, if requested */ if (cur -> close_idle > 0 && now - cur -> statistics.lastevent > cur -> close_idle) { if (last_changed_window == cur) last_changed_window = NULL; deleted_entry_in_array = close_window(loop, cur, MY_TRUE); if (deleted_entry_in_array >= 0) { set_do_refresh(2); goto closed_window; } } if (cur -> mark_interval) { if (now - cur -> statistics.lastevent >= cur -> mark_interval) { add_markerline(loop, cur, MARKER_IDLE, NULL); if (get_do_refresh() == 0) set_do_refresh(1); cur -> statistics.lastevent = now; } } cur = cur -> next; } while(cur); if (deleted_entry_in_array > 0) break; } } /* see if any processes have died (processes started * by matchin regular expressions) */ if (need_died_procs_check) { need_died_procs_check = 0; if (check_for_died_processes()) { c = -1; break; } } closed_window: /* any window changed? then we may want to update the terminal window header */ if ((last_changed_window != NULL || mail != prev_mail_status) && set_title != NULL) { prev_mail_status = mail; draw_gui_window_header(last_changed_window); } if (deleted_entry_in_array >= 0) { c = -1; break; } } return c; } multitail-6.0/utils.h0000644000175000017500000000420312245202637014574 0ustar folkertfolkert#define max(x, y) ((x)>(y)?(x):(y)) #define min(x, y) ((x)<(y)?(x):(y)) char *abbreviate_filesize(off64_t fsize); void add_to_iat(int_array_t *piat, int element); char * amount_to_str(long long int amount); void check_died_children(void); void compile_re(regex_t *whereto, char *what); char * convert_regexp_error(int error, const regex_t *preg); void delete_array(char **list, int n); void double_ts_to_str(double ts, char *format_str, char *dest, int dest_size); void duplicate_es_array(strip_t *pes_in, int n_esin, strip_t **pes_out, int *n_esout); void duplicate_re_array(re *pre_in, int n_rein, re **pre_out, int *n_reout); int file_exist(char *filename); int file_info(char *filename, off64_t *file_size, time_field_t tft, time_t *ts, mode_t *mode); int find_char_offset(char *str, char what); int find_editscheme(char *name); int find_filterscheme(char *name); char * find_most_recent_file(char *filespec, char *cur_file); char *find_next_par(char *start); int find_path_max(void); void free_iat(int_array_t *piat); void free_re(re *cur_re); char *gethome(char *user); int get_iat_element(int_array_t *piat, int index); int get_iat_size(int_array_t *piat); void get_load_values(double *v1, double *v2, double *v3); void get_now_ts(char *format_str, char *dest, int dest_size); double get_ts(void); char * getusername(void); int get_value_arg(char *par, char *string, valcheck_t check); int get_vmsize(pid_t pid); void grow_mem_if_needed(char **what, int *cur_len, int requested_len); void init_children_reaper(void); void init_iat(int_array_t *piat); int match_files(char *search_for, char **path, char ***found, char **isdir); int myclose(int fd); int mydup(int old_fd); int myopen(char *path, int mode); int myrand(int max); char *myrealpath(char *in); int open_null(void); struct passwd *getuserinfo(void); int READ(int fd, char *where_to, int max_len, char *for_whom); char * read_line_fd(int fd); void setup_for_childproc(int fd, char close_fd_0, char *term); char * shorten_filename(char *in, int max_len); void stop_process(pid_t pid); char * term_t_to_string(term_t term); ssize_t WRITE(int fd, char *whereto, size_t len, char *for_whom); char zerotomin(char c); multitail-6.0/cv.h0000644000175000017500000000020312245202637014040 0ustar folkertfolkertchar *convert(int_array_t *pconversions, char *line); void add_conversion_scheme(int_array_t *conversions, char *conversion_name); multitail-6.0/globals.c0000644000175000017500000001215512245202637015057 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include "mt.h" #include "mem.h" NEWWIN **splitlines = NULL; NEWWIN *menu_win = NULL; buffer *lb = NULL; char *config_file = NULL; char *mail_spool_file = NULL; char *nsubwindows = NULL; char *set_title = NULL; char *shell = NULL; char *tail = NULL; char *ts_format = NULL; char *statusline_ts_format = NULL; char *syslog_ts_format = NULL; char *cnv_ts_format = NULL; char *line_ts_format = NULL; char *window_number = NULL; char *subwindow_number = NULL; char **color_names = NULL; char *replace_by_markerline = NULL; const char *F1 = "For help at any time press F1."; int *n_win_per_col = NULL; int *vertical_split = NULL; color_scheme *cschemes = NULL; const char *version_str = " --*- multitail " VERSION " (C) 2003-2013 by folkert@vanheusden.com -*--"; conversion *conversions = NULL; keybinding *keybindings = NULL; pars_per_file *ppf = NULL; proginfo *pi = NULL; check_dir_glob *cdg = NULL; int n_cdg = 0; editscheme *pes = NULL; int n_es = 0; char *defaultcscheme = NULL; proginfo *terminal_index = NULL; char *global_find = NULL; int terminal_main_index = -1; int default_color_scheme = -1; int max_y, max_x; int min_n_bufferlines = -1; int mode_statusline = 1; int n_children = 0; int n_conversions = 0; int n_cschemes = 0; int n_keybindings = 0; int n_pars_per_file = 0; int n_splitlines = 0; int nfd = 0; int update_interval = 0; int n_colors_defined = 0; int check_for_mail = 5; /* check for mail every 5 seconds */ int default_maxnlines = 0; int default_maxbytes = 0; int popup_refresh_interval = 5; /* every 5 seconds */ int default_line_wrap_offset = 0; int inverse_attrs = A_REVERSE; int beep_interval = -1; int linecounter_for_beep = 0; int did_n_beeps = 0; int default_min_shrink = 10; int wordwrapmaxlength = 10; int default_bg_color = -1; int total_wakeups = 0; int split = 0; int syslog_port = 514; double heartbeat_interval = 0.0; double heartbeat_t = 0.0; off64_t msf_prev_size = 0; dtime_t msf_last_check = 0; dtime_t mt_started; pid_t children_list[MAX_N_SPAWNED_PROCESSES]; pid_t tail_proc = 0; /* process used by checker-proc */ struct stat64 msf_info; term_t term_type = TERM_IGNORE; myattr_t markerline_attrs = { -1, -1 }; myattr_t changeline_attrs = { -1, -1 }; myattr_t idleline_attrs = { -1, -1 }; myattr_t msgline_attrs = { -1, -1 }; myattr_t statusline_attrs = { -1, -1 }; myattr_t splitline_attrs = { -1, -1 }; colorpairs cp; mode_t def_umask = 0022; history_t search_h = { NULL, 0, NULL }; history_t cmdfile_h = { NULL, 0, NULL }; mybool_t re_case_insensitive = MY_FALSE; chtype box_bottom_left_hand_corner=0; chtype box_bottom_right_hand_corner=0; chtype box_bottom_side=0; chtype box_left_side=0; chtype box_right_side=0; chtype box_top_left_hand_corner=0; chtype box_top_right_hand_corner=0; chtype box_top_side=0; beeb_t beep_method = BEEP_BEEP; double beep_popup_length = 0.0; char afs = 0; /* abbreviate filesize */ char allow_8bit = 0; /* allow 8 bits ascii*/ char banner = 1; char bright_colors = 0; char do_refresh = 0; char filename_only = 0; char got_sigusr1 = 0; char mail = 0; char no_linewrap = 0; char prev_term_char = -1; char show_subwindow_id = 0; char tab_width = 4; /* some people use 8 */ char terminal_changed = 0; char timestamp_in_markerline = 0; char use_colors = 0; char warn_closed = 1; char caret_notation = 0; char statusline_above_data = 0; char global_marker_of_other_window = 0; char markerline_char = '-'; char changeline_char = '-'; char idleline_char = '-'; char msgline_char = '-'; char default_bufferwhat = 'f'; char load_global_config = 1; char default_linewrap = 'a'; char default_follow_filename = 0; char do_not_close_closed_windows = 0; char suppress_empty_lines = 1; char splitline_mode = SL_REGULAR; char abort_key = 7; char exit_key = 3; char default_sb_showwinnr = 0; char reuse_searchstring = 1; char need_died_procs_check = 0; char scrollback_no_colors = 0; char scrollback_search_new_window = 1; mybool_t posix_tail = 0; mybool_t resolv_ip_addresses = 1; mybool_t show_severity_facility = 1; mybool_t gnu_tail = 0; regex_t global_highlight_re; char *global_highlight_str = NULL; filterscheme *pfs = NULL; int n_fs = 0; char *sigs[] = { NULL, "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT/SIGIOT", "SIGIOT", "SIGBUS", "SIGFPE", "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO/SIGPOLL", "SIGPWR", "SIGSYS/SIGUNUSED" }; int n_known_sigs = 32; char *severities[8] = { "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug" }; char *facilities[24] = { "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", "news", "uucp", "cron", "authpriv", "ftp", "?", "?", "?", "?", "local0", "local1", "local2", "local3", "local4", "local5", "local6", "local7" }; void set_do_refresh(char val) { do_refresh = val; } char get_do_refresh() { return do_refresh; } multitail-6.0/term.h0000644000175000017500000000301112245202637014377 0ustar folkertfolkertvoid wrong_key(void); int ask_yes_no(int what_help, NEWWIN *popup); void color_on(NEWWIN *win, int colorpair_index); void color_off(NEWWIN *win, int colorpair_index); void myattr_on(NEWWIN *win, myattr_t attr); void myattr_off(NEWWIN *win, myattr_t attr); void ui_inverse_on(NEWWIN *win); void ui_inverse_off(NEWWIN *win); void draw_line(NEWWIN *win, linepos_t where); char * edit_string(NEWWIN *win, int win_y, int win_x, int win_width, int max_width, char numbers_only, char *input_string, int what_help, char first_char, history_t *ph, mybool_t *pcase_insensitive); NEWWIN * create_popup(int n_lines, int n_colls); void delete_popup(NEWWIN *mywin); void mydelwin(NEWWIN *win); void mydoupdate(); NEWWIN * mynewwin(int nlines, int ncols, int begin_y, int begin_x); void escape_print(NEWWIN *win, int y, int x, char *str); void win_header(NEWWIN *win, char *str); void gui_window_header(char *string); int find_colorpair(int fgcolor, int bgcolor, char fuzzy); int find_or_init_colorpair(int fgcolor, int bgcolor, char ignore_errors); int colorstr_to_nr(char *str); int attrstr_to_nr(char *str); myattr_t parse_attributes(char *str); void set_ncurses_colorpairs(void); myattr_t find_attr(int fgcolor, int bgcolor, int attrs); char *color_to_string(int nr); char *attr_to_str(int attr); void determine_terminal_size(int *max_y, int *max_x); char * emulate_terminal(char *string, color_offset_in_line **cmatches, int *n_cmatches); void get_terminal_type(void); void init_ncurses(void); void init_colornames(void); void draw_border(NEWWIN *mywin); multitail-6.0/multitail.10000644000175000017500000003066512245202637015364 0ustar folkertfolkert.\" Copyright Folkert van Heusden, 2007 .\" .\" This file may be copied under the conditions described .\" in the GNU GENERAL PUBLIC LICENSE, Version 1, September 1998 .\" that should have been distributed together with this file. .\" .TH MULTITAIL 1 2007-02 "MultiTail" .SH NAME MultiTail \- browse through several files at once .SH SYNOPSIS .BI "MultiTail [" options "] .sp options: .BI "[\-cs|\-Cs|\-c\-] [\-s] [\-i] inputfile [\-i anotherinputfile] [...]" .sp .SH DESCRIPTION The program .B MultiTail lets you view one or multiple files like the original tail program. The difference is that it creates multiple windows on your console (with ncurses). It can also monitor wildcards: if another file matching the wildcard has a more recent modification date, it will automatically switch to that file. That way you can, for example, monitor a complete directory of files. Merging of 2 or even more logfiles is possible. It can also use colors while displaying the logfiles (through regular expressions), for faster recognition of what is important and what not. It can also filter lines (again with regular expressions). It has interactive menus for editing given regular expressions and deleting and adding windows. One can also have windows with the output of shell scripts and other software. When viewing the output of external software, MultiTail can mimic the functionality of tools like 'watch' and such. When new mail arrives for the current user, the statuslines will become green. To reset this "mail has arrived"-state, press ' ' (a space). For help at any time, press F1. .PP .SH OPTIONS .TP .B "\-i" file Select a file to monitor. You can have multiple .BI "\-i " file parameters. You only need to add .BI "\-i " file in front of a filename if the filename starts with a dash ('-'). .TP .B "\-I" file Same as .BI "\-i " file but add the output to the previous window (so the output is merged). .TP .B "-iw" file interval .B "-Iw" file interval Like '-i'/'-I' but expects the parameter to be a wildcard and the second(!) an interval. Initially MultiTail will start monitoring the first file with the most recent modification time. Every interval it will check if any new files were created (or modified) and start tailing that one. *Don't forget* to put quotation marks around the filename as otherwhise the shell will try to substitute them! .TP .B "\-l" command Command to execute in a window. Parameter is the command. Do not forget to use "'s if the external command needs parameter! (e.g. -l "ping host"). .TP .B "\-L" command Same as .BI "\-l" but add the output to the previous window (so the output is merged). .TP .B "\-j" Read from stdin (can be used only once as there is only 1 stdin). .TP .B "\-J" Same as .BI "-j" but add the output to the previous window (so the output is merged). .TP .B "\-\-mergeall" Merge all of the following files into the same window (see '--no-mergeall'). .TP .B "\-\-no\-mergeall" Stop merging all files into one window (see '--mergeall'); .TP .B "\-\-no\-repeat" When the same line is repeated, it will be suppressed while printing a "Last message repeated x times" message. .TP .B "\-\-mark\-interval x" Print every 'x' seconds a mark-line when nothing else was printed. .TP .B "\-q i path" Check path for new files with interval 'i', all in new windows. One can enter paths here understood by the shell. E.g. "/tmp/*". Note: do not forget to add quotes around the pathname to prevent the shell from parsing it! .TP .B "\-Q i path" Like -q: but merge them all in one window. .TP .B "\-\-new\-only" For -q/-Q: only create windows for files created after MultiTail was started. .TP .B "\-\-closeidle x Close windows when more then 'x' seconds no new data was processed. .TP .B "\-a x" Write the output also to file 'x' (like 'tee') AFTER it was filtered by MultiTail. .TP .B "\-A x" Write the output also to file 'x' (like 'tee') BEFORE it was filtered by MultiTail. .TP .B "-g x" Send the output also to command 'x' AFTER it was filtered by MultiTail. .TP .B "-G x" Send the output also to command 'x' BEFORE it was filtered by MultiTail. .TP .B "\-S" Prepend merged output with subwindow-number. .TP .B "\-t" title With this switch, "title" is displayed in the statusline instead of the filename or commandline. .TP .B "\-n" number_of_lines Number of lines to tail initially. The default depends on the size of the terminal-window. .TP .B "\-N" number_of_lines Like -n but this parameter will be used for all files/commands you tail after this parameter. .TP .B "\-r" interval Restart the command (started with -l/-L) after it has exited. With interval you can set how long to sleep before restarting. .TP .B "\-R" interval Restarts a command like -r only this one shows the difference in output compared to the previous run. .TP .B "\-rc" / "\-Rc" interval Like \-r / \-R but clears the window before each iteration. .TP .B "\-h" The help. .TP .B "\-f" Follow the following filename, not the descriptor. .TP .B "\--follow-all" For all files after this switch: follow the following filename, not the descriptor. .TP .B "\-fr" filter Use the predefined filter(s) from the configfile. .TP .B "\-e" Use the next regular expression on the following file. .TP .B "\-ex" Use regular expression on the following file and execute the command when it matches. The command gets as commandline parameter the whole matching line. .TP .B "\-eX" Like '-ex' but only give the matching substring as parameter. This requires a regular expression with '(' and ')'. .TP .B "\-ec" Use regular expression on the following file and display the matches. .TP .B "\-eC" Use regular expression on the following file but display everything and display the matches inverted. .TP .B "\-E" Use the next regular expression on the following files. .TP .B "\-v" Negate the next regular expression. .TP .B "\-s x" Splits the screen vertically in 'x' columns. .TP .B "\-sw x" At what position to split the screen. e.g. '-sw 20,40,,10' (=4 columns) .TP .B "\-sn x" How many windows per column for vertical split (use with -s or -sw). e.g. '-sn 3,,2'. .TP .B "\-wh x" Sets the height of a window (advisory: if it won't fit, the height is adjusted). .TP .B "\-cS" scheme Show the next given file using the colorscheme selected with 'scheme' (as defined in multitail.conf). .TP .B "\-CS" scheme Show all following files using the colorscheme selected with 'scheme' (as defined in multitail.conf). .TP .B "\-csn" Extra switch for the following switches; do not use reverse (inverted) colors. .TP .B "\-cs" Show the next given file in colors (syslog). .TP .B "\-c" Show the next given file in colors. .TP .B "\-Cs" Show all following files in color (through syslog-scheme). .TP .B "\-C" Show all following files in color. .TP .B "\-Cf field_index delimiter" Show all following files in color depending on field selected with field_index. Fields are delimited by the defined delimiter. .TP .B "\-cf field_index delimiter" Show the next file in color depending on field selected with field_index. Fields are delimited by the defined delimiter. .TP .B "\-ci color" Use a specific color. Usefull when merging multiple outputs. .TP .B "\-cT terminalmode" Interpret terminal codes. Only ANSI supported at this time. .TP .B "\-c\-" Do NOT colorize the following file. .TP .B "\-C\-" Do NOT colorize the following files. .TP .B "\-ts" Add a timestamp to each line (format is configurable in multitail.conf). .TP .B "\-Z color" Specify the color-attributes for the markerline. .TP .B "\-T" A timestamp will be placed in the markerline. .TP .B "\-d" Do NOT update statusline. .TP .B "\-D" Do not display a statusline at all. .TP .B "\-du" Put the statusline above the data window. .TP .B "\-z" Do not display "window closed" windows. .TP .B "\-u" Set screen updateinterval (for slow links). .TP .B "\-m nlines" Set buffersize Set .BI "nlines" to 0 (zero) if you want no limits on the buffering. .TP .B "\-mb x" Set scrollback buffer size (in bytes, use xKB/MB/GB). .TP .B "\-M nlines" Set the buffersize on ALL following files. .TP .B "\-p x [y]" Set linewrap: a = print everything including linewrap. l = just show everything starting at the left until the rightside of the window is reached. r = show everything starting from the right of the line. s = show everything starting with the processname. S = show everything starting after the processname. o = show everything starting at offset 'y'. .TP .B "\-P x [y]" Like -p but for all following windows. .TP .B "\-ke x" Strip parts of the input using regular expression 'x'. .TP .B "\-kr x y" Strip parts of the input starting at offset x and ending (not including!) offset y. .TP .B "\-kc x y" Strip parts of the input: strip column 'y' with delimiter 'x'. .TP .B "\-ks x" Use editscheme 'x' from configfile. .TP .B "\-w" Do not use colors. .TP .B "\-b" n Sets the TAB-width. .TP .B "\--config" filename Load the configuration from given filename. .TP .B "\-x" Set xterm-title: %f will be replaced with the last changed file, %h with the hostname, %l with the load of the system, %m with "New mail!" when the current user has new mail, %u with the current effective user, %t timestamp of last changed file, %% with a % .TP .B "\-o" configfile-item Proces a configurationfile item via the commandline in case you cannot edit the default configfile. .TP .B "\--cont" Reconnect lines with a '\' at the end. .TP .B "\--mark-interval interval" When nothing comes in, print a '---mark---' line every 'interval' seconds. .TP .B "\--mark-change" When multiple files are merged an multitail switches between two windows, print a markerline with the filename. .TP .B "\--no-mark-change" Do NOT print the markerline when the file changes (overrides the configfile). .TP .B "\--label text" Put "text" in front of each line. Usefull when merging multiple files and/or commands. .TP .B "\--retry Keep trying to open the following file if it is inaccessible. .TP .B "\--retry-all Like --retry but for all following files. .TP .B "\-cv x" Use conversion scheme 'x' (see multitail.conf). .TP .B "\--basename" Only display the filename (and not the path) in the statusline. .TP .B "\-F file" Use 'file' as configfile (instead of default configfile). .TP .B "\--no-load-global-config" Do NOT load the global configfile. .TP .B "\--beep-interval x" Let the terminal beep for every x-th line processed. Press 'i' in the main menu to see how many times it beeped. .TP .B "\--bi x" Like '\--beep-interval' but only for current (sub-)window. Statistics on the number of beeps can be found in the statistics for this (sub-)window. Press 't' in the main menu. .TP .B "\-H" Show heartbeat (to keep your sessions alive). .TP .B "\-V" Show the version and exit. .SH KEYS You can press a couple of keys while the program runs. To see a list of them, press F1 (or ^h). You can press F1 (or ^h) at any time: it gives you context related information. Press 'q' to exit the program. .SH EXAMPLES See http://www.vanheusden.com/multitail/examples.html for more and other examples. .TP .B "multitail /var/log/apache/access_log logfile \-i \-filestartingwithdatsh" This creates three windows. One with the contents of /var/log/apache/access_log, one with the contents of logfile and so on. .TP .B "multitail -R 2 -l \(dqnetstat -t\(dq" This runs netstat every 2 seconds and then shows what has changed since the previous run. That way one can see new connections being made and closed connections fading away. .TP .B "multitail logfile \-l \(dqping 192.168.1.3\(dq This creates two windows. One with the contents of logfile, one with with the output of 'ping 192.168.1.3'. .TP .B "multitail /var/log/apache/access_log \-I /var/log/apache/error_log" This creates .BI "one" window with the contents of /var/log/apache/access_log .BI "merged" with the contents of /var/log/apache/error_log. .TP .B "multitail \-M 0 /var/log/apache/access_log \-I /var/log/apache/error_log" Same as previous example. This example will store all logged entries in a buffer so that you can later on browse through them (by pressing ' .BI "b" '). .SH BUGS As this program grew larger and larger over the time with new functionality sometimes added ad-hoc, some bugs may have been introduced. Please notify folkert@vanheusden.com if you find any. .PP Well, except for the resizing of your terminal window. The program might crash when doing such things. Upgrading the ncurses library to at least version 5.3 might help in that case. .SH "SEE ALSO" .BR http://www.vanheusden.com/multitail/ .SH NOTES This page describes .B MultiTail as found in the multitail-4.3.1 package; other versions may differ slightly. Mail corrections and additions to folkert@vanheusden.com. Report bugs in the program to folkert@vanheusden.com. multitail-6.0/convert-simple.pl0000755000175000017500000000021112245202637016565 0ustar folkertfolkert#!/usr/bin/perl # disable I/O buffering (this is essential) $| = 1; while(<>) { # add 'bla' in front of the string print "bla".$_; } multitail-6.0/makefile.minix0000644000175000017500000000545412245202637016117 0ustar folkertfolkertinclude version DESTDIR=/ CONFIG_FILE=$(DESTDIR)/etc/multitail.conf DEBUG=-g LDFLAGS+=-L/usr/pkg/lib -lpanel -lncurses -lutil -lm $(DEBUG) -rdynamic CFLAGS+=-funsigned-char -D`uname` -O2 -Wall -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" -I/usr/pkg/include OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o all: multitail multitail: $(OBJS) $(CC) -Wall -W $(OBJS) $(LDFLAGS) -o multitail multitail_ccmalloc: $(OBJS) ccmalloc --no-wrapper $(CC) -Wall -W $(OBJS) $(LDFLAGS) -o ccmultitail install: multitail cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual*.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # cp multitail.conf $(CONFIG_FILE).new mkdir -p $(DESTDIR)/etc/multitail/ cp convert-* colors-* $(DESTDIR)/etc/multitail/ rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz gzip -9 $(DESTDIR)/usr/share/man/man1/multitail.1 # # There's a mailinglist! # Send an e-mail to minimalist@vanheusden.com with in the subject # 'subscribe multitail' to subscribe. # # you might want to run 'make thanks' now :-) # http://www.vanheusden.com/wishlist.php # # How do YOU use multitail? Please send me an e-mail so that I can # update the examples page. uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz rm -f $(CONFIG_FILE) rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core gmon.out *.da ccmultitail package: clean # source package rm -rf multitail-$(VERSION)* mkdir multitail-$(VERSION) cp conversion-scripts/* *.conf *.c *.h multitail.1 manual*.html Makefile makefile.* Changes INSTALL license.txt readme.txt thanks.txt version multitail-$(VERSION) tar czf multitail-$(VERSION).tgz multitail-$(VERSION) rm -rf multitail-$(VERSION) thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com echo Is your company using MultiTail and you would like to be echo mentioned on http://www.vanheusden.com/multitail/usedby.html ? echo Then please send me a logo -not too big- and a link and I will echo add it to that page. echo echo Oh, blatant plug: http://keetweej.vanheusden.com/wishlist.html check: cppcheck -v --force -j 3 --enable=all --inconclusive -I. . 2> err.txt # make clean scan-build make coverity: make clean rm -rf cov-int CC=gcc cov-build --dir cov-int make all tar vczf ~/site/coverity/multitail.tgz README cov-int/ putsite -q /home/folkert/.coverity-mt.sh multitail-6.0/diff.h0000644000175000017500000000013712245202637014346 0ustar folkertfolkertvoid store_for_diff(diff_t *diff, char *string); void generate_diff(int winnr, proginfo *cur); multitail-6.0/selbox.h0000644000175000017500000000042012245202637014725 0ustar folkertfolkertint selection_box(void **list, char *needs_mark, int nlines, char type, int what_help, char *heading); int select_window(int what_help, char *heading); proginfo * select_subwindow(int f_index, int what_help, char *heading); char * select_file(char *input, int what_help); multitail-6.0/cv.c0000644000175000017500000001705212245202637014045 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mt.h" #include "mem.h" #include "error.h" #include "utils.h" #include "exec.h" #include "globals.h" int cv_offsets_compare(const void *a, const void *b) { cv_off *pa = (cv_off *)a, *pb = (cv_off *)b; if (pa -> start > pb -> start) return -1; else if (pa -> start == pb -> start) { if (pa -> end > pb -> end) return -1; } return 0; } char * epoch_to_str(time_t epoch) { char *new_string; struct tm *ptm = localtime(&epoch); if (!ptm) return NULL; new_string = mymalloc(4096); if (!strftime(new_string, 4096, cnv_ts_format, ptm)) error_exit("An error occured whilte converting timestamp format '%s'.\n", cnv_ts_format); return new_string; } char *do_convert(char *what, int what_len, int type, script *pscript) { switch(type) { case CONVTYPE_SIGNRTOSTRING: { int signr = atoi(what); if (signr > n_known_sigs || signr < 1) return mystrdup("unknown signal"); return mystrdup(sigs[signr]); } case CONVTYPE_TAI64NTODATE: { long long int v2_62 = (long long int)1 << (long long int)62; long long int val = 0; int loop; if (what[0] == '@') what++; /* http://cr.yp.to/libtai/tai64.html#tai64n */ /* convert to 64 bit integer */ for(loop=0; loop<(8 * 2); loop++) { int c = tolower(what[loop]); val <<= (long long int)4; if (c >= 'a') val += 10 + c - 'a'; else val += c - '0'; } if (val >= v2_62) /* FIXME: 2^63 are reserved, not checking for that, sorry */ { char *new_str = epoch_to_str((time_t)(val - v2_62)); if (new_str) return new_str; else return mystrdup("cannot convert current 'TAI64N'-date to string"); } else { /* before 1970/1/1 now what should I do with that? */ return mystrdup("cannot convert 'TAI64N'-dates before the epoch"); } } case CONVTYPE_IP4TOHOST: { if (resolv_ip_addresses) { struct hostent *ht; in_addr_t addr = inet_addr(what); if ((int)addr == -1) return mystrdup(what); if ((ht = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == NULL) return mystrdup(what); return mystrdup(ht -> h_name); } return mystrdup(what); } break; case CONVTYPE_EPOCHTODATE: { char *new_str = epoch_to_str((time_t)atoll(what)); if (new_str) return new_str; else return mystrdup("cannot convert current epoch value"); } break; case CONVTYPE_ERRNO: { return mystrdup(strerror(atoi(what))); } case CONVTYPE_HEXTODEC: { long long int result = strtoll(what, NULL, 16); char result_str[128]; snprintf(result_str, sizeof(result_str), "%lld", result); return mystrdup(result_str); } case CONVTYPE_DECTOHEX: { long long int result = atoll(what); char result_str[128]; snprintf(result_str, sizeof(result_str), "%llx", result); return mystrdup(result_str); } case CONVTYPE_SCRIPT: { int rc; char *send_buffer = mymalloc(what_len + 1 + 1); char *result_str = mymalloc(SCRIPT_IO_BUFFER_SIZE); exec_script(pscript); memcpy(send_buffer, what, what_len); send_buffer[what_len] = '\n'; send_buffer[what_len + 1] = 0x00; WRITE(pscript -> fd_w, send_buffer, what_len + 1, "conversion script (is it still running?)"); myfree(send_buffer); rc = READ(pscript -> fd_r, result_str, SCRIPT_IO_BUFFER_SIZE - 1, pscript -> script); result_str[rc > 0?rc - 1:rc] = 0x00; return result_str; } case CONVTYPE_ABBRTOK: return amount_to_str(atoll(what)); default: error_exit("Internal error: unknown conversion type %d.\n", type); } return "do_convert: INTERNAL ERROR"; } char *convert(int_array_t *pconversions, char *line) { conversion *cur_conv = NULL; cv_off *cv_offsets = NULL; int conv_index; int conv_req; int new_len = 0; int cur_n_cv_matches = 0; char *new_string = NULL; int offset_old = 0, offset_new = 0; int old_len = strlen(line); int n_conversions = get_iat_size(pconversions); if (n_conversions == 0) return line; for(conv_req=0; conv_req < n_conversions; conv_req++) { cur_conv = &conversions[get_iat_element(pconversions, conv_req)]; /* find where they match */ for(conv_index=0; conv_index n; conv_index++) { int offset = 0; do { int cur_match_index; int cur_offset = offset; regmatch_t matches[MAX_N_RE_MATCHES]; /* FIXME: what to do with regexp errors? */ if (regexec(&(cur_conv -> pcb)[conv_index].regex, &line[offset], MAX_N_RE_MATCHES, matches, offset?REG_NOTBOL:0) != 0) break; for(cur_match_index=1; cur_match_index pcb)[conv_index].match_count++; cv_offsets = (cv_off *)myrealloc(cv_offsets, sizeof(cv_off) * (cur_n_cv_matches + 1)); cv_offsets[cur_n_cv_matches].start = match_start; cv_offsets[cur_n_cv_matches].end = match_end; dummylen = match_end - match_start; dummy = mymalloc(dummylen + 1); memcpy(dummy, &line[match_start], dummylen); dummy[dummylen] = 0x00; cv_offsets[cur_n_cv_matches].newstr = do_convert(dummy, dummylen, (cur_conv -> pcb)[conv_index].type, &(cur_conv -> pcs)[conv_index]); myfree(dummy); cur_n_cv_matches++; } } while (offset < old_len); } } if (cur_n_cv_matches) { int n_copy; /* sort */ if (cur_n_cv_matches > 1) qsort(cv_offsets, cur_n_cv_matches, sizeof(cv_off), cv_offsets_compare); /* create new string */ for(conv_index=0; conv_index < cur_n_cv_matches; conv_index++) { n_copy = cv_offsets[conv_index].start - offset_old; if (n_copy > 0) { new_string = myrealloc(new_string, new_len + n_copy + 1); memcpy(&new_string[offset_new], &line[offset_old], n_copy); new_string[offset_new + n_copy] = 0x00; new_len += n_copy; offset_new += n_copy; } offset_old = cv_offsets[conv_index].end; n_copy = strlen(cv_offsets[conv_index].newstr); new_string = myrealloc(new_string, new_len + n_copy + 1); memcpy(&new_string[offset_new], cv_offsets[conv_index].newstr, n_copy); new_string[offset_new + n_copy] = 0x00; myfree(cv_offsets[conv_index].newstr); new_len += n_copy; offset_new += n_copy; } n_copy = old_len - offset_old; if (n_copy) { new_string = myrealloc(new_string, new_len + n_copy + 1); memcpy(&new_string[offset_new], &line[offset_old], n_copy); new_string[offset_new + n_copy] = 0x00; } } else { new_string = line; } myfree(cv_offsets); return new_string; } int find_conversion_scheme(char *name) { int loop; for(loop=0; loop #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _POSIX_PRIORITY_SCHEDULING #include #endif #include #include #include #if defined(sun) || defined(__sun) #include #endif #include #include #include #include #include #include "version.h" #include "error.h" #include "mem.h" #include "mt.h" #include "term.h" #include "globals.h" #include "utils.h" int find_path_max(void) { #ifdef PATH_MAX int path_max = PATH_MAX; #else int path_max = pathconf("/", _PC_PATH_MAX); if (path_max <= 0) { if (errno) error_exit("pathconf() failed\n"); path_max = 4096; } else path_max++; /* since its relative to root */ #endif if (path_max > 4096) path_max = 4096; return path_max; } int myrand(int max) { return (int)(((double)max * (double)rand()) / (double)RAND_MAX); } ssize_t WRITE(int fd, char *whereto, size_t len, char *for_whom) { ssize_t cnt=0; while(len>0) { ssize_t rc; rc = write(fd, whereto, len); if (rc == -1) { if (errno != EINTR && errno != EAGAIN) error_exit("Problem writing to file descriptor while processing %s.\n", for_whom); } else if (rc == 0) { break; } else { whereto += rc; len -= rc; cnt += rc; } } return cnt; } void get_load_values(double *v1, double *v2, double *v3) { #if !defined(__UCLIBC__) && (defined(__FreeBSD__) || defined(linux) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined(__GNU__) || defined(__sun) || defined(sun)) #if defined(__GLIBC__) && ( __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2)) /* Older glibc doesn't have getloadavg() - use sysinfo() */ /* thanks to Ville Herva for this code! */ double scale = 1 << SI_LOAD_SHIFT; struct sysinfo si; if (sysinfo(&si) == -1) { /* let's exit: if these kind of system- * calls start to fail, something must be * really wrong */ error_exit("sysinfo() failed\n"); } *v1 = (double)si.loads[0] / scale; *v2 = (double)si.loads[1] / scale; *v3 = (double)si.loads[2] / scale; #else double loadavg[3]; if (getloadavg(loadavg, 3) == -1) { /* see comment on sysinfo() */ error_exit("getloadavg() failed\n"); } *v1 = loadavg[0]; *v2 = loadavg[1]; *v3 = loadavg[2]; #endif #else *v1 = *v2 = *v3 = -1.0; #endif } int get_vmsize(pid_t pid) { int vmsize = -1; #if defined(linux) FILE *fh; int path_max = find_path_max(); char *path = mymalloc(path_max); assert(pid > 1); snprintf(path, path_max, "/proc/%d/stat", pid); fh = fopen(path, "r"); if (fh) { char *dummystr = mymalloc(path_max); char dummychar; int dummy; if (fscanf(fh, "%d %s %c %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", &dummy, dummystr, &dummychar, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &vmsize) != 23) vmsize = -1; fclose(fh); myfree(dummystr); } myfree(path); #endif return vmsize; } int mykillpg(pid_t pid, int sigtype) { #ifdef AIX return kill(pid, sigtype); #else return killpg(pid, sigtype); #endif } /** stop_process * - in: int pid pid of process * - returns: nothing * this function sends a TERM-signal to the given process, sleeps for 1009 microseconds * and then sends a KILL-signal to the given process if it still exists. the TERM signal * is send so the process gets the possibility to gracefully exit. if it doesn't do that * in 100 microseconds, it is terminated */ void stop_process(pid_t pid) { assert(pid > 1); if (mykillpg(pid, SIGTERM) == -1) { if (errno != ESRCH) error_exit("Problem stopping child process with PID %d (SIGTERM).\n", pid); } usleep(1000); /* process still exists? */ if (mykillpg(pid, SIGTERM) == 0) { /* sleep for a millisecond... */ usleep(1000); /* ...and then really terminate the process */ if (mykillpg(pid, SIGKILL) == -1) { if (errno != ESRCH) error_exit("Problem stopping child process with PID %d (SIGKILL).\n", pid); } } else if (errno != ESRCH) error_exit("Problem stopping child process with PID %d (SIGTERM).\n", pid); /* wait for the last remainder of the died process to go away, * otherwhise we'll find zombies on our way */ if (waitpid(pid, NULL, WNOHANG | WUNTRACED) == -1) { if (errno != ECHILD) error_exit("waitpid() failed\n"); } } /** delete_array * - in: char **list array of strings to free * int n number of strings in this array * - returns: nothing * this function frees an array of strings: all strings are freed and * also the pointer-list itself is freed */ void delete_array(char **list, int n) { int loop; assert(n >= 0); for(loop=n-1; loop>=0; loop--) myfree(list[loop]); myfree(list); } int find_char_offset(char *str, char what) { char *pnt; assert(what > 0); pnt = strchr(str, what); if (!pnt) return -1; return (int)(pnt - str); } int file_info(char *filename, off64_t *file_size, time_field_t tft, time_t *ts, mode_t *mode) { struct stat64 buf; if (stat64(filename, &buf) == -1) { if (errno != ENOENT) error_exit("Error while obtaining details of file %s.\n", filename); return -1; } if (file_size) *file_size = buf.st_size; if (ts) { if (tft == TT_ATIME) *ts = buf.st_atime; else if (tft == TT_MTIME) *ts = buf.st_mtime; else if (tft == TT_CTIME) *ts = buf.st_ctime; else { assert(tft == 0); } } if (mode) *mode = buf.st_mode; return 0; } int file_exist(char *filename) { struct stat buf; int rc = stat(filename, &buf); if (rc == -1 && errno != ENOENT) error_exit("stat() on file %s failed.\n", filename); return rc; } char * convert_regexp_error(int error, const regex_t *preg) { /* errors are specified not to be longer then 256 characters */ char *multitail_string = "MultiTail warning: regular expression failed, reason: "; int len = strlen(multitail_string); char *error_out = NULL; const int max_err_len = 256; assert(error != 0); if (error != REG_NOMATCH) { error_out = (char *)mymalloc(max_err_len + len + 1); memcpy(error_out, multitail_string, len); /* convert regexp error */ regerror(error, preg, &error_out[len], max_err_len); } return error_out; } char * amount_to_str(long long int amount) { char *out = mymalloc(AMOUNT_STR_LEN); /* ...XB\0 */ assert(amount >= 0); if (amount >= M_GB) /* GB */ snprintf(out, AMOUNT_STR_LEN, "%dGB", (int)((amount + M_GB - 1) / M_GB)); else if (amount >= M_MB) /* MB */ snprintf(out, AMOUNT_STR_LEN, "%dMB", (int)((amount + M_MB - 1) / M_MB)); else if (amount >= M_KB) /* KB */ snprintf(out, AMOUNT_STR_LEN, "%dKB", (int)((amount + M_KB - 1) / M_KB)); else snprintf(out, AMOUNT_STR_LEN, "%d", (int)(amount)); return out; } struct passwd *getuserinfo(void) { struct passwd *pp = getpwuid(geteuid()); if (!pp) error_exit("Failed to get passwd-structure for effective user id %d.\n", geteuid()); return pp; } char * getusername(void) { static char username[128] = { 0 }; if (!username[0]) { strncpy(username, getuserinfo() -> pw_name, sizeof(username)); username[sizeof(username) - 1] = 0x00; } return username; } /* these are there because AIX/IRIX can return EINTR for those */ int myopen(char *path, int mode) { int fd; for(;;) { fd = open64(path, mode); if (fd == -1) { if (errno == EINTR || errno == EAGAIN) /* for AIX */ continue; return fd; } break; } return fd; } int myclose(int fd) { for(;;) { if (close(fd) == -1) { if (errno == EINTR || errno == EAGAIN) /* for AIX */ continue; return -1; } return 0; } } char * shorten_filename(char *in, int max_len) { static char buffer[4096]; int len = strlen(in); int cutlen, dummy; assert(max_len >= 0); if (len <= max_len) return in; cutlen = (max_len - 3) / 2; memcpy(buffer, in, cutlen); memcpy(&buffer[cutlen], "...", 3); dummy = max_len - (cutlen + 3); memcpy(&buffer[cutlen + 3], &in[len - dummy], dummy + 1); return buffer; } double get_ts(void) { struct timeval ts; struct timezone tz; if (gettimeofday(&ts, &tz) == -1) error_exit("gettimeofday() failed"); return (((double)ts.tv_sec) + ((double)ts.tv_usec)/1000000.0); } int match_files(char *search_for, char **path, char ***found, char **isdir) { DIR *dir; struct dirent *entry; char *cur_dir = mymalloc(find_path_max() + 1); char *fname; char **list = NULL; int nfound = 0; size_t fname_size; int path_len; int s1, s2; char *slash = strrchr(search_for, '/'); if (slash) { fname = mystrdup(slash + 1); *(slash + 1) = 0x00; *path = mystrdup(search_for); } else { *path = mystrdup("./"); fname = mystrdup(search_for); } fname_size = strlen(fname); path_len = strlen(*path); dir = opendir(*path); if (!dir) { free(cur_dir); return 0; } memcpy(cur_dir, *path, path_len + 1); while((entry = readdir(dir)) != NULL) { if ((fname_size == 0 || strncmp(entry -> d_name, fname, fname_size) == 0) && strcmp(entry -> d_name, ".") != 0 && strcmp(entry -> d_name, "..") != 0) { struct stat finfo; /* get filename */ list = (char **)myrealloc(list, (nfound + 1) * sizeof(char *)); list[nfound] = mystrdup(entry -> d_name); /* check if the file is a directory */ *isdir = (char *)myrealloc(*isdir, (nfound + 1) * sizeof(char)); strncpy(&cur_dir[path_len], entry -> d_name, max(0,find_path_max() - path_len)); if (stat(cur_dir, &finfo) == -1) { if (errno != ENOENT) /* file did not disappear? then something is very wrong */ error_exit("Error while invoking stat() %s.\n", cur_dir); } (*isdir)[nfound] = S_ISDIR(finfo.st_mode)?1:0; nfound++; } } if (closedir(dir) == -1) error_exit("closedir() failed\n"); /* qsort( (void *)list, (size_t)nfound, sizeof(char *), compare_filenames); */ for(s1=0; s1<(nfound - 1); s1++) { for(s2=s1+1; s2 regex_str); if (cur_re -> use_regex) regfree(&cur_re -> regex); myfree(cur_re -> cmd); } } char * find_most_recent_file(char *filespec, char *cur_file) { glob_t files; char *selected_file = NULL; unsigned int loop; time_t prev_ts = (time_t)0; /* get timestamp of previous file */ if (cur_file) { if (file_info(cur_file, NULL, TT_MTIME, &prev_ts, NULL) == -1) { prev_ts = (time_t)0; } } /* get list of files that match */ #if defined(__APPLE__) || defined(__CYGWIN__) if (glob(filespec, GLOB_ERR | GLOB_NOSORT, NULL, &files) != 0) #else if (glob(filespec, GLOB_ERR | GLOB_NOSORT | GLOB_NOESCAPE, NULL, &files) != 0) #endif { return NULL; } /* see if any of them is more recent than the current one */ for(loop=0; loop prev_ts) { selected_file = files.gl_pathv[loop]; prev_ts = new_ts; } } /* found a file? then remember the filename */ if (selected_file != NULL) { selected_file = mystrdup(selected_file); } globfree(&files); return selected_file; } char zerotomin(char c) { if (c == 0) return 'n'; if (c == 1) return 'y'; return c; } char *find_next_par(char *start) { char *dummy = strchr(start, ':'); if (dummy) { *dummy = 0x00; dummy++; } return dummy; } int mydup(int old_fd) { int new_fd = -1; for(;;) { new_fd = dup(old_fd); if (new_fd == -1) { if (errno == EINTR) continue; error_exit("dup() failed\n"); } break; } return new_fd; } char * term_t_to_string(term_t term) { switch(term) { case TERM_ANSI: return "ansi"; case TERM_XTERM: return "xterm"; case TERM_IGNORE: return "dumb"; } return "dumb"; } void double_ts_to_str(dtime_t ts, char *format_str, char *dest, int dest_size) { time_t now = (time_t)ts; struct tm *ptm = localtime(&now); if (!ptm) error_exit("localtime() failed\n"); assert(ts > 0); assert(dest_size > 0); (void)strftime(dest, dest_size, format_str, ptm); } void get_now_ts(char *format_str, char *dest, int dest_size) { double_ts_to_str(get_ts(), format_str, dest, dest_size); } void duplicate_re_array(re *pre_in, int n_rein, re **pre_out, int *n_reout) { int loop, old_n = *n_reout; assert(n_rein >= 0); *n_reout += n_rein; if (*n_reout) { *pre_out = (re *)myrealloc(*pre_out, (*n_reout) * sizeof(re)); for(loop=0; loop= 0); *n_esout += n_esin; *pes_out = (strip_t *)myrealloc(*pes_out, (*n_esout) * sizeof(strip_t)); for(loop=0; loop 0); while(*cur_len < requested_len) { changed = 1; if (*cur_len) (*cur_len) *= 2; else *cur_len = 128; } if (changed) *what = myrealloc(*what, *cur_len); } int READ(int fd, char *where_to, int max_len, char *for_whom) { assert(max_len > 0); for(;;) { int rc = read(fd, where_to, max_len); if (rc >= 0) return rc; if (errno != EINTR && errno != EAGAIN) error_exit("Error writing to fd for %s.\n", for_whom); } } int get_value_arg(char *par, char *string, valcheck_t check) { long int result; int len, loop; int multiplier = 1; if (!string) error_exit("%s needs a parameter.\n", par); len = strlen(string); for(loop=0; loop INT_MAX || result == LONG_MIN || result == LONG_MAX) error_exit("Value %s for parameter %s is too large.\n", string, par); result *= multiplier; if (check == VAL_ZERO_POSITIVE) { if (result < 0) error_exit("Value for %s must be >= 0.\n", par); } else if (check == VAL_POSITIVE_NOT_1) { if (result < 0 || result == 1) error_exit("Value for %s must be >= 0 and must not be 1.\n", par); } else if (check == VAL_POSITIVE) { if (result <= 0) error_exit("Value for %s must be > 0.\n", par); } else { assert(0); } return (int)result; } void add_to_iat(int_array_t *piat, int element) { if (piat -> n == piat -> size) { piat -> size = USE_IF_SET(piat -> size, 8) * 2; piat -> elements = (int *)myrealloc(piat -> elements, piat -> size * sizeof(int)); } (piat -> elements)[piat -> n] = element; (piat -> n)++; } int get_iat_size(int_array_t *piat) { return piat -> n; } void init_iat(int_array_t *piat) { piat -> elements = NULL; piat -> size = piat -> n = 0; } void free_iat(int_array_t *piat) { myfree(piat -> elements); init_iat(piat); } int get_iat_element(int_array_t *piat, int index) { assert(index < piat -> n); return (piat -> elements)[index]; } char *gethome(char *user) { struct passwd *pw; if (user) pw = getpwnam(user); else pw = getpwuid(getuid()); return mystrdup(pw->pw_dir); } char *myrealpath(char *in) { char *home, *pin; int home_len; char *pout; if (in[0] != '~') return mystrdup(in); if (in[1] != '/') { char *user; char *slash = strchr(in, '/'); int len; if (slash) { len = (int)((slash - in) - 1); pin = slash + 1; } else { len = strlen(in) - 1; pin = in + len + 1; } user = (char *)mymalloc(len + 1); memcpy(user, &in[1], len); user[len] = 0x00; home = gethome(user); myfree(user); } else { home = gethome(NULL); pin = in + 1; } if (!home) error_exit("Cannot expand path %s:\nhome directory not found.\n", in); home_len = strlen(home); pout = (char *)mymalloc(home_len + strlen(in) + 1); sprintf(pout, "%s/%s", home, pin); myfree(home); return pout; } multitail-6.0/exec.c0000644000175000017500000002371212245202637014361 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include #include #include #include #include "mt.h" #include "error.h" #include "utils.h" #include "mem.h" #include "term.h" #include "my_pty.h" #include "globals.h" #include "ui.h" int start_tail(char *filename, char retry_open, char follow_filename, int initial_tail, int *pipefd) { pid_t pid; /* start child process */ if ((pid = fork()) == 0) { char *pars[16], *posix_version = NULL; int npars = 0; char nlines_buffer[32]; #ifndef __minix setpgid(0,0); #endif setup_for_childproc(pipefd[1], 0, "dumb"); /* create command for take last n lines & follow and start tail */ /* the command to start */ pars[npars++] = tail; /* Linux' tail has the --retry option, but not all * other UNIX'es have this, so I implemented it * myself */ if (retry_open) { int rc; struct stat64 buf; for(;;) { rc = stat64(filename, &buf); if (rc == -1) { if (errno != ENOENT) { fprintf(stderr, "Error while looking for file %s: %d\n", filename, errno); exit(EXIT_FAILURE); } } else if (rc == 0) break; usleep(WAIT_FOR_FILE_DELAY * 1000); } #if defined(linux) || defined(__CYGWIN__) || defined(__GNU__) pars[npars++] = "--retry"; #endif } /* get the posix compliance level */ posix_version = getenv("_POSIX2_VERSION"); /* follow filename is only supported on *BSD and Linux */ #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(linux) || defined(__CYGWIN__) || defined(__APPLE__) || defined(__GNU__) if (follow_filename) { #if defined(linux) || defined(__CYGWIN__) || defined(__GNU__) pars[npars++] = "--follow=name"; #elif defined(__OpenBSD__) pars[npars++] = "-f"; #else pars[npars++] = "-F"; #endif /* number of lines to tail initially */ pars[npars++] = "-n"; snprintf(nlines_buffer, sizeof(nlines_buffer), "%d", initial_tail); pars[npars++] = nlines_buffer; } else #endif { #if !defined(linux) && !defined(__CYGWIN__) && !defined(__GNU__) if (follow_filename && gnu_tail) pars[npars++] = "--follow=name"; #endif /* check the posix compliance level */ if ((posix_version && atoi(posix_version) >= 200112) || posix_tail == MY_TRUE) { pars[npars++] = "-f"; pars[npars++] = "-n"; snprintf(nlines_buffer, sizeof(nlines_buffer), "%d", initial_tail); pars[npars++] = nlines_buffer; } else { /* number of lines to tail initially and 'follow file' ('f') */ snprintf(nlines_buffer, sizeof(nlines_buffer), "-%dlf", initial_tail); pars[npars++] = nlines_buffer; } } /* add the filename to monitor */ pars[npars++] = filename; pars[npars] = NULL; /* run tail! */ if (-1 == execvp(pars[0], pars)) error_exit("Error while starting process %s.\n", pars[0]); /* if execlp returns, an error occured */ error_exit("An error occured while starting process %s!\n", pars[0]); } return pid; } void start_proc_signal_handler(int sig) { if (sig != SIGTERM) error_popup("Signal handler(2)", -1, "Unexpected signal %d.\n", sig); stop_process(tail_proc); exit(1); } int start_proc(proginfo *cur, int initial_tail) { cur -> n_runs++; if (cur -> wt == WT_COMMAND) { int fd_master, fd_slave; /* allocate pseudo-tty & fork*/ cur -> pid = get_pty_and_fork(&fd_master, &fd_slave); if (-1 == cur -> pid) error_exit("An error occured while invoking get_pty_and_fork.\n"); /* child? */ if (cur -> pid == 0) { #ifndef __minix setpgid(0,0); #endif /* reset signal handler for SIGTERM */ signal(SIGTERM, SIG_DFL); /* sleep if requested and only when 2nd or 3d (etc.) execution time */ if (cur -> restart.restart && cur -> restart.first == 0) sleep(cur -> restart.restart); /* connect slave-fd to stdin/out/err */ setup_for_childproc(fd_slave, 1, term_t_to_string(cur -> cdef.term_emul)); /* start process */ if (-1 == execlp(shell, shell, "-c", cur -> filename, (void *)NULL)) error_exit("Error while starting \"%s -c '%s'\".\n", shell, cur -> filename); /* if execlp returns, an error occured */ error_exit("Error while starting process %s!\n", shell); } #if defined(sun) || defined(__sun) || defined(AIX) || defined(_HPUX_SOURCE) || defined(OSF1) || defined(scoos) /* these platforms only have the slave-fd available in the childprocess * * so don't try to close it as the parent process doesn't have it * */ #else if (myclose(fd_slave) == -1) error_exit("An error occured while closing the slave fd (pseudo tty, fd %d)\n", fd_slave); #endif /* remember master-fd (we'll read from that one) */ cur -> fd = fd_master; cur -> wfd = fd_master; /* next time, sleep */ cur -> restart.first = 0; } else if (cur -> wt == WT_FILE) { int pipefd[2]; /* create a pipe, will be to child-process */ if (-1 == pipe(pipefd)) error_exit("Error creating pipe.\n"); if (cur -> check_interval) { /* start the process that will check every 'interval' seconds * if a matching file with a more recent mod-time is available */ cur -> pid = fork(); if (cur -> pid == 0) { char *cur_file = NULL, *new_file; #ifndef __minix setpgid(0,0); #endif signal(SIGTERM, start_proc_signal_handler); for(;;) { /* find (new) file */ new_file = find_most_recent_file(cur -> filename, cur_file); /* if find_most_recent_file returned NOT null, a file was found * which is more recent */ if (new_file != NULL) { /* if there was a previous file, see if it is different * from the new filename. this should always be the case! */ if (cur_file && strcmp(new_file, cur_file) != 0) { stop_process(tail_proc); } /* remember new filename */ myfree(cur_file); cur_file = new_file; /* and start a proc process */ tail_proc = start_tail(cur_file, cur -> retry_open, cur -> follow_filename, initial_tail, pipefd); if (tail_proc == -1) { break; } } else { /* LOG("no file found for pattern %s\n", cur -> filename); */ } sleep(cur -> check_interval); } /* LOG("stopped checking for file pattern %s\n", cur -> filename); */ exit(1); } } else { cur -> pid = start_tail(cur -> filename, cur -> retry_open, cur -> follow_filename, initial_tail, pipefd); } cur -> fd = pipefd[0]; cur -> wfd = pipefd[1]; } if (cur -> pid > -1) return 0; return -1; } int execute_program(char *execute, char bg) { int status; pid_t child; if (bg) { /* to prevent meltdowns, only a limited number of * processes can be executed */ if (n_children >= MAX_N_SPAWNED_PROCESSES) return 0; } else endwin(); child = fork(); if (child == 0) { #ifndef __minix setpgid(0,0); #endif if (bg) setup_for_childproc(open_null(), 1, "dumb"); /* start process */ if (-1 == execlp(shell, shell, "-c", execute, (void *)NULL)) error_exit("Error while starting \"%s -c '%s'\".\n", execute); /* if execlp returns, an error occured */ error_exit("Error while starting process!\n"); } else if (child == -1) { error_exit("Failed to fork child process.\n"); } if (bg) { /* remember this childprocess: we'll see if it has * died in the main-loop */ children_list[n_children++] = child; } else { /* wait for the childprocess to exit */ if (waitpid(child, &status, 0) == -1) error_exit("Error while waiting for process to exit.\n"); /* restore (n)curses */ mydoupdate(); } return 0; } void init_children_reaper(void) { /* init list of pids to watch for exit */ memset(children_list, 0x00, sizeof(children_list)); } pid_t exec_with_pipe(char *command, int pipe_to_proc[], int pipe_from_proc[]) { pid_t pid = -1; if (pipe(pipe_to_proc) == -1) error_exit("Error creating pipe.\n"); if (pipe(pipe_from_proc) == -1) error_exit("Error creating pipe.\n"); if ((pid = fork()) == -1) error_exit("fork() failed.\n"); if (pid == 0) { myclose(0); if (mydup(pipe_to_proc[0]) == -1) error_exit("dup() failed.\n"); myclose(pipe_to_proc[1]); /* will not write to itself, only parent writes to it */ myclose(1); myclose(2); if (mydup(pipe_from_proc[1]) == -1) error_exit("dup() failed.\n"); if (mydup(pipe_from_proc[1]) == -1) error_exit("dup() failed.\n"); myclose(pipe_from_proc[0]); /* start process */ /* if (-1 == execlp(shell, shell, "-c", command, (void *)NULL)) error_exit("execlp of %s failed\n", command); */ if (-1 == execlp(command, command, (void *)NULL)) error_exit("Error while starting '%s'.\n", command); /* if execlp returns, an error occured */ error_exit("Error while starting process '%s'.\n", command); } return pid; } pid_t exec_with_pty(char *command, int *fd) { int fd_master = -1, fd_slave = -1; pid_t pid = get_pty_and_fork(&fd_master, &fd_slave); if (-1 == pid) error_exit("An error occured while invoking get_pty_and_fork.\n"); if (pid == 0) { #ifndef __minix setpgid(0,0); #endif myclose(fd_master); /* connect slave-fd to stdin/out/err */ setup_for_childproc(fd_slave, 1, "dumb"); /* start process */ if (-1 == execlp(command, command, (void *)NULL)) error_exit("Error while starting '%s'.\n", command); } *fd = fd_master; #if defined(sun) || defined(__sun) || defined(AIX) || defined(_HPUX_SOURCE) || defined(OSF1) || defined(scoos) /* see start_proc */ #else if (myclose(fd_slave) == -1) error_exit("Error closing slave-fd (pseudo tty, fd %d)\n", fd_slave); #endif return pid; } void exec_script(script *pscript) { if (pscript -> pid == 0) { int to[2], from[2]; pscript -> pid = exec_with_pipe(pscript -> script, to, from); pscript -> fd_r = from[0]; pscript -> fd_w = to[1]; /* int fd; pscript -> pid = exec_with_pty(pscript -> script, &fd); pscript -> fd_r = pscript -> fd_w = fd; */ } } multitail-6.0/globals.h0000644000175000017500000001014712245202637015063 0ustar folkertfolkertextern proginfo *pi; extern buffer *lb; extern int nfd; extern int max_y, max_x; extern char terminal_changed; extern int split; extern char banner; extern NEWWIN **splitlines; extern int n_splitlines; extern NEWWIN *menu_win; extern int mode_statusline; extern char warn_closed; extern char use_colors; extern int min_n_bufferlines; extern color_scheme *cschemes; extern int n_cschemes; extern pars_per_file *ppf; extern int n_pars_per_file; extern char mail; extern int check_for_mail; extern char tab_width; extern time_t mt_started; extern int *vertical_split; extern int *n_win_per_col; extern proginfo *terminal_index; extern int terminal_main_index; extern char prev_term_char; extern int n_keybindings; extern keybinding *keybindings; extern char *set_title; extern pid_t tail_proc; extern char bright_colors; extern char *mail_spool_file; extern struct stat64 msf_info; extern off64_t msf_prev_size; extern dtime_t msf_last_check; extern int update_interval; extern char *tail; extern char afs; extern colorpairs cp; extern char show_subwindow_id; extern myattr_t markerline_attrs; extern myattr_t changeline_attrs; extern myattr_t idleline_attrs; extern myattr_t statusline_attrs; extern myattr_t msgline_attrs; extern char timestamp_in_markerline; extern double heartbeat_t; extern double heartbeat_interval; extern const char *version_str; extern char *ts_format; extern char *statusline_ts_format; extern char *cnv_ts_format; extern conversion *conversions; extern int n_conversions; extern char *shell; extern char no_linewrap; extern char *config_file; extern char got_sigusr1; extern char filename_only; extern int default_color_scheme; extern char *nsubwindows; extern char allow_8bit; extern char **color_names; extern int n_colors_defined; extern beeb_t beep_method; extern double beep_popup_length; extern char caret_notation; extern mode_t def_umask; extern check_dir_glob *cdg; extern int n_cdg; extern char statusline_above_data; extern char *F1; extern int default_maxnlines; extern int default_maxbytes; extern int popup_refresh_interval; extern char global_marker_of_other_window; extern char markerline_char; extern char changeline_char; extern char idleline_char; extern char msgline_char; extern char *replace_by_markerline; extern char default_bufferwhat; extern color_scheme *cschemes; extern int n_cschemes; extern colorpairs cp; extern pid_t children_list[MAX_N_SPAWNED_PROCESSES]; extern int n_children; extern term_t term_type; extern regex_t global_highlight_re; extern char *global_highlight_str; extern mybool_t re_case_insensitive; extern filterscheme *pfs; extern int n_fs; extern char load_global_config; extern char default_linewrap; extern int default_line_wrap_offset; extern char default_follow_filename; extern char do_not_close_closed_windows; extern char suppress_empty_lines; extern editscheme *pes; extern int n_es; extern char splitline_mode; extern myattr_t splitline_attrs; extern int inverse_attrs; extern char abort_key; extern char exit_key; extern int beep_interval; extern int linecounter_for_beep; extern int did_n_beeps; extern char *defaultcscheme; extern char *line_ts_format; extern int default_min_shrink; extern char default_sb_showwinnr; extern int wordwrapmaxlength; extern history_t search_h; extern history_t cmdfile_h; extern int default_bg_color; extern char reuse_searchstring; extern char *sigs[]; extern int n_known_sigs; extern char need_died_procs_check; extern chtype box_bottom_left_hand_corner; extern chtype box_bottom_right_hand_corner; extern chtype box_bottom_side; extern chtype box_left_side; extern chtype box_right_side; extern chtype box_top_left_hand_corner; extern chtype box_top_right_hand_corner; extern chtype box_top_side; extern char *window_number; extern char *subwindow_number; extern int total_wakeups; extern mybool_t posix_tail; extern mybool_t resolv_ip_addresses; extern mybool_t show_severity_facility; extern char *severities[]; extern char *facilities[]; extern char *syslog_ts_format; extern char scrollback_no_colors; extern int syslog_port; extern char scrollback_search_new_window; extern char *global_find; extern mybool_t gnu_tail; void set_do_refresh(char val); char get_do_refresh(); multitail-6.0/selbox.c0000644000175000017500000002070612245202637014731 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include "mt.h" #include "term.h" #include "mem.h" #include "utils.h" #include "globals.h" /** create_subwindow_list * - in: int f_index window number * char ***swlist pointer to an array of strings * - returns: int number of elements in the array of strings * this function creates for a given window (f_index) a list of subwindows: * subwindows are created when you're merging the output of several files/ * commands */ int create_subwindow_list(int f_index, char ***swlist) { char **list = NULL; int n = 0; proginfo *cur = &pi[f_index]; int subwin = 0; do { list = (char **)myrealloc(list, (n + 1) * sizeof(char *)); if (show_subwindow_id) { int len = strlen(cur -> filename); list[n] = (char *)mymalloc(len + 1); memcpy(list[n], cur -> filename, len + 1); } else list[n] = mystrdup(cur -> filename); n++; cur = cur -> next; subwin++; } while(cur); *swlist = list; return n; } char generate_string(char *whereto, void **list, selbox_type_t type, int wcols, int index) { char invert = 0; if (type == SEL_WIN) { snprintf(whereto, wcols + 1, "%02d %s", index, shorten_filename(((proginfo *)list)[index].filename, wcols)); invert = ((proginfo *)list)[index].hidden | ((proginfo *)list)[index].paused; } else if (type == SEL_SUBWIN) snprintf(whereto, wcols + 1, "%02d %s", index, shorten_filename(((char **)list)[index], wcols)); else if (type == SEL_FILES) strncpy(whereto, ((char **)list)[index], min(strlen(((char **)list)[index]), wcols) + 1); else if (type == SEL_CSCHEME) strncpy(whereto, ((color_scheme *)list)[index].name, min(strlen(((color_scheme *)list)[index].name), wcols) + 1); else if (type == SEL_HISTORY) strncpy(whereto, ((char **)list)[index], min(strlen(((char **)list)[index]), wcols) + 1); whereto[min(strlen(whereto), wcols - 1)] = 0x00; return invert; } int find_sb_string(void **list, selbox_type_t type, int nentries, char *compare_string) { int index, len = strlen(compare_string); for(index=0; index win); if (heading) win_header(mywin, heading); else if (type == SEL_WIN) win_header(mywin, "Select window"); else if (type == SEL_SUBWIN) win_header(mywin, "Select subwindow"); else if (type == SEL_FILES) win_header(mywin, "Select file"); else if (type == SEL_CSCHEME) win_header(mywin, "Select color scheme"); else if (type == SEL_HISTORY) win_header(mywin, "Select string from history"); for(loop=0; loop win, loop + 2, 1, "*"); mvwprintw(mywin -> win, loop + 2, 2, "%s", dummy); if (invert) color_off(mywin, find_colorpair(COLOR_YELLOW, -1, 0)); if (loop == offs) ui_inverse_off(mywin); } draw_border(mywin); ppos = pos; poffs = offs; } else if (poffs != offs) { int yellow_cp = find_colorpair(COLOR_YELLOW, -1, 0); char invert = generate_string(dummy, list, type, wcols, poffs + pos); if (invert) color_on(mywin, yellow_cp); mvwprintw(mywin -> win, poffs + 2, 2, "%s", dummy); if (invert) color_off(mywin, yellow_cp); invert = generate_string(dummy, list, type, wcols, offs + pos); ui_inverse_on(mywin); if (invert) color_on(mywin, yellow_cp); if (needs_mark && needs_mark[offs + pos]) mvwprintw(mywin -> win, loop + 2, 1, "*"); mvwprintw(mywin -> win, offs + 2, 2, "%s", dummy); if (invert) color_off(mywin, yellow_cp); ui_inverse_off(mywin); poffs = offs; } if (first) { first = 0; color_on(mywin, find_colorpair(COLOR_GREEN, -1, 0)); mvwprintw(mywin -> win, total_win_size - 2, 2, "Press ^G to abort"); color_off(mywin, find_colorpair(COLOR_GREEN, -1, 0)); } else { int loop, len = strlen(selstr); for(loop=0; loop win, total_win_size - 2, 1 + loop, " "); if (!selfound) color_on(mywin, find_colorpair(COLOR_RED, -1, 0)); mvwprintw(mywin -> win, total_win_size - 2, 1, "%s", &selstr[max(0, len - wcols)]); if (!selfound) color_off(mywin, find_colorpair(COLOR_RED, -1, 0)); } mydoupdate(); c = wait_for_keypress(what_help, 0, mywin, 1); if (c == KEY_UP) { if ((offs + pos) > 0) { if (offs) offs--; else pos--; } else { wrong_key(); } } else if (c == KEY_DOWN) { if ((pos + offs) < (nlines-1)) { if (offs < (wlines-1)) offs++; else pos++; } else { wrong_key(); } } else if (c == KEY_NPAGE) { if ((pos + offs) < (nlines - 1)) { pos += min(wlines, (nlines - 1) - (pos + offs)); } else { wrong_key(); } } else if (c == KEY_PPAGE) { if ((pos + offs - wlines) >= 0) { if (pos > wlines) { pos -= wlines; } else { pos -= (wlines - offs); offs = 0; } } else if (offs > 0) { offs = 0; } else if (pos > 0) { pos = 0; } else { wrong_key(); } } else if (c == KEY_ENTER || c == 13 || c == 10) { sel = pos + offs; break; } else if (c == abort_key || c == -1) { break; } else if ((c > 31 && c != 127) || (c == KEY_BACKSPACE)) { int index, curlen; curlen = strlen(selstr); if (c == KEY_BACKSPACE) { if (curlen > 0) selstr[curlen - 1] = 0x00; else wrong_key(); } else if (curlen < path_max) { selstr[curlen] = c; selstr[curlen + 1] = 0x00; } else wrong_key(); curlen = strlen(selstr); if (curlen > 0) { index = find_sb_string(list, type, nlines, selstr); if (index != -1) { ppos = -1; sel = pos = index; selfound = 1; } else { selfound = 0; } } } else { wrong_key(); } } delete_popup(mywin); myfree(dummy); myfree(selstr); return sel; } int select_window(int what_help, char *heading) { return selection_box((void **)pi, NULL, nfd, SEL_WIN, what_help, heading); } proginfo * select_subwindow(int f_index, int what_help, char *heading) { proginfo *cur = NULL; char **list; int list_n, index, loop; if (f_index == -1) return NULL; list_n = create_subwindow_list(f_index, &list); index = selection_box((void **)list, NULL, list_n, SEL_SUBWIN, what_help, heading); if (index != -1) { cur = &pi[f_index]; for(loop=0; loop next; } } delete_array(list, list_n); return cur; } char * select_file(char *input, int what_help) { char **list = NULL, *isdir = NULL, *path = NULL; char *new_fname = NULL; struct stat64 statbuf; int list_n, index; int strbufsize = find_path_max(); list_n = match_files(input, &path, &list, &isdir); if (list_n == 0) { myfree(path); flash(); return NULL; } index = selection_box((void **)list, isdir, list_n, SEL_FILES, what_help, NULL); if (index != -1) { new_fname = (char *)mymalloc(strbufsize + 1); snprintf(new_fname, strbufsize, "%s%s", path, list[index]); if (stat64(new_fname, &statbuf) == -1) { myfree(new_fname); new_fname = NULL; flash(); } else { if (S_ISDIR(statbuf.st_mode)) { strncat(new_fname, "/", strbufsize); } } } delete_array(list, list_n); myfree(isdir); myfree(path); return new_fname; } multitail-6.0/color.c0000644000175000017500000002530412245202637014552 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include "doassert.h" #include #include #include #include #include #include #include #include "mt.h" #include "error.h" #include "mem.h" #include "term.h" #include "exec.h" #include "utils.h" #include "globals.h" #include "ui.h" color_offset_in_line *realloc_color_offset_in_line(color_offset_in_line *oldp, int n_entries) { assert(n_entries > 0); return (color_offset_in_line *)myrealloc(oldp, n_entries * sizeof(color_offset_in_line)); } void add_color_scheme(int_array_t *schemes, int cur_scheme) { assert(cur_scheme >= 0); add_to_iat(schemes, cur_scheme); } int gen_color(char *start, char *end) { char *loop; int chk = 0; assert(end != NULL); for(loop=start; loop= 16) { string = strchr(&string[16], ' '); } if (string) { char *end1, *end2, *end3, *end = NULL; while(isspace(*string)) string++; end1 = strchr(string, '['); end2 = strchr(string, ' '); end3 = strchr(string, ':'); end = end1; if ((end2 && end2 < end) || (end == NULL)) end = end2; if ((end3 && end3 < end) || (end == NULL)) end = end3; if (end) cdev.colorpair_index = gen_color(string, end); else cdev.colorpair_index = gen_color(string, &string[strlen(string)]); } } return cdev; } myattr_t gen_color_from_field(char *string, char *field_del, int field_nr) { myattr_t cdev = { -1, -1 }; assert(field_nr >= 0); if (use_colors) { int field_del_len = strlen(field_del), loop; char *dummy = NULL; for(loop=0; loop fd_w, line_buf, line_len + 1, pscript -> script); myfree(line_buf); for(;!finished;) { char *workpnt = iobuffer; int rc = READ(pscript -> fd_r, iobuffer, SCRIPT_IO_BUFFER_SIZE - 1, pscript -> script); if (rc <= 1) break; iobuffer[rc] = 0x00; for(;;) { char *komma; char *lf = strchr(workpnt, '\n'); if (!lf) break; *lf = 0x00; if (workpnt[0] == 0x00) { finished = 1; break; } if (*cur_n_cmatches == *n_cmatches) { *cur_n_cmatches = (*cur_n_cmatches + 8) * 2; *cmatches = realloc_color_offset_in_line(*cmatches, *cur_n_cmatches); } komma = strchr(workpnt, ','); if (!komma) { error_popup("Color scheme script", -1, "Malformed line returned by color selection script '%s': first ',' missing.\n", pscript -> script); break; } memset(&(*cmatches)[*n_cmatches], 0x00, sizeof(color_offset_in_line)); (*cmatches)[*n_cmatches].start = atoi(workpnt); (*cmatches)[*n_cmatches].end = atoi(komma + 1); komma = strchr(komma + 1, ','); if (!komma) { error_popup("Color scheme script", -1, "Malformed line returned by color selection script '%s': second ',' missing.\n", pscript -> script); break; } (*cmatches)[*n_cmatches].attrs = parse_attributes(komma + 1); (*n_cmatches)++; workpnt = lf + 1; } } myfree(iobuffer); } void get_colors_from_colorscheme(char *line, int_array_t *color_schemes, color_offset_in_line **cmatches, int *n_cmatches, mybool_t *has_merge_colors) { int cs_index, loop; regmatch_t colormatches[MAX_N_RE_MATCHES]; int cur_n_cmatches = 0; int len = strlen(line); *n_cmatches = 0; *cmatches = NULL; *has_merge_colors = MY_FALSE; for(loop=0; loop 0) memcpy(valstr, &line[this_start_offset], val_size); valstr[val_size] = 0x00; value = atof(valstr); myfree(valstr); if (flags == CSREFLAG_CMP_VAL_LESS && value < cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cmp_value) value_match = 1; else if (flags == CSREFLAG_CMP_VAL_BIGGER && value > cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cmp_value) value_match = 1; else if (flags == CSREFLAG_CMP_VAL_EQUAL && value == cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cmp_value) value_match = 1; if (!value_match) continue; } if (cur_n_cmatches == *n_cmatches) { cur_n_cmatches = (cur_n_cmatches + 8) * 2; *cmatches = realloc_color_offset_in_line(*cmatches, cur_n_cmatches); } memset(&(*cmatches)[*n_cmatches], 0x00, sizeof(color_offset_in_line)); (*cmatches)[*n_cmatches].start = this_start_offset; (*cmatches)[*n_cmatches].end = this_end_offset; if (cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cdef.ac_index == 0) (*cmatches)[*n_cmatches].attrs = cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cdef.attrs1; else (*cmatches)[*n_cmatches].attrs = cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cdef.attrs2; if (cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].merge_color == MY_TRUE) *has_merge_colors = MY_TRUE; (*cmatches)[*n_cmatches].merge_color = cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].merge_color; (*n_cmatches)++; } /* iterate all substringmatches or just the first which is the globl one */ /* in case we have alternating colors, alternate them */ if (cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cdef.use_alternating_colors == MY_TRUE) { cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cdef.ac_index = 1 - cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cdef.ac_index; } } while(offset < len); /* do the whole line */ } /* go through all lines of the current colorscheme */ } /* go through all colorschemes for this (sub-)window */ } myattr_t choose_color(char *string, proginfo *cur, color_offset_in_line **cmatches, int *n_cmatches, mybool_t *has_merge_colors, char **new_string) { myattr_t cdev = { -1, -1 }; *new_string = NULL; if (cur -> cdef.term_emul != TERM_IGNORE) { *new_string = emulate_terminal(string, cmatches, n_cmatches); return find_attr(COLOR_WHITE, -1, -1); } /* find color */ switch(cur -> cdef.colorize) { case 'i': cdev = cur -> cdef.attributes; break; case 'a': /* alternating colors */ if (string[0] != 0x00) { if (cur -> cdef.alt_col) cdev = cur -> cdef.alt_col_cdev1; else cdev = cur -> cdef.alt_col_cdev2; cur -> cdef.alt_col = !cur -> cdef.alt_col; } break; case 's': /* use the program name for the color */ cdev = gen_syslog_progname_color(string); break; case 'f': /* user selected field for coloring */ cdev = gen_color_from_field(string, cur -> cdef.field_del, cur -> cdef.field_nr); break; case 'm': /* complete string */ if (use_colors) cdev.colorpair_index = gen_color(string, &string[strlen(string)]); break; case 'S': /* colorscheme or colorscripts */ get_colors_from_colorscheme(string, &cur -> cdef.color_schemes, cmatches, n_cmatches, has_merge_colors); break; default: assert(0); break; } if (cdev.colorpair_index == -1) cdev.colorpair_index = 0; if (cdev.attrs == -1) cdev.attrs = A_NORMAL; if (cur -> cdef.syslog_noreverse) cdev.colorpair_index %= DEFAULT_COLORPAIRS; return cdev; } int find_colorscheme(char *name) { int loop; for(loop=0; loop #include #include #include #include #include #include #include #include #include #include #include #include #if defined(sun) || defined(__sun) #include #endif #ifndef AIX #include /* needed on Solaris 8 */ #endif #include #include #include "mt.h" #include "error.h" #include "mem.h" #include "term.h" #include "color.h" #include "utils.h" #include "help.h" #include "globals.h" #include "history.h" #include "ui.h" void wrong_key(void) { if (beep_method == BEEP_FLASH) flash(); else if (beep_method == BEEP_BEEP) beep(); else if (beep_method == BEEP_POPUP) { NEWWIN *beep_win = create_popup(5, 9); color_on(beep_win, find_colorpair(COLOR_GREEN, -1, 0)); mvwprintw(beep_win -> win, 3, 2, "Beep!"); color_off(beep_win, find_colorpair(COLOR_GREEN, -1, 0)); (void)wait_for_keypress(-1, beep_popup_length, beep_win, 0); delete_popup(beep_win); } else if (beep_method == BEEP_NONE) { /* do nothing */ } flushinp(); } void draw_border(NEWWIN *mywin) { wborder(mywin -> win, box_left_side, box_right_side, box_top_side, box_bottom_side, box_top_left_hand_corner, box_top_right_hand_corner, box_bottom_left_hand_corner, box_bottom_right_hand_corner); } int ask_yes_no(int what_help, NEWWIN *popup) { for(;;) { int c = toupper(wait_for_keypress(what_help, 0, popup, 0)); if (c == abort_key) return -1; switch(c) { case 'Y': case 'J': return 1; case 'N': return 0; case 'Q': return -1; } wrong_key(); } } void color_on(NEWWIN *win, int colorpair_index) { if (use_colors && colorpair_index != -1) wattron(win -> win, COLOR_PAIR(colorpair_index)); } void color_off(NEWWIN *win, int colorpair_index) { if (use_colors && colorpair_index != -1) wattroff(win -> win, COLOR_PAIR(colorpair_index)); } void myattr_on(NEWWIN *win, myattr_t attrs) { color_on(win, attrs.colorpair_index); if (attrs.attrs != -1) wattron(win -> win, attrs.attrs); } void myattr_off(NEWWIN *win, myattr_t attrs) { color_off(win, attrs.colorpair_index); if (attrs.attrs != -1) wattroff(win -> win, attrs.attrs); } void ui_inverse_on(NEWWIN *win) { wattron(win -> win, A_REVERSE); } void ui_inverse_off(NEWWIN *win) { wattroff(win -> win, A_REVERSE); } void draw_line(NEWWIN *win, linepos_t where) { int mx = getmaxx(win -> win), my = getmaxy(win -> win); if (where == LINE_LEFT) mvwvline(win -> win, 0, 0, ' ', my); else if (where == LINE_RIGHT) mvwvline(win -> win, 0, mx-1, ' ', my); else if (where == LINE_TOP) mvwhline(win -> win, 0, 0, ' ', mx); else if (where == LINE_BOTTOM) mvwhline(win -> win, my-1, 0, ' ', mx); } char * edit_string(NEWWIN *win, int win_y, int win_x, int win_width, int max_width, char numbers_only, char *input_string, int what_help, char first_char, history_t *ph, mybool_t *pcase_insensitive) { char *string = (char *)mymalloc(max_width + 1); int str_pos = 0, x = 0; int line_width = win_width; if (pcase_insensitive) mvwprintw(win -> win, win_y + 1, win_x - 1, "[%c] case insensitive (press TAB)", *pcase_insensitive?'X':' '); if (input_string) { int input_string_len = strlen(input_string), copy_len = min(min(win_width, max_width), input_string_len); int dummy = max(0, str_pos - line_width); memcpy(string, input_string, copy_len); string[copy_len] = 0x00; str_pos = dummy; mvwprintw(win -> win, win_y, win_x, &string[dummy]); x = strlen(string) - dummy; } else { string[0] = 0x00; } wmove(win -> win, win_y, win_x + x); mydoupdate(); for(;;) { char force_redraw = 0; int prev_str_pos = str_pos; int c; if (first_char != (char)-1) { c = first_char; first_char = -1; } else { c = wait_for_keypress(what_help, 0, NULL, 1); } /* confirm */ if (c == KEY_ENTER || c == 13 || c == 10 ) break; /* abort */ if (c == abort_key || c == 17 || c == 24) /* ^g / ^q / ^x */ { string[0] = 0x00; break; } switch(c) { case 1: /* ^A */ str_pos = x = 0; break; case 5: /* ^E */ { int dummy = strlen(string); if (dummy > line_width) { str_pos = dummy - (line_width / 2); x = (line_width / 2); } else { str_pos = 0; x = dummy; } } break; case 9: /* tab (filename completion/switch to case insensitve field) */ if (pcase_insensitive) { ask_case_insensitive(pcase_insensitive); force_redraw = 1; } else if (numbers_only) { wrong_key(); } else { int dummy; char *file = select_file(string, -1); if (file) { strncpy(string, file, max_width); string[max_width] = 0x00; myfree(file); } dummy = strlen(string); if (dummy > line_width) { str_pos = dummy - (line_width / 2); x = (line_width / 2); } else { str_pos = 0; x = dummy; } force_redraw = 1; } break; case 21: /* ^U */ string[0] = 0x00; str_pos = x = 0; force_redraw = 1; break; case 23: /* ^W delete word */ { int spos = str_pos + x; int dpos = spos; /* remove spaces upto the first word */ while(dpos > 0 && string[dpos] == ' ') dpos --; /* remove that word we found */ while(dpos > 0 && string[dpos] != ' ') dpos--; memmove(&string[dpos], &string[spos], (max_width - spos) + 1); str_pos = max(0, dpos - (line_width / 2)); x = dpos - str_pos; force_redraw = 1; } break; case 127: /* DEL */ case 4: /* ^D */ { int spos = str_pos + x; int n_after = strlen(&string[spos]); if (n_after > 0) { memmove(&string[spos], &string[spos + 1], n_after); force_redraw = 1; } } break; case KEY_DOWN: /* cursor down */ case 18: /* ^R */ if (ph == NULL || ph -> history_size <= 0 || ph -> history_file == NULL) { wrong_key(); } else { int dummy; char *hs = search_history(ph, string); if (hs) { strncpy(string, hs, max_width); string[max_width] = 0x00; myfree(hs); } dummy = strlen(string); if (dummy > line_width) { str_pos = dummy - (line_width / 2); x = (line_width / 2); } else { str_pos = 0; x = dummy; } force_redraw = 1; } break; case KEY_BACKSPACE: { int spos = str_pos + x; if (spos > 0) { memmove(&string[spos - 1], &string[spos], (max_width - spos) + 1); if (x > 0) { x--; } else { str_pos--; } force_redraw = 1; } } break; case KEY_LEFT: if (x > 0) { x--; } else if (str_pos > 0) { str_pos--; } break; case KEY_RIGHT: if ((x + str_pos) < strlen(string)) { if (x < line_width) x++; else str_pos++; } else { wrong_key(); } break; default: { int len = strlen(string); /* only allow valid ASCII */ if (c < 32) { wrong_key(); break; } if (numbers_only && (c < '0' || c > '9')) { wrong_key(); break; } if (len == max_width) { wrong_key(); break; } /* cursor at end of string? */ if (str_pos == len) { string[str_pos + x] = c; string[str_pos + x + 1] = 0x00; waddch(win -> win, c); } else /* add character to somewhere IN the string */ { memmove(&string[str_pos + x + 1], &string[str_pos + x], strlen(&string[str_pos + x]) + 1); string[str_pos + x] = c; force_redraw = 1; } if ((x + str_pos) < max_width) { if (x < line_width) x++; else str_pos++; } else { wrong_key(); } } break; } if (str_pos != prev_str_pos || force_redraw) { int loop; char *dummy = mystrdup(&string[str_pos]); dummy[min(strlen(dummy), line_width)] = 0x00; for(loop=strlen(dummy); loop win, win_y, win_x + loop, " "); mvwprintw(win -> win, win_y, win_x, "%s", dummy); myfree(dummy); if (pcase_insensitive) mvwprintw(win -> win, win_y + 1, win_x, "%c", *pcase_insensitive?'X':' '); force_redraw = 0; } wmove(win -> win, win_y, win_x + x); mydoupdate(); } if (string[0] == 0x00) { myfree(string); string = NULL; } else if (ph != NULL) { history_add(ph, string); } return string; } NEWWIN * create_popup(int n_lines, int n_colls) { NEWWIN *newwin; int ocols = (max_x/2) - (n_colls/2); int olines = (max_y/2) - (n_lines/2); /* create new window */ newwin = mynewwin(n_lines, n_colls, olines, ocols); werase(newwin -> win); draw_border(newwin); show_panel(newwin -> pwin); return newwin; } void delete_popup(NEWWIN *mywin) { if (mywin) { mydelwin(mywin); update_panels(); doupdate(); myfree(mywin); } } void mydelwin(NEWWIN *win) { bottom_panel(win -> pwin); if (ERR == del_panel(win -> pwin)) error_exit("del_panel() failed\n"); if (ERR == delwin(win -> win)) error_exit("delwin() failed\n"); } void mydoupdate() { update_panels(); doupdate(); } NEWWIN * mynewwin(int nlines, int ncols, int begin_y, int begin_x) { NEWWIN *nwin = (NEWWIN *)mymalloc(sizeof(NEWWIN)); /* nwin -> win = subwin(stdscr, nlines, ncols, begin_y, begin_x); */ nwin -> win = newwin(nlines, ncols, begin_y, begin_x); if (!nwin -> win) error_exit("Failed to create window with dimensions %dx%d at offset %d,%d (terminal size: %d,%d)\n", ncols, nlines, begin_x, begin_y, COLS, LINES); nwin -> pwin = new_panel(nwin -> win); if (!nwin -> pwin) error_exit("Failed to create panel.\n"); nwin -> x_off = begin_x; nwin -> y_off = begin_y; nwin -> width = ncols; nwin -> height = nlines; if (bright_colors) wattron(nwin -> win, A_BOLD); if (default_bg_color != -1) wbkgdset(nwin -> win, COLOR_PAIR(find_or_init_colorpair(-1, default_bg_color, 1))); return nwin; } void escape_print(NEWWIN *win, int y, int x, char *str) { int loop, index = 0, len = strlen(str); char inv = 0, ul = 0, bold = 0; for(loop=0; loop win, y, x + index++, "^"); loop++; } else { if (!inv) ui_inverse_on(win); else ui_inverse_off(win); inv = 1 - inv; } } else if (str[loop] == '_') { if (str[loop + 1] == '_') /* __ is _ */ { /* just print a _ */ mvwprintw(win -> win, y, x + index++, "_"); loop++; } else { if (!ul) wattron(win -> win, A_UNDERLINE); else wattroff(win -> win, A_UNDERLINE); ul = 1 - ul; } } else if (str[loop] == '*') { if (str[loop + 1] == '*') { /* just print a * */ mvwprintw(win -> win, y, x + index++, "*"); loop++; } else { if (!bold) wattron(win -> win, A_BOLD); else wattroff(win -> win, A_BOLD); bold = 1 - bold; } } else { mvwprintw(win -> win, y, x + index++, "%c", str[loop]); } } if (inv) ui_inverse_off(win); if (ul) wattroff(win -> win, A_UNDERLINE); if (bold) wattroff(win -> win, A_BOLD); } void win_header(NEWWIN *win, char *str) { wattron(win -> win, A_BOLD); mvwprintw(win -> win, 1, 2, str); wattroff(win -> win, A_BOLD); } void gui_window_header(char *string) { if (term_type == TERM_XTERM) { fprintf(stderr, "\033]2;%s\007", string); #if 0 /* this code gives problems */ /* \033]0;%s\007 */ putp("\033]0;"); putp(string); putp("\007"); #endif } } int find_colorpair(int fgcolor, int bgcolor, char fuzzy) { int loop; for(loop=0; loop=0; loop++) { if (cp.fg_color[loop] == fgcolor && cp.bg_color[loop] == -1) return loop; else if (cp.fg_color[loop] == -1 && cp.bg_color[loop] == bgcolor) return loop; } } return -1; } myattr_t find_attr(int fgcolor, int bgcolor, int attrs) { myattr_t cdev; if (attrs == -1) attrs = A_NORMAL; cdev.attrs = attrs; cdev.colorpair_index = find_colorpair(fgcolor, bgcolor, 0); return cdev; } /* ignore errors when doing TERM-emulation */ int find_or_init_colorpair(int fgcolor, int bgcolor, char ignore_errors) { int index; if (use_colors) { index = find_colorpair(fgcolor, bgcolor, 0); if (index != -1) return index; if (cp.n_def == cp.size && cp.size > 0) { if (ignore_errors) { index = find_colorpair(fgcolor, bgcolor, 1); if (index != -1) return index; return 0; } error_exit("Too many (%d) colorpairs defined.\n", cp.n_def); } cp.fg_color[cp.n_def] = fgcolor; cp.bg_color[cp.n_def] = bgcolor; init_pair(cp.n_def, cp.fg_color[cp.n_def], cp.bg_color[cp.n_def]); cp.n_def++; return cp.n_def - 1; } return 0; } int colorstr_to_nr(char *str) { int loop; if (str[0] == 0x00) return -1; for(loop=0; loop= COLORS) return "???"; return color_names[nr]; } void attr_to_str_helper(char *to, char *what) { int len = strlen(to); if (len) sprintf(&to[len], "/%s", what); else sprintf(&to[len], "%s", what); } char *attr_to_str(int attr) { char buffer[128] = { 0 }; if (attr & A_BOLD) attr_to_str_helper(buffer, "bold"); if (attr & A_BLINK) attr_to_str_helper(buffer, "blink"); if (attr & A_REVERSE) attr_to_str_helper(buffer, "inverse"); if (attr & A_UNDERLINE) attr_to_str_helper(buffer, "underline"); if (attr & A_DIM) attr_to_str_helper(buffer, "dim"); return mystrdup(buffer); } void determine_terminal_size(int *max_y, int *max_x) { struct winsize size; *max_x = *max_y = 0; /* changed from 'STDIN_FILENO' as that is incorrect: we're * outputting to stdout! */ if (ioctl(1, TIOCGWINSZ, &size) == 0) { *max_y = size.ws_row; *max_x = size.ws_col; } if (!*max_x || !*max_y) { char *dummy = getenv("COLUMNS"); if (dummy) *max_x = atoi(dummy); else *max_x = 80; dummy = getenv("LINES"); if (dummy) *max_x = atoi(dummy); else *max_x = 24; } } int ansi_code_to_color(int value) { switch(value) { case 30: return COLOR_BLACK; case 31: return COLOR_RED; case 32: return COLOR_GREEN; case 33: return COLOR_YELLOW; case 34: return COLOR_BLUE; case 35: return COLOR_MAGENTA; case 36: return COLOR_CYAN; case 37: return COLOR_WHITE; } return -1; } char * emulate_terminal(char *string, color_offset_in_line **cmatches, int *n_cmatches) { int len = strlen(string), new_offset = 0, loop; char *new_string = (char *)mymalloc(len + 1); int cur_n_cmatches = 0; *n_cmatches = 0; *cmatches = NULL; /* FIXME: this is ANSI/vt100-only */ for(loop=0; loop= 'a' && cur_char <= 'z') break; } if (string[find_cmd] == 'm') { int fg_i = -1, bg_i = -1, attr = 0; char *p = &string[loop + 2]; string[find_cmd] = 0x00; while(p) { int dummy; char *newp = strchr(p, ';'); if (newp) *newp = 0x00; dummy = atoi(p); if (dummy >= 40 && dummy <= 47) bg_i = ansi_code_to_color(dummy - 10); else if (dummy >= 30 && dummy <= 37) fg_i = ansi_code_to_color(dummy); else if (dummy == 1) attr = A_BOLD; else if (dummy == 4) attr = A_UNDERLINE; else if (dummy == 5) attr = A_BLINK; else if (dummy == 7) attr = A_REVERSE; p = newp; if (p) p++; } ci.colorpair_index = find_or_init_colorpair(fg_i, bg_i, 1); ci.attrs = attr; string[find_cmd] = 'm'; } loop = find_cmd; } if (ci.colorpair_index != -1) { if (cur_n_cmatches == *n_cmatches) { cur_n_cmatches = (cur_n_cmatches + 8) * 2; *cmatches = realloc_color_offset_in_line(*cmatches, cur_n_cmatches); } memset(&(*cmatches)[*n_cmatches], 0x00, sizeof(color_offset_in_line)); (*cmatches)[*n_cmatches].start = new_offset; (*cmatches)[*n_cmatches].end = -1; (*cmatches)[*n_cmatches].attrs = ci; (*n_cmatches)++; } } else { new_string[new_offset++] = string[loop]; } } for(loop=0; loop<(*n_cmatches - 1); loop++) (*cmatches)[loop].end = (*cmatches)[loop + 1].start; if (*n_cmatches >= 1) (*cmatches)[*n_cmatches - 1].end = new_offset; new_string[new_offset] = 0x00; return new_string; } void get_terminal_type(void) { char *dummy = getenv("TERM"); if (dummy && strstr(dummy, "xterm") != NULL) { term_type = TERM_XTERM; } } void init_ncurses(void) { initscr(); if (use_colors) start_color(); /* don't care if this one failes */ keypad(stdscr, TRUE); cbreak(); intrflush(stdscr, FALSE); noecho(); nonl(); refresh(); nodelay(stdscr, TRUE); meta(stdscr, TRUE); /* enable 8-bit input */ idlok(stdscr, TRUE); /* may give a little clunky screenredraw */ idcok(stdscr, TRUE); /* may give a little clunky screenredraw */ leaveok(stdscr, FALSE); max_y = LINES; max_x = COLS; } void init_colornames(void) { int loop, dummy; dummy = min(256, COLORS); if (use_colors) { color_names = (char **)mymalloc(dummy * sizeof(char *)); memset(color_names, 0x00, dummy * sizeof(char *)); color_names[COLOR_RED] = "red"; color_names[COLOR_GREEN] = "green"; color_names[COLOR_YELLOW] = "yellow"; color_names[COLOR_BLUE] = "blue"; color_names[COLOR_MAGENTA]= "magenta"; color_names[COLOR_CYAN] = "cyan"; color_names[COLOR_WHITE] = "white"; color_names[COLOR_BLACK] = "black"; } /* FIXME: is this needed? or are COLOR_* always at position 0...7? */ for(loop=dummy - 1; loop>=0; loop--) { if (color_names[loop]) { n_colors_defined = loop + 1; break; } } } multitail-6.0/error.c0000644000175000017500000000337612245202637014572 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include #if defined(__GLIBC__) #include #endif #include #include #include "mt.h" #include "utils.h" #include "version.h" void error_exit_(char *file, const char *function, int line, char *format, ...) { int e = errno; va_list ap; #if defined(__GLIBC__) int index; void *trace[128]; int trace_size = backtrace(trace, 128); char **messages = backtrace_symbols(trace, trace_size); #endif (void)endwin(); fprintf(stderr, version_str, VERSION); fprintf(stderr, "\n\n"); fprintf(stderr, "The following error occured:\n"); fprintf(stderr, "---------------------------\n"); va_start(ap, format); (void)vfprintf(stderr, format, ap); va_end(ap); fprintf(stderr, "\n"); fprintf(stderr, "\n"); fprintf(stderr, "If this is a bug, please report the following information:\n"); fprintf(stderr, "---------------------------------------------------------\n"); fprintf(stderr, "This problem occured at line %d in function %s (from file %s):\n", line, function, file); if (e) fprintf(stderr, "errno variable was then: %d which means \"%s\"\n", e, strerror(e)); fprintf(stderr, "Binary build at %s %s\n", __DATE__, __TIME__); #if defined(__GLIBC__) fprintf(stderr, "Execution path:\n"); for(index=0; index #include #include #include #include #include #include #include #include #include "mt.h" #include "error.h" #include "utils.h" #include "mem.h" #include "selbox.h" #include "ui.h" #include "help.h" #include "globals.h" void init_history(history_t *ph) { } void load_history(history_t *ph) { if (ph -> history == NULL) { int array_in_bytes = sizeof(char *) * ph -> history_size; ph -> history = (char **)mymalloc(array_in_bytes); memset(ph -> history, 0x00, array_in_bytes); if (file_exist(ph -> history_file) == 0) { int loop; FILE *fh = fopen(ph -> history_file, "r"); if (!fh) error_popup("Load history", -1, "Failed to open history file %s.\n", ph -> history_file); else { for(loop=0; loop history_size; loop++) { char *lf; char buffer[HISTORY_IO_BUFFER]; if (!fgets(buffer, sizeof(buffer), fh)) break; lf = strchr(buffer, '\n'); if (lf) *lf = 0x00; (ph -> history)[loop] = mystrdup(buffer); } fclose(fh); } } } } int history_find_null_entry(history_t *ph) { int loop; for(loop=0; loop history_size; loop++) { if (!(ph -> history)[loop]) { return loop; } } return -1; } void save_history(history_t *ph) { int loop; int last_entry = history_find_null_entry(ph); int n_entries = last_entry == -1 ? ph -> history_size : last_entry; FILE *fh = fopen(ph -> history_file, "w+"); if (!fh) { error_popup("Error saving history", -1, "Error creating/opening file %s: %s", ph -> history_file, strerror(errno)); return; } for(loop=0; loop history)[loop]); } fclose(fh); } void history_add(history_t *ph, char *string) { if (ph -> history_size > 0) { int loop; int found = -1; load_history(ph); /* bail out if this string is already in the history */ for(loop=0; loop history_size; loop++) { if ((ph -> history)[loop] != NULL && strcmp((ph -> history)[loop], string) == 0) return ; } /* find free spot */ found = history_find_null_entry(ph); /* when no free spot was found, free-up an entry */ if (found == -1) { myfree((ph -> history)[ph -> history_size - 1]); memmove(&(ph -> history)[1], &(ph -> history)[0], sizeof(char *) * (ph -> history_size - 1)); found = 0; } (ph -> history)[found] = mystrdup(string); save_history(ph); } } char * search_history(history_t *ph, char *search_string) { if (ph -> history_size > 0) { int n_entries, free_entry; load_history(ph); free_entry = history_find_null_entry(ph); n_entries = free_entry == -1 ? ph -> history_size : free_entry; if (n_entries > 0) { int sel_index = selection_box((void **)ph -> history, NULL, n_entries, SEL_HISTORY, HELP_HISTORY, NULL); if (sel_index >= 0) return mystrdup((ph -> history)[sel_index]); } else error_popup("Search history", -1, "The history list is empty."); } return NULL; } multitail-6.0/scrollback.h0000644000175000017500000000060612245202637015556 0ustar folkertfolkertint find_string(int window, char *find, int offset); void scrollback_help(void); void scrollback_savefile(int window); int get_lines_needed(char *string, int terminal_width); void scrollback_displayline(NEWWIN *win, int window, int offset, int terminal_offset); void scrollback(void); void delete_mark(void); void merged_scrollback_with_search(char *search_for, mybool_t case_insensitive); multitail-6.0/makefile.solaris_gcc0000644000175000017500000000524412245202637017260 0ustar folkertfolkertinclude version DESTDIR=/ CONFIG_FILE=$(DESTDIR)/etc/multitail.conf CC=gcc DEBUG=#-g -D_DEBUG #-pg #-fprofile-arcs CFLAGS=${EXTRA_CFLAGS} -O2 -I/usr/local/include/ -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" LDFLAGS=${EXTRA_LDFLAGS} -L/usr/local/lib/ -lsocket -lpanel -lncurses -lnsl -lm $(DEBUG) OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o color.o stripstring.o selbox.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail install: multitail /usr/sbin/install -m 0755 -u root -g sys -d $(DESTDIR)/usr /usr/sbin/install -m 0755 -u root -g bin -d $(DESTDIR)/usr/bin cp multitail $(DESTDIR)/usr/bin /usr/sbin/install -m 0755 -u root -g sys -d $(DESTDIR)/usr/share /usr/sbin/install -m 0755 -u root -g bin -d $(DESTDIR)/usr/share/man /usr/sbin/install -m 0755 -u root -g bin -d $(DESTDIR)/usr/share/man/man1 cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # /usr/sbin/install -m 0755 -u root -g sys -d $(DESTDIR)/etc cp multitail.conf $(DESTDIR)/etc/multitail.conf.new /usr/sbin/install -m 0755 -u root -g other -d $(DESTDIR)/usr/share/doc /usr/sbin/install -m 0755 -u root -g other -d $(DESTDIR)/usr/share/doc/multitail-$(VERSION) #mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php solarisbinpackage: multitail rm -rf usr etc mkdir -p etc cp multitail.conf etc mkdir -p usr/bin mkdir -p usr/share/man/man1 mkdir -p usr/share/doc/multitail-$(VERSION) cp multitail usr/bin cp multitail.1 usr/share/man/man1 cp *.txt INSTALL manual.html usr/share/doc/multitail-$(VERSION) tar cvf multitail-$(VERSION)-solaris.tar usr etc rm -rf usr etc gzip -9 multitail-$(VERSION)-solaris.tar uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/man/man1/multitail.1.gz rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-6.0/ui.h0000644000175000017500000000336012245202637014054 0ustar folkertfolkertint swap_window(void); int delete_window(void); int hide_window(void); char ask_negate_regexp(NEWWIN *win, int line); int select_schemes(void *schemes_in, int n_schemes_in, scheme_t tscheme, int_array_t *schemes_out); char ask_colors(NEWWIN *win, int line, char cur, char *fieldnr, char **fielddel, int_array_t *color_schemes, myattr_t *attrs); char ask_regex_type(NEWWIN *win, int line); int add_window(void); int toggle_colors(void); int edit_regexp(void); int toggle_vertical_split(void); void do_pause(void); void set_buffering(void); void list_keybindings(void); void write_script(void); int set_window_widths(void); int set_window_sizes(void); void selective_pause(); void wipe_window(void); void wipe_all_windows(void); void do_terminal(char c); void set_linewrap(void); void horizontal_scroll(int d); void terminal_mode(void); void regexp_error_popup(int rc, regex_t *pre); void draw_gui_window_header(proginfo *last_changed_window); void send_signal(void); void screendump_do(WINDOW *win); void screendump(void); void truncate_file(void); void edit_colors(int index); int color_management(myattr_t *org, myattr_t *new); int hide_all_but(void); int unhide_all_windows(void); void error_popup(char *title, int help, char *message, ...); void add_markerline(int index, proginfo *cur, proginfo *type, char *text); void draw_marker_line(NEWWIN *win, char *string, proginfo *marker_type); void search_in_all_windows(void); void highlight_in_all_windows(void); void toggle_regexp_case_insensitive(void); void restart_window(void); void inverse_on(NEWWIN *win); void inverse_off(NEWWIN *win); void select_conversionschemes(void); void toggle_subwindow_nr(void); void clear_a_buffer(void); void clear_all_buffers(void); void ask_case_insensitive(mybool_t *pcase_insensitive); multitail-6.0/cmdline.h0000644000175000017500000000005512245202637015050 0ustar folkertfolkertvoid do_commandline(int argc, char *argv[]); multitail-6.0/makefile.tru640000644000175000017500000000323712245202637015754 0ustar folkertfolkertinclude version CONFIG_FILE=$(DESTDIR)/etc/multitail.conf LDFLAGS=-lutil -lm -lpanel -lncurses CFLAGS=-Ae -DOSF1 -O -DVERSION=\"$(VERSION)\" -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail install: multitail cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # cp multitail.conf $(DESTDIR)/etc/multitail.conf.new mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) rm -f $(DESTDIR)/usr/man/man1/multitail.1.gz gzip -9 $(DESTDIR)/usr/man/man1/multitail.1 # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/man/man1/multitail.1.gz rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-6.0/history.h0000644000175000017500000000021412245202637015133 0ustar folkertfolkertvoid init_history(history_t *ph); void history_add(history_t *ph, char *string); char * search_history(history_t *ph, char *search_string); multitail-6.0/mt.h0000644000175000017500000002462612245202637014067 0ustar folkertfolkert#define WAIT_FOR_FILE_DELAY (250) /* sleep when waiting for a file to become available */ #define MAX_N_RE_MATCHES (80) /* max. number of regex matches: used for coloring matches */ #define MIN_N_BUFFERLINES (100) /* number of lines to buffer at minimum */ #define MAX_N_SPAWNED_PROCESSES (16) /* max. nr. of processes executed by matching regexps */ #define MAX_N_COLUMNS (15) /* max number of columns */ #define DEFAULT_TAB_WIDTH (4) #define DEFAULT_COLORPAIRS (8) #define MAX_BACKTRACE_LENGTH (256) #define SCRIPT_IO_BUFFER_SIZE (4096) #define CONFIG_READ_BUFFER (4096) #define HISTORY_IO_BUFFER (4096) #define TIMESTAMP_EXTEND_BUFFER (1024) #define SL_REGULAR (1) #define SL_NONE (0) #define SL_ATTR (2) #define LOADAVG_STR_LEN (20) #define AMOUNT_STR_LEN (3 + 2 + 1) #ifndef __GNUC__ #define __PRETTY_FUNCTION__ "(unknown)" #define USE_IF_SET(x, y) ((x)?(x):(y)) #else #define USE_IF_SET(x, y) ((x)?:(y)) #endif typedef enum { MY_FALSE = 0, MY_TRUE = 1 } mybool_t; typedef enum { VAL_ZERO_POSITIVE = 1, VAL_POSITIVE_NOT_1, VAL_POSITIVE } valcheck_t; #define M_KB (1024) #define M_MB (M_KB * 1024) #define M_GB (M_MB * 1024) typedef enum { BEEP_FLASH = 1, BEEP_BEEP, BEEP_POPUP, BEEP_NONE } beeb_t; typedef enum { LINE_LEFT = 1, LINE_RIGHT, LINE_TOP, LINE_BOTTOM } linepos_t; typedef enum { TERM_IGNORE = 0, TERM_XTERM, TERM_ANSI /* or vt100 */} term_t; typedef enum { SCHEME_TYPE_EDIT = 0, SCHEME_TYPE_FILTER } filter_edit_scheme_t; #ifndef _BSD_SOURCE #define _BSD_SOURCE /* don't worry: it'll still work if you don't have a BSD system */ #endif #ifndef __USE_BSD #define __USE_BSD /* manpage says _BSD_SOURCE, stdlib.h says __USE_BSD */ #endif #ifdef UTF8_SUPPORT #include #include #else #if defined(sun) || defined(__sun) || defined(scoos) || defined(_HPUX_SOURCE) || defined(AIX) || defined(__CYGWIN__) #include #include #else #include #include #endif #endif /* it seems the default HP-UX c-compiler doesn't understand atoll and * strtoll while it does understand 'long long' */ #if defined(_HPUX_SOURCE) || defined(__APPLE__) #ifndef atoll #define atoll(x) atol(x) #endif #endif #ifndef strtoll #define strtoll(x, y, z) strtol(x, y, z) #endif #ifndef atoll #define atoll(a) strtoll((a), (char **)NULL, 10) #endif /* Tru64 workaround */ #if defined(OSF1) #undef getmaxyx #define getmaxyx(w,y,x) y = w->_maxy; x = w->_maxx #endif #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__minix) #define off64_t off_t #define stat64 stat #define open64 open #endif #define MARKER_REGULAR (NULL) #define MARKER_CHANGE ((proginfo *)-1) #define MARKER_IDLE ((proginfo *)-2) #define MARKER_MSG ((proginfo *)-3) #define IS_MARKERLINE(x) ((x) == MARKER_REGULAR || (x) == MARKER_CHANGE || (x) == MARKER_IDLE || (x) == MARKER_MSG) typedef enum { SEL_WIN = 1, SEL_SUBWIN, SEL_FILES, SEL_CSCHEME, SEL_HISTORY } selbox_type_t; typedef enum { TT_ATIME = 1, TT_MTIME, TT_CTIME } time_field_t; typedef double dtime_t; typedef struct { char *history_file; int history_size; char **history; } history_t; typedef struct { int *elements; int n; int size; } int_array_t; typedef struct { char *glob_str; int check_interval; dtime_t last_check; time_field_t new_only; char in_one_window; int window_nr; /* if 'in_one_window' is set, merge into the window 'window_nr' */ } check_dir_glob; typedef struct { WINDOW *win; PANEL *pwin; int x_off, y_off; int width, height; } NEWWIN; typedef struct { char *regex_str; regex_t regex; char invert_regex; char use_regex; int match_count; /* command to run if matches */ char *cmd; } re; typedef struct { char *fs_name; char *fs_desc; int n_re; re *pre; } filterscheme; typedef enum { STRIP_TYPE_REGEXP = 1, STRIP_TYPE_RANGE, STRIP_TYPE_COLUMN, STRIP_KEEP_SUBSTR } striptype_t; typedef struct { striptype_t type; regex_t regex; char *regex_str; int start, end; int col_nr; char *del; int match_count; } strip_t; typedef struct { char *es_name; char *es_desc; int n_strips; strip_t *strips; } editscheme; #define MAX_COLORS_PER_LINE (80) typedef struct { int *fg_color; int *bg_color; int size; /* COLOR_PAIRS */ int n_def; /* n defined, at least 1 as color_pair(0) is the default * terminal colors (white on black mostly) which also * cannot be changed */ } colorpairs; typedef struct { int colorpair_index; int attrs; } myattr_t; typedef enum { REDIRECTTO_NONE = 0, REDIRECTTO_PIPE_FILTERED = 1, REDIRECTTO_PIPE, REDIRECTTO_FILE_FILTERED, REDIRECTTO_FILE, REDIRECTTO_SOCKET_FILTERED, REDIRECTTO_SOCKET } redirecttype_t; typedef struct { char *redirect; redirecttype_t type; int fd; pid_t pid; struct sockaddr_in sai; int prio_fac; } redirect_t; typedef struct { dtime_t lastevent; double prev_deltat, total_deltat; double med; double dev; char sccfirst; double scc, sccu0, scclast, scct1, scct2, scct3; int n_events; dtime_t start_ts; long long int bytes_processed; } statistics_t; typedef struct { char colorize; char field_nr; char *field_del; int_array_t color_schemes; myattr_t attributes; char alt_col; myattr_t alt_col_cdev1, alt_col_cdev2; char syslog_noreverse; term_t term_emul; } cdef_t; typedef struct { char **bcur, **bprev; int ncur, nprev; } diff_t; typedef struct { int restart; char restart_clear; /* clear after each iteration? */ char is_restarted; char first; char do_diff; diff_t diff; } restart_t; typedef struct { char suppress_repeating_lines; char *last_line; int n_times_repeated; } repeatinglines_t; typedef enum { WT_FILE=1, WT_COMMAND, WT_STDIN, WT_SOCKET } windowtype_t; typedef struct _subwindow_ { char *filename; windowtype_t wt; int last_exit_rc; int n_runs; int check_interval; int fd; /* read */ int wfd; /* write */ pid_t pid; int n_redirect; redirect_t *predir; off64_t last_size; char cont; /* "re-connect" lines with \ at the end */ char add_timestamp; char *label; /* put in front of each line */ int_array_t conversions; char paused; char closed; char *incomplete_line; char *win_title; char line_wrap; int line_wrap_offset; int win_height; /* repeatingly start a program */ restart_t restart; int initial_n_lines_tail; int mark_interval; repeatinglines_t repeat; cdef_t cdef; char hidden; char follow_filename; char retry_open; int close_idle; statistics_t statistics; struct { int beep_interval; int linecounter_for_beep; int did_n_beeps; } beep; NEWWIN *status; NEWWIN *data; int n_re; re *pre; int n_strip; strip_t *pstrip; struct _subwindow_ *next; } proginfo; typedef struct { char *Bline; proginfo *pi; double ts; } buffered_entry; typedef struct { buffered_entry *be; int curpos; char bufferwhat; int maxnlines; int maxbytes, curbytes; proginfo *last_win; char marker_of_other_window; } buffer; typedef enum { CSREFLAG_SUB = 1, /* substring matching */ CSREFLAG_CMP_VAL_LESS, /* compare with value: value less then what is configured? */ CSREFLAG_CMP_VAL_BIGGER, /* compare with value: value higher then what is configured? */ CSREFLAG_CMP_VAL_EQUAL /* compare with value: value equal to what is configured? */ } csreflag_t; typedef struct { char *script; pid_t pid; int fd_r, fd_w; } script; typedef struct { myattr_t attrs1; myattr_t attrs2; mybool_t use_alternating_colors; int ac_index; } cse_main; typedef struct { cse_main cdef; regex_t regex; csreflag_t flags; double cmp_value; mybool_t merge_color; } color_scheme_entry; typedef struct { char *name; char *descr; script color_script; int n; color_scheme_entry *pentries; } color_scheme; typedef enum { CONVTYPE_IP4TOHOST = 1, CONVTYPE_EPOCHTODATE, CONVTYPE_ERRNO, CONVTYPE_HEXTODEC, CONVTYPE_DECTOHEX, CONVTYPE_TAI64NTODATE, CONVTYPE_SCRIPT, CONVTYPE_ABBRTOK, CONVTYPE_SIGNRTOSTRING } conversion_t; typedef struct { conversion_t type; regex_t regex; int match_count; } conversion_bundle_t; typedef struct { char *name; int n; conversion_bundle_t *pcb; /* conversion script */ script *pcs; } conversion; typedef enum { SCHEME_CONVERSION = 1, SCHEME_COLOR } scheme_t; typedef struct cv_off { int start, end; char *newstr; } cv_off; typedef struct { int n_colorschemes; char **colorschemes; int buffer_maxnlines; int buffer_bytes; char change_win_marker; regex_t regex; char *re_str; int_array_t filterschemes; int_array_t editschemes; int n_conversion_schemes; char **conversion_schemes; } pars_per_file; typedef struct { regoff_t start; regoff_t end; myattr_t attrs; mybool_t merge_color; } color_offset_in_line; typedef struct { char key; char *command; } keybinding; void do_exit(void); char * select_file(char *input, int what_help); char check_no_suppress_lines_filter(proginfo *cur); void color_print(int f_index, NEWWIN *win, proginfo *cur, char *string, regmatch_t *matches, int matching_regex, mybool_t force_to_winwidth, int start_at_offset, int end_at_offset, double ts, char show_window_nr); int select_window(int what_help, char *heading); char check_filter(proginfo *cur, char *string, regmatch_t **pmatch, char **error, int *matching_regex, char do_re, char *display); int wait_for_keypress(int what_help, double max_wait, NEWWIN *popup, char shift_cursor); int toggle_colors(void); void regexp_error_popup(int rc, regex_t *pre); void redraw_statuslines(void); void buffer_replace_pi_pointers(int f_index, proginfo *org, proginfo *new); char delete_entry(int f_index, proginfo *sub); char * key_to_keybinding(char what); void do_buffer(int f_index, proginfo *cur, char *string, char filter_match, double now); void do_print(int f_index, proginfo *cur, char *string, regmatch_t *matches, int matching_regex, double ts); void do_set_bufferstart(int f_index, char store_what_lines, int maxnlines); void emit_myattr_t(FILE *fh, myattr_t what); void update_statusline(NEWWIN *status, int win_nr, proginfo *cur); void write_escape_str(FILE *fh, char *string); void check_proc_sigh(int sig); void info(void); void statistics(void); int emit_to_buffer_and_term(int f_index, proginfo *cur, char *line); void add_pars_per_file(char *re, char *colorscheme, int n_buffer_lines, int buffer_bytes, char change_win_marker, int fs, int es, char *conversion_scheme); void version(void); void usage(void); void create_new_win(proginfo **cur, int *nr); void delete_be_in_buffer(buffer *pb); void do_restart_window(proginfo *cur); void LOG(char *str, ...); multitail-6.0/misc.h0000644000175000017500000000022012245202637014362 0ustar folkertfolkertvoid info(void); void statistics_menu(void); void heartbeat(void); void do_check_for_mail(); void store_statistics(proginfo *cur, dtime_t now); multitail-6.0/scrollback.c0000644000175000017500000004713212245202637015556 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include #include "mt.h" #include "error.h" #include "my_pty.h" #include "utils.h" #include "term.h" #include "help.h" #include "mem.h" #include "ui.h" #include "misc.h" #include "globals.h" int scrollback_search_to_new_window(buffer *pbuf, char *org_title, char *find_str, mybool_t case_insensitive); int find_string(buffer *pbuf, char *find, int offset, char direction, mybool_t case_insensitive) { int loop, index = -1, rc; regex_t regex; /* compile the searchstring (which can be a regular expression) */ if ((rc = regcomp(®ex, find, REG_EXTENDED | (case_insensitive == MY_TRUE?REG_ICASE:0)))) { regexp_error_popup(rc, ®ex); return -1; /* failed -> not found */ } if (direction == 1) { for(loop=offset; loop curpos; loop++) { if ((pbuf -> be)[loop].Bline == NULL) continue; if (regexec(®ex, (pbuf -> be)[loop].Bline, 0, NULL, 0) == 0) { index = loop; break; } } } else if (direction == (char)-1) { for(loop=offset; loop>=0; loop--) { if ((pbuf -> be)[loop].Bline == NULL) continue; if (regexec(®ex, (pbuf -> be)[loop].Bline, 0, NULL, 0) == 0) { index = loop; break; } } } regfree(®ex); return index; } void scrollback_find_popup(char **find_str, mybool_t *pcase_insensitive) { char *dummy; NEWWIN *mywin = create_popup(5, 40); win_header(mywin, "Find"); dummy = edit_string(mywin, 3, 2, 40, 80, 0, reuse_searchstring?*find_str:NULL, HELP_SCROLLBACK_EDIT_SEARCH_STRING, -1, &search_h, pcase_insensitive); myfree(*find_str); *find_str = dummy; delete_popup(mywin); } void scrollback_savefile(buffer *pbuf) { char *file = NULL; NEWWIN *mywin = create_popup(8, 40); win_header(mywin, "Save buffer to file"); mvwprintw(mywin -> win, 4, 2, "Select file"); file = edit_string(mywin, 5, 2, 40, find_path_max(), 0, NULL, HELP_SCROLLBACK_SAVEFILE_ENTER_FILENAME, -1, &cmdfile_h, NULL); if (file) { FILE *fh = fopen(file, "w"); if (fh) { int loop; for(loop=0; loop curpos; loop++) { if ((pbuf -> be)[loop].Bline) { char display = 1; char *error; int dummy = -1; regmatch_t *pmatch = NULL; /* check filter */ if (!IS_MARKERLINE((pbuf -> be)[loop].pi)) { (void)check_filter((pbuf -> be)[loop].pi, (pbuf -> be)[loop].Bline, &pmatch, &error, &dummy, 0, &display); if (error) { fprintf(fh, "%s\n", error); myfree(error); } } if (display) { fprintf(fh, "%s\n", USE_IF_SET((pbuf -> be)[loop].Bline, "")); } if (pmatch) myfree(pmatch); } } fclose(fh); } else { error_popup("Save scrollback buffer", -1, "Cannot write to file, reason: %s", strerror(errno)); } } delete_popup(mywin); } int get_lines_needed(char *string, int terminal_width) { if (string) return (strlen(string) + terminal_width - 1) / terminal_width; else return 1; } void scrollback_displayline(int window_nr, NEWWIN *win, buffer *pbuf, int buffer_offset, int terminal_offset, int offset_in_line, mybool_t force_to_winwidth, char show_winnr) { char *cur_line = (pbuf -> be)[buffer_offset].Bline; wmove(win -> win, terminal_offset, 0); if (cur_line) { proginfo *cur_line_meta = (pbuf -> be)[buffer_offset].pi; double ts = (pbuf -> be)[buffer_offset].ts; char old_color_settings = 0; if (scrollback_no_colors && cur_line_meta != NULL) { old_color_settings = cur_line_meta -> cdef.colorize; cur_line_meta -> cdef.colorize = 0; } if (IS_MARKERLINE(cur_line_meta)) { color_print(window_nr, win, cur_line_meta, cur_line, NULL, -1, MY_FALSE, 0, 0, ts, show_winnr); } else /* just a buffered line */ { char *error = NULL; regmatch_t *pmatch = NULL; int matching_regex = -1; char display; (void)check_filter(cur_line_meta, cur_line, &pmatch, &error, &matching_regex, 0, &display); if (error) { color_print(window_nr, win, cur_line_meta, error, NULL, -1, MY_FALSE, 0, 0, ts, show_winnr); myfree(error); } if (display) { if (offset_in_line) { int line_len = strlen(cur_line); int new_size = 0; if (offset_in_line < line_len) new_size = min(win -> width, line_len - offset_in_line); color_print(window_nr, win, cur_line_meta, cur_line, pmatch, matching_regex, force_to_winwidth, offset_in_line, offset_in_line + new_size, ts, show_winnr); } else { color_print(window_nr, win, cur_line_meta, cur_line, pmatch, matching_regex, force_to_winwidth, 0, 0, ts, show_winnr); } } myfree(pmatch); } if (scrollback_no_colors && cur_line_meta != NULL) { cur_line_meta -> cdef.colorize = old_color_settings; } } else /* an empty line */ { /* do nothing */ } } int scrollback_do(int window_nr, buffer *pbuf, int *winnrs, char *header) { int rc = 0; char *find = NULL; NEWWIN *mywin1, *mywin2; int nlines = max_y - 6, ncols = max_x - 6; int offset = max(0, pbuf -> curpos - nlines); // FIXME: aantal regels laten afhangen van lengte char redraw = 2; int line_offset = 0; char show_winnr = default_sb_showwinnr; mybool_t case_insensitive = re_case_insensitive; buffer cur_lb; int loop; memset(&cur_lb, 0x00, sizeof(cur_lb)); for(loop=0; loop curpos; loop++) { if ((pbuf -> be)[loop].Bline == NULL) continue; cur_lb.be = myrealloc(cur_lb.be, (cur_lb.curpos + 1) * sizeof(buffered_entry)); cur_lb.be[cur_lb.curpos].pi = (pbuf -> be)[loop].pi; if ((pbuf -> be)[loop].pi != NULL && (!IS_MARKERLINE((pbuf -> be)[loop].pi)) && (pbuf -> be)[loop].pi -> cdef.term_emul != TERM_IGNORE) { color_offset_in_line *cmatches; int n_cmatches; cur_lb.be[cur_lb.curpos].Bline = emulate_terminal((pbuf -> be)[loop].Bline, &cmatches, &n_cmatches); myfree(cmatches); } else cur_lb.be[cur_lb.curpos].Bline = strdup((pbuf -> be)[loop].Bline); cur_lb.be[cur_lb.curpos].ts = (pbuf -> be)[loop].ts; cur_lb.curpos++; } LOG("---\n"); if (global_highlight_str) { find = mystrdup(global_highlight_str); } mywin1 = create_popup(max_y - 4, max_x - 4); mywin2 = create_popup(nlines, ncols); scrollok(mywin2 -> win, FALSE); /* supposed to always return OK, according to the manpage */ for(;;) { int c, uc; if (redraw == 2) { int index = 0; int lines_used = 0; ui_inverse_on(mywin1); mvwprintw(mywin1 -> win, nlines + 1, 1, "%s - %d buffered lines", shorten_filename(header, max(24, ncols - 24)), cur_lb.curpos); ui_inverse_off(mywin1); if (!no_linewrap) ui_inverse_on(mywin1); mvwprintw(mywin1 -> win, nlines + 1, ncols - 8, "LINEWRAP"); if (!no_linewrap) ui_inverse_off(mywin1); werase(mywin2 -> win); if (!no_linewrap && line_offset > 0) { int temp_line_offset = line_offset; int n_chars_to_display_left = strlen((cur_lb.be)[offset].Bline) - temp_line_offset; while(lines_used < nlines && n_chars_to_display_left > 0) { scrollback_displayline(winnrs?winnrs[offset]:window_nr, mywin2, &cur_lb, offset, lines_used, temp_line_offset, 1, show_winnr); temp_line_offset += ncols; n_chars_to_display_left -= ncols; lines_used++; } index++; } for(;(offset + index) < cur_lb.curpos && lines_used < nlines;) { int lines_needed = get_lines_needed((cur_lb.be)[offset + index].Bline, ncols); if (no_linewrap || lines_needed == 1) { scrollback_displayline(winnrs?winnrs[offset + index]:window_nr, mywin2, &cur_lb, offset + index, lines_used, no_linewrap?line_offset:0, no_linewrap, show_winnr); lines_used++; } else { int cur_line_offset = 0; while(lines_used < nlines && lines_needed > 0) { scrollback_displayline(winnrs?winnrs[offset + index]:window_nr, mywin2, &cur_lb, offset + index, lines_used, cur_line_offset, 1, show_winnr); cur_line_offset += ncols; lines_used++; lines_needed--; } } index++; } redraw = 1; } if (redraw == 1) { mydoupdate(); redraw = 0; } c = wait_for_keypress(HELP_SCROLLBACK_HELP, 0, NULL, 1); uc = toupper(c); if (c == 'q' || uc == 'X' || c == abort_key || c == KEY_CLOSE || c == KEY_EXIT) { break; } else if (c == 'Q' || c == -1) /* Q: close whole stack of scrollbackwindows, -1: something got closed */ { rc = -1; break; } else if (c == 20 && winnrs != NULL) /* ^t */ { show_winnr = 1 - show_winnr; redraw = 2; } else if (c == 'Y') { no_linewrap = !no_linewrap; redraw = 2; line_offset = 0; } else if (c == 't') { statistics_menu(); } else if ((c == KEY_LEFT || c == KEY_BACKSPACE) && no_linewrap) { if (line_offset > 0) line_offset--; redraw = 2; } else if (c == KEY_SLEFT && no_linewrap) { if (line_offset >= (ncols / 2)) line_offset -= (ncols / 2); else line_offset = 0; redraw = 2; } else if (c == KEY_SRIGHT && no_linewrap) { line_offset += (ncols / 2); redraw = 2; } else if (c == KEY_BEG && no_linewrap) { if (line_offset) { line_offset = 0; redraw = 2; } } else if (c == KEY_BTAB) { if (line_offset >= 4) line_offset -= 4; else line_offset = 0; redraw = 2; } else if (c == KEY_RIGHT && no_linewrap) { line_offset++; redraw = 2; } else if ((c == KEY_UP || c == 'y' || c == 25 || /* ^y */ c == 'k' || /* c == 11 || *//* ^k */ c == 16) /* ^p */ && (offset > 0 || (!no_linewrap && line_offset > 0))) { if (no_linewrap) { offset--; } else if (line_offset > 0) { line_offset = max(0, line_offset - ncols); } else { offset--; line_offset = (get_lines_needed((cur_lb.be)[offset].Bline, ncols) - 1) * ncols; } wmove(mywin2 -> win, 0, 0); winsdelln(mywin2 -> win, 1); scrollback_displayline(winnrs?winnrs[offset]:window_nr, mywin2, &cur_lb, offset, 0, line_offset, no_linewrap, show_winnr); redraw = 1; } else if ((c == KEY_DOWN || c == 'e' || c == 5 || /* ^e */ c == 'j' || c == 14 || /* ^n */ c == 13 || c == KEY_ENTER) && offset < (cur_lb.curpos - 1)) { if (no_linewrap) { offset++; } else if (strlen((cur_lb.be)[offset].Bline) > (line_offset + ncols)) { line_offset += ncols; } else if (offset < (cur_lb.curpos - 1)) { if (strlen((cur_lb.be)[offset].Bline) > (line_offset + ncols)) line_offset += ncols; else { line_offset = 0; offset++; } } redraw = 2; } else if ((c == KEY_NPAGE || c == 'f' || c == 6 || /* ^f */ c == ('V' - 65 + 1) || /* ^v */ c == ' ' || c == 'z' || c == 'u' || c == ('U' - 65 + 1)) /* ^u */ && offset < (cur_lb.curpos - 1)) { if (no_linewrap) { offset += nlines; if (offset >= cur_lb.curpos) offset = cur_lb.curpos - 1; } else { int n_lines_to_move = nlines; while(n_lines_to_move > 0 && offset < (cur_lb.curpos)) { if (line_offset > 0) { if (line_offset + ncols >= strlen((cur_lb.be)[offset].Bline)) { line_offset = 0; offset++; n_lines_to_move--; } else { line_offset += ncols; n_lines_to_move--; } } else { n_lines_to_move -= get_lines_needed((cur_lb.be)[offset].Bline, ncols); offset++; } } if (n_lines_to_move < 0) line_offset = (-n_lines_to_move) * ncols; } redraw = 2; } else if ((c == KEY_PPAGE || c == 'b' || c == 2 || /* ^b */ c == 'w' || c == 'd' || c == 4) /* ^d */ && offset > 0) { if (no_linewrap) { offset -= nlines; if (offset < 0) offset = 0; } else { int n_lines_to_move = nlines; if (line_offset) n_lines_to_move -= line_offset / ncols; while(n_lines_to_move > 0 && offset > 0) { offset--; n_lines_to_move -= get_lines_needed((cur_lb.be)[offset].Bline, ncols); if (n_lines_to_move < 0) { line_offset = (get_lines_needed((cur_lb.be)[offset].Bline, ncols) + n_lines_to_move) * ncols; } } } redraw = 2; } else if ((c == KEY_HOME || c == 'g' || c == '<' || c == KEY_SBEG) && offset > 0) { line_offset = offset = 0; redraw = 2; } else if ((c == KEY_END || c == 'G' || c == '>' || c == KEY_SEND) && offset < (cur_lb. curpos - 1)) { offset = cur_lb. curpos - 1; redraw = 2; } else if (uc == 'R' || c == ('R' - 65 + 1) || c == ('L' - 65 + 1) || c == KEY_REFRESH) { redraw = 2; } else if (c == ('K' - 65 + 1) || c == KEY_MARK) { scrollback_find_popup(&find, &case_insensitive); if (find) { int rc; regfree(&global_highlight_re); myfree(global_highlight_str); global_highlight_str = NULL; if ((rc = regcomp(&global_highlight_re, find, (case_insensitive == MY_TRUE?REG_ICASE:0) | REG_EXTENDED))) { regexp_error_popup(rc, &global_highlight_re); myfree(find); } else { global_highlight_str = find; } redraw = 2; /* force redraw */ } } else if (c == 'f' || c == '/' || c == '?' || c == KEY_FIND || c == KEY_SFIND) { char direction = (c == '?' || c == KEY_SFIND) ? -1 : 1; scrollback_find_popup(&find, &case_insensitive); if (find) { if (scrollback_search_new_window) { if (scrollback_search_to_new_window(&cur_lb, header, find, case_insensitive) == -1) { /* cascaded close */ rc = -1; break; } } else { int new_f_index; redraw = 2; /* force redraw */ regfree(&global_highlight_re); myfree(global_highlight_str); global_highlight_str = NULL; new_f_index = find_string(&cur_lb, find, 0, direction, case_insensitive); if (new_f_index == -1) { wrong_key(); } else { offset = new_f_index; line_offset = 0; } } } } else if (uc == 'N' || c == KEY_NEXT || c == KEY_PREVIOUS || c == KEY_SNEXT) { if (find != NULL) { char direction = (c == 'n' || c == KEY_NEXT) ? 1 : -1; int start_offset = offset + direction; int new_f_index = find_string(&cur_lb, find, start_offset, direction, case_insensitive); if (new_f_index == -1) { wrong_key(); } else { redraw = 2; /* force redraw */ offset = new_f_index; line_offset = 0; } } else { wrong_key(); } } else if (c == 's' || c == KEY_SAVE) { scrollback_savefile(&cur_lb); redraw = 2; /* force redraw */ } else if (c == 'h') { show_help(HELP_SCROLLBACK_HELP); } else if (c == 'c') { toggle_colors(); redraw = 2; /* force redraw */ } else if (c == 'i') { info(); } else if (c == 'T') { statistics_menu(); } else if (c == 20) { toggle_subwindow_nr(); redraw = 2; /* force redraw */ } else { wrong_key(); } } delete_popup(mywin2); delete_popup(mywin1); myfree(find); delete_be_in_buffer(&cur_lb); return rc; } void scrollback(void) { int window = 0; if (nfd > 1) { window = select_window(HELP_SCROLLBACK_SELECT_WINDOW, NULL); } if (window != -1) { if (lb[window].bufferwhat == 0) error_popup("Scrollback", HELP_SCROLLBACK_NO_MARK, "Cannot scrollback: buffering is disabled."); } if (window != -1 && lb[window].bufferwhat != 0) { int header_size = strlen(pi[window].filename) + 4; char *header = (char *)mymalloc(header_size + 1); snprintf(header, header_size, "%02d] %s", window, pi[window].filename); scrollback_do(window, &lb[window], NULL, header); myfree(header); } } void merged_scrollback_with_search(char *search_for, mybool_t case_insensitive) { int lc_size = nfd * sizeof(int); int *last_check = (int *)mymalloc(lc_size); int *winnr = NULL; buffer buf; regex_t reg; int rc; memset(last_check, 0x00, lc_size); memset(&buf, 0x00, sizeof(buf)); /* compile the search string which is supposed to be a valid regular * expression */ if (search_for) { if ((rc=regcomp(®, search_for, REG_EXTENDED | (case_insensitive == MY_TRUE?REG_ICASE:0)))) { regexp_error_popup(rc, ®); free(last_check); return; } } /* merge all windows into one */ for(;;) { int loop; double chosen_ts = (double)(((long long int)1) << 62); int chosen_win = -1; int curline; char *string; int checked_all = 0; for(loop=0; loop last_check[loop]) last_check[loop]++; } continue; } if (!IS_MARKERLINE(lb[chosen_win].be[last_check[chosen_win]].pi)) { /*** ADD LINE TO BUFFER ***/ buf.be = (buffered_entry *)myrealloc(buf.be, sizeof(buffered_entry) * (buf.curpos + 1)); winnr = (int *)myrealloc(winnr, sizeof(int) * (buf.curpos + 1)); curline = buf.curpos++; /* add the logline itself */ string = lb[chosen_win].be[last_check[chosen_win]].Bline; if (string) buf.be[curline].Bline = mystrdup(string); else buf.be[curline].Bline = NULL; /* remember pointer to subwindow (required for setting colors etc.) */ buf.be[curline].pi = lb[chosen_win].be[last_check[chosen_win]].pi; buf.be[curline].ts = lb[chosen_win].be[last_check[chosen_win]].ts; /* remember window nr. */ winnr[curline] = chosen_win; } last_check[chosen_win]++; } if (buf.curpos == 0) error_popup("Search in all windows", -1, "Nothing found."); else { char *header; if (search_for) { char *help = "Searched for: "; int len = strlen(help) + strlen(search_for) + 1; header = mymalloc(len); snprintf(header, len, "%s%s", help, search_for); } else { char *help = "Merge view"; header = mymalloc(strlen(help) + 1); sprintf(header, "%s", help); } scrollback_do(-1, &buf, winnr, header); myfree(header); } delete_be_in_buffer(&buf); myfree(winnr); myfree(last_check); } int scrollback_search_to_new_window(buffer *pbuf, char *org_title, char *find_str, mybool_t case_insensitive) { int loop, rc; regex_t regex; buffer cur_lb; char *new_header; /* compile the searchstring (which can be a regular expression) */ if ((rc = regcomp(®ex, find_str, REG_EXTENDED | (case_insensitive == MY_TRUE?REG_ICASE:0)))) { regexp_error_popup(rc, ®ex); return 0; } memset(&cur_lb, 0x00, sizeof(buffer)); for(loop=0; loop curpos; loop++) { if ((pbuf -> be)[loop].Bline == NULL) continue; if (regexec(®ex, (pbuf -> be)[loop].Bline, 0, NULL, 0) == 0) { cur_lb.be = myrealloc(cur_lb.be, (cur_lb.curpos + 1) * sizeof(buffered_entry)); cur_lb.be[cur_lb.curpos].Bline = (pbuf -> be)[loop].Bline; cur_lb.be[cur_lb.curpos].pi = (pbuf -> be)[loop].pi; cur_lb.be[cur_lb.curpos].ts = (pbuf -> be)[loop].ts; cur_lb.curpos++; } } new_header = (char *)mymalloc(strlen(org_title) + 1 + strlen(find_str) + 1); sprintf(new_header, "%s %s", org_title, find_str); rc = scrollback_do(-1, &cur_lb, NULL, new_header); myfree(new_header); myfree(cur_lb.be); regfree(®ex); return rc; } multitail-6.0/makefile.solaris_sunwspro0000644000175000017500000000525012245202637020421 0ustar folkertfolkertinclude version DESTDIR=/ CONFIG_FILE=$(DESTDIR)/etc/multitail.conf DEBUG=#-g -D_DEBUG #-pg #-fprofile-arcs CFLAGS=${EXTRA_CFLAGS} -O2 -I/usr/local/include/ -D_STDC_C99 -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" LDFLAGS=${EXTRA_LDFLAGS} -L/usr/local/lib/ -lsocket -lpanel -lncurses -lnsl -lm $(DEBUG) OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o color.o stripstring.o selbox.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail install: multitail /usr/sbin/install -m 0755 -u root -g sys -d $(DESTDIR)/usr /usr/sbin/install -m 0755 -u root -g bin -d $(DESTDIR)/usr/bin cp multitail $(DESTDIR)/usr/bin /usr/sbin/install -m 0755 -u root -g sys -d $(DESTDIR)/usr/share /usr/sbin/install -m 0755 -u root -g bin -d $(DESTDIR)/usr/share/man /usr/sbin/install -m 0755 -u root -g bin -d $(DESTDIR)/usr/share/man/man1 cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # /usr/sbin/install -m 0755 -u root -g sys -d $(DESTDIR)/etc cp multitail.conf $(DESTDIR)/etc/multitail.conf.new /usr/sbin/install -m 0755 -u root -g other -d $(DESTDIR)/usr/share/doc /usr/sbin/install -m 0755 -u root -g other -d $(DESTDIR)/usr/share/doc/multitail-$(VERSION) mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php solarisbinpackage: multitail rm -rf usr etc mkdir -p etc cp multitail.conf etc mkdir -p usr/bin mkdir -p usr/share/man/man1 mkdir -p usr/share/doc/multitail-$(VERSION) cp multitail usr/bin cp multitail.1 usr/share/man/man1 cp *.txt INSTALL manual.html usr/share/doc/multitail-$(VERSION) tar cvf multitail-$(VERSION)-solaris.tar usr etc rm -rf usr etc gzip -9 multitail-$(VERSION)-solaris.tar uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/man/man1/multitail.1.gz rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-6.0/cmdline.c0000644000175000017500000011011612245202637015043 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include "doassert.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mt.h" #include "mem.h" #include "error.h" #include "color.h" #include "term.h" #include "utils.h" #include "config.h" #include "cv.h" #include "exec.h" #include "globals.h" void usage(void) { printf("%s", version_str); printf("\n\nmultitail [-cs|-Cs|-c-] [-i] inputfile [-i anotherinputfile] [...]\n"); printf("-i x the following parameter is a filename (in case it starts with a dash)\n"); printf("-I x like -i only this one merges this logfile into the previous window\n"); printf("-l x parameter is a command to be executed\n"); printf("-L x see -l but add to the previous window\n"); printf("-j read from stdin (can be used (of course) only once)\n"); printf("-J like -j but merge into previous window\n"); printf("--listen [interface]:port behave like a syslog server. port is normally 514\n"); printf("--Listen [interface]:port like --listen but merge into previous window\n"); printf("-r interval restart the command when it died after `interval' seconds\n"); printf("-R interval same as -r, only with this one only the difference is displayed\n"); printf("-Rc/-rc interval like -r/-R but clean the window before each iteration\n"); printf("--cont reconnect lines with a '\' at the end\n"); printf("--mergeall merge all of the following files into the same window (in the previous window)\n"); printf("--mergeall-new merge all of the following files into the same window (in a new window)\n"); printf("--no-mergeall stop merging all files into one window\n"); printf("--no-repeat suppress repeating lines and replace them with a \"last message repeated x times\"\n"); printf("--mark-interval x when nothing comes in, print a '---mark---' line every 'x' seconds\n"); printf("--mark-change when multiple files are merged an multitail switches between two windows, print a markerline with the filename\n"); printf("--no-mark-change do NOT print the markerline when the file changes (overrides the configfile)\n"); printf("-n x initial number of lines to tail\n"); printf("-m x set scrollback buffer size (# lines)\n"); printf("-mb x set scrollback buffer size (in bytes, use xKB/MB/GB)\n"); printf("-bw a/f what to buffer: 'a'll or what went through the 'f'ilter\n"); printf("-a x like 'tee': write (filtered) input to file 'x'\n"); printf("-A x see -a: but write the unfiltered(!) input to file 'x'\n"); printf("-g x redirect the input also (filtered) to command/process 'x'\n"); printf("-G x redirect the unfiltered input also to command/process 'x'\n"); printf("--label x put in front of each line\n"); printf("-q i path check every 'i' seconds for new files in 'path', create a new window for those\n"); printf("-Q i path check every 'i' seconds for new files in 'path', put them all in the same window (using subwindows)\n"); printf("--closeidle x close windows when more then 'x' seconds no new data was processed\n"); printf("--new-only (for -q/-Q) only create windows for files created after multitail was started\n"); printf("-s x vertical split screen (in 'x' columns)\n"); printf("-sw x,x,... at what columns to split the screen, use '0' for automatic size\n"); printf("-sn x,x,... number of windows per column\n"); printf("-wh x height of window\n"); printf("-S prepend merged output with subwindow-number\n"); printf("-f follow the following filename, not the descriptor\n"); printf("--follow-all for all files after this switch; follow the filename instead of the descriptor\n"); printf("--retry keep trying to open the following file if it is inaccessible\n"); printf("--retry-all like --retry but for all following files\n"); printf("-fr scheme use the predefined filter from the configfile\n"); printf("-e[m] print only when matching with this regexp\n"); printf("-ev print only when NOT matching with this regexp\n"); printf("-ec use regular expression but display the matches inverted on following file\n"); printf("-eC use regexp, display everything but matches inverted on following file\n"); printf("-ex execute command ('-ex regexp command') when matches, matching line is given as commandline parameter\n"); printf("-eX like -ex but only give the matching substring as commandline parameter to the command\n"); printf("-E use regular expression on following files\n"); printf("-Ec use regular expression but display the matches inverted on following files\n"); printf("-EC use regexp, display everything but matches inverted on following files\n"); printf("-ke x strip parts of the input using regular expression 'x'\n"); printf("-kr x y strip parts of the input starting at offset x and ending (not including!) offset y\n"); printf("-kc x y strip parts of the input: strip column 'y' with delimiter 'x'\n"); printf("-ks x use edit scheme 'x' (defined in configfile)\n"); printf("-kS x only show the substrings matched by the substring-selects (the parts between '(' and ')') in the regular epxression 'x'\n"); printf("-v invert next regular expression (do not use with -ev/em)\n"); printf("-cv x use conversion scheme 'x' (see multitail.conf)\n"); printf("-c colorize current\n"); printf("-cS scheme use colorscheme 'scheme' (as defined in multitail.conf)\n"); printf("-csn extra switch for the following switches; do not use reverse (inverted) colors\n"); printf("-Cs colorize all following files with syslog-scheme\n"); printf("-C colorize all following files\n"); printf("-Cf/-cf field delimiter colorize next/all file(s) depending on the given field number. fields are delimited with the given field-delimiter\n"); printf("-ci color use 'color' (red, green, etc), usefull when merging multiple inputs\n"); printf("-c- do NOT colorize the following file\n"); printf("-C- do NOT colorize the following files\n"); printf("-cT term interpret terminal-codes from file/command (for terminal type 'term')\n"); printf("-Z color set color for markerline\n"); printf("-ts add a timestamp (format configurable in multitail.conf) before each line\n"); printf("-T put a timestamp in markerlines\n"); printf("-d do NOT update the status-line\n"); printf("-D do not display a status-line at all\n"); printf("-du put the statusline above the data window\n"); printf("-z do not \"window closed\" windows\n"); printf("-w do not use colors\n"); printf("-u set update interval (for slow links)\n"); printf("-p x [y] set linewrap (l=left/a=all/r=right/s=syslog,S=syslog w/o procname,o=offset -> 'y',w=wordwrap)\n"); printf("-P like -p but for all following files\n"); printf("-b n set TAB-width\n"); printf("--basename only display the filename (and not the path) in the statusline\n"); printf("-x str switch on the xtermtitlebar stuff\n"); #ifndef S_SPLINT_S printf("-F file use 'file' as configfile (instead of " CONFIG_FILE ")\n"); printf("--no-load-global-config do not read " CONFIG_FILE "\n"); #endif printf("-o configfileparameter do a setting which would normally be set in the configfile\n"); printf("-H x show heartbeat (to keep your sessions alive)\n"); printf("-iw file i check every 'i' seconds if 'file' appeared in the filesystem\n"); printf("-t x display 'x' in the window-title (when MultiTail runs in an xterm)\n"); printf("--beep-interval x beep every x lines processed\n"); printf("--bi x like '--beep-interval' but only for current (sub-)window\n"); printf("-V show version and exit\n"); printf("-h this help\n"); printf("\n"); printf("You can have multiple regular expressions per file/command. Be warned: if\n"); printf("you define multiple and one of them is specified with '-E' (=for every\n"); printf("following file), _all_ of the current regular expressions are for all\n"); printf("following files!\n"); printf("\n"); printf("%s\n", F1); } void add_redir_to_file(char mode, char *file, redirect_t **predir, int *n_redirect) { int cur_index = (*n_redirect)++; *predir = (redirect_t *)myrealloc(*predir, (*n_redirect) * sizeof(redirect_t)); assert(mode == 'A' || mode == 'a'); if (mode == 'a') (*predir)[cur_index].type = REDIRECTTO_FILE_FILTERED; else (*predir)[cur_index].type = REDIRECTTO_FILE; (*predir)[cur_index].redirect = mystrdup(file); (*predir)[cur_index].fd = open((*predir)[cur_index].redirect, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR); if ((*predir)[cur_index].fd == -1) error_exit("%s: cannot open file %s for write access.\n", file, (*predir)[cur_index].redirect); } void add_redir_to_proc(char mode, char *proc, redirect_t **predir, int *n_redirect) { int fds_to_proc[2], fds_from_proc[2]; assert(mode == 'G' || mode == 'g'); *predir = (redirect_t *)myrealloc(*predir, (*n_redirect + 1) * sizeof(redirect_t)); if ((*predir)[*n_redirect].type != REDIRECTTO_NONE) error_exit("One can only set one redirection-type per (sub-)window.\n"); if (mode == 'g') (*predir)[*n_redirect].type = REDIRECTTO_PIPE_FILTERED; else (*predir)[*n_redirect].type = REDIRECTTO_PIPE; (*predir)[*n_redirect].redirect = mystrdup(proc); (*predir)[*n_redirect].pid = exec_with_pipe((*predir)[*n_redirect].redirect, fds_to_proc, fds_from_proc); myclose(fds_to_proc[0]); myclose(fds_from_proc[0]); myclose(fds_from_proc[1]); (*predir)[*n_redirect].fd = fds_to_proc[1]; (*n_redirect)++; } void add_redir_to_socket(char filtered, char *prio, char *fac, char *address, redirect_t **predir, int *n_redirect) { char *local_address = mystrdup(address); char *colon = strchr(local_address, ':'); int prio_nr = -1, fac_nr = -1; int loop; char* node; char* service; struct addrinfo hints; struct addrinfo* result; struct addrinfo* rp; int s, sfd = -1; *predir = (redirect_t *)myrealloc(*predir, (*n_redirect + 1) * sizeof(redirect_t)); assert(filtered == 1 || filtered == 0); if (filtered) (*predir)[*n_redirect].type = REDIRECTTO_SOCKET_FILTERED; else (*predir)[*n_redirect].type = REDIRECTTO_SOCKET; (*predir)[*n_redirect].redirect = mystrdup(address); if (colon) { *colon = 0x00; node = local_address; service = colon + 1; } else { node = local_address; service = "syslog"; } memset(&hints, 0x00, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = 0; hints.ai_protocol = 0; s = getaddrinfo(node, service, &hints, &result); if (s != 0) error_exit("Cannot create socket for redirecting via syslog protocol: %s.\n", gai_strerror(s)); for (rp = result; rp != NULL; rp = rp -> ai_next) { sfd = socket(rp -> ai_family, rp -> ai_socktype, rp -> ai_protocol); if (sfd == -1) continue; if (connect(sfd, rp -> ai_addr, rp -> ai_addrlen) != -1) break; close(sfd); } freeaddrinfo(result); if (rp == NULL) error_exit("Cannot create socket for redirecting via syslog protocol.\n"); (*predir)[*n_redirect].fd = sfd; for(loop=0; loop<8; loop++) { if (strcasecmp(severities[loop], prio) == 0) { prio_nr = loop; break; } } if (prio_nr == -1) error_exit("Priority '%s' is not recognized.\n", prio); for(loop=0; loop<24; loop++) { if (strcasecmp(facilities[loop], fac) == 0) { fac_nr = loop; break; } } if (fac_nr == -1) error_exit("Facility '%s' is not known.\n", fac); (*predir)[*n_redirect].prio_fac = (fac_nr * 8) + prio_nr; myfree(local_address); (*n_redirect)++; } void argv_set_window_widths(char *widths) { if (split != 0) error_exit("-s and -sw are mutual exclusive.\n"); split = 0; for(;;) { int cur_width; char *pnt; pnt = strtok(widths, ","); if (!pnt) break; split++; vertical_split = (int *)myrealloc(vertical_split, split * sizeof(int)); cur_width = get_value_arg("-sw", pnt, VAL_POSITIVE); widths = NULL; if (cur_width < 4) { if (cur_width == 0) cur_width = -1; else error_exit("The width of a column must be 4 or greater (or '0' for automatic size).\n"); } vertical_split[split - 1] = cur_width; } if (split == 1) error_exit("You have to give the width for each window or set it to 0 (=auto width).\n"); } void argv_set_n_windows_per_column(char *pars) { int index = 0; if (split == 0) error_exit("First use -s or -sw to define the number of columns.\n"); for(;;) { int cur_n; char *pnt; pnt = strtok(pars, ","); if (!pnt) break; index++; n_win_per_col = (int *)myrealloc(n_win_per_col, index * sizeof(int)); cur_n = get_value_arg("-sn", pnt, VAL_POSITIVE); pars = NULL; if (cur_n < 0) error_exit("The number of windows must be either 0 (=auto) or >= 1.\n"); n_win_per_col[index - 1] = cur_n; } } int argv_add_re(char *mode, char *pars[], char invert_regex, re **pre_cur, int *n_re_cur, re **pre_all, int *n_re_all) { char *cmd = NULL, *expr = NULL; char regex_mode = 0; int n_pars_used = 0; re **pre = pre_cur; int *n_re = n_re_cur; /* -e => only for this file, -E => for all following files */ if (mode[1] == 'E') { pre = pre_all; n_re = n_re_all; } /* c/C/m define colors, x says: execute */ if (toupper(mode[2]) == 'C') regex_mode = mode[2]; else if (toupper(mode[2]) == 'B') regex_mode = mode[2]; else if (mode[2] == 'm' || mode[2] == 0x00) regex_mode = 'm'; /* m = match, only print when matches */ else if (mode[2] == 'v') regex_mode = 'v'; /* v = !match, only print when not matching */ else if (toupper(mode[2]) == 'X') regex_mode = mode[2]; /* x = execute */ else error_exit("%s is an unknown switch.\n", mode); /* get expression */ expr = pars[n_pars_used++]; /* and if there's anything to execute, get commandline */ if (toupper(regex_mode) == 'X') { cmd = pars[n_pars_used++]; if (regex_mode == 'X' && (strchr(expr, '(') == NULL || strchr(expr, ')') == NULL)) error_exit("Filterscheme rule: -eX requires a regular expression which selects a substring using '(' and ')'.\n"); } /* compile & set expression */ /* allocate new structure */ *pre = (re *)myrealloc(*pre, sizeof(re) * (*n_re + 1)); /* initialize structure */ memset(&(*pre)[*n_re], 0x00, sizeof(re)); /* compile */ compile_re(&(*pre)[*n_re].regex, expr); /* remember string for later edit */ (*pre)[*n_re].regex_str = mystrdup(expr); /* set flag on current file */ (*pre)[*n_re].use_regex = regex_mode; if (mode[1] == 'E') regex_mode = 0; /* wether to invert the reg exp or not */ if ((regex_mode == 'v' || regex_mode == 'm') && invert_regex) { error_exit("-e[m] / -ev cannot be used together with -v\n"); } (*pre)[*n_re].invert_regex = invert_regex; /* what to execute (if...) */ if (cmd) (*pre)[*n_re].cmd = mystrdup(cmd); else (*pre)[*n_re].cmd = NULL; (*n_re)++; return n_pars_used; } int argv_color_settings(char *mode, char *pars[], char *allcolor, char *curcolor, int *field_index, char **field_delimiter, myattr_t *cdef, term_t *cur_term_emul, int_array_t *cur_color_schemes, myattr_t *alt_col_cdev1, myattr_t *alt_col_cdev2) { int n_pars_used = 0; char cur_mode = mode[2], doall = 0; if (mode[1] == 'C') doall = 1; if (cur_mode == 's') /* syslog-file coloring? */ { } else if (cur_mode == 'a') /* alternating colors */ { *alt_col_cdev1 = parse_attributes(pars[n_pars_used++]); *alt_col_cdev2 = parse_attributes(pars[n_pars_used++]); } else if (cur_mode == 'i') /* use one specific color */ { *cdef = parse_attributes(pars[n_pars_used++]); } else if (cur_mode == 'T') /* terminal mode */ { if (pars[n_pars_used] != NULL && (strcasecmp(pars[n_pars_used], "ANSI") == 0 || strcasecmp(pars[n_pars_used], "vt100") == 0)) *cur_term_emul = TERM_ANSI; else error_exit("Terminal emulation '%s' is not known.\n", pars[n_pars_used]); n_pars_used++; } else if (cur_mode == 'S') /* use colorscheme */ { int cur_scheme_index; char *cur_cscheme = pars[n_pars_used++]; if (!cur_cscheme) error_exit("%s requires a color scheme name.\n", mode); if ((cur_scheme_index = find_colorscheme(cur_cscheme)) == -1) { if (use_colors) error_exit("Color scheme %s not found! Please check your configuration file.\n", cur_cscheme); else error_exit("Color schemes are not supported on monochrome terminals.\n"); } add_color_scheme(cur_color_schemes, cur_scheme_index); } else if (cur_mode == '-') /* do not color current */ { cur_mode = 'n'; } else if (cur_mode == 'f') /* select field for coloring */ { *field_index = get_value_arg(mode, pars[n_pars_used++], VAL_ZERO_POSITIVE); *field_delimiter = pars[n_pars_used++]; } else if (cur_mode == 0x00) /* use complete line for coloring */ { cur_mode = 'm'; } else { error_exit("Invalid -c mode: '%c'.\n", cur_mode); } if (doall) { *allcolor = cur_mode; *curcolor = 'n'; } else { *curcolor = cur_mode; *allcolor = 'n'; } return n_pars_used; } int argv_add_stripper(char *mode, char *pars[], strip_t **pstrip, int *n_strip) { int n_pars_used = 0; if (mode[2] == 'e' || mode[2] == 'S') { char *re = pars[n_pars_used++]; *pstrip = myrealloc(*pstrip, (*n_strip + 1) * sizeof(strip_t)); if (mode[2] == 'e') (*pstrip)[*n_strip].type = STRIP_TYPE_REGEXP; else if (mode[2] == 'S') (*pstrip)[*n_strip].type = STRIP_KEEP_SUBSTR; (*pstrip)[*n_strip].regex_str = mystrdup(re); (*pstrip)[*n_strip].del = NULL; compile_re(&(*pstrip)[*n_strip].regex, re); (*n_strip)++; } else if (mode[2] == 'r') { *pstrip = myrealloc(*pstrip, (*n_strip + 1) * sizeof(strip_t)); (*pstrip)[*n_strip].type = STRIP_TYPE_RANGE; (*pstrip)[*n_strip].start = get_value_arg(mode, pars[n_pars_used++], VAL_ZERO_POSITIVE); (*pstrip)[*n_strip].end = get_value_arg(mode, pars[n_pars_used++], VAL_ZERO_POSITIVE); (*pstrip)[*n_strip].del = NULL; if ((*pstrip)[*n_strip].end <= (*pstrip)[*n_strip].start) error_exit("'-kr start end': end must be higher then start.\n"); (*n_strip)++; } else if (mode[2] == 'c') { *pstrip = myrealloc(*pstrip, (*n_strip + 1) * sizeof(strip_t)); (*pstrip)[*n_strip].type = STRIP_TYPE_COLUMN; (*pstrip)[*n_strip].del = mystrdup(pars[n_pars_used++]); (*pstrip)[*n_strip].col_nr = get_value_arg(mode, pars[n_pars_used++], VAL_ZERO_POSITIVE); (*n_strip)++; } else if (mode[2] == 's') { char *scheme = pars[n_pars_used++]; int editscheme_index = find_editscheme(scheme); if (editscheme_index == -1) error_exit("-ks %s: scheme not found.\n", scheme); duplicate_es_array(pes[editscheme_index].strips, pes[editscheme_index].n_strips, pstrip, n_strip); } else error_exit("'%s' is not recognized.\n", mode); return n_pars_used; } void add_glob_check(char *check_glob, int check_interval, char merge, char new_only) { cdg = (check_dir_glob *)myrealloc(cdg, sizeof(check_dir_glob) * (n_cdg + 1)); cdg[n_cdg].glob_str = mystrdup(check_glob); cdg[n_cdg].check_interval = check_interval; cdg[n_cdg].in_one_window = merge; cdg[n_cdg].new_only = new_only; cdg[n_cdg].window_nr = -1; cdg[n_cdg].last_check = (dtime_t)0.0; n_cdg++; } void do_commandline(int argc, char *argv[]) { int loop; char curcolor = 'n', allcolor = 'n'; int field_index = 0; char *field_delimiter = NULL; char follow_filename = -1, retry = 0, invert_regex = 0; char retry_all = 0; int maxlines = -1; char setallmaxlines = 0; int restart = -1; char restart_clear = 0; char do_diff = 0; char cur_line_wrap = -1; int cur_line_wrap_offset = -1; char all_line_wrap = 0; char *label = NULL; re *pre = NULL; int n_re = 0; re *pre_all = NULL; int n_re_all = 0; int_array_t cur_color_schemes = { NULL, 0, 0 }; myattr_t cdef = { -1, -1 }; int window_height = -1; int initial_n_lines_tail = min_n_bufferlines; char *win_title = NULL; term_t cur_term_emul = TERM_IGNORE; strip_t *pstrip = NULL; int n_strip = 0; int n_redirect = 0; redirect_t *predir = NULL; mybool_t used_stdin = MY_FALSE; char merge_all = 0; char merge_in_new_first = 0; int close_idle = 0; char setallmaxbytes = 0; int maxbytes = -1; myattr_t alt_col_cdev1 = { -1, -1 }, alt_col_cdev2 = { -1, -1 }; time_field_t new_only = 0; char no_repeat = 0; int mark_interval = 0; char syslog_noreverse = 0; char cont = 0; char marker_of_other_window = 0; char no_marker_of_other_window = 0; char bufferwhat = -1; int cur_beep_interval = -1; char do_add_timestamp = 0; int_array_t conversions = { NULL, 0, 0 }; /* first, before we load the main configfile, see if we should load the global * file or not */ for(loop=1; loop= 0.\n"); } else error_exit("-H requires a parameter.\n"); } else if (strcasecmp(argv[loop], "-a") == 0) { char mode = argv[loop][1]; add_redir_to_file(mode, argv[++loop], &predir, &n_redirect); } else if (strcasecmp(argv[loop], "-g") == 0) { char mode = argv[loop][1]; add_redir_to_proc(mode, argv[++loop], &predir, &n_redirect); } else if (argv[loop][0] == '-' && argv[loop][1] == 'U') { char *prio, *fac, *addr; char filtered; // -U[af][as] host[:port] if (argv[loop][2] != 'a' && argv[loop][2] != 'f') error_exit("-Ux where x needs to be either 'a' or 'f'"); filtered = argv[loop][2] == 'f'; prio = argv[++loop]; fac = argv[++loop]; addr = argv[++loop]; add_redir_to_socket(filtered, prio, fac, addr, &predir, &n_redirect); } else if (strcmp(argv[loop], "-F") == 0 || strcmp(argv[loop], "--config") == 0) { config_file = argv[++loop]; if (file_exist(config_file) == -1) error_exit("Configuration file %s does not exist.\n", config_file); (void)do_load_config(-1, NULL, config_file); } else if (strcmp(argv[loop], "-Z") == 0) { markerline_attrs = parse_attributes(argv[++loop]); } else if (strcmp(argv[loop], "-T") == 0) { timestamp_in_markerline = 1; } else if (strcmp(argv[loop], "-S") == 0) { show_subwindow_id = 1; } else if (strcmp(argv[loop], "-t") == 0) { ++loop; if (!argv[loop]) error_exit("-t requires a parameter.\n"); win_title = mystrdup(argv[loop]); } else if (strcmp(argv[loop], "-x") == 0) { ++loop; if (!argv[loop]) error_exit("-x requires a parameter.\n"); set_title = mystrdup(argv[loop]); } else if (argv[loop][0] == '-' && toupper(argv[loop][1]) == 'P') { char *lw = argv[++loop]; if (lw) { if (argv[loop - 1][1] == 'P') all_line_wrap = 1; else all_line_wrap = 0; cur_line_wrap = lw[0]; if (cur_line_wrap == 'o') cur_line_wrap_offset = get_value_arg("-p", argv[++loop], VAL_ZERO_POSITIVE); else if (cur_line_wrap != 'a' && cur_line_wrap != 'l' && cur_line_wrap != 'r' && toupper(cur_line_wrap) != 'S' && cur_line_wrap != 'w') error_exit("Invalid mode for -p\n"); } else error_exit("-p requires a parameter.\n"); } else if (strcmp(argv[loop], "--retry") == 0) { retry = 1; } else if (strcmp(argv[loop], "--retry-all") == 0) { retry_all = 1; retry = 1; } else if (strcmp(argv[loop], "-n") == 0) { initial_n_lines_tail = get_value_arg("-n", argv[++loop], VAL_ZERO_POSITIVE); } else if (strcmp(argv[loop], "-N") == 0) { initial_n_lines_tail = min_n_bufferlines = get_value_arg("-n", argv[++loop], VAL_ZERO_POSITIVE); } else if (strcmp(argv[loop], "-b") == 0) { tab_width = get_value_arg("-b", argv[++loop], VAL_ZERO_POSITIVE); } else if (strcmp(argv[loop], "-u") == 0) { update_interval = get_value_arg("-u", argv[++loop], VAL_ZERO_POSITIVE); } else if (argv[loop][0] == '-' && toupper(argv[loop][1]) == 'R') { if (argv[loop][1] == 'R') do_diff = 1; if (argv[loop][2] == 'c') restart_clear = 1; restart = get_value_arg("-r/R", argv[++loop], VAL_ZERO_POSITIVE); } else if (strcmp(argv[loop], "-s") == 0) { split = get_value_arg("-s", argv[++loop], VAL_POSITIVE_NOT_1); } else if (strcmp(argv[loop], "-sw") == 0) { argv_set_window_widths(argv[++loop]); } else if (strcmp(argv[loop], "-sn") == 0) { argv_set_n_windows_per_column(argv[++loop]); } else if (strcmp(argv[loop], "-wh") == 0) { window_height = get_value_arg("-wh", argv[++loop], VAL_POSITIVE); } else if (strcmp(argv[loop], "-fr") == 0) { char *par = argv[++loop]; for(;;) { int filter; char *komma = strchr(par, ','); if (komma) *komma = 0x00; filter = find_filterscheme(par); if (filter == -1) error_exit("'%s' is not a known filter scheme.\n", par); duplicate_re_array(pfs[filter].pre, pfs[filter].n_re, &pre, &n_re); if (!komma) break; par = komma + 1; } } else if (strcmp(argv[loop], "-cv") == 0) { char *par = argv[++loop]; for(;;) { char *komma = strchr(par, ','); if (komma) *komma = 0x00; add_conversion_scheme(&conversions, par); if (!komma) break; par = komma + 1; } } else if (argv[loop][0] == '-' && toupper(argv[loop][1]) == 'E') { loop += argv_add_re(argv[loop], &argv[loop + 1], invert_regex, &pre, &n_re, &pre_all, &n_re_all); } else if (strcmp(argv[loop], "-v") == 0) { invert_regex = 1; } else if (strcmp(argv[loop], "-csn") == 0) { syslog_noreverse = 1; } else if (argv[loop][0] == '-' && toupper(argv[loop][1]) == 'C') { loop += argv_color_settings(argv[loop], &argv[loop + 1], &allcolor, &curcolor, &field_index, &field_delimiter, &cdef, &cur_term_emul, &cur_color_schemes, &alt_col_cdev1, &alt_col_cdev2); } else if (strcmp(argv[loop], "-f") == 0) { follow_filename = 1; } else if (strcmp(argv[loop], "--follow-all") == 0) { default_follow_filename = 1; follow_filename = 1; } else if (strcmp(argv[loop], "-w") == 0) { use_colors = 0; } else if (argv[loop][0] == '-' && argv[loop][1] == 'k') { loop += argv_add_stripper(argv[loop], &argv[loop + 1], &pstrip, &n_strip); } else if (strcasecmp(argv[loop], "-m") == 0) { if (argv[loop][1] == 'M') setallmaxlines = 1; maxlines = get_value_arg("-m/M", argv[++loop], VAL_ZERO_POSITIVE); } else if (strcasecmp(argv[loop], "-mb") == 0) { if (argv[loop][1] == 'M') setallmaxbytes = 1; maxbytes = get_value_arg("-m/M", argv[++loop], VAL_ZERO_POSITIVE); } else if (strcasecmp(argv[loop], "--label") == 0) { label = mystrdup(argv[++loop]); } else if (strcasecmp(argv[loop], "-i") == 0 || argv[loop][0] != '-' || strcasecmp(argv[loop], "-l") == 0 || strcasecmp(argv[loop], "-j") == 0 || strcasecmp(argv[loop], "-iw") == 0 || strcasecmp(argv[loop], "--listen") == 0) { struct stat64 buf; char *dummy; char is_cmd = 0; char is_sub = 0; char is_giw = 0; char is_sock = 0; char is_stdin = 0; int check_interval = 0; proginfo *cur; if (strcasecmp(argv[loop], "-l") == 0) { is_cmd = 1; } else if (strcasecmp(argv[loop], "-j") == 0) { if (used_stdin == MY_TRUE) error_exit("One can use %s only once.\n", argv[loop]); is_stdin = used_stdin = 1; } else if (strcasecmp(argv[loop], "-iw") == 0) { if (argv[loop + 2]) { if ((argc - loop) > 2 && isdigit(argv[loop+2][0])) { is_giw = 1; check_interval = atoi(argv[loop + 2]); } else { check_interval = 5; } } else error_exit("-iw requires 2 parameters.\n"); } else if (strcasecmp(argv[loop], "--listen") == 0) { is_sock = 1; } if (strcmp(argv[loop], "-L") == 0 || strcmp(argv[loop], "-I") == 0 || strcmp(argv[loop], "-Iw") == 0 || strcmp(argv[loop], "-J") == 0 || strcmp(argv[loop], "--Listen") == 0 || merge_all) { is_sub = 1; if (merge_all & (merge_in_new_first == 1)) { is_sub = 0; merge_in_new_first = 0; } } if (argv[loop][0] == '-' && toupper(argv[loop][1]) != 'J') { loop++; } dummy = argv[loop]; if (is_sub == 1 && nfd > 0) { cur = &pi[nfd - 1]; while(cur -> next) { cur = cur -> next; } cur -> next = (proginfo *)mymalloc(sizeof(proginfo)); cur = cur -> next; nsubwindows[nfd-1]++; } else { pi = (proginfo *)myrealloc(pi, (nfd + 1) * sizeof(proginfo)); lb = (buffer *)myrealloc(lb, (nfd + 1) * sizeof(buffer)); nsubwindows = (char *)myrealloc(nsubwindows, (nfd + 1) * sizeof(char)); nsubwindows[nfd] = 1; memset(&lb[nfd], 0x00, sizeof(buffer)); lb[nfd].maxnlines = maxlines; if (!setallmaxlines) maxlines = -1; lb[nfd].bufferwhat = bufferwhat; bufferwhat = default_bufferwhat; lb[nfd].maxbytes = maxbytes; if (!setallmaxbytes) maxbytes = -1; if (marker_of_other_window || lb[nfd].marker_of_other_window) lb[nfd].marker_of_other_window = 1; else if (no_marker_of_other_window == 1) lb[nfd].marker_of_other_window = -1; /* override global configfile setting */ no_marker_of_other_window = marker_of_other_window = 0; cur = &pi[nfd]; nfd++; } memset(cur, 0x00, sizeof(proginfo)); /* see if file exists */ if (check_interval == 0 && is_cmd == 0 && is_stdin == 0 && is_sock == 0 && retry == 0 && stat64(dummy, &buf) == -1) { fprintf(stderr, "Error opening file %s (%s)\n", dummy, strerror(errno)); exit(EXIT_FAILURE); } /* init. struct. for this file */ if (is_stdin == 0) { if (!dummy) error_exit("No filename given.\n"); cur -> filename = mystrdup(dummy); } else { cur -> filename = mystrdup("STDIN"); } if (is_cmd) cur -> wt = WT_COMMAND; else if (is_stdin) cur -> wt = WT_STDIN; else if (is_sock) cur -> wt = WT_SOCKET; else cur -> wt = WT_FILE; cur -> check_interval = check_interval; cur -> win_title = win_title; win_title = NULL; cur -> close_idle = close_idle; close_idle = 0; /* initial number of lines to tail */ cur -> initial_n_lines_tail = initial_n_lines_tail; initial_n_lines_tail = min_n_bufferlines; /* default window height */ cur -> win_height = window_height; window_height = -1; /* store regular expression(s) */ cur -> pre = pre; cur -> n_re = n_re; pre = NULL; n_re = 0; if (n_re_all > 0) duplicate_re_array(pre_all, n_re_all, &cur -> pre, &cur -> n_re); /* hide this window? */ cur -> hidden = 0; /* add timestamp in front of each line? */ cur -> add_timestamp = do_add_timestamp; do_add_timestamp = 0; /* strip */ cur -> pstrip = pstrip; cur -> n_strip = n_strip; pstrip = NULL; n_strip = 0; cur -> conversions = conversions; init_iat(&conversions); /* line wrap */ cur -> line_wrap = cur_line_wrap; cur -> line_wrap_offset = cur_line_wrap_offset; if (!all_line_wrap) { cur_line_wrap = default_linewrap; cur_line_wrap_offset = default_line_wrap_offset; } cur -> retry_open = retry; cur -> follow_filename = follow_filename; follow_filename = default_follow_filename; cur -> cont = cont; cont = 0; /* 'watch' functionality configuration (more or less) */ cur -> restart.restart = restart; cur -> restart.restart_clear = restart_clear; restart = -1; /* invalidate for next parameter */ restart_clear = 0; cur -> restart.first = 1; cur -> restart.do_diff = do_diff; do_diff = 0; cur -> repeat.suppress_repeating_lines = no_repeat; cur -> mark_interval = mark_interval; /* colors */ cur -> cdef.attributes = cdef; cur -> cdef.alt_col_cdev1 = alt_col_cdev1; cur -> cdef.alt_col_cdev2 = alt_col_cdev2; cur -> cdef.syslog_noreverse = syslog_noreverse; if (curcolor != 'n' && curcolor != 0) { cur -> cdef.colorize = curcolor; cur -> cdef.color_schemes = cur_color_schemes; init_iat(&cur_color_schemes); } else { if (allcolor == 'n' && default_color_scheme != -1) { cur -> cdef.colorize = 'S'; add_color_scheme(&cur -> cdef.color_schemes, default_color_scheme); } else if (allcolor == 'n') { cur -> cdef.colorize = 0; } else if (allcolor == 'S') { cur -> cdef.colorize = 'S'; cur -> cdef.color_schemes = cur_color_schemes; } else { cur -> cdef.colorize = allcolor; } } cur -> cdef.field_nr = field_index; cur -> cdef.field_del = field_delimiter ? mystrdup(field_delimiter) : NULL; cur -> statistics.sccfirst = 1; curcolor = 'n'; if (!retry_all) retry = 0; if (is_giw) /* skip over the checkinterval */ loop++; cur -> cdef.term_emul = cur_term_emul; cur_term_emul = TERM_IGNORE; /* redirect input also to a file or pipe */ cur -> n_redirect = n_redirect; n_redirect = 0; cur -> predir = predir; predir = NULL; cur -> beep.beep_interval = cur_beep_interval; cur_beep_interval = -1; cur -> label = label; label = NULL; } else if (strcmp(argv[loop], "-du") == 0) { statusline_above_data = 1; } else if (strcmp(argv[loop], "-d") == 0) { mode_statusline = 0; } else if (strcmp(argv[loop], "-D") == 0) { mode_statusline = -1; } else if (strcmp(argv[loop], "-z") == 0) { warn_closed = 0; } else if (strcmp(argv[loop], "--new-only") == 0) { char *type = argv[++loop]; if (type) { if (strcasecmp(type, "atime") == 0) new_only = TT_ATIME; else if (strcasecmp(type, "mtime") == 0) new_only = TT_MTIME; else if (strcasecmp(type, "ctime") == 0) new_only = TT_CTIME; else error_exit("--new-only requires either atime, mtime or ctime as parameter, not '%s'.\n", type); } else error_exit("--new-only requires a parameter.\n"); } else if (strcasecmp(argv[loop], "-q") == 0) { int merge = argv[loop][1] == 'Q'; int check_interval = get_value_arg("-q/-Q", argv[++loop], VAL_ZERO_POSITIVE); char *check_glob = argv[++loop]; add_glob_check(check_glob, check_interval, merge, new_only); } else if (strcmp(argv[loop], "--mark-change") == 0) { marker_of_other_window = 1; } else if (strcmp(argv[loop], "--no-mark-change") == 0) { no_marker_of_other_window = 1; } else if (strcmp(argv[loop], "-o") == 0) { loop++; if (!argv[loop]) error_exit("-o requires a parameter.\n"); else config_file_entry(-1, argv[loop]); } else if (strcmp(argv[loop], "--beep-interval") == 0) { beep_interval = get_value_arg("--beep-interval", argv[++loop], VAL_POSITIVE); } else if (strcmp(argv[loop], "--bi") == 0) { cur_beep_interval = get_value_arg("--bi", argv[++loop], VAL_POSITIVE); } else { if (strcmp(argv[loop], "-h") != 0) fprintf(stderr, "unknown parameter '%s'\n", argv[loop]); usage(); exit(1); } } for(loop=0; loopnew(GEOIP_STANDARD); $| = 1; while(<>) { chomp($_); $country = $gi->country_code_by_addr($_); if ($country eq '') { $country = '?'; } print "$_ ($country)\n"; } multitail-6.0/Changes0000644000175000017500000011057212245202637014565 0ustar folkertfolkert1.2 - can now also vertically split the screen 1.3 - this version fixes an important bug: previous versions did not stop the tail-processes they started. 1.4 - this version adds regular expressions. You can now define for each window (or for all) what lines should be displayed 1.5 - this version adds a nice interactive menu for adding and deleting windows 1.6 - fixed printing of extended ascii (accents etc.) - added swapping of window-positions - added toggling of windows verticalsplit - switched on extended regular expressions parsing 1.7 - small fixes and additions - MultiTail should now compile on Solaris 8 1.8 - adds negation for regular expressions - one can now also select the field which is used to determine the color (when you prefer to select the date, for example) - some small fixes for FreeBSD. 1.9 - you can now also tail external commands with MultiTail, for example the output of ping. 1.91 - sometimes when using the '-l' command to get the output of other commands into MultiTail, nothing would appear in the window(s). this release should fix that. 2.0 - negating regular expressions did not work. fixed 2.1 - this release features filename completion when adding files through the menu 2.2 - fileselectors now shows the files sorted, has page up/down support and actually works. - also the windows are now initially filled as much as possible. 2.3 - field delimiters (for -cf) can be any size now (not just one character) - statuslines are now optional - if a window closes, a popup-box is displayed (can be switched off) - fixed a small memory-leak - fixed a potential segfault - fixed a couple of (sp)lint-warnings - radically changed the errorhandling - some fixes to get thing work (again) on MacOS-X. WARNING: the installationlocation has been changed to /usr/bin! So first do make uninstall on the previous release! 2.4 - --retry now also works on platforms where tail does not support --retry - MultiTail works again on Solaris - added "merge-mode": all files are merged into one window - made window-selection into a scrollable window - fixed compilation-warnings - windows are filled as much as possible again on Solaris 2.5 - file completion on current directory now no longer segfaults - removed the "merge windows"-facility and added multiple files/commands per windows (for greater flexibility) - cleaned up source - re-introduced the window-numbers (for easier navigation) - fixes for Solaris - highlighting of regular expressions - MultiTail can now write its own startup-scripts 2.5.1 - fixed deleting of subwindows: sometimes left-over processes would still be in memory - fixed regular expressions 2.5.2 - one can now set the screenrefresh interval: usefull when you use MultiTail over a slow link - sets the terminal explicit to 'dumb' when starting a process, so one can now use for example -l "top" - several small fixes (long filenames would garble the screen, etc.) 2.6 - compiles and works again when compiling with CygWin - several small fixes - one can now exit most menus by pressing q or x - one can now set a mark in a window and scroll back (and forth) 2.6.1 - some cosmetic changes - one can now safely press ctrl+c 2.6.2 - fixed important bug in scrollbackfunctionality - also simplified scrollback a lot (no need to explicitly set a mark anymore) 2.6.3 - memory leak fix - small enhancements to userinterface 2.6.4 - MultiTail can now be started without any parameters: a menu is then presented enabling you to add windows on the fly 2.7 - adding another entry to a 'merged' entry did not work correctly (would overwrite 2nd and further entries) - fixed 2.7.1 - "follow filename" no longer worked: fixed 2.7.2 - screen did not refresh after displaying info or help: fixed - added a pause option ('p') 2.7.3 - first release to run under Irix - you can now set DESTDIR when running 'make install' - ctrl+z works again 2.7.4 - first release that runs on HP-UX 2.8 - you can now run a command in a loop with a delay, like the 'watch' command (just starting watch with the -l parameter doesn't work since it wants to redraw the complete display) - first release that runs on AIX 2.8.1 - it is now possible to only have the difference printed for each run of a command 2.8.2 - a couple of small fixes 2.8.3 - added option which enables/disables linewrap - can also set if you want everything from the right on 2.8.4 - you can now explicitly set an offset for linewrap 2.8.5 - multiple regular expressions per file are now possible - no more need to explicitly set a regular expression when adding a file/command: you can do that through the new regular-expression edit menu 2.9 - small enhancements to regularexpression edit window 2.9.1 - you can now also let a beep be heard when a regular expression matches - fixed a bug with regular expressions 2.9.2 - code cleanup: a lot of redundant code was removed, making the application somewhat smaller and the code a little better to read 2.9.3 - One can now set *what part* of a line is what color through regular expressions which are loaded from a file (see multitail.conf). 2.9.4 - introduced default colorscheme - couple of small fixes 3.0.0 - added manual, fixed '-CS' switch 3.0.1 - fixed get_load() for older GLIBC systems (thanks to Ville Herva) - color-selection menu did not correctly handle the 'n'-key, fixed (thanks to Rene Engelhard) 3.0.2 - the '-m'/'-M' (setting the number of buffered lines) did not work, fixed now - the fileselector would sometimes segfault 3.0.4 - adding windows would make MultiTail sometimes segfault 3.0.5 - now the windows get only updated every there's really something to update - the status-line will be updated ones for all current changes to a window - keys 1...0 will add a marker-line to window 1...10(!) - I had accidently left some debuggingcode in the scrollback-code, that is now removed 3.0.6 - changed window-number of the markerline to 0...9 3.1.0 - moved some functions from mt.c to utils.c/term.c - added a statisticswindow ('t' in main window) - small optimalisations - added mailcheck: the statusline will become green if there's new mail - configfile is now first loaded from /etc/multitail.conf and then from ~/.multitailrc - fixed a couple of memory leaks (not all windows were cleaned-up after) and made screenredraw a little smarter - One can now also select default colorschemes for files. 3.1.1 - TABs are now also printed correctly - tab-stop/width can be configured through the configurationfile 3.1.2 - several small fixes - more details are given when the compilation of a regular expression fails - you can now always press 'q' to leave a popupwindowmenu - window sizes are now configurable - one can now bind actions (external programs) to keys which no current function in MultiTail (for example: 'g' starts /usr/bin/ping) 3.1.3 - no longer fails if you use a maildir instead of a mailfile 3.1.4 - improved scrolling in "scrollback-screen" quiet a lot - -e switch did not work correctly - added -ex/Ex which executes a given command if the regexp matches - fixed small memoryleak that happened when multiple regexps fail 3.1.5 - -ex did not work when there was a " or a ` in the matching string - if the program executed did output anything, the screen got scrambled: output is now all redirected to /dev/null 3.1.6 - one can now select the number of lines to tail initially - scrollback would segfault for very large lines 3.1.7 - code cleanup - macosx now also uses ncurses, this means that you need at least MacOS X 10.2 3.1.8 - the previous version would only compile on Linux - in MacOS X 'off_t' is defined as a 64 bit integer, this requires some specific handling for printf() - would fail when selecting colorschemes via the commandline - The displayed modificationdate would also update if the terminal was resized. 3.1.9 - more efficient use of terminal: no more empty line above the statusline 3.1.91 - preventing (n)curses to redraw the whole display when only one window was cleared 3.1.92 - one can now set the xterm title to some arbitrary text 3.2.1 - when setting a nodename in the xterm titlebar, multitail would segfault. 3.2.2 - multitail now also compiles and runs on Tru64 v5.1b and HP-UX v11.23 3.2.3 - repeatingly executing the same command did not work 3.3.0 - use information of getpwent() instead of environmentvariables - xterm title is cleared when multitail exits - statusline can be set to a descriptive description instead of the filename/commandline 3.3.1 - rewrote terminal-resize code, should be much more stable now - added code for handling 'F5': that refreshes the terminal - one can now press 'F1' at any time to get context sensitive help - some small layout fixes - when adding a file one can select wether to follow the filename or the filedescriptor 3.3.2 - in the first 5 seconds a message is now displayed in the statusline indicating that F1 will give help 3.3.3 - one can now give a filepattern to look for: if a new file (more recent modification time) appears matching the given filepattern multitail will automatically start tailing that file (-iw pattern check_interval => don't forget the quotationmarks around the filepattern!) 3.3.4 - if you enabled mailcheck and deleted the mail file - multitail would exit with an error indicating it couldn't check the file for changes - ^h (ctrl+h) now also shows you help 3.3.5 - fixed the way parameters were given to the tail process (it worked, but it was not the correct way to do it) - MultiTail should compile again on MacOS X - code-cleanup - it no longer bails out when the mailfolder did not exist - removed C++-style comments - fixed handling of wildcards 3.3.6 - use default background color instead of black - some changes - makefile for CYGWIN (win32) 3.3.7 - one can now explicitly select a configurationfile (using --conf filename) 3.3.8 - added makefile for creating CSW packages 3.4.0 - merged stable version 3.2.3 with development version 3.3.8 3.4.1 - compiles again on CygWIN 3.4.2 - added 'black' for color-output 3.4.3 - added blink/underline/reverse/bold for color-output - fixed compilation warning on SUN - Solaris now also uses ncurses 3.4.4 - output for certain logfiles and the output of some external commands (like wget) would show up garbled, that is now fixed 3.4.5 - multitail is now 64bit safe: it can now monitor files > 2GB 3.4.6 - compiles again on Free-, Open and NetBSD 3.4.7 - in certain cases, lines would appear split in two 3.4.8 - fixed a handle leak which occured when repeatingly viewing the output of an external command 3.4.9 - compiles again on MacOS X, fixed typo in on-line help 3.5.0 - replaced '-v -e[m]' by '-e[m]' (print if match) and '-ev' (print if not match). that is different from the previous situation: it a regular expression is found while checking the filter which says -e (or -em), output will only be given if the regexp matches with the pattern in the inputbuffer. -ev: only print if the regexp does NOT match. checking of the filters stops when there's a match for either -e[m] or -ev 3.5.1 - fixed a segfault when scrolling back 3.5.2 - fixed a segfault when giving incomplete commandline parameters - 'l' now lists the user-keybindings defined in /etc/multitail.conf - some UI fixes 3.5.4 - checks for posix version 200112 and acts to it - the 'pause'-key is now really working - improved gui - improved fileselector 3.5.5 - improved fileselector (on can now see what entry is a directory) - pause no really works 3.5.6 - added functionality so that programs or files with terminal escape-codes in them for colors are displayed correctly (WITH their colors) 3.5.7 - the 'r' was ignored when entering filenames (fixed) - when the program was started without any parameters, only a blank screen was showed (fixed) 3.6.0 - merged the stable release 3.4.8 with the development release 3.5.7 - replaced 'q' with ctrl+'g' 3.6.1 - when cut-off (instead of linewrap) was switched on blank lines would appear, fixed 3.6.2 - getloadavg doesn't exist in uClibc 3.7.0 - SIGHUP now restarts the tail-processes - one can now define in the configurationfile what program to use for tail: for example when one uses turbotail 3.7.1 - one can now set each logfile to one (1) color; usefull when merging multiple logfiles - starting multitail without any inputs and then adding a program would fail (fixed) 3.7.2 - one can now have filesizes be abbreviated (to KB/MB/GB) 3.7.3 - parts of the input-data can now be stripped using ranges, reg.exps or columns 3.7.4 - the regexp edit menu now also display how often a regexp matched - no more segfaults when exiting multitail while using '-E...' - no more segfaults when the buffer was cleared - the terminal can now be splitted in more then 2 columns - number of windows per column can now be configured - commandline parameters are now checked for validity - when stripping using regexps: multiple matches are now processed correctly - fixed some small memoryleaks 3.7.5 - delimiters can now be multicharacters - fixed a memory leak in the strip-code - some fixes for compilers where a char is unsigned: statusline mode would not work, aborting a "yes/no" question would fail, monitoring of processes that stop would give errornous results - when the 'o' (or 'O') is pressed in the main-menu, a window is cleared - improved scrollback-searchfunction-regularexpression errorhandling - code cleanups 3.7.6 - updated a couple of help-pages - 'O' (uppercase 'o') now clears all windows - fixed saving scrollback-buffer to a file (would segfault) - counting of matching regexps did not work 3.7.7 - fixed small memory leak *** Current stable tree *** 3.8.0 - merge with 3.7.7 3.8.1 - on some platforms starting multitail would fail (HP-UX for example) due to the new strip-code - stripping in a line with regexps would most of the time not work 3.8.2 - specific colors (instead of a colorscheme) would not work - a colorscheme was always used. that is now fixed. 3.8.3 - if one (or more) window(s) was hidden, clearing that (or all) window would cause a segfault 3.8.4 - fixed a memoryleak 3.8.5 - running other programs from within multitail would not work on Solaris/AIX/HP-UX 3.8.6 - in the scrollback screen (press 'b') the cursor keys up and down did not work 3.8.7 - added complete GNU license textfile to the tarball instead of the website reference 3.8.8 - fixes to make it compile on Solaris 10 with the www.sunfreeware.com gcc-package and the SUNWspro c-compiler 3.8.9 - the 'toggle color'-menu did not fit in its window - -s/-cS/-cT without a parameter would segfault 3.8.10 - pressing delete when editing a line could give garbage - searching in the scrollback window would give segfaults in certain situations, also "search next" would not work *** Current development tree *** 3.9.0 - one can now set the backgroundcolor as well (for colorschemes and such) 3.9.1 - when merging multiple inputs (e.g. logfiles) one can now prepend the logged line with the subwindow-number (-S) so that you can easily determine which one is logging - one can now using -Z set the colors of the markerline - one can now also get a timestamp in the markerline - configfile can now be set in the makefile or via the commandline (-F) 3.9.2 - fixed a segfault in scrollback caused by the changes to the markerline - number of matches-counters are now updated every 2 seconds in the regexp edit menu - in the regexp edit menu the popup window would get garbled under certain circumstances - added a colorscheme for 'rsstail' (http://www.vanheusden.com/rsstail/) - long filenames are now abbreviated with '...' in the middle 3.9.3 - like the 'tee'-command or like using the '|' the input can now be written to a file or send to an other process, before or after filtering 3.9.4 - removed heartbeat (bouncing cursor) as it didn't work anyway, merged fix from 3.8.5 3.9.5 - added several colorschemes (for squid, asterisk, acctail, wtmptail, (isc-)dhcpd, smartd, firewall logging, bind9, kerberos, samba, httping, mailscanner, exim, sendmail, netstat and tcpdump) - multiple colorscheme per file is now possible - multitail can now convert ip-adresses to hostnames and seconds since the UNIX epoch to user configurable timeformats - in case one is monitoring something without timestamps multitail can add them itself - type ahead in the fileselector only worked for colorlists - brought back the visual heartbeat (fixed it!) - fixed the 'change color' menu (would sometimes not be big enough) - fix for colorscheme substring regular expression, code cleanups. 3.9.6 - added colorschemes for oracle and ntpd - multitail can now check if values in the input are <, > or == and choose a color on the outcome - previous versions would segfault on B&W terminals - fixed scrolling up and down in the scrollback menu - added scroll left/right in the scrollbackmenu - one can now set the linewrap from within multitail - if there are more colorschemes then will fit in one view it will scroll - rewrote the "write script" fuctionality (to include all commandline parameters) 3.9.7 - scrollup in the scrollback window when linewrap is disabled would give garbled output - added menu for editing part-of-string-filters - code cleanup - added complete GNU license textfile to the tarball instead of the website reference - one can now redirect output to multiple files and processes at once ('multitee') 3.9.8: - fixes to make it compile on Solaris 10 with the www.sunfreeware.com gcc-package and the SUNWspro c-compiler - multitail can now read from STDIN (like the original tail) 3.9.9: - the next parameter behind "-j"/"-J" was (incorrectly) ignored - added colorscheme for nagtail - code cleanup 3.9.10 - "edit/delete strip-regexp" while no regexps were defined would cause a segfault (same thing for horizontal split) - added "--[no-]mergeall" switch - added new coloschemes (for WebsPhere, procmail, nntpcache and Veritas Netbackup) - "no linewrap"-mode in scrollback did not work - reverse in colorschemes worked only for 1 character (the first one) - one can choose to only display the filename and not the complete path in the statusline - now multiple default colorschemes per file can be configured in multitail.conf - fixed "diff"-view 3.9.11 - certain invalid parameters would cause a segmentation fault instead of an error message - several code cleanups - (sub-)windows can now automatically be closed when longer idle then a given time in seconds - extended ascii and control characters can now be suppresed via the configuration-file - added "alternating colors" - statusline timestamp format is now configurable - can now convert errno to a descriptive string - can convert decimal values to hex and vice versa - '-cT' did not work when monitoring output of external commands - -s/-cS/-cT without a parameter would segfault, added colorscheme for Checkpoint Firewall-1 logging 3.9.12: - removed maximum colors per line limit - buffersize can now be set in bytes instead of number of lines - beep method can be changed (beep/flash/none) - ported to SCO OpenServer (tested on v6) 3.9.13: - control characters can be displayed in caret notation - one can now send signals to running commands - the last exit status of command (when using -l/-L with -R) is now displayed in the statusline - when a window closes, the exitcode of the command that ran is displayed - added pppd colorscheme - added screendump functionality - logfiles can now be truncated from within multitail (press 'T') - fixed a memoryleak - some small optimalizations - improved line editor - default umask for created files can be set in configurationfile - fixed a bug where adding a new subwindow through the UI would fail - 8 bit ascii was printed (incorrectly) in reverse 3.9.14: - MultiTail can now automatically open a new window if a new file was found - improved color selection interface - statusline can now be put above the data window - colors and attributes of statusline can be configured - added colorscheme for Netscape Directory server (LDAP) - added color for 'vmstat' - multiple attributes (bold, underline, reverse, etc.) can now be set for a colorscheme - repeating lines can be suppressed - a line (like the syslog '--- mark ---' line) can be printed when there's no activity in a window for a configurable time - improved navigating the on-line help text - some fixes for default number of lines/kb to buffer - pressing delete when editing a line could give garbage - searching in the scrollback window would give segfaults in certain situations, also "search next" would not work - added key for hiding all windows but the one selected - added key for unhiding all windows at once 3.9.14.1: - merging a window and then setting colors would cause a segfault - csn prevents multitail from using inverted colors 3.9.14.2: - 'write_script' sometimes wrote incorrect scripts - width of the window-selectionbox was not always big enough - '---- MARK ----' is replaced by the usual markerline - some fixes in the veritas netbackup colorscheme 3.9.15: - added colorscheme for log4j - lines ending with '\' can now be reconnected into one again - when multitail switches from one logfile to the other in one window, a markerline can be printed (like the original tail does) - fixed a segfault that happened when adding a new non-merged window via the gui - the colorscheme(s) in the "write script" functionality did not work - -em/-ev etc. hopefully finally work - cygwin version had problems finding its configfile - some colorconfigurations produced back-on-black text - fixed the code that decides on the sizes of the windows to display - for the 'default parameters for filepattern' configurationfile settings the real path is used - a window that closed by itself could cause an occasional segfault - when the terminal gets resized, the current popup window is moved - on really small windows the markerline would get garbled - cursor key down in the scrollback window now goes back one line as it should - the example multitail.conf is now copied to multitail.conf.new so that your own personalized version is not overwritten 3.9.16: - color attributes were not always correctly emitted when writing scripts - improvements to the window-size management window - when a subwindow-change happened and the new line was suppressed, it would incorrectly emit a markerline (if enabled) - markerline for when multitail changes subwindow can now have its own colors - "idle markerline" can now have its own colors - standard syslog '-- MARK --' can now automatically replaced by the multitail markerline 4.0.0-rc1: - merged stable release 3.8.10 with development release 3.9.16 4.0.0-rc2: - the functionality which determines what is buffered and what not no longer worked. fixed. 4.0.0-rc3: - multitail did not notice file truncations - closing windows did not tell what their contents was - strftime on Solaris and IRIX doesn't like %2d (the '2') - some makefiles would still overwrite existing makefiles 4.0.1: - realpath(), fscanf() and dup() returncodes were ignored, fixed a bufferoverrun in set_window_sizes() - on at least FreeBSD all colors were gone, fixed that 4.0.2: - runs again on B&W terminals 4.0.3: - -C on a B&W terminal would crash - statusline was not inverse on B&W 4.0.4: - compiles again on HP-UX - -t/-x commandline parsing fixed - replaced SIGHUP by SIGUSR1: this fixes the problem that multitail doesn't exits when your terminal gets closed. it would then use all cpu 4.0.5: - %2d for strftime does not work on all platforms - when your shell was different from bash processes would not be killed correctly - ported to AIX 5.3 with the standard AIX c-compiler (thanks to BPSolutions) 4.0.6: - multitail still did not stop all child processes (in some situations) when it was stopped. that should now be fixed 4.0.7: - fixed bug in the Makefile: 'make thanks' would sometimes fail 4.1.0: - one can now set configurationfile settings from the commandline with '-o' - -rc and -Rc now clear the window between each run - priority of the regular expressions for filtering can now be changed at run-time - using '/' in the main-menu, one can now search in all windows; the result will be merged in a new popup-window - using shift + '/' ('?') in the main-menu, one can now highlight a search-string in all windows - case sensitivity of searches can now be set in at run-time by pressing 'I' or in the configurationfile (for scrollback search, merge search and highlight search) 4.1.1: - can now convert 'TAI64' timestrings (as emitted by Q-mail) to ascii string - now removes markerlines when merging search-results of multiple windows - fixed a segfault that happens when the last line in a window is a markerline and a redraw of the screen is forced - -iw did not work - added --no-load_global_config to prevent loading of configfile from /etc - scrolling to the right in the scrollback window now keeps the colors - wether to follow the filename or the filedescriptor (the file that we started to follow initially) default parameter can be set in the configfile - default linewrap mode can be set in the configfile - added -P which is like -p but for all following windows - linewrap handling was broken when there were tabs in the input - add --retry-all which is like --retry but for all following files - -cv can now handle multiple conversionschemes at once. e.g.: -cv scheme1,scheme2,scheme3 etc. no spaces around the komma! - one can now predefine a set of filters in the configfile - fixes for statistics popup (number of runs/last rc were not displayed) - --retry did not work - 'B' in the main window will start the scrollback window with all windows merged into one window - empty lines in the scrollback window would give a garbled screen - tabs were not always printed correctly - -P worked only for the first window - ^h in the main menu would not work - added keybindings similar to those of 'less' to the scrollbackwindow 4.1.2: - performance optimalisation of the idle loop - performance optimalisation of the xterm header update routine - normal (not merged) scrollback no longer worked - optimised 'strip string' for the cases were nothing was found to strip - one can configure multitail not to close a window when the process underneath stopped/exited - windows can be restarted - one can now set if empty lines should be suppressed or not (in the configfile) 4.2.0: - merged 4.1.2 with stable release 4.0.7 - typo fix in stripstring.c 4.2.1: - on AIX, the program would exit because of an realloc of 0 bytes 4.3.0: - one can now define a set of edit-rules in the configfile - fix to get it to compile (and run) on MacOS X again - splitline can be removed and attributes can be configured (in the configfile) 4.3.1: - a label can be set which will be put in front of each line using '--label "text"' - cnv_ts_format would overrule the the settings for ts_format, that is now fixed - -eC/-ec did not work - compilation fixes voor freebsd 4.x - on GNU LIBC systems, a backtrace is now shown in error situations - attributes for reverse can now be configured in the configfile (used for, e.g. -eC) - optimized code - tabs would garble the display - the abort/exit key can now be configured in the configfile - updated help (-h) output - updated man-page 4.3.2: - ^l now also redraws the terminal - cursor keys did not work anymore - accidently still had -pg (profiling support) in the Makefile switched on - convert (-cv) can now also use an external script/program for the conversions! - multiple occurences for a convert can now be in a line 4.3.3: - can convert a value to KB/MB/GB - added colorscheme for 'p0f' - added beep-functionality: multitail can now beep every line it processes (or every x-th line) - colorschemes can now be determined by an external script - added colorscheme for mpstat (part of sysstat) - 'fixed' (it was not really a bug) a deadlock for colorschemes and conversionschemes where no substring select was done by the regexp - cleaned up and optimised (for speed - loading 67000 colorschemes is now 200x faster) the configfileparser - added page-up/down for colorscheme selection - order of colorschemes and script:-lines in the configfile no longer matters 4.3.4: - terminal emulation can now also be selected via the menus - default conversion scheme for a file can now be selected - conversion schemes can now be selected at runtime (^v) (only for new lines) - adding subwindow-id before each line can now be toggled at runtime (only for new lines) - when selecting a subwindow, the subwindow number is displayed in front of each subwindow-name as well - when adding a timestamp, a subwindownumber or a label before each line, colorschemes would get garbled - code cleanups - performance optimalisation of buffering; expected speed-up is 90% - added colorscheme for portsentry - updated on-line help - lists of subwindows did not show file/command name - fixed memoryleak in merged scrollback - fixes in the cs_re* logic - fixed sysstat colorschemes - merged view (after a (global) search) can now show the window/subwindow number in front of each line (press ^t) - fixes so that on IRIX MultiTail compiles without path-changes to the Makefile - added -[eE]X which only gives the matching substring as a parameter to the command which is invoked for each matching line - fixed a warning about TERM_IGNORE in utils.c (harmless but anoying netherless) - check_mail value of 0 was not accepted altough it should be - Ex (e.g. use the following for all following files) would fail when you would edit the command from within the menu - -[qQ] did not use the default linewrap mode - added wordwrap (-p w) 4.3.5: - 'n' (clear a buffer+window) did not work - added 'N' which clears all buffers and windows - in the scrollback menu, search next ('n') did not work - global search now remembers the last searchstring - added history to inputfields like searchstrings: press ^r or cursor key down when entering a search-string or filename or command 4.3.6: - MultiTail now verifies that you did not accidentely tried to pipe something into multitail while not using -j/-J - do a read of SSIZE_MAX or 65536 (whichever is smaller) instead of just hardcoded 65535 bytes: that way it'll also run on UNIX systems where SSIZE_MAX < 65535 - when reading from stdin and the end of the file is reached, multitail would busy-loop causing high cpu load - terminal-mode can now also select the subwindow - disabled terminal-mode for stdin (as that would not work) - added mcsre/mcsre_s/etc. (like cs_re/cs_re_s/etc.) but which merges these attributes to the attributes of a previous matched cs_re/mcsre - statistics screen now also shows the number of colorpairs in use - added colorscheme for Argus (http://qosient.com/argus/) - copied from http://wtf.hijacked.us/wiki/index.php/Multitail - added support (in colorschemes) for alternating colors 4.3.7: - added case insensitive toggle to searchfields 5.0.0: - merge with developmentversion 4.3.7 - default linewrap mode in configurationfile was incorrectly parsed - added colorscheme for motion 5.0.1: - fixed a segfault in the 'find'-code - --mergeall would merge all the following files into the previous window(!) which may be unwanted, so added '--mergeall-new' which creates a new window in which all the following files upto --no-mergeall are merged - conversion schemes/color schemes selection would fail if no schemes were defined in the config file. - on certain terminals one need to explicitly set the background color to be usable - version 5.0.0 always remembered the last searchstring, this can now be turned off in the configfile 5.0.2: - removed some debugging code that garbled the screen 5.0.3: - removed bogus error message when aborting 'write script' - added color scheme for the BOINC project - not all commandline settings were written when executing the 'write script' functionality 5.0.4: - changed default changeline_color to white on blue which is better readable then the previous colors - fixes for systems where 'char' is actually unsigned - fix for freebsd: one can now set the characters to use for borders 5.0.5: - HOME/END in scrollback did not redraw the screen - compilation fix for uClibc - page up (in the scrollbackwindow) did not work when there were lines bigger then the scrollbackwindow 5.1.0: - added conversion signal number to signal name - MultiTail used to wake up 4 times a second, it now only wakes up if there is really something to do - fixed a cosmetic bug in the 'add window' menu - made more parameters configurable - error messaging is more uniform - added -kS which, like sed, can select parts of strings to keep. e.g. -kS "^.*(TCP[^ ]*).*$" would keep the string matched by what is between ( and ) - fixed 2 memory leaks - replaced 2 strcat()s with strncat() - one can now explicitly wether the 'tail' used is a posix only tail (e.g. inotail) or also supports the old "-[0-9]" format - MultiTail can now act as a syslog-server (e.g. listening on UDP port 514 for messages). this is usefull when you want to monitor syslog capable devices (UNIX servers, Cisco switches, etc.) without the need for a syslog server - made some errors not fatal - e.g. let them give an error popup instead - unfiltered redirection would only emit the first line in certain situations - fixed a segfault (occuring only on AIX) at exit - Solaris problems fixed (malloc of 0 bytes) - one can now redirect to a socket (syslog/udp/514) as well - colors can now be suppressed in the scrollback window to improve scrollingspeed - improved scrolling in the scrollback menu: also scrolling in a line is now possible 5.1.1: - errors in error popups would not fit sometimes - moving to the beginning of the first line when it is longer then the screenwidth would not work in scrollback - searching would not work when you were halfway a line in scrollback - searching in the scrollback window can now open a new window as well - removed 3 memoryleaks - added colorscheme for acpitail 5.1.2: - some compilation/installation fixes for cygwin - when a -E was used also all -e's were copied as well 5.1.3: - fixed small memory leak (global filter was not freed) 5.2.0: - merged 5.0.5 with 5.1.3 5.2.1: - labels would give incorrect linewraps in the scrollbackwindow - the enter key will now add a markerline in all windows - fixed segfault when saving buffer in the scrollback window - added horizontal scroll for non-linewrapped windows (patch by René Berber) 5.2.2: - fixed problem with editrules - fixes for solaris makefiles - write script: won't write --bi with value < 1 anymore - re-enabled 'suspend' (^z) - fixed assertion error happening when doing a search with the '-ts' switch in the scrollback window - configuration-file parameters with 'y' instead of 'yes' were taken for no - added '--follow-all' (see -f for semantics) 5.3.0: - shrunk symboltable when not compiling in _DEBUG-mode - added '--difference-only' (which works at character level) - --retry added to tail when --retry is requested - suppress_empty_lines did not work multitail-6.0/thanks.txt0000644000175000017500000000277112245202637015324 0ustar folkertfolkertIn alphabetic order. Bas Aartsen - Spanish translation Bernd Ahlers - OpenBSD build fixes René Berber - stripline bugfix / horizontal scroll in main menu enhancement David Blank-Edelman - Solaris 9 fixes Oliver Braun - FreeBSD packaging Lenny Cartier - Mandrake package Steve Christensen - SUN Solaris packages http://www.sunfreeware.com/ Rene Engelhard - Debian package, testing Doruk Fisek - packaging for Pardus distribution Udo van den Heuvel - lots of feedback and first draft of the wikipedia article Mads Martin Joergensen - SuSE package Peterpaul Kloosterman - Logo! Adam Lewandowski - CygWin build fixes Dean Moore - MacOS X fixes Peter Neilson - wikipedia article Kevin Piche - ArchLinux package Computer Services, www.smartcsc.com - build on SCO Bennett Todd - uClibc port Dries Verachtert - RedHat Enterprise Linux packages Aaron Walker - GenToo package Dag Wieers - RedHat Enterprise Linux packages Christian Weisgerber - several bugreports and OpenBSD port issues Dr. Volker Zell - CygWin packaging multitail-6.0/manual.html0000644000175000017500000002516112245202637015434 0ustar folkertfolkert An introduction to MultiTail

An introduction to MultiTail

Introduction

What is MultiTail?

MultiTail lets you view one or multiple files like the original tail program. The difference is that it creates multiple windows on your console (with ncurses). Merging of 2 or even more logfiles is possible. It can also use colors while displaying the logfiles (through regular expressions), for faster recognition of what is important and what not. It can also filter lines (again with regular expressions). It has interactive menus for editing given regular expressions and deleting and adding windows. One can also have windows with the output of shell scripts and other software. When viewing the output of external software, MultiTail can mimic the functionality of tools like 'watch' and such.

Why this text?

When you start multitail without any parameters, it clears the screen and shows a couple of keys you can press together with a short explanation what they do. You can then press any of those keys or 'x', 'q' or 'CTRL'+'C' to exit the program. If you like to know what commandlineparameters can be given, start multitail with the '-h' parameter.
The "help" that is given with the methods described above might not be sufficient, that's why this text was written. If there is still anything not clear after reading this, do not hesitate to contact me at the following e-mail address:
folkert@vanheusden.com

The Basics

The most trivial use of MultiTail is as follows:
multitail [-i] file
This shows the file 'file' in your terminal-window. At the bottom, a line (the statusline) is displayed with the name of the file, its size and the date/time of when the last time the file changed. You can make this status- line static (not updated) with the '-d' commandline parameter. With '-D' no statusline is displayed at all.
You only need to specify the '-i' when the filename starts with a dash ('-'). Something a little more complex is this:
multitail [-i] file1 [-i] file2
This splits your terminalwindow in two windows. In each window, one file is displayed. Both windows have a statusline giving info. The window with 'file1' is displayed above the window with 'file2'. Instead of above each other, you can also have them displayed side by side. For that, you can enter the parameter '-s' on the commandline or press the 'v' key while the program is running.
Press keys 0 to 9 for adding a thick red line at the current position to window 0 to 9.

Scrolling

Of course you can scroll in the shown files. For that, press the 'b' key. When you're viewing multiple files, you'll first get a fileselector. Then a window is displayed with the buffered contents of the selected file (=window). You can then scroll with the cursorkeys and the page-up and pagedown key. Press 'x' or 'q' to exit this window. You cannot scroll the whole file, only the last 100 lines. To reset this limit to something bigger (or smaller), press the 'm' key. You will then be asked to enter a new value, e.g. 1000. This parameter can also be set from the commandline with the '-m value' parameter. With '-m' you set the limit for the next file, with '-M value' you'll set this parameter for all following files on the commandline. When you press the 'm'-key, the current buffer is cleared. So it is also a replacement for pressing the enter key a few times when using 'tail -f' to view a file.

Merging Files

Then there's the '-I' commandline parameter. It is the same as '-i', only '-I' merges two or more files together. For example:
multitail [-i] file1 -I file2
a reallife example:
multitail /var/log/apache/access.log -I /var/log/apache/error.log
These two examples will merge the output of the given files into one window. This can be usefull with, for example, the files given in the second example: with this example you see what happend just before an error appeared in the Apache errorlog.

Viewing Output of External Programs

As I mentioned in the foreword, one can not only view files with MultiTail, also the output of other programs (scripts/executables/etc.) can be put inside one or more windows. To make this happen, you need to use the '-l' switch. For example:
multitail -l ls
another example:
multitail -l "ping localhost"
As you can see, you need to add doublequotes around the command when it needs parameters, otherwhise MultiTail would not be able to recognize what parameters are intended for the selected program or for MultiTail itself.
You might have tried the example with the ls-command. You then saw that MultiTail automatically closes the window when the external command has finished. There are a few options you can use to control this behaviour. For example the '-z' parameter: when given, the window is just closed, the screen redrawed and MultiTail goes on without the popup window telling you that the program ended.
Another option is: '-r interval': this will cause the command to be run every 'interval' seconds. Instead of '-r interval' also the '-R interval' option is available: when fed to MultiTail, it makes it run the next command with an interval of 'interval' seconds displaying only the difference with the previous run of the command! So if you run MultiTail like this:
multitail -R 3 -l "netstat -p tcp"
you will see state-changes for every TCP-connection: new connections and connections getting closed.
As with '-I file', '-L command' also merges the output of the external command to the previous file or command. Yes: output of commands can be safely merged with logfiles. Multiple commands, multiple logfiles, most things you can think of are possible.

Colors

When you have been watching logfiles scrolling by, it can get a little though after a while to still recognize what is important and what not. Because of that, MultiTail has the ability to display logfiles in color. When you give the '-c' parameter, the next given file or command is showed in color. It decides what color to use by looking at the whole log-line. If you want it to only use at the programname causing that logline (when monitoring syslog logfiles for example), you can use the '-cs' switch. The last option is the '-cS colorscheme' switch. As parameter it needs the name of a colorscheme. The colorschemes are read from multitail.conf. In multitail.conf you set by entering regular expressions what color to use for what "patterns". By default, MultiTail looks for multitail.conf in the current directory and in the /etc directory. With the '-z' parameter you can explicitly define what file it should use.
An example:
colorscheme:postfix
cs_re:yellow:status=sent
cs_re:magenta:queue active
The first line names the current colorscheme. The 'cs_re'-lines define combinations of regular expressions and a color. With the first 'cs_re'- line you define that if MultiTail encounters the string 'status=sent' in a logline that it should print it in the color yellow. The next line defines that the string 'queue active' must be printed in magenta. Another example, a little more complex:
colorscheme:syslog
cs_re:green:\[|\]
cs_re:blue:^... .. ..:..:..
The first 'cs_re'-line sets all occurences of '[' or ']' to green and all lines starting with a date in the format 'Mon DD HH:MM:SS' in blue. For more details on regular expressions: o'Reilly has few books on this topic.
One last thing on colors: if you use '-C' (uppercase 'C') instead of '-c', all following files will use the parameters you specify at that time, unless you override them with a new '-cx' or '-Cx' parameter.

Filtering using regular expressions

For filtering MultiTail uses regular expressions. To keep things simple, it uses them the exact same way as 'grep' does: '-e' says: a regular expression follows and '-v' says: invert it. Since version 3.5.0 this has changed somewhat: if you want to match the lines that do NOT have a certain pattern, use -ev.
Examples:
multitail -e "gnu-pop3d" /var/log/messages
multitail -v -e "ssh" -v -e "gnu-pop3d" -e "localhost" /var/log/messages
The first example shows only lines from /var/log/messages which have the string "gnu-pop3d" somewhere in them. The second example only shows lines which do not have the string "ssh" and not have the string "gnu-pop3d" and DO HAVE the string "localhost" in them.

Miscellaneous Options

There are a few other options not fitting elsewhere, these are:
-fThis makes MultiTail follow the file. In case the original file gets renamed and a new file is created with the original filename, MultiTail will start watching the file with the original filename (the one you entered).
-u secondsWhen using MultiTail over a slow link (a modem connection or maybe even over HAM) you might want to have a little less frequent updates. With this parameter you set how frequently MultiTail updates the screen. The default is immediately.
-H intervalIf you have a connection to some host (on which you're using MultiTail) which gets automatically disconnected when nothing happens for a while, you can use '-H'. When used, MultiTail moves the cursor around the screen generating traffic, keeping your line up.
-VIn case you're wondering what version of MultiTail you're using, you can start it with the '-V' option. It'll then display its version and exit. You can also press the 'i' key while it is running.

Is that all?

Not everything was covered in this manual. For a complete list of options and keys you can press while MultiTail runs, have a look at the man-page, the output of the '-h' commandline parameter and the help when you press 'h'-key while the program runs.
And let's not forget the sourcecode!



The latest version of MultiTail can always be found here: http://www.vanheusden.com/multitail/ multitail-6.0/makefile.hpux0000644000175000017500000000327112245202637015752 0ustar folkertfolkertinclude version CONFIG_FILE=$(DESTDIR)/etc/multitail.conf LDFLAGS=-L/usr/local/lib/ -lpanel -lncurses -lm CFLAGS=-Ae -I/usr/local/include/ -O -DVERSION=\"$(VERSION)\" -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail install: multitail cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # cp multitail.conf $(DESTDIR)/etc/multitail.conf.new mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) rm -f $(DESTDIR)/usr/man/man1/multitail.1.gz gzip -9 $(DESTDIR)/usr/man/man1/multitail.1 # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/man/man1/multitail.1.gz rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-6.0/stripstring.c0000644000175000017500000003142612245202637016026 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include "mt.h" #include "help.h" #include "mem.h" #include "error.h" #include "term.h" #include "utils.h" #include "selbox.h" #include "globals.h" int edit_stripper_edit_regexp(NEWWIN *win, char **old_re, mybool_t *case_insensitive) { int changed = 0; char *new_re; /* ask new reg exp */ mvwprintw(win -> win, 15, 2, "Edit regular expression: "); new_re = edit_string(win, 16, 2, 58, 128, 0, *old_re, HELP_ENTER_REGEXP, -1, &search_h, case_insensitive); if (new_re) { myfree(*old_re); *old_re = new_re; changed = 1; } return changed; } int edit_stripper_edit_offsets(NEWWIN *win, int *offset_start, int *offset_end) { char *str_start = NULL, *str_end = NULL; char buffer[32] = { 0 }; char linebuf[68 + 1]; int changed = 0; if (*offset_start != -1) snprintf(buffer, sizeof(buffer), "%d", *offset_start); mvwprintw(win -> win, 15, 2, "Enter start offset: "); str_start = edit_string(win, 16, 2, 5, 5, 1, buffer, HELP_STRIPPER_START_OFFSET, -1, NULL, NULL); if (str_start) { if (*offset_end != -1) snprintf(buffer, sizeof(buffer), "%d", *offset_end); else buffer[0] = 0x00; mvwprintw(win -> win, 15, 2, "Enter end offset: "); memset(linebuf, ' ', sizeof(linebuf)); linebuf[sizeof(linebuf) - 1] = 0x00; mvwprintw(win -> win, 16, 1, "%s", linebuf); str_end = edit_string(win, 16, 2, 5, 5, 1, buffer, HELP_STRIPPER_END_OFFSET, -1, NULL, NULL); if (str_end) { *offset_start = atoi(str_start); *offset_end = atoi(str_end); changed = 1; } } myfree(str_start); myfree(str_end); return changed; } int edit_stripper_edit_field(NEWWIN *win, char **del, int *col_nr) { int changed = 0; char *new_del = NULL; char *new_col_nr = NULL; char buffer[32] = { 0 }; char linebuf[58 + 1]; mvwprintw(win -> win, 15, 2, "Enter delimiter: "); new_del = edit_string(win, 16, 2, 58, 128, 0, *del, HELP_STRIPPER_DELIMITER, -1, NULL, NULL); if (new_del) { mvwprintw(win -> win, 15, 2, "Enter column number: "); memset(linebuf, ' ', sizeof(linebuf)); linebuf[sizeof(linebuf) - 1] = 0x00; mvwprintw(win -> win, 16, 1, "%s", linebuf); if (*col_nr != -1) snprintf(buffer, sizeof(buffer), "%d", *col_nr); new_col_nr = edit_string(win, 16, 2, 5, 5, 1, buffer, HELP_STRIPPER_COL_NR, -1, NULL, NULL); } if (new_del != NULL && new_col_nr != NULL) { *del = mystrdup(new_del); *col_nr = atoi(new_col_nr); changed = 1; } myfree(new_col_nr); myfree(new_del); return changed; } int edit_strippers(void) { int changed = 0; NEWWIN *mywin; proginfo *cur; int f_index = 0; int cur_strip = 0; mybool_t case_insensitive = re_case_insensitive; /* select window */ if (nfd > 1) f_index = select_window(HELP_ENTER_STRIPPER_SELECT_WINDOW, "Select window (stripper editing)"); if (f_index == -1) /* user pressed Q/X */ return 0; /* select subwindow */ cur = &pi[f_index]; if (cur -> next) cur = select_subwindow(f_index, HELP_ENTER_STRIPPER_SELECT_SUBWINDOW, "Select subwindow (stripper editing)"); if (!cur) return 0; /* create window */ mywin = create_popup(23, 70); for(;;) { int c, key; int loop; char linebuf[68 + 1]; werase(mywin -> win); draw_border(mywin); win_header(mywin, "Edit strip regular expressions"); mvwprintw(mywin -> win, 2, 2, "%s", cur -> filename); escape_print(mywin, 3, 2, "^a^dd, ^e^dit, ^d^elete, ^q^uit"); /* clear */ memset(linebuf, ' ', sizeof(linebuf) - 1); linebuf[sizeof(linebuf) - 1] = 0x00; for(loop=4; loop<22; loop++) mvwprintw(mywin -> win, loop, 1, linebuf); /* display them lines */ for(loop=0; loop n_strip; loop++) { if (loop == cur_strip) ui_inverse_on(mywin); switch((cur -> pstrip)[loop].type) { case STRIP_KEEP_SUBSTR: case STRIP_TYPE_REGEXP: strncpy(linebuf, (cur -> pstrip)[loop].regex_str, 55); linebuf[55] = 0x00; mvwprintw(mywin -> win, 4 + loop, 1, "RE: %s", linebuf); break; case STRIP_TYPE_RANGE: mvwprintw(mywin -> win, 4 + loop, 1, "Range: %d - %d", (cur -> pstrip)[loop].start, (cur -> pstrip)[loop].end); break; case STRIP_TYPE_COLUMN: mvwprintw(mywin -> win, 4 + loop, 1, "Column: %d (delimiter: '%s')", (cur -> pstrip)[loop].col_nr, (cur -> pstrip)[loop].del); break; } if (loop == cur_strip) ui_inverse_off(mywin); mvwprintw(mywin -> win, 4 + loop, 60, "%d", (cur -> pstrip)[loop].match_count); } mydoupdate(); /* wait for key */ for(;;) { key = wait_for_keypress(HELP_REGEXP_MENU, 2, mywin, 1); c = toupper(key); /* convert return to 'E'dit */ if (key == 13) key = c = 'E'; /* any valid keys? */ if (c == 'Q' || c == abort_key || c == 'X' || c == 'A' || c == 'E' || c == 'D' || key == KEY_DOWN || key == 13 || key == KEY_UP) break; if (key == -1) /* T/O waiting for key? then update counters */ { for(loop=0; loop n_strip; loop++) mvwprintw(mywin -> win, 4 + loop, 60, "%d", (cur -> pstrip)[loop].match_count); mydoupdate(); } else wrong_key(); } /* exit this screen? */ if (c == 'Q' || c == abort_key || c == 'X') break; if (key == KEY_UP) { if (cur_strip > 0) cur_strip--; else wrong_key(); } else if (key == KEY_DOWN || key == 13) { if (cur_strip < (cur -> n_strip -1)) cur_strip++; else wrong_key(); } /* add or edit */ if (c == 'A') { int rc; char *regex_str = NULL; int offset_start = -1, offset_end = -1; char *del = NULL; int col_nr = -1; int c; int cur_changed = 0; /* max. 10 regular expressions */ if (cur -> n_strip == 10) { wrong_key(); continue; } /* ask type */ escape_print(mywin, 15, 2, "reg.^e^xp., ^r^ange, ^c^olumn, ^S^ubstrings"); mydoupdate(); for(;;) { c = toupper(wait_for_keypress(HELP_STRIPPER_TYPE, 0, mywin, 0)); if (c == 'E' || c == 'R' || c == 'C' || c == 'S' || c == abort_key || c == 'Q' || c == 'X') break; wrong_key(); } if (c == abort_key || c == 'Q' || c == 'X') continue; if (c == 'E' || c == 'S') { /* user did not abort edit? */ if (edit_stripper_edit_regexp(mywin, ®ex_str, &case_insensitive)) cur_changed = 1; } else if (c == 'R') { if (edit_stripper_edit_offsets(mywin, &offset_start, &offset_end)) cur_changed = 1; } else if (c == 'C') { if (edit_stripper_edit_field(mywin, &del, &col_nr)) cur_changed = 1; } if (!cur_changed) continue; changed = 1; cur_strip = cur -> n_strip++; cur -> pstrip = (strip_t *)myrealloc(cur -> pstrip, cur -> n_strip * sizeof(strip_t)); memset(&(cur -> pstrip)[cur_strip], 0x00, sizeof(strip_t)); /* compile */ if (c == 'E' || c == 'S') { if ((rc = regcomp(&(cur -> pstrip)[cur_strip].regex, regex_str, REG_EXTENDED))) { regexp_error_popup(rc, &(cur -> pre)[cur_strip].regex); cur -> n_strip--; if (cur -> n_strip == cur_strip) cur_strip--; myfree(regex_str); } else { /* compilation went well, remember everything */ (cur -> pstrip)[cur_strip].regex_str = regex_str; (cur -> pstrip)[cur_strip].type = (c == 'E' ? STRIP_TYPE_REGEXP : STRIP_KEEP_SUBSTR); } } else if (c == 'R') { (cur -> pstrip)[cur_strip].type = STRIP_TYPE_RANGE; (cur -> pstrip)[cur_strip].start = offset_start; (cur -> pstrip)[cur_strip].end = offset_end; } else if (c == 'C') { (cur -> pstrip)[cur_strip].type = STRIP_TYPE_COLUMN; (cur -> pstrip)[cur_strip].col_nr = col_nr; (cur -> pstrip)[cur_strip].del = del; } /* reset counter (as it is a different regexp which has not matched anything at all) */ (cur -> pstrip)[cur_strip].match_count = 0; } /* delete entry */ else if (c == 'D') { if (cur -> n_strip > 0) { changed = 1; /* delete entry */ myfree((cur -> pstrip)[cur_strip].regex_str); /* update administration */ if (cur -> n_strip == 1) { myfree(cur -> pstrip); cur -> pstrip = NULL; } else { int n_to_move = (cur -> n_strip - cur_strip) - 1; if (n_to_move > 0) memmove(&(cur -> pstrip)[cur_strip], &(cur -> pstrip)[cur_strip+1], sizeof(strip_t) * n_to_move); } cur -> n_strip--; /* move cursor */ if (cur_strip > 0 && cur_strip == cur -> n_strip) { cur_strip--; } } else wrong_key(); } else if (c == 'E') { if (cur -> n_strip > 0) { if ((cur -> pstrip)[cur_strip].type == STRIP_TYPE_REGEXP || (cur -> pstrip)[cur_strip].type == STRIP_KEEP_SUBSTR) { if (edit_stripper_edit_regexp(mywin, &(cur -> pstrip)[cur_strip].regex_str, &case_insensitive)) changed = 1; } else if ((cur -> pstrip)[cur_strip].type == STRIP_TYPE_RANGE) { if (edit_stripper_edit_offsets(mywin, &(cur -> pstrip)[cur_strip].start, &(cur -> pstrip)[cur_strip].end)) changed = 1; } else if ((cur -> pstrip)[cur_strip].type == STRIP_TYPE_COLUMN) { if (edit_stripper_edit_field(mywin, &(cur -> pstrip)[cur_strip].del, &(cur -> pstrip)[cur_strip].col_nr)) changed = 1; } } else wrong_key(); } } delete_popup(mywin); return changed; } void zero_str(char *what, int start, int end) { memset(&what[start], 0x00, end-start); } char do_strip_re(regex_t *pre, int *match_count, char *in, char *strip_what) { char changed = 0; int search_offset = 0; regmatch_t matches[MAX_N_RE_MATCHES]; do { int new_offset = -1; if (regexec(pre, &in[search_offset], MAX_N_RE_MATCHES, matches, 0) != REG_NOMATCH) { int match_i; for(match_i=0; match_i n_strip == 0) return NULL; len = strlen(in); if (len == 0) return NULL; strip_what = (char *)mymalloc(len); new_string = (char *)mymalloc(len + 1); memset(strip_what, 0x01, len); for(loop=0; loop n_strip; loop++) { if ((cur -> pstrip)[loop].type == STRIP_TYPE_RANGE) { memset(&strip_what[(cur -> pstrip)[loop].start], 0x00, (cur -> pstrip)[loop].end - (cur -> pstrip)[loop].start); changed = 1; } else if ((cur -> pstrip)[loop].type == STRIP_TYPE_REGEXP) { changed |= do_strip_re(&(cur -> pstrip)[loop].regex, &(cur -> pstrip)[loop].match_count, in, strip_what); } else if ((cur -> pstrip)[loop].type == STRIP_TYPE_COLUMN) { changed |= do_strip_column((cur -> pstrip)[loop].del, (cur -> pstrip)[loop].col_nr, in, strip_what); } else if ((cur -> pstrip)[loop].type == STRIP_KEEP_SUBSTR) { changed |= do_keep_re(&(cur -> pstrip)[loop].regex, &(cur -> pstrip)[loop].match_count, in, strip_what); } } if (changed) { for(loop=0; loop #include #include #include #include #include #include #include #include #include #include #include #include "mt.h" #include "mem.h" #include "error.h" #include "utils.h" #include "color.h" #include "term.h" #include "exec.h" #include "globals.h" #include "config.h" /* "local global" */ int cur_colorscheme_nr = -1; int cur_filterscheme_nr = -1; int cur_editscheme_nr = -1; mybool_t config_yes_no(char *what) { if (what[0] == '1' || strcasecmp(what, "yes") == 0 || strcasecmp(what, "y") == 0 || strcasecmp(what, "on") == 0) { return MY_TRUE; } return MY_FALSE; } long long int kb_str_to_value(char *field, char *str) { char *mult; long long int bytes = atoll(str); if (bytes < -1) error_exit("%s: value cannot be < -1\n", field); mult = &str[strlen(str) - 2]; if (strcasecmp(mult, "kb") == 0) bytes *= 1024; else if (strcasecmp(mult, "mb") == 0) bytes *= 1024 * 1024; else if (strcasecmp(mult, "gb") == 0) bytes *= 1024 * 1024 * 1024; return bytes; } void config_error_exit(int linenr, char *format, ...) { va_list ap; fprintf(stderr, version_str, VERSION); fprintf(stderr, "\n\n"); if (linenr != -1) fprintf(stderr, "Error while processing configuration file at line %d:\n", linenr); va_start(ap, format); (void)vfprintf(stderr, format, ap); va_end(ap); exit(EXIT_FAILURE); } void add_cs_re(int linenr, char *incmd, char *par) { if (use_colors) { char *re = NULL, *val = NULL; char *cmd = &incmd[5]; char *colon; if (strncmp(cmd, "_val", 4) == 0) { val = find_next_par(par); if (!val) config_error_exit(linenr, "cs_re_val...-entry malformed: value missing.\n"); re = find_next_par(val); } else re = find_next_par(par); if (re == NULL) config_error_exit(linenr, "cs_re-entry malformed: color or regular expression missing.\n"); if (cur_colorscheme_nr == -1) config_error_exit(linenr, "For cs_re one needs to define a color scheme name first.\n"); /* find colorscheme */ if (cschemes[cur_colorscheme_nr].color_script.script) config_error_exit(linenr, "One cannot let a color script have the same name has a color scheme."); /* grow/create list */ cschemes[cur_colorscheme_nr].pentries = (color_scheme_entry *)myrealloc(cschemes[cur_colorscheme_nr].pentries, (cschemes[cur_colorscheme_nr].n + 1) * sizeof(color_scheme_entry)); /* add to list */ if (cmd[0] == 0x00) cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].flags = 0; else if (strcmp(cmd, "_s") == 0) cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].flags = CSREFLAG_SUB; else if (strcmp(cmd, "_val_less") == 0) cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].flags = CSREFLAG_CMP_VAL_LESS; else if (strcmp(cmd, "_val_bigger") == 0) cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].flags = CSREFLAG_CMP_VAL_BIGGER; else if (strcmp(cmd, "_val_equal") == 0) cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].flags = CSREFLAG_CMP_VAL_EQUAL; /* sanity check */ if (cmd[0] != 0x00 && strchr(re, '(') == NULL) config_error_exit(linenr, "%s is missing substring selections! ('(' and ')')\n", cmd); if (val) cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].cmp_value = atof(val); cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].merge_color = incmd[0] == 'm' ? MY_TRUE : MY_FALSE; cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].cdef.ac_index = 0; colon = strchr(par, '|'); if (colon) { *colon = 0x00; cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].cdef.use_alternating_colors = MY_TRUE; cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].cdef.attrs2 = parse_attributes(colon + 1); } else { cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].cdef.use_alternating_colors = MY_FALSE; } cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].cdef.attrs1 = parse_attributes(par); /* compile regular expression */ compile_re(&cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].regex, re); cschemes[cur_colorscheme_nr].n++; } } void add_colorscheme(int linenr, char *cmd, char *par) { if (use_colors) { char *descr = find_next_par(par); cur_colorscheme_nr = n_cschemes++; cschemes = (color_scheme *)myrealloc(cschemes, n_cschemes * sizeof(color_scheme)); memset(&cschemes[cur_colorscheme_nr], 0x00, sizeof(color_scheme)); cschemes[cur_colorscheme_nr].descr = mystrdup(USE_IF_SET(descr, "")); cschemes[cur_colorscheme_nr].name = mystrdup(par); } } void add_colorscript(int linenr, char *cmd, char *par) { char *dummy1, *dummy2; dummy1 = find_next_par(par); /* find script */ dummy2 = find_next_par(dummy1); /* find description */ cur_colorscheme_nr = n_cschemes++; cschemes = (color_scheme *)myrealloc(cschemes, n_cschemes * sizeof(color_scheme)); memset(&cschemes[cur_colorscheme_nr], 0x00, sizeof(color_scheme)); cschemes[cur_colorscheme_nr].name = mystrdup(par); cschemes[cur_colorscheme_nr].descr = mystrdup(dummy2); cschemes[cur_colorscheme_nr].color_script.script = mystrdup(dummy1); cur_colorscheme_nr = -1; } void add_editscheme(int linenr, char *cmd, char *par) { char *descr = find_next_par(par); pes = (editscheme *)myrealloc(pes, sizeof(editscheme) * (n_es + 1)); memset(&pes[n_es], 0x00, sizeof(editscheme)); pes[n_es].es_name = mystrdup(par); pes[n_es].es_desc = mystrdup(USE_IF_SET(descr, "")); cur_editscheme_nr = n_es++; } void add_editrule(int linenr, char *cmd, char *par) { char *type_str = par; char *par1 = find_next_par(type_str); char *par2 = NULL; striptype_t type = STRIP_TYPE_REGEXP; int rule_index = -1; if (!par1) config_error_exit(linenr, "editrule:%s requires a parameter.\n", type_str); if (strcmp(type_str, "kr") == 0) type = STRIP_TYPE_RANGE; else if (strcmp(type_str, "ke") == 0) type = STRIP_TYPE_REGEXP; else if (strcmp(type_str, "kc") == 0) type = STRIP_TYPE_COLUMN; else if (strcmp(type_str, "kS") == 0) type = STRIP_KEEP_SUBSTR; else config_error_exit(linenr, "editrule requirs either ke, kr, kS or kc.\n"); if (type == STRIP_TYPE_RANGE || type == STRIP_TYPE_COLUMN) { par2 = find_next_par(par1); if (!par2) config_error_exit(linenr, "editrule:%s requires another parameter.\n", type_str); } rule_index = pes[cur_editscheme_nr].n_strips; pes[cur_editscheme_nr].strips = (strip_t *)myrealloc(pes[cur_editscheme_nr].strips, (pes[cur_editscheme_nr].n_strips + 1) * sizeof(strip_t)); memset(&pes[cur_editscheme_nr].strips[pes[cur_editscheme_nr].n_strips], 0x00, sizeof(strip_t)); pes[cur_editscheme_nr].n_strips++; pes[cur_editscheme_nr].strips[rule_index].type = type; if (type == STRIP_TYPE_RANGE) { pes[cur_editscheme_nr].strips[rule_index].start = atoi(par1); pes[cur_editscheme_nr].strips[rule_index].end = atoi(par2); } else if (type == STRIP_TYPE_REGEXP || type == STRIP_KEEP_SUBSTR) { pes[cur_editscheme_nr].strips[rule_index].regex_str = mystrdup(par1); compile_re(&pes[cur_editscheme_nr].strips[rule_index].regex, par1); } else if (type == STRIP_TYPE_COLUMN) { pes[cur_editscheme_nr].strips[rule_index].del = mystrdup(par1); pes[cur_editscheme_nr].strips[rule_index].col_nr = atoi(par2); } } void add_filterscheme(int linenr, char *cmd, char *par) { char *descr = find_next_par(par); pfs = (filterscheme *)myrealloc(pfs, sizeof(filterscheme) * (n_fs + 1)); memset(&pfs[n_fs], 0x00, sizeof(filterscheme)); pfs[n_fs].fs_name = mystrdup(par); pfs[n_fs].fs_desc = mystrdup(USE_IF_SET(descr, "")); cur_filterscheme_nr = n_fs++; } void add_filterscheme_rule(int linenr, char *cmd, char *par) { char *type = par; int use_regex = 0x00; char *re_cmd = NULL; char *re_str = find_next_par(par); if (!re_str) config_error_exit(linenr, "Missing regular expression in rule-line for scheme %s.\n", pfs[cur_filterscheme_nr].fs_name); if (type[0] != 'e') config_error_exit(linenr, "Regular expression type '%s' is not recognized.\n", type); if (type[1] == 0x00 || type[1] == 'm') use_regex = 'm'; else if (type[1] == 'v') use_regex = 'v'; else if (type[1] == 'c') use_regex = 'c'; else if (type[1] == 'C') use_regex = 'C'; else if (toupper(type[1]) == 'X') { char *dummy = find_next_par(re_str); if (!dummy) config_error_exit(linenr, "Missing command for rule of type 'e%c' for scheme %s.\n", type[1], pfs[cur_filterscheme_nr].fs_name); re_cmd = mystrdup(dummy); use_regex = type[1]; if (use_regex == 'X' && (strchr(re_str, '(') == NULL || strchr(re_str, ')') == NULL)) config_error_exit(linenr, "Filter scheme rule: -eX requires a regular expression which selects a substring using '(' and ')'.\n"); } pfs[cur_filterscheme_nr].pre = (re *)myrealloc(pfs[cur_filterscheme_nr].pre, (pfs[cur_filterscheme_nr].n_re + 1) * sizeof(re)); memset(&pfs[cur_filterscheme_nr].pre[pfs[cur_filterscheme_nr].n_re], 0x00, sizeof(re)); pfs[cur_filterscheme_nr].pre[pfs[cur_filterscheme_nr].n_re].use_regex = use_regex; pfs[cur_filterscheme_nr].pre[pfs[cur_filterscheme_nr].n_re].regex_str = mystrdup(re_str); compile_re(&pfs[cur_filterscheme_nr].pre[pfs[cur_filterscheme_nr].n_re].regex, re_str); pfs[cur_filterscheme_nr].pre[pfs[cur_filterscheme_nr].n_re].cmd = re_cmd; pfs[cur_filterscheme_nr].n_re++; } void add_convert(int linenr, char *cmd, char *par) { char *conv_name = par; char *conv_type = NULL; conversion_t type = 0; char *conv_script = NULL; char *conv_re = NULL; int loop; /* parse type */ conv_type = find_next_par(conv_name); if (!conv_type) config_error_exit(linenr, "'convert'-entry malformed: conversion type missing.\n"); if (strncmp(conv_type, "script:", 7) == 0) { conv_script = find_next_par(conv_type); if (conv_script) conv_re = find_next_par(conv_script); else config_error_exit(linenr, "Convert: script filename missing.\n"); } else conv_re = find_next_par(conv_type); if (!conv_re) config_error_exit(linenr, "'convert'-entry malformed: type or regular expression missing.\n"); /* find this conversion: is it from an already known group? */ for(loop=0; loop 2) config_error_exit(linenr, "bind parameter malformed: unknown keyselection.\n"); keybindings = (keybinding *)myrealloc(keybindings, sizeof(keybinding) * (n_keybindings + 1)); if (par[0] == '^') keybindings[n_keybindings].key = toupper(par[1]) - 'A' + 1; else keybindings[n_keybindings].key = par[0]; keybindings[n_keybindings].command = mystrdup(proc); n_keybindings++; } void set_suppress_empty_lines(int linenr, char *cmd, char *par) { suppress_empty_lines = config_yes_no(par); } void set_close_closed_windows(int linenr, char *cmd, char *par) { do_not_close_closed_windows = !config_yes_no(par); } void set_follow_filename(int linenr, char *cmd, char *par) { default_follow_filename = config_yes_no(par); } void set_default_linewrap(int linenr, char *cmd, char *par) { default_linewrap = par[0]; if (default_linewrap == 'o') { char *dummy = find_next_par(par); if (!dummy) config_error_exit(linenr, "default_linewrap: 'o' needs a wrap offset parameter.\n"); default_line_wrap_offset = atoi(dummy); } } void set_umask(int linenr, char *cmd, char *par) { def_umask = strtol(par, NULL, 0); } void set_shell(int linenr, char *cmd, char *par) { myfree(shell); shell = mystrdup(par); } void set_statusline_above_data(int linenr, char *cmd, char *par) { statusline_above_data = config_yes_no(par); } void set_caret_notation(int linenr, char *cmd, char *par) { caret_notation = config_yes_no(par); } void set_searches_case_insensitive(int linenr, char *cmd, char *par) { re_case_insensitive = MY_TRUE; } void set_beep_method(int linenr, char *cmd, char *par) { if (strcmp(par, "beep") == 0) beep_method = BEEP_BEEP; else if (strcmp(par, "flash") == 0) beep_method = BEEP_FLASH; else if (strcmp(par, "popup") == 0) beep_method = BEEP_POPUP; else if (strcmp(par, "none") == 0) beep_method = BEEP_NONE; else config_error_exit(linenr, "'%s' is an unknown beep method.\n"); } void set_beep_popup_length(int linenr, char *cmd, char *par) { beep_popup_length = atof(par); if (beep_popup_length < 0.0) config_error_exit(linenr, "beep_popup_length must be >= 0.0\n"); } void set_allow_8bit(int linenr, char *cmd, char *par) { allow_8bit = config_yes_no(par); } void set_dsblwm(int linenr, char *cmd, char *par) { no_linewrap = 1 - config_yes_no(par); } void set_warn_closed(int linenr, char *cmd, char *par) { warn_closed = config_yes_no(par); } void set_basename(int linenr, char *cmd, char *par) { filename_only = config_yes_no(par); } void set_bright(int linenr, char *cmd, char *par) { bright_colors = config_yes_no(par); } void set_ts_format(int linenr, char *cmd, char *par) { myfree(ts_format); ts_format = mystrdup(par); } void set_cnv_ts_format(int linenr, char *cmd, char *par) { myfree(cnv_ts_format); cnv_ts_format = mystrdup(par); } void set_statusline_ts_format(int linenr, char *cmd, char *par) { myfree(statusline_ts_format); statusline_ts_format = mystrdup(par); } void set_markerline_attrs(int linenr, char *cmd, char *par) { markerline_attrs = parse_attributes(par); } void set_idleline_color(int linenr, char *cmd, char *par) { idleline_attrs = parse_attributes(par); } void set_msgline_color(int linenr, char *cmd, char *par) { msgline_attrs = parse_attributes(par); } void set_changeline_color(int linenr, char *cmd, char *par) { changeline_attrs = parse_attributes(par); } void set_statusline_attrs(int linenr, char *cmd, char *par) { statusline_attrs = parse_attributes(par); } void set_splitline_attrs(int linenr, char *cmd, char *par) { splitline_attrs = parse_attributes(par); } void set_inverse_attrs(int linenr, char *cmd, char *par) { inverse_attrs = attrstr_to_nr(par); } void set_splitline(int linenr, char *cmd, char *par) { if (strcmp(par, "none") == 0) splitline_mode = SL_NONE; else if (strcmp(par, "regular") == 0) splitline_mode = SL_REGULAR; else if (strcmp(par, "attributes") == 0) splitline_mode = SL_ATTR; else config_error_exit(linenr, "Parameter '%s' for 'splitline' not recognized.\n", par); } void set_show_subwindow_id(int linenr, char *cmd, char *par) { show_subwindow_id = config_yes_no(par); } void set_markerline_timestamp(int linenr, char *cmd, char *par) { timestamp_in_markerline = config_yes_no(par); } void set_global_default_nlines(int linenr, char *cmd, char *par) { default_maxnlines = atoi(par); } void set_global_default_nkb(int linenr, char *cmd, char *par) { default_maxbytes = kb_str_to_value(cmd, par); } void set_default_nlines(int linenr, char *cmd, char *par) { char *re; int n_lines; if ((re = find_next_par(par)) == NULL) /* format: cs_re:color:regular expression */ config_error_exit(linenr, "Scheme entry malformed: scheme name or regular expression missing.\n"); /* find colorscheme */ n_lines = atoi(par); if (n_lines < -1) config_error_exit(linenr, "default_nlines: value cannot be lower then -1.\n"); add_pars_per_file(re, NULL, n_lines, -1, -1, -1, -1, NULL); } void set_default_mark_change(int linenr, char *cmd, char *par) { char *re; if ((re = find_next_par(par)) == NULL) /* format: cs_re:color:regular expression */ config_error_exit(linenr, "Scheme entry malformed: scheme name or regular expression missing.\n"); add_pars_per_file(re, NULL, -1, -1, config_yes_no(par), -1, -1, NULL); } void set_default_bytes(int linenr, char *cmd, char *par) { int bytes; char *re; if ((re = find_next_par(par)) == NULL) /* format: cs_re:color:regular expression */ config_error_exit(linenr, "Scheme entry malformed: scheme name or regular expression missing.\n"); bytes = kb_str_to_value(cmd, par); add_pars_per_file(re, NULL, -1, bytes, -1, -1, -1, NULL); } void set_check_mail(int linenr, char *cmd, char *par) { check_for_mail = get_value_arg("check_mail", par, VAL_ZERO_POSITIVE); } void set_tab_stop(int linenr, char *cmd, char *par) { tab_width = atoi(par); } void set_tail(int linenr, char *cmd, char *par) { myfree(tail); tail = mystrdup(par); } void set_titlebar(int linenr, char *cmd, char *par) { myfree(set_title); set_title = mystrdup(par); } void set_abbreviate_filesize(int linenr, char *cmd, char *par) { afs = config_yes_no(par); } void set_replace_by_markerline(int linenr, char *cmd, char *par) { myfree(replace_by_markerline); replace_by_markerline = mystrdup(par); } void set_popup_refresh_interval(int linenr, char *cmd, char *par) { popup_refresh_interval = get_value_arg("popup_refresh_interval", par, VAL_POSITIVE); } void set_msgline_char(int linenr, char *cmd, char *par) { msgline_char = par[0]; } void set_idleline_char(int linenr, char *cmd, char *par) { idleline_char = par[0]; } void set_changeline_char(int linenr, char *cmd, char *par) { changeline_char = par[0]; } void set_markerline_char(int linenr, char *cmd, char *par) { markerline_char = par[0]; } void set_global_mark_change(int linenr, char *cmd, char *par) { global_marker_of_other_window = config_yes_no(par); } void set_default_bufferwhat(int linenr, char *cmd, char *par) { char what = par[0]; if (what != 'a' && what != 'f') config_error_exit(linenr, "default_bufferwhat expects either 'a' or 'f'. Got: '%c'.\n", what); default_bufferwhat = what; } void set_abort_key(int linenr, char *cmd, char *par) { int dummy = atoi(par); if (dummy < 0 || dummy > 255) config_error_exit(linenr, "abort_key expects an ascii value which is >= 0 && <= 255.\n"); abort_key = dummy; } void set_exit_key(int linenr, char *cmd, char *par) { int dummy = atoi(par); if (dummy < 0 || dummy > 255) config_error_exit(linenr, "exit_key expects an ascii value which is >= 0 && <= 255.\n"); exit_key = dummy; } void set_line_ts_format(int linenr, char *cmd, char *par) { myfree(line_ts_format); line_ts_format = mystrdup(par); } void set_default_min_shrink(int linenr, char *cmd, char *par) { default_min_shrink = get_value_arg("min_shrink", par, VAL_POSITIVE); } void set_scrollback_show_winnrs(int linenr, char *cmd, char *par) { default_sb_showwinnr = config_yes_no(par); } void set_wordwrapmaxlength(int linenr, char *cmd, char *par) { wordwrapmaxlength = get_value_arg("wordwrapmaxlength", par, VAL_POSITIVE); } void set_searchhistory_file(int linenr, char *cmd, char *par) { if (par[0] == 0x00) { search_h.history_file = NULL; search_h.history_size = 0; } else { search_h.history_file = myrealpath(par); } } void set_searchhistory_size(int linenr, char *cmd, char *par) { int hs = atoi(par); if (hs <= 0) { search_h.history_file = NULL; search_h.history_size = 0; } else { search_h.history_size = hs; } } void set_cmdfile_history_file(int linenr, char *cmd, char *par) { if (par[0] == 0x00) { cmdfile_h.history_file = NULL; cmdfile_h.history_size = 0; } else { cmdfile_h.history_file = myrealpath(par); } } void set_cmdfile_history_size(int linenr, char *cmd, char *par) { int hs = atoi(par); if (hs <= 0) { cmdfile_h.history_file = NULL; cmdfile_h.history_size = 0; } else { cmdfile_h.history_size = hs; } } void set_default_background_color(int linenr, char *cmd, char *par) { if (use_colors) { default_bg_color = colorstr_to_nr(par); if (default_bg_color == -1) config_error_exit(linenr, "default_background_color: '%s' is not a recognized color.", par); } } void set_reuse_searchstring(int linenr, char *cmd, char *par) { reuse_searchstring = config_yes_no(par); } void set_min_n_bufferlines(int linenr, char *cmd, char *par) { min_n_bufferlines = atoi(par); if (min_n_bufferlines < 0) config_error_exit(linenr, "min_n_bufferlines must have a value >= 0."); } void set_box_bottom_left_hand_corner(int linenr, char *cmd, char *par) { box_bottom_left_hand_corner = par[0]; } void set_box_bottom_right_hand_corner(int linenr, char *cmd, char *par) { box_bottom_right_hand_corner = par[0]; } void set_box_bottom_side(int linenr, char *cmd, char *par) { box_bottom_side = par[0]; } void set_box_left_side(int linenr, char *cmd, char *par) { box_left_side = par[0]; } void set_box_right_side(int linenr, char *cmd, char *par) { box_right_side = par[0]; } void set_box_top_left_hand_corner(int linenr, char *cmd, char *par) { box_top_left_hand_corner = par[0]; } void set_box_top_right_hand_corner(int linenr, char *cmd, char *par) { box_top_right_hand_corner = par[0]; } void set_box_top_side(int linenr, char *cmd, char *par) { box_top_side = par[0]; } void set_window_number(int linenr, char *cmd, char *par) { myfree(window_number); window_number = mystrdup(par); } void set_subwindow_number(int linenr, char *cmd, char *par) { myfree(subwindow_number); subwindow_number = mystrdup(par); } void set_posix_tail(int linenr, char *cmd, char *par) { posix_tail = config_yes_no(par); } void set_syslog_ts_format(int linenr, char *cmd, char *par) { myfree(syslog_ts_format); syslog_ts_format = mystrdup(par); } void set_resolv_ip_addresses(int linenr, char *cmd, char *par) { resolv_ip_addresses = config_yes_no(par); } void set_show_severity_facility(int linenr, char *cmd, char *par) { show_severity_facility = config_yes_no(par); } void set_scrollback_no_colors(int linenr, char *cmd, char *par) { scrollback_no_colors = config_yes_no(par); } void set_scrollback_search_new_window(int linenr, char *cmd, char *par) { scrollback_search_new_window = config_yes_no(par); } void set_gnu_tail(int linenr, char *cmd, char *par) { gnu_tail = config_yes_no(par); } config_file_keyword cf_entries[] = { { "abbreviate_filesize", set_abbreviate_filesize }, { "abort_key", set_abort_key }, { "allow_8bit", set_allow_8bit }, { "basename", set_basename }, { "beep_method", set_beep_method }, { "beep_popup_length", set_beep_popup_length }, { "bind", bind_char }, { "box_bottom_left_hand_corner", set_box_bottom_left_hand_corner }, { "box_bottom_right_hand_corner", set_box_bottom_right_hand_corner }, { "box_bottom_side", set_box_bottom_side }, { "box_left_side", set_box_left_side }, { "box_right_side", set_box_right_side }, { "box_top_left_hand_corner", set_box_top_left_hand_corner }, { "box_top_right_hand_corner", set_box_top_right_hand_corner }, { "box_top_side", set_box_top_side }, { "bright", set_bright }, { "caret_notation", set_caret_notation }, { "changeline_char", set_changeline_char }, { "changeline_color", set_changeline_color }, { "check_mail", set_check_mail }, { "close_closed_windows", set_close_closed_windows }, { "cmdfile_history_file", set_cmdfile_history_file }, { "cmdfile_history_size", set_cmdfile_history_size }, { "cnv_ts_format", set_cnv_ts_format }, { "colorscheme", add_colorscheme }, { "colorscript", add_colorscript }, { "convert", add_convert }, { "cs_re", add_cs_re }, { "cs_re_s", add_cs_re }, { "cs_re_val_bigger", add_cs_re }, { "cs_re_val_equal", add_cs_re }, { "cs_re_val_less", add_cs_re }, { "default_background_color", set_default_background_color }, { "default_bufferwhat", set_default_bufferwhat }, { "default_bytes", set_default_bytes }, { "default_convert", set_default_convert }, { "default_linewrap", set_default_linewrap }, { "default_mark_change", set_default_mark_change }, { "default_nlines", set_default_nlines }, { "defaultcscheme", set_defaultcscheme }, { "dsblwm", set_dsblwm }, { "editrule", add_editrule }, { "editscheme", add_editscheme }, { "exit_key", set_exit_key }, { "filterscheme", add_filterscheme }, { "follow_filename", set_follow_filename }, { "global_default_nkb", set_global_default_nkb }, { "global_default_nlines", set_global_default_nlines }, { "global_mark_change", set_global_mark_change }, { "gnu_tail", set_gnu_tail }, { "idleline_char", set_idleline_char }, { "idleline_color", set_idleline_color }, { "include", do_load_config }, { "inverse", set_inverse_attrs }, { "line_ts_format", set_line_ts_format }, { "markerline_char", set_markerline_char }, { "markerline_color", set_markerline_attrs }, { "markerline_timestamp", set_markerline_timestamp }, { "mcsre", add_cs_re }, { "mcsre_s", add_cs_re }, { "mcsre_val_bigger", add_cs_re }, { "mcsre_val_equal", add_cs_re }, { "mcsre_val_less", add_cs_re }, { "min_n_bufferlines", set_min_n_bufferlines }, { "min_shrink", set_default_min_shrink }, { "msgline_char", set_msgline_char }, { "msgline_color", set_msgline_color }, { "popup_refresh_interval", set_popup_refresh_interval }, { "posix_tail", set_posix_tail }, { "replace_by_markerline", set_replace_by_markerline }, { "resolv_ip_addresses", set_resolv_ip_addresses }, { "reuse_searchstring", set_reuse_searchstring }, { "rule", add_filterscheme_rule }, { "scheme", scheme }, { "scrollback_no_colors", set_scrollback_no_colors }, { "scrollback_search_new_window", set_scrollback_search_new_window }, { "scrollback_show_winnrs", set_scrollback_show_winnrs }, { "searches_case_insensitive", set_searches_case_insensitive }, { "searchhistory_file", set_searchhistory_file }, { "searchhistory_size", set_searchhistory_size }, { "shell", set_shell }, { "show_severity_facility", set_show_severity_facility }, { "show_subwindow_id", set_show_subwindow_id }, { "splitline", set_splitline }, { "splitline_attrs", set_splitline_attrs }, { "statusline_above_data", set_statusline_above_data }, { "statusline_attrs", set_statusline_attrs }, { "statusline_ts_format", set_statusline_ts_format }, { "subwindow_number", set_subwindow_number }, { "suppress_empty_lines", set_suppress_empty_lines }, { "syslog_ts_format", set_syslog_ts_format }, { "tab_stop", set_tab_stop }, { "tail", set_tail }, { "titlebar", set_titlebar }, { "ts_format", set_ts_format }, { "umask", set_umask }, { "useeditscheme", use_editscheme }, { "usefilterscheme", use_filterscheme }, { "warn_closed", set_warn_closed }, { "window_number", set_window_number }, { "wordwrapmaxlength", set_wordwrapmaxlength } }; int find_config_entry_in_dispatch_table(char *cmd_name) { int left = 0; int right = (sizeof(cf_entries) / sizeof(config_file_keyword)) - 1; while(left <= right) { int mid = (left + right) / 2; int compare = strcmp(cmd_name, cf_entries[mid].config_keyword); if (compare > 0) left = mid + 1; else if (compare < 0) right = mid - 1; else return mid; } return -1; } int config_file_entry(int linenr, char *cmd) { int function_nr; char *par = NULL; /* remove spaces at the beginning of the line */ while (isspace(*cmd)) cmd++; /* skip empty lines and comments */ if (cmd[0] == 0x00 || cmd[0] == '#' || cmd[0] == ';') return 0; /* lines are in the format of command:parameter */ if ((par = find_next_par(cmd)) == NULL) config_error_exit(linenr, "Malformed configuration line found: %s (command delimiter (':') missing).\n", cmd); function_nr = find_config_entry_in_dispatch_table(cmd); if (function_nr == -1) return -1; cf_entries[function_nr].function(linenr, cmd, par); return 0; } int sort_colorschemes_compare(const void *a, const void *b) { color_scheme *pa = (color_scheme *)a; color_scheme *pb = (color_scheme *)b; return strcmp(pa -> name, pb -> name); } /* returns the default color scheme or -1 if none */ void do_load_config(int dummynr, char *dummy, char *file) { static char sorted = 0; FILE *fh; int linenr = 0; /* given file */ fh = fopen(file, "r"); if (fh == NULL) { if (errno == ENOENT) /* file does not exist, not an error */ return; error_exit("do_load_config: error loading configfile '%s'\n", file); } for(;;) { char read_buffer[CONFIG_READ_BUFFER]; char *dummy; char *cmd = fgets(read_buffer, sizeof(read_buffer) - 1, fh); if (!cmd) break; linenr++; /* strip LF */ dummy = strchr(cmd, '\n'); if (dummy) *dummy = 0x00; else error_exit("line %d of file '%s' is too long!\n", linenr, file); /* LOG("%d: %s\n", linenr, cmdin); */ if (config_file_entry(linenr, cmd) == -1) error_exit("Configuration parameter '%s' is unknown (file: %s, line: %d).\n", read_buffer, file, linenr); } fclose(fh); if (!sorted) { sorted = 1; qsort(cschemes, n_cschemes, sizeof(color_scheme), sort_colorschemes_compare); } } void load_configfile_wrapper(char *config_file) { /* load configurationfile (if any) */ if (load_global_config) do_load_config(-1, NULL, CONFIG_FILE); if (config_file) { do_load_config(-1, NULL, config_file); } else { int path_max = find_path_max(); char *path = mymalloc(path_max + 1); char *home = getenv("HOME"); struct passwd *pp = getuserinfo(); if (home) snprintf(path, path_max, "%s/.multitailrc", home); else snprintf(path, path_max, "%s/.multitailrc", pp -> pw_dir); do_load_config(-1, NULL, path); myfree(path); } } char load_configfile(char *config_file) { static char config_loaded = 0; if (config_loaded == 0) { /* load configurationfile (if any) */ load_configfile_wrapper(config_file); config_loaded = 1; return 1; } return 0; } multitail-6.0/version.h0000644000175000017500000000004012245202637015114 0ustar folkertfolkertextern const char *version_str; multitail-6.0/makefile.cross-arm-linux0000644000175000017500000000217112245202637020027 0ustar folkertfolkertinclude version CONFIG_FILE=$(DESTDIR)/etc/multitail.conf DEBUG=#-g -D_DEBUG #-pg #-fprofile-arcs LDFLAGS=-lpanel -lncurses -lutil -lm $(DEBUG) CFLAGS=-D$(shell uname) -O2 -Wall -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o all: multitail multitail: $(OBJS) $(CC) -Wall -W $(OBJS) $(LDFLAGS) -o multitail # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php clean: rm -f $(OBJS) multitail core gmon.out *.da thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-6.0/makefile.sco-openserver60000644000175000017500000000467512245202637020037 0ustar folkertfolkertinclude version CONFIG_FILE=$(DESTDIR)/etc/multitail.conf DEBUG=-g # -D_DEBUG # -pedantic #-pg #-fprofile-arcs LDFLAGS=-L/usr/local/lib/ -lpanel -lncurses -lm -lsocket $(DEBUG) CFLAGS=-Dscoos -O2 -I/usr/local/include/ -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail install: multitail cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual*.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # cp multitail.conf $(DESTDIR)/etc/multitail.conf.new rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz gzip -9 $(DESTDIR)/usr/share/man/man1/multitail.1 # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz rm -f $(DESTDIR)/etc/multitail.conf rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core gmon.out *.da package: clean # source package rm -rf multitail-$(VERSION)* mkdir multitail-$(VERSION) cp *.conf *.c *.h multitail.1 manual*.html Makefile makefile.* Changes INSTALL license.txt readme.txt version multitail-$(VERSION) tar czf multitail-$(VERSION).tgz multitail-$(VERSION) rm -rf multitail-$(VERSION) thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com echo Is your company using MultiTail and you would like to be echo mentioned on http://www.vanheusden.com/multitail/usedby.html ? echo Then please send me a logo -not too big- and a link and I will echo add it to that page. echo echo Oh, blatant plug: http://keetweej.vanheusden.com/wishlist.html multitail-6.0/license.txt0000644000175000017500000003545112245202637015457 0ustar folkertfolkert GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS multitail-6.0/readme.txt0000644000175000017500000000441412245202637015265 0ustar folkertfolkertWho? ---- This program was written by mail@vanheusden.com Check http://www.vanheusden.com/multitail/ for new(er) versions. Help? ----- For help at any time, press F1. How to compile -------------- By default multitail requires the ncursesw library (ncursesW!) to compile and run. You can choose to use the non-wide char version (ncurses) by setting UTF8_SUPPORT to "no" in the makefile. Note that this disables UTF8 support. House for sale -------------- www.sneeuwbalstraat-denhaag.nl Mailinglist ----------- There's a mailinglist. Send an e-mail to minimalist@vanheusden.com with in the subject 'subscribe multitail' to subscribe. Tips ---- You can also use MultiTail to view logfiles on other hosts! How? Like this: multitail -l "ssh username@host tail -f file" Q: but then I cannot enter the password! A1: use authentication via keys A2: or use "ssh-agent": then you only once have to enter your passphrase (so login once to that host manually, and then start MultiTail) Q & A ----- Q: the program fails then resizing the terminal-window A: solution: upgrade ncursesw to version 5.3 (or more recent) Q: when I use the -l option on some program, I get nothing in the window A: now that is strange! please tell me what program you're trying to interface to MultiTail. please do: any help is appreciated! It ain't workin'! ----------------- Please send me an e-mail telling me all details and all. And if the file is not too big, send it please as well. License ------- This program is free software: you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . For help, suggestions or anything else you can write to: folkert@vanheusden.com Please consider using PGP. Key-ID is: 1f28d8ae, this key is available on most public PGP key servers and also here: http://www.vanheusden.com/key.txt multitail-6.0/mem.c0000644000175000017500000001236712245202637014217 0ustar folkertfolkert#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include #include #include #include "mt.h" #include "error.h" #include "globals.h" #include "term.h" #include "utils.h" #include "mem.h" void myfree(void *p) { free(p); } void * myrealloc(void *oldp, int new_size) { /* ---------------------------------------------------- * add code for repeatingly retry? -> then configurable * via configurationfile with number of retries and/or * sleep * ---------------------------------------------------- */ void *newp = realloc(oldp, new_size); if (!newp) error_exit("Failed to reallocate a memory block to %d bytes.\n", new_size); return newp; } void * mymalloc(int size) { return myrealloc(NULL, size); } char * mystrdup(char *in) { char *newp = strdup(in); if (!newp) error_exit("Failed to duplicate a string: out of memory?\n"); return newp; } void clean_memory(void) { int loop; /* color schemes */ for(loop=0; loop n_strip; loop2++) { if ((cur -> pstrip)[loop2].type == STRIP_TYPE_REGEXP || (cur -> pstrip)[loop2].type == STRIP_KEEP_SUBSTR) { regfree(&(cur -> pstrip)[loop2].regex); myfree ((cur -> pstrip)[loop2].regex_str); } myfree ((cur -> pstrip)[loop2].del); } myfree(cur -> pstrip); myfree(cur -> filename); for(loop2=0; loop2 n_redirect; loop2++) myfree((cur -> predir)[loop2].redirect); myfree(cur -> predir); myfree(cur -> label); free_iat(&cur -> conversions); myfree(cur -> incomplete_line); myfree(cur -> win_title); delete_array(cur -> restart.diff.bcur, cur -> restart.diff.ncur); delete_array(cur -> restart.diff.bprev, cur -> restart.diff.nprev); myfree(cur -> repeat.last_line); free_iat(&cur -> cdef.color_schemes); delete_popup(cur -> status); delete_popup(cur -> data); for(loop2=0; loop2 n_re; loop2++) free_re(&(cur -> pre)[loop2]); myfree(cur -> pre); cur = cur -> next; if (pcur != &pi[loop]) myfree(pcur); } } myfree(pi); /* parameters per file */ for(loop=0; loop script); myfree(conversions[loop].pcs); } myfree(conversions); /* filterschemes */ for(loop=0; loop #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mt.h" #include "help.h" #include "error.h" #include "term.h" #include "mem.h" #include "utils.h" #include "selbox.h" #include "color.h" #include "exec.h" #include "globals.h" #include "scrollback.h" #include "cv.h" void error_popup(char *title, int help, char *message, ...) { va_list ap; NEWWIN *mywin; char buffer[4096]; myattr_t cdev; cdev.colorpair_index = find_colorpair(COLOR_RED, -1, 1); cdev.attrs = A_BLINK; va_start(ap, message); vsnprintf(buffer, sizeof(buffer), message, ap); va_end(ap); mywin = create_popup(9, max(33, find_char_offset(buffer, '\n')) + 4); win_header(mywin, title); myattr_on(mywin, cdev); mvwprintw(mywin -> win, 3, 2, "%s", buffer); myattr_off(mywin, cdev); escape_print(mywin, 7, 2, "_Press any key to exit this screen_"); mydoupdate(); wrong_key(); wait_for_keypress(help, 0, mywin, 0); delete_popup(mywin); } void edit_color(int index) { int example_color = COLOR_RED; NEWWIN *mywin = create_popup(15, 40); int colorbar = find_or_init_colorpair(example_color, example_color, 1); short old_r, old_g, old_b; double scale = 255.0 / 1000.0; int cursor_y = 0; short cur_r, cur_g, cur_b; color_content(example_color, &old_r, &old_g, &old_b); color_content(index, &cur_r, &cur_g, &cur_b); init_color(example_color, cur_r, cur_g, cur_b); for(;;) { int c; color_content(example_color, &cur_r, &cur_g, &cur_b); werase(mywin -> win); win_header(mywin, "Edit color"); mvwprintw(mywin -> win, 3, 2, "Color to edit:"); wattron(mywin -> win, COLOR_PAIR(colorbar)); mvwprintw(mywin -> win, 3, 17, " "); wattroff(mywin -> win, COLOR_PAIR(colorbar)); mvwprintw(mywin -> win, 3, 23, "%s", color_names[index]); mvwprintw(mywin -> win, 5, 17, " Red Green Blue"); mvwprintw(mywin -> win, 6, 15, "%c %4d %4d %4d", cursor_y == 0?'>':' ', cur_r, cur_g, cur_b); mvwprintw(mywin -> win, 7, 15, "%c %3d %3d %3d", cursor_y == 1?'>':' ', (int)((double)cur_r * scale), (int)((double)cur_g * scale), (int)((double)cur_b * scale)); mvwprintw(mywin -> win, 8, 15, "%c %2x %2x %2x ", cursor_y == 2?'>':' ', (int)((double)cur_r * scale), (int)((double)cur_g * scale), (int)((double)cur_b * scale)); escape_print(mywin, 10, 2, "^r^ edit red"); escape_print(mywin, 11, 2, "^g^ edit green"); escape_print(mywin, 12, 2, "^b^ edit blue"); escape_print(mywin, 13, 2, "^c^ change colorname"); mvwprintw(mywin -> win, 14, 2, "Press ^g to exit this screen"); draw_border(mywin); mydoupdate(); c = wait_for_keypress(HELP_EDIT_COLOR, 0, mywin, 1); if (c == abort_key) break; else if (c == KEY_UP) { if (cursor_y) cursor_y--; else wrong_key(); } else if (c == KEY_DOWN) { if (cursor_y < 2) cursor_y++; else wrong_key(); } else if (c == 'c') { char *newname = edit_string(mywin, 13, 23, 10, 10, 0, USE_IF_SET(color_names[index], ""), HELP_EDIT_COLOR_CHANGE_NAME, 0, NULL, NULL); if (newname) { myfree(color_names[index]); color_names[index] = newname; } } else if (c == 'r' || c == 'g' || c == 'b') { int y = -1; char val_str[5] = { 0 }; int val = -1; char *newval = NULL; if (c == 'r') { y = 0; val = cur_r; } else if (c == 'g') { y = 1; val = cur_g; } else if (c == 'b') { y = 2; val = cur_b; } mvwprintw(mywin -> win, 10 + y, 19, ">"); if (cursor_y == 0) snprintf(val_str, sizeof(val_str), "%d", val); else if (cursor_y == 1) snprintf(val_str, sizeof(val_str), "%d", (int)((double)val * scale)); else if (cursor_y == 2) snprintf(val_str, sizeof(val_str), "%2x", (int)((double)val * scale)); newval = edit_string(mywin, 10 + y , 20, 5, 5, (cursor_y < 2)?1:0, val_str, HELP_EDIT_COLOR_EDIT, -1, NULL, NULL); if (newval) { if (cursor_y == 0) val = max(0, min(atoi(newval), 1000)); else if (cursor_y == 1) val = max(0, (double)min(atoi(newval) / scale, 255)); else if (cursor_y == 2) val = max(0, (double)min(255, strtol(newval, NULL, 16) / scale)); if (c == 'r') cur_r = val; else if (c == 'b') cur_g = val; else if (c == 'g') cur_b = val; myfree(newval); } init_color(example_color, cur_r, cur_g, cur_b); } } init_color(example_color, old_r, old_g, old_b); delete_popup(mywin); } int color_management(myattr_t *org, myattr_t *new) { NEWWIN *mywin = create_popup(21, 64); short cur_fg = -1, cur_bg = -1; int cur_attr = 0; int offset = 0, cursor_y = 0, cursor_x = 0; int changed = 0; char *attrs[] = { "bold", "blink", "reverse", "underline", "normal", "dim" }; int attr_x[] = { A_BOLD, A_BLINK, A_REVERSE, A_UNDERLINE, A_NORMAL, A_DIM }; const int n_attrs = sizeof(attr_x) / sizeof(int); if (new != NULL && org != NULL) { if (org -> attrs != -1) cur_attr = org -> attrs; if (org -> colorpair_index != -1) pair_content(org -> colorpair_index, &cur_fg, &cur_bg); } for(;;) { int loop, c; werase(mywin -> win); if (new) win_header(mywin, "Select colors and attributes"); else win_header(mywin, "Select color to edit"); if (can_change_color()) mvwprintw(mywin -> win, 20, 2, "Press ^g to abort, 'e' to edit and 'a' to add a color"); else mvwprintw(mywin -> win, 20, 2, "Press ^g to abort"); wattron(mywin -> win, A_UNDERLINE); mvwprintw(mywin -> win, 2, 2, "foreground"); if (new) { mvwprintw(mywin -> win, 2, 22, "background"); mvwprintw(mywin -> win, 2, 42, "attributes"); } wattroff(mywin -> win, A_UNDERLINE); for(loop=0; loop<15; loop++) { int cur_offset = offset + loop; if (cur_offset >= COLORS) break; if (new) { mvwprintw(mywin -> win, 3 + loop, 2, "[%c] %s", cur_fg == cur_offset?'X':' ', color_names[cur_offset]?color_names[cur_offset]:""); mvwprintw(mywin -> win, 3 + loop, 22, "[%c] %s", cur_bg == cur_offset?'X':' ', color_names[cur_offset]?color_names[cur_offset]:""); } else mvwprintw(mywin -> win, 3 + loop, 2, "%s", USE_IF_SET(color_names[cur_offset], "")); } if (new) { for(loop=0; loop= n_attrs) break; mvwprintw(mywin -> win, 3 + loop, 42, "[%c] %s", cur_attr & attr_x[cur_offset]?'X':' ', attrs[loop]); } } draw_border(mywin); wmove(mywin -> win, 3 + cursor_y, 3 + cursor_x * 20); mydoupdate(); c = wait_for_keypress(HELP_SELECT_COLOR_AND_ATTRIBUTES, 0, mywin, 1); if (c == abort_key || c == 'Q' || c == 'X') { break; } else if (c == 'a' && can_change_color()) { /* find empty slot */ for(loop=0; loop win, 19, 2, "Enter color name: "); name = edit_string(mywin, 19, 20, 10, 10, 0, "", -1, -1, NULL, NULL); if (name) { color_names[loop] = name; edit_color(loop); /* no free! */ } } else error_popup("Create new color", -1, "Too many defined colors (%d max)", COLORS); } else if (c == KEY_UP) { if (cursor_y) cursor_y--; else if (offset) offset--; else wrong_key(); } else if (c == KEY_DOWN) { if (cursor_x == 2) { if ((offset + cursor_y) < (n_attrs - 1)) { if (cursor_y < (n_attrs - offset - 1)) cursor_y++; else offset++; } else wrong_key(); } else { if ((offset + cursor_y) < (n_colors_defined - 1)) { if (cursor_y < (n_colors_defined - offset - 1)) cursor_y++; else offset++; } else wrong_key(); } } else if (new != NULL && c == KEY_LEFT) { if (cursor_x) cursor_x--; else wrong_key(); } else if (new != NULL && c == KEY_RIGHT) { if (cursor_x == 0) cursor_x++; else if ((offset + cursor_y) < n_attrs && cursor_x == 1) cursor_x++; else wrong_key(); } else if (c == 13) break; else if (c == 'e' && (cursor_x == 0 || cursor_x == 1)) { edit_color(offset + cursor_y); } else if (new != NULL && c == ' ') { int sel = offset + cursor_y; changed = 1; if (cursor_x == 0) cur_fg = sel; else if (cursor_x == 1) cur_bg = sel; else if (cursor_x == 2) { if (sel >= n_attrs) error_exit("Internal error.\n"); if (cur_attr & attr_x[sel]) cur_attr -= attr_x[sel]; else cur_attr |= attr_x[sel]; } else error_exit("Internal error.\n"); } } delete_popup(mywin); if (new != NULL && changed) { new -> colorpair_index = find_or_init_colorpair(cur_fg, cur_bg, 1); new -> attrs = cur_attr; } return changed; } int swap_window(void) { int swapped = 0; if (nfd > 1) { int f1_index, f2_index; f1_index = select_window(HELP_SWAP_WIN1, "Swap windows, select window 1"); if (f1_index != -1) { f2_index = select_window(HELP_SWAP_WIN2, "Swap windows, select window 2"); if (f2_index != -1) { proginfo dummy; buffer dummy2; buffer_replace_pi_pointers(f1_index, &pi[f1_index], &pi[f2_index]); buffer_replace_pi_pointers(f2_index, &pi[f2_index], &pi[f1_index]); memmove(&dummy, &pi[f1_index], sizeof(proginfo)); memmove(&pi[f1_index], &pi[f2_index], sizeof(proginfo)); memmove(&pi[f2_index], &dummy, sizeof(proginfo)); memmove(&dummy2, &lb[f1_index], sizeof(buffer)); memmove(&lb[f1_index], &lb[f2_index], sizeof(buffer)); memmove(&lb[f2_index], &dummy2, sizeof(buffer)); swapped = 1; } } } else { error_popup("Swap windows", -1, "There's only 1 window!"); } return swapped; } void restart_window(void) { int f_index = 0; if (nfd > 1) f_index = select_window(HELP_SELECT_RESTART_WINDOW, "Select window to restart"); if (f_index != -1) { char all = 1; if (pi[f_index].next) { NEWWIN *mywin = create_popup(13, 24); win_header(mywin, "Restart window"); escape_print(mywin, 3, 2, "Restart all? (^y^/^n^)"); mydoupdate(); all = ask_yes_no(HELP_SELECT_RESTART_WINDOW_ALL, mywin); delete_popup(mywin); } if (all == 1) /* pressed 'Y' */ { proginfo *cur = &pi[f_index]; do { do_restart_window(cur); cur = cur -> next; } while(cur); } else { do_restart_window(&pi[f_index]); } } } int delete_window(void) { int deleted = 0; int f_index = 0; /* only popup selectionbox when more then one window */ if (nfd > 1) f_index = select_window(HELP_DELETE_SELECT_WINDOW, "Select window to delete"); /* user did not press q/x? */ if (f_index != -1) { char all = 1; /* multiple files for this window? then ask if delete all * or just one */ if (pi[f_index].next) { NEWWIN *mywin = create_popup(13, 24); win_header(mywin, "Delete window"); escape_print(mywin, 3, 2, "Delete all? (^y^/^n^)"); mydoupdate(); all = ask_yes_no(HELP_DELETE_WINDOW_DELETE_ALL_SUBWIN, mywin); delete_popup(mywin); } if (all == 1) /* pressed 'Y' */ { delete_entry(f_index, NULL); deleted = 1; } else if (all == 0) /* pressed 'N' */ { proginfo *cur = select_subwindow(f_index, HELP_DELETE_SELECT_SUBWINDOW, "Select subwindow"); if (cur) { delete_entry(f_index, cur); deleted = 1; } } /* otherwhise: pressed 'Q' */ } return deleted; } int hide_window(void) { int f_index = select_window(HELP_HIDE_WINDOW, "Select window to hide"); if (f_index != -1) pi[f_index].hidden = 1 - pi[f_index].hidden; return f_index != -1 ? 1 : 0; } int hide_all_but(void) { int f_index = select_window(HELP_HIDE_BUT_WINDOW, "Select window to keep open"); int loop; if (f_index != -1) { for(loop=0; loop win); win_header(win, "Select scheme(s)"); mvwprintw(win -> win, 2, 2, "Space to toggle, enter to proceed, ^g to abort"); for(loop=0; loop<16; loop++) { char *descr = "", *name; if (cur_offset + loop >= n_schemes_in) break; if (tscheme == SCHEME_COLOR) { name = ((color_scheme *)schemes_in)[cur_offset + loop].name; descr = ((color_scheme *)schemes_in)[cur_offset + loop].descr; } else { name = ((conversion *)schemes_in)[cur_offset + loop].name; } mvwprintw(win -> win, 3 + loop, 26, "%s", descr); if (loop == cur_line) ui_inverse_on(win); mvwprintw(win -> win, 3 + loop, 2, "[%c] %s", selected_schemes[cur_offset + loop]?'X':' ', name); if (loop == cur_line) ui_inverse_off(win); } draw_border(win); mydoupdate(); c = toupper(wait_for_keypress(tscheme == SCHEME_COLOR ? HELP_SELECT_COLORSCHEMES : HELP_SELECT_CONVERSIONSCHEMES, 0, win, 1)); if (c == abort_key || c == 'Q' || c == 'X') { c = -1; break; } else if (c == KEY_HOME) { cur_line = cur_offset = 0; } else if (c == KEY_END) { cur_line = 0; cur_offset = n_schemes_in - 1; } else if (c == KEY_PPAGE) { int todo = 16 - cur_line; cur_line = 0; if (cur_offset > todo) cur_offset -= todo; else cur_offset = 0; } else if (c == KEY_NPAGE) { int todo = cur_line; cur_line = 15; if ((cur_offset + todo) < (n_schemes_in - 1)) cur_offset += todo; else cur_offset = n_schemes_in - 1; } else if (c == KEY_UP) { if (cur_line > 0) cur_line--; else if (cur_offset > 0) cur_offset--; else wrong_key(); } else if (c == KEY_DOWN) { if ((cur_line + cur_offset) < (n_schemes_in - 1)) { if (cur_line < (16 - 1)) cur_line++; else cur_offset++; } else wrong_key(); } else if (c == ' ') { selected_schemes[cur_line + cur_offset] = 1 - selected_schemes[cur_line + cur_offset]; } else if (c == 13) { break; } else wrong_key(); } if (c != -1) { /* return result */ free_iat(schemes_out); for(loop=0; loop win, 2, 2, "1. ANSI/VT100"); mvwprintw(popup -> win, 3, 2, "2. no emulation"); mvwprintw(popup -> win, 4, 2, "0. abort"); mvwprintw(popup -> win, 6, 2, "Please contact folkert@vanheusden.com if"); mvwprintw(popup -> win, 7, 2, "you require other emulations as well."); mydoupdate(); for(;;) { int c = wait_for_keypress(HELP_SELECT_TERMINAL_EMULATION, 0, popup, 1); if (c == '1') { *term = TERM_ANSI; break; } else if (c == '2') { *term = TERM_IGNORE; break; } else if (c == '0' || toupper(c) == 'Q' || c == abort_key) { rc = -1; break; } wrong_key(); } delete_popup(popup); return rc; } char ask_colors(NEWWIN *win, int line, char cur, char *fieldnr, char **fielddel, int_array_t *color_schemes, myattr_t *attrs, term_t *term_emul) { char ok = 0; escape_print(win, line, 2, "Colors? (^s^yslog/^m^isc/^f^ield/^n^one/^S^cheme/^l^ist/^t^erm)"); mydoupdate(); for(;;) { int c = wait_for_keypress(HELP_ASK_COLORS, 0, win, 0); if (c == abort_key) return -1; switch(c) { case 't': if (ask_terminal_emulation(term_emul) == 0) return 0; return -1; case 'q': return -1; case 'S': if (select_schemes(cschemes, n_cschemes, SCHEME_COLOR, color_schemes) == -1) return -1; return 'S'; case 's': case 'm': return 'm'; case 'n': return 0; case 'l': { myattr_t new_attrs = { -1, -1 }; if (color_management(attrs, &new_attrs)) { *attrs = new_attrs; return 'i'; } } break; case 'f': { int loop, fld_dummy; char *dummy, nr[5]; /* xxx\0 and a little :-) */ snprintf(nr, sizeof(nr), "%d", *fieldnr); mvwprintw(win -> win, line + 2, 1, "Enter field number: "); dummy = edit_string(win, line + 3, 2, 42, 39, 1, nr, HELP_COLORS_FIELD_NR, -1, NULL, NULL); if (dummy) { fld_dummy = atoi(dummy); myfree(dummy); mvwprintw(win -> win, line + 4, 1, "Enter delimiter (eg a space):"); dummy = edit_string(win, line + 5, 2, 42, 39, 0, *fielddel, HELP_COLORS_FIELD_DELIMITER, -1, NULL, NULL); if (dummy) { myfree(*fielddel); *fielddel = dummy; *fieldnr = fld_dummy; ok = 1; } } for(loop=0; loop<4; loop++) mvwprintw(win -> win, line + 1 + loop, 1, " "); } if (ok) return 'f'; break; case 13: if (cur != (char)-1) return cur; default: wrong_key(); } } } char ask_regex_type(NEWWIN *win, int line) { escape_print(win, line+0, 2, "Usage of regexp? (^m^atch, ^v^ do not match"); escape_print(win, line+1, 2, "^C^olor, ^B^ell, ^b^ell + colorize, e^x^ecute)"); mydoupdate(); for(;;) { int c = wait_for_keypress(HELP_REGEXP_USAGE, 0, win, 0); if (c == 'm' || toupper(c) == 'C' || toupper(c) == 'B' || toupper(c) == 'X' || c == 'v') return c; if (toupper(c) == 'Q' || c == abort_key) return -1; wrong_key(); } } int add_window(void) { int added = 0; char *fname; NEWWIN *mywin = create_popup(12, 53); int col, fc; proginfo *cur = NULL; proginfo *set_next_pointer = NULL; char ask_add_to; int what_help = -1; if (nfd) ask_add_to = 1; else ask_add_to = 0; for(;;) { int_array_t color_schemes = { NULL, 0, 0 }; char field_nr = 0; char *field_del = NULL; char follow_filename = 0; char see_difference = 0; myattr_t cdev = { -1, -1 }; term_t term_emul = TERM_IGNORE; win_header(mywin, "Add window"); if (ask_add_to) { int rc; ask_add_to = 0; mvwprintw(mywin -> win, 2, 2, "Add (merge) to existing window?"); mydoupdate(); rc = ask_yes_no(HELP_ADD_WINDOW_MERGE_SUBWIN, mywin); if (rc == 1) /* pressed 'Y' */ { int index = 0; if (nfd > 1) index = select_window(HELP_ADD_WINDOW_SELECT_MERGE_WINDOW, NULL); if (index == -1) break; cur = &pi[index]; } else if (rc == -1) /* pressed 'Q' */ { break; } } /* check if this extra window will still fit */ if (!cur) { if ((max_y / (nfd + 1)) < 3) { error_popup("Add window", -1, "More windows would not fit."); break; } } escape_print(mywin, 2, 2, "File or command? (^f^/^c^) "); mydoupdate(); for(;;) { fc = toupper(wait_for_keypress(HELP_ADD_FILE_OR_CMD, 0, mywin, 0)); if (fc == 'F' || fc == 'C' || fc == 'Q' || fc == abort_key) break; wrong_key(); } if (fc == 'Q' || fc == abort_key) break; else if (fc == 'F') { mvwprintw(mywin -> win, 2, 2, "Enter filename: "); what_help = HELP_ENTER_FILENAME_TO_MONITOR; } else { mvwprintw(mywin -> win, 2, 2, "Enter command: "); what_help = HELP_ENTER_CMD_TO_MONITOR; } fname = edit_string(mywin, 3, 2, 41, find_path_max(), 0, NULL, what_help, -1, &cmdfile_h, NULL); if (!fname) break; if (fc == 'F') { escape_print(mywin, 4, 2, "Follow filename? (^y^/^n^)"); mydoupdate(); follow_filename = ask_yes_no(HELP_ADD_FILE_FOLLOW_FILENAME, mywin); } col = ask_colors(mywin, 5, -1, &field_nr, &field_del, &color_schemes, &cdev, &term_emul); if (col == -1) break; if (cur == NULL) { create_new_win(&cur, NULL); added = 1; } else { /* skip to end of chain */ while (cur -> next) cur = cur -> next; /* the last entry in the chain will point to this newly created structure * don't set this pointer yet as this will give problems as soon as we * call wait_for_keypress() */ set_next_pointer = cur; /* allocate new entry */ cur = (proginfo *)mymalloc(sizeof(proginfo)); } memset(cur, 0x00, sizeof(proginfo)); cur -> restart.restart = -1; if (fc == 'C') { char *dummy; mvwprintw(mywin -> win, 6, 2, "Repeat command interval: (empty for don't)"); dummy = edit_string(mywin, 7, 2, 10, 10, 1, NULL, HELP_ADD_WINDOW_REPEAT_INTERVAL, -1, NULL, NULL); if (dummy) { cur -> restart.restart = atoi(dummy); myfree(dummy); mvwprintw(mywin -> win, 8, 2, "Do you whish to only see the difference?"); mydoupdate(); see_difference = ask_yes_no(HELP_ADD_FILE_DISPLAY_DIFF, mywin); } } cur -> filename = fname; cur -> status = cur -> data = NULL; cur -> wt = (fc == 'F' ? WT_FILE : WT_COMMAND); cur -> retry_open = 0; cur -> follow_filename = follow_filename; cur -> restart.do_diff = see_difference; cur -> next = NULL; cur -> restart.first = 1; cur -> line_wrap = 'a'; cur -> win_height = -1; cur -> statistics.sccfirst = 1; cur -> cdef.colorize = col; cur -> cdef.field_nr = field_nr; cur -> cdef.field_del = field_del; cur -> cdef.color_schemes = color_schemes; cur -> cdef.attributes = cdev; cur -> cdef.term_emul = term_emul; init_iat(&cur -> conversions); cur -> statistics.start_ts = time(NULL); /* start tail-process */ if (start_proc(cur, max_y / (nfd + 1)) == -1) { mvwprintw(mywin -> win, 8, 2, "error opening file!"); mydoupdate(); wait_for_keypress(HELP_FAILED_TO_START_TAIL, 0, mywin, 0); nfd--; break; } /* and set pointer to the newly allocated entry */ if (set_next_pointer) set_next_pointer -> next = cur; mvwprintw(mywin -> win, 10, 2, "Add another to this new window?"); mydoupdate(); if (ask_yes_no(HELP_MERGE_ANOTHER_WINDOW, mywin) <= 0) break; werase(mywin -> win); draw_border(mywin); } delete_popup(mywin); return added; } int toggle_colors(void) { int changed = 0; int f_index = 0; if (nfd > 1) f_index = select_window(HELP_TOGGLE_COLORS_SELECT_WINDOW, "Toggle colors: select window"); if (f_index != -1) { char col; proginfo *cur = &pi[f_index]; NEWWIN *mywin = NULL; char *dummy = NULL; if (cur -> next) cur = select_subwindow(f_index, HELP_TOGGLE_COLORS_SELECT_SUBWINDOW, "Toggle colors: select subwindow"); if (cur) { mywin = create_popup(11, 53); win_header(mywin, "Toggle colors"); dummy = mystrdup(cur -> filename); dummy[min(strlen(dummy), 40)] = 0x00; mvwprintw(mywin -> win, 3, 1, dummy); col = ask_colors(mywin, 4, cur -> cdef.colorize, &cur -> cdef.field_nr, &cur -> cdef.field_del, &cur -> cdef.color_schemes, &cur -> cdef.attributes, &cur -> cdef.term_emul); if (col != (char)-1) { changed = 1; cur -> cdef.colorize = col; } delete_popup(mywin); myfree(dummy); } } return changed; } void swap_re(re *a, re *b) { re temp; memcpy(&temp, a, sizeof(temp)); memcpy(a, b, sizeof(*a)); memcpy(b, &temp, sizeof(*b)); } int edit_regexp(void) { int changed = 0; NEWWIN *mywin; proginfo *cur; int f_index = 0; int cur_re = 0; mybool_t case_insensitive = re_case_insensitive; /* select window */ if (nfd > 1) f_index = select_window(HELP_ENTER_REGEXP_SELECT_WINDOW, "Select window (reg.exp. editing)"); if (f_index == -1) /* user pressed Q/X */ return 0; /* select subwindow */ cur = &pi[f_index]; if (cur -> next) cur = select_subwindow(f_index, HELP_ENTER_REGEXP_SELECT_SUBWINDOW, "Select subwindow (reg.exp. editing)"); if (!cur) return 0; /* create window */ mywin = create_popup(23, 70); for(;;) { int c, key; int loop; char buffer[58 + 1]; buffer[58] = 0x00; werase(mywin -> win); win_header(mywin, "Edit reg.exp."); mvwprintw(mywin -> win, 2, 2, "%s", cur -> filename); escape_print(mywin, 3, 2, "^a^dd, ^e^dit, ^d^elete, ^q^uit, move ^D^own, move ^U^p, ^r^eset counter"); /* display them lines */ for(loop=0; loop n_re; loop++) { strncpy(buffer, (cur -> pre)[loop].regex_str, 34); if (loop == cur_re) ui_inverse_on(mywin); mvwprintw(mywin -> win, 4 + loop, 1, "%c%c %s", zerotomin((cur -> pre)[loop].invert_regex), zerotomin((cur -> pre)[loop].use_regex), buffer); if (toupper((cur -> pre)[loop].use_regex) == 'X') { char dummy[18]; strncpy(dummy, (cur -> pre)[loop].cmd, min(17, strlen((cur -> pre)[loop].cmd))); dummy[17]=0x00; mvwprintw(mywin -> win, 4 + loop, 42, dummy); wmove(mywin -> win, 4 + loop, 41); } if (loop == cur_re) ui_inverse_off(mywin); mvwprintw(mywin -> win, 4 + loop, 60, "%d", (cur -> pre)[loop].match_count); } draw_border(mywin); mydoupdate(); /* wait for key */ for(;;) { key = wait_for_keypress(HELP_REGEXP_MENU, 2, mywin, 1); c = key; /* convert return to 'E'dit */ if (key == 13) key = c = 'E'; /* any valid keys? */ if (toupper(c) == 'Q' || c == abort_key || toupper(c) == 'X' || c == 'a' || c == 'e' || toupper(c) == 'D' || c == 'U' || key == KEY_DOWN || key == 13 || key == KEY_UP || c == 'r') break; if (key == -1) /* T/O waiting for key? then update counters */ { for(loop=0; loop n_re; loop++) mvwprintw(mywin -> win, 4 + loop, 60, "%d", (cur -> pre)[loop].match_count); mydoupdate(); } else wrong_key(); } /* exit this screen? */ if (toupper(c) == 'Q' || c == abort_key || toupper(c) == 'X') break; if (key == KEY_UP) { if (cur_re > 0) cur_re--; else wrong_key(); } else if (key == KEY_DOWN || key == 13) { if (cur_re < (cur -> n_re -1)) cur_re++; else wrong_key(); } /* add or edit */ else if (c == 'a' || c == 'e') { int invert_regex = 0, regex_type; int rc; char *regex_str, *cmd_str = NULL; /* max. 10 regular expressions */ if (c == 'a' && cur -> n_re == 10) { wrong_key(); continue; } if (c == 'e' && cur -> n_re == 0) { wrong_key(); continue; } /* ask new reg exp */ mvwprintw(mywin -> win, 15, 2, "Edit regular expression:"); if (c == 'e') regex_str = edit_string(mywin, 16, 2, 58, 128, 0, (cur -> pre)[cur_re].regex_str, HELP_ENTER_REGEXP, -1, &search_h, &case_insensitive); else regex_str = edit_string(mywin, 16, 2, 58, 128, 0, NULL, HELP_ENTER_REGEXP, -1, &search_h, &case_insensitive); /* user did not abort edit? */ if (regex_str == NULL) continue; regex_type = ask_regex_type(mywin, 18); if (regex_type == -1) continue; if (regex_type != 'm' && regex_type != 'v') { invert_regex = ask_negate_regexp(mywin, 17); if (invert_regex == -1) /* pressed 'Q' */ continue; } if (toupper(regex_type) == 'X') { mvwprintw(mywin -> win, 20, 2, "Edit command:"); if (c == 'e') cmd_str = edit_string(mywin, 21, 2, 58, 128, 0, (cur -> pre)[cur_re].cmd, HELP_ENTER_CMD, -1, &cmdfile_h, NULL); else cmd_str = edit_string(mywin, 21, 2, 58, 128, 0, NULL, HELP_ENTER_CMD, -1, &cmdfile_h, NULL); /* did the user cancel things? */ if (!cmd_str) { /* then free the memory for the new regexp * and continue with the menu */ myfree(regex_str); continue; } } changed = 1; /* edit: free previous */ if (c == 'e') { regfree(&(cur -> pre)[cur_re].regex); myfree((cur -> pre)[cur_re].regex_str); } /* add: allocate new */ else { cur_re = cur -> n_re++; cur -> pre = (re *)myrealloc(cur -> pre, cur -> n_re * sizeof(re)); memset(&(cur -> pre)[cur_re], 0x00, sizeof(re)); } /* wether to negate this expression or not */ (cur -> pre)[cur_re].invert_regex = invert_regex; /* compile */ if ((rc = regcomp(&(cur -> pre)[cur_re].regex, regex_str, REG_EXTENDED | (case_insensitive == MY_TRUE?REG_ICASE:0)))) { regexp_error_popup(rc, &(cur -> pre)[cur_re].regex); if (c == 'a') { cur -> n_re--; if (cur -> n_re == cur_re) cur_re--; } myfree(regex_str); } else { /* compilation went well, remember everything */ (cur -> pre)[cur_re].regex_str = regex_str; (cur -> pre)[cur_re].use_regex = regex_type; (cur -> pre)[cur_re].cmd = cmd_str; } /* reset counter (as it is a different regexp which has not matched anything at all) */ (cur -> pre)[cur_re].match_count = 0; } /* delete entry */ else if (c == 'd') { changed = 1; /* delete entry */ free_re(&(cur -> pre)[cur_re]); /* update administration */ if (cur -> n_re == 1) { myfree(cur -> pre); cur -> pre = NULL; } else { int n_to_move = (cur -> n_re - cur_re) - 1; if (n_to_move > 0) memmove(&(cur -> pre)[cur_re], &(cur -> pre)[cur_re+1], sizeof(re) * n_to_move); } cur -> n_re--; /* move cursor */ if (cur_re > 0 && cur_re == cur -> n_re) { cur_re--; } } else if (c == 'D') { if (cur_re < (cur -> n_re - 1)) { swap_re(&(cur -> pre)[cur_re], &(cur -> pre)[cur_re + 1]); } else wrong_key(); } else if (c == 'U') { if (cur_re > 0) { swap_re(&(cur -> pre)[cur_re], &(cur -> pre)[cur_re - 1]); } else wrong_key(); } else if (c == 'r') { (cur -> pre)[cur_re].match_count = 0; } } delete_popup(mywin); return changed; } int toggle_vertical_split(void) { int changed = 0; if (split) { if ((max_y / nfd) < 3) { error_popup("Toggle vertical split", -1, "That is not possible: the new configuration won't fit."); } else { split = 0; changed = 1; myfree(vertical_split); vertical_split = NULL; myfree(n_win_per_col); n_win_per_col = NULL; } } else { if (nfd == 1) error_popup("Toggle vertical split", -1, "There's only 1 window!"); else { char *str; NEWWIN *mywin = create_popup(7, 35); escape_print(mywin, 2, 2, "Enter number of columns"); str = edit_string(mywin, 4, 2, 20, 3, 1, NULL, HELP_SET_VERTICAL_SPLIT_N_WIN, -1, NULL, NULL); if (str) { split = atoi(str); if (split > 1) { changed = 1; } else { split = 0; } myfree(str); } delete_popup(mywin); } } return changed; } void list_keybindings(void) { NEWWIN *mywin; int prevpos = -1, curpos = 0; mywin = create_popup(20, 40); for(;;) { int c; if (curpos != prevpos) { int loop; werase(mywin -> win); win_header(mywin, "Keybindings"); for(loop=0; loop<17; loop++) { if ((loop + curpos) >= n_keybindings) break; ui_inverse_on(mywin); mvwprintw(mywin -> win, 2 + loop, 2, "%2s", key_to_keybinding(keybindings[loop + curpos].key)); ui_inverse_off(mywin); mvwprintw(mywin -> win, 2 + loop, 5, "%s", keybindings[loop + curpos].command); } draw_border(mywin); mydoupdate(); prevpos = curpos; } c = wait_for_keypress(HELP_LIST_KEYBINDINGS, 0, mywin, 0); if (toupper(c) == 'Q' || c == abort_key) break; else if (c == KEY_UP && curpos > 0) curpos--; else if ((c == KEY_DOWN || c == 13) && curpos < (n_keybindings - 1)) curpos++; else wrong_key(); } delete_popup(mywin); } void write_script(void) { FILE *fh = NULL; char *fname; NEWWIN *mywin = create_popup(12, 42); win_header(mywin, "Write script"); mvwprintw(mywin -> win, 2, 2, "Enter filename:"); fname = edit_string(mywin, 3, 2, 41, find_path_max(), 0, NULL, HELP_WRITE_SCRIPT, -1, &cmdfile_h, NULL); if (fname) fh = fopen(fname, "w"); if (fh) { int loop; fprintf(fh, "#!/bin/sh\n\n"); fprintf(fh, "multitail"); if (heartbeat_interval != 0.0) fprintf(fh, " -H %f", heartbeat_interval); if (config_file) { fprintf(fh, " -F "); write_escape_str(fh, config_file); } /* markerline color */ if (markerline_attrs.colorpair_index != -1 || markerline_attrs.attrs != -1) { fprintf(fh, " -Z "); emit_myattr_t(fh, markerline_attrs); } if (timestamp_in_markerline) fprintf(fh, " -T"); if (show_subwindow_id) fprintf(fh, " -S"); if (set_title) { fprintf(fh, " -x "); write_escape_str(fh, set_title); } if (tab_width != DEFAULT_TAB_WIDTH) fprintf(fh, " -b %d", tab_width); if (update_interval) fprintf(fh, " -u %d", update_interval); if (vertical_split) { fprintf(fh, " -sw "); for(loop=0; loop beep.beep_interval > 0) fprintf(fh, " --bi %d", cur -> beep.beep_interval); if (cur -> check_interval) fprintf(fh, " -iw %d", cur -> check_interval); if (cur -> cont) fprintf(fh, " --cont"); /* terminal emulation */ if (cur -> cdef.term_emul == TERM_ANSI) fprintf(fh, " -cT ANSI"); /* vt100 */ /* add timestamp to lines */ if (cur -> add_timestamp) fprintf(fh, " -ts"); if (cur -> label) fprintf(fh, " --label %s", cur -> label); /* title in statusline */ if (cur -> win_title) { fprintf(fh, " -t "); write_escape_str(fh, cur -> win_title); } if (cur -> mark_interval) fprintf(fh, " --mark-interval %d", cur -> mark_interval); if (cur -> repeat.suppress_repeating_lines) fprintf(fh, " --no-repeat"); /* height of window */ if (cur -> win_height != -1) fprintf(fh, " -wh %d", cur -> win_height); /* redirect output to a file or other command */ for(lloop=0; lloop < cur -> n_redirect; lloop++) { if ((cur -> predir)[lloop].type == REDIRECTTO_PIPE || (cur -> predir)[lloop].type == REDIRECTTO_PIPE_FILTERED) fprintf(fh, " -g "); else if ((cur -> predir)[lloop].type == REDIRECTTO_FILE || (cur -> predir)[lloop].type == REDIRECTTO_FILE_FILTERED) fprintf(fh, " -a "); write_escape_str(fh, (cur -> predir)[lloop].redirect); } /* conversion scheme to use */ for(conv_index=0; conv_index conversions); conv_index++) fprintf(fh, " -cv %s", conversions[get_iat_element(&cur -> conversions, conv_index)].name); /* linewrap */ if (cur -> line_wrap != 'a') { fprintf(fh, " -p %c", cur -> line_wrap); if (cur -> line_wrap == 'o') fprintf(fh, " %d", cur -> line_wrap_offset); } /* for commands: restart interval and do diff or not */ if (cur -> restart.restart != -1) { char mode = 'r'; if (cur -> restart.do_diff) mode = 'R'; if (cur -> restart.restart_clear) fprintf(fh, " -%cc", mode); else fprintf(fh, " -%c", mode); fprintf(fh, " %d", cur -> restart.restart); } /* initial number of lines to tail */ if (cur -> initial_n_lines_tail != -1) fprintf(fh, " -n %d", cur -> initial_n_lines_tail); /* folow filename or filedescriptor */ if (cur -> follow_filename) fprintf(fh, " -f"); /* if the file could not be openend, retry open? */ if (cur -> retry_open) fprintf(fh, " --retry"); /* colors and colorschemes */ if (cur -> cdef.colorize != 'n' && cur -> cdef.colorize != 0) { switch(cur -> cdef.colorize) { case 'a': fprintf(fh, " -ca "); emit_myattr_t(fh, cur -> cdef.alt_col_cdev1); fprintf(fh, " "); emit_myattr_t(fh, cur -> cdef.alt_col_cdev2); break; case 'i': fprintf(fh, " -ci "); emit_myattr_t(fh, cur -> cdef.attributes); break; case 's': fprintf(fh, " -cs"); break; case 'm': fprintf(fh, " -cm"); break; case 'f': fprintf(fh, " -cf %d ", cur -> cdef.field_nr); write_escape_str(fh, cur -> cdef.field_del); break; case 'S': { int cs_index; for(cs_index=0; cs_index cdef.color_schemes); cs_index++) fprintf(fh, " -cS %s", cschemes[get_iat_element(&cur -> cdef.color_schemes, cs_index)].name); } break; } } /* regular expressions */ for(lloop=0; lloop n_re; lloop++) { if ((cur -> pre)[lloop].invert_regex) fprintf(fh, " -v"); fprintf(fh, " -e%c ", (cur -> pre)[lloop].use_regex); write_escape_str(fh, (cur -> pre)[lloop].regex_str); if ((cur -> pre)[lloop].use_regex == 'x') { fprintf(fh, " "); write_escape_str(fh, (cur -> pre)[lloop].cmd); } } /* what to strip */ for(lloop=0; lloop n_strip; lloop++) { switch((cur -> pstrip)[lloop].type) { case STRIP_TYPE_REGEXP: fprintf(fh, " -ke "); write_escape_str(fh, (cur -> pstrip)[lloop].regex_str); break; case STRIP_KEEP_SUBSTR: fprintf(fh, " -kS "); write_escape_str(fh, (cur -> pstrip)[lloop].regex_str); break; case STRIP_TYPE_RANGE: fprintf(fh, " -kr %d %d", (cur -> pstrip)[lloop].start, (cur -> pstrip)[lloop].end); break; case STRIP_TYPE_COLUMN: fprintf(fh, " -kc "); write_escape_str(fh, (cur -> pstrip)[lloop].del); fprintf(fh, " %d", (cur -> pstrip)[lloop].col_nr); break; } } /* add filename/command */ if (cur == &pi[loop]) { if (cur -> wt == WT_COMMAND) fprintf(fh, " -l"); else if (cur -> wt == WT_STDIN) fprintf(fh, " -j"); else if (cur -> wt == WT_SOCKET) fprintf(fh, " --listen"); else if (cur -> wt == WT_FILE) { if ((cur -> filename)[0] == '-') fprintf(fh, " -i"); } } else { if (cur -> wt == WT_COMMAND) fprintf(fh, " -L"); else if (cur -> wt == WT_STDIN) fprintf(fh, " -J"); else if (cur -> wt == WT_SOCKET) fprintf(fh, " --Listen"); else if (cur -> wt == WT_FILE) fprintf(fh, " -I"); } fprintf(fh, " "); write_escape_str(fh, cur -> filename); cur = cur -> next; } while(cur); } if (fchmod(fileno(fh), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) { error_exit("write_script: error setting mode-bits on output file.\n"); } fprintf(fh, "\n"); fclose(fh); } else if (fname != NULL) { error_popup("Write script", HELP_ERROR_WRITE_SCRIPT_CREATE_FILE, "Error creating file: %s", strerror(errno)); } myfree(fname); delete_popup(mywin); } void selective_pause() { int selected_window = select_window(HELP_PAUSE_A_WINDOW, "Pause a window"); if (selected_window != -1) { pi[selected_window].paused = 1 - pi[selected_window].paused; update_statusline(pi[selected_window].status, selected_window, &pi[selected_window]); mydoupdate(); } } void do_pause(void) { NEWWIN *mywin = create_popup(3, 8); color_on(mywin, 1); mvwprintw(mywin -> win, 1, 1, "Paused"); draw_border(mywin); color_off(mywin, 1); wmove(mywin -> win, 1, 2); mydoupdate(); (void)getch(); delete_popup(mywin); } void set_buffering(void) { char winchoice, whatlines = 'a', maxnlines = 0; NEWWIN *mywin = create_popup(13, 40); win_header(mywin, "Set buffer size"); if (nfd > 1) { escape_print(mywin, 3, 2, "Set on ^a^ll or ^o^ne"); mvwprintw(mywin -> win, 4, 2, "window(s)?"); mydoupdate(); for(;;) { winchoice = toupper(wait_for_keypress(HELP_SET_BUFFERING, 0, mywin, 0)); if (winchoice == 'Q' || winchoice == 'X' || winchoice == abort_key) { winchoice = 'Q'; break; } if (winchoice == 'A' || winchoice == 'O') break; wrong_key(); } } else { winchoice = 'A'; } /* ask wether to store all lines or just the ones matchine to the * regular expression (if any) */ if (winchoice != 'Q' && winchoice != abort_key) { escape_print(mywin, 5, 2, "Store ^a^ll or ^m^atching regular"); mvwprintw(mywin -> win, 6, 2, "expressions (if any)?"); mydoupdate(); for(;;) { whatlines = toupper(wait_for_keypress(HELP_SET_BUFFERING_STORE_WHAT, 0, mywin, 0)); if (whatlines == 'Q' || whatlines == 'X' || whatlines == abort_key) { winchoice = 'Q'; break; } if (whatlines == 'A' || whatlines == 'M') break; wrong_key(); } } /* get number of lines to store */ if (winchoice != 'Q' && winchoice != abort_key) { char *dummy; mvwprintw(mywin -> win, 7, 2, "Store how many lines? (0 for all)"); dummy = edit_string(mywin, 8, 2, 40, 7, 1, NULL, HELP_ENTER_NUMBER_OF_LINES_TO_STORE, -1, NULL, NULL); if (!dummy) { winchoice = 'Q'; } else { maxnlines = atoi(dummy); myfree(dummy); } } /* do set mark */ if (winchoice != 'Q' && winchoice != abort_key) { if (winchoice == 'A') { int loop; for(loop=0; loop win); escape_print(mywin, 1, 2, "^Manage columns^"); escape_print(mywin, 2, 2, "^a^dd, ^d^elete, ^e^dit, ^q^uit"); escape_print(mywin, 3, 2, "width # windows"); if (split < 2) { color_on(mywin, find_colorpair(COLOR_RED, -1, 0)); escape_print(mywin, 5, 2, "No columns (no vertical split)"); color_off(mywin, find_colorpair(COLOR_RED, -1, 0)); } else { for(loop=0; loop win, 4 + loop, 2, "%2d %2d", vs, nw); if (loop == y) ui_inverse_off(mywin); } } draw_border(mywin); mydoupdate(); key = toupper(wait_for_keypress(HELP_MANAGE_COLS, 0, mywin, 1)); if (key == 'Q' || key == abort_key) break; if (key == KEY_UP) { if (y) y--; else wrong_key(); } else if (key == KEY_DOWN) { if (y < (split - 1)) y++; else wrong_key(); } else if (key == 'A') { if (split == MAX_N_COLUMNS) { error_popup("Add column", -1, "Maximum number of columns (%d) reached", MAX_N_COLUMNS); } else { int is_new = 0; if (split == 0) { is_new = 1; split = 2; } else split++; if (!vertical_split) is_new = 1; vertical_split = (int *)myrealloc(vertical_split, sizeof(int) * split); n_win_per_col = (int *)myrealloc(n_win_per_col, sizeof(int) * split); if (is_new) { memset(vertical_split, 0x00, sizeof(int) * split); memset(n_win_per_col , 0x00, sizeof(int) * split); } else { vertical_split[split - 1] = 0; n_win_per_col[split - 1] = 0; } resized = 1; } } else if (key == 'D') { if (split > 0) { resized = 1; if (split == 2) { split = 0; myfree(vertical_split); vertical_split = NULL; myfree(n_win_per_col); n_win_per_col = NULL; } else { int n_to_move = (split - y) - 1; if (vertical_split) memmove(&vertical_split[y], &vertical_split[y+1], sizeof(int) * n_to_move); if (n_win_per_col ) memmove(&n_win_per_col [y], &n_win_per_col [y+1], sizeof(int) * n_to_move); split--; } if (y >= split && split > 0) y--; resized = 1; } else wrong_key(); } else if (key == 'E') { if (split > 0) { char oldval[64], *result; NEWWIN *editwin = create_popup(6, 33); escape_print(editwin, 1, 2, "Column width:"); if (!vertical_split) { vertical_split = mymalloc(split * sizeof(int)); memset(vertical_split, 0x00, split * sizeof(int)); } if (!n_win_per_col) { n_win_per_col = mymalloc(split * sizeof(int)); memset(n_win_per_col, 0x00, split * sizeof(int)); } snprintf(oldval, sizeof(oldval), "%d", vertical_split[y]); result = edit_string(editwin, 2, 2, 20, 3, 1, oldval, HELP_COLUMN_WIDTH, -1, NULL, NULL); if (result) { int vs = atoi(result); myfree(result); escape_print(editwin, 3, 2, "Number of windows per column:"); snprintf(oldval, sizeof(oldval), "%d", n_win_per_col[y]); result = edit_string(editwin, 4, 2, 20, 3, 1, oldval, HELP_N_WIN_PER_COL, -1, NULL, NULL); if (result) { vertical_split[y] = vs; n_win_per_col [y] = atoi(result); myfree(result); resized = 1; } } delete_popup(editwin); } else wrong_key(); } else wrong_key(); } delete_popup(mywin); return resized; } int set_window_sizes(void) { int resized = 0; NEWWIN *mywin = create_popup(8, 35); for(;;) { int key; werase(mywin -> win); escape_print(mywin, 1, 2, "^m^ manage columns"); escape_print(mywin, 2, 2, "^h^ set height of a window"); escape_print(mywin, 3, 2, "^q^ quit this menu"); draw_border(mywin); mydoupdate(); key = toupper(wait_for_keypress(HELP_SET_WINDOWSIZES, 0, mywin, 0)); if (key == 'Q' || key == abort_key) break; if (key == 'M') { resized |= set_window_widths(); } else if (key == 'H') { if (nfd > 1) { int window = select_window(HELP_SET_WINDOW_HEIGHT_SELECT_WINDOW, NULL); if (window != -1) { char *str; char oldval[5] = { 0 }; if (pi[window].win_height > -1) snprintf(oldval, sizeof(oldval), "%d", pi[window].win_height); else if (pi[window].data) snprintf(oldval, sizeof(oldval), "%d", pi[window].data -> height); mvwprintw(mywin -> win, 4, 2, "Enter number of lines:"); mvwprintw(mywin -> win, 5, 2, "(0 = automatic)"); mydoupdate(); str = edit_string(mywin, 6, 2, 20, 3, 1, oldval, HELP_SET_WINDOW_HEIGHT, -1, NULL, NULL); if (str) { int dummy = atoi(str); myfree(str); if (dummy == 0) dummy = -1; if (dummy == -1 || dummy >= 2) { pi[window].win_height = dummy; resized = 1; break; } error_popup("Set window size", HELP_SET_WINDOW_HEIGHT, "Invalid height (%d) entered.", dummy); } } } else { mvwprintw(mywin -> win, 5, 2, "You need at least 2 windows."); } mydoupdate(); } else { wrong_key(); } } delete_popup(mywin); return resized; } void terminal_mode(void) { proginfo *cur = NULL; int f_index = -1; if (nfd > 0) { NEWWIN *mywin = create_popup(8 + nfd, 40); win_header(mywin, "Enter terminal mode"); mydoupdate(); f_index = select_window(HELP_TERMINAL_MODE_SELECT_WINDOW, NULL); delete_popup(mywin); } if (f_index != -1) { cur = &pi[f_index]; if (cur -> next) cur = select_subwindow(f_index, HELP_TOGGLE_COLORS_SELECT_SUBWINDOW, "Enter terminalmode: select subwindow"); } if (cur) { if (cur -> wt != WT_COMMAND) { error_popup("Enter terminal mode", -1, "Terminal mode is only possible for commands."); } else { terminal_index = cur; terminal_main_index = f_index; prev_term_char = -1; redraw_statuslines(); } } } void wipe_window(void) { NEWWIN *mywin = create_popup(5, 35); escape_print(mywin, 1, 2, "^Clear window^"); escape_print(mywin, 3, 2, "Press 0...9"); mydoupdate(); for(;;) { int c = wait_for_keypress(HELP_WIPE_WINDOW, 0, mywin, 0); if (c == abort_key || toupper(c) == 'Q') break; if (c < '0' || c > '9') { wrong_key(); continue; } c -= '0'; if (c >= nfd) { wrong_key(); continue; } if (pi[c].hidden) { wrong_key(); continue; } werase(pi[c].data -> win); mydoupdate(); break; } delete_popup(mywin); } void wipe_all_windows(void) { int loop; for(loop=0; loop win); } } void add_markerline(int index, proginfo *cur, proginfo *type, char *text) { char buffer[TIMESTAMP_EXTEND_BUFFER] = { 0 }; char *ml; char *curfname = cur ? cur -> filename : ""; char *curtext = USE_IF_SET(text, ""); double now = get_ts(); int str_len; if (timestamp_in_markerline) get_now_ts(statusline_ts_format, buffer, sizeof(buffer)); str_len = strlen(curfname) + 1 + strlen(buffer) + 1 + strlen(curtext) + 1 + 1; ml = (char *)mymalloc(str_len); snprintf(ml, str_len, "%s%s%s%s%s", curtext, curtext[0]?" ":"", curfname, (curtext[0] || curfname[0])?" ":"", buffer); do_print(index, type, ml, NULL, -1, now); do_buffer(index, type, ml, 1, now); myfree(ml); update_panels(); } void do_terminal(char c) { if (prev_term_char == 1) /* ^A */ { if (c == 'd') { terminal_main_index = -1; terminal_index = NULL; redraw_statuslines(); mydoupdate(); } else { WRITE(terminal_index -> wfd, &prev_term_char, 1, "do_terminal"); WRITE(terminal_index -> wfd, &c, 1, "do_terminal"); } } else { if (c != 1) /* ^A */ WRITE(terminal_index -> wfd, &c, 1, "do_terminal"); } prev_term_char = c; } void set_linewrap(void) { NEWWIN *mywin = create_popup(6, 64); int f_index = 0; proginfo *cur = NULL; escape_print(mywin, 1, 2, "^Set linewrap^"); escape_print(mywin, 3, 2, "^l^eft/^a^ll/^r^ight/^s^yslog/^S^yslog: no procname/^o^ffset/^w^ordwrap"); mydoupdate(); /* only popup selectionbox when more then one window */ if (nfd > 1) f_index = select_window(HELP_DELETE_SELECT_WINDOW, "Select window"); if (f_index != -1) { if (pi[f_index].next) cur = select_subwindow(f_index, HELP_DELETE_SELECT_SUBWINDOW, "Select subwindow"); else cur = &pi[f_index]; } if (cur) { for(;;) { int c = wait_for_keypress(HELP_SET_LINEWRAP, 0, mywin, 0); if (c == abort_key || toupper(c) == 'Q') break; if (c == 'l' || c == 'a' || c == 'r' || c == 's' || c == 'S' || c == 'o' || c == 'w') { if (c == 'o') { char offset[5], *new_offset; snprintf(offset, sizeof(offset), "%d", cur -> line_wrap_offset); mvwprintw(mywin -> win, 4, 2, "Offset:"); new_offset = edit_string(mywin, 4, 10, 4, 4, 1, offset, -1, -1, NULL, NULL); if (!new_offset) break; cur -> line_wrap_offset = atoi(new_offset); myfree(new_offset); } cur -> line_wrap = c; break; } wrong_key(); } } delete_popup(mywin); } void horizontal_scroll(int direction) { proginfo *cur = &pi[0]; do { if (cur->line_wrap == 'l') { if (direction == KEY_RIGHT) { cur->line_wrap_offset++; } else { if (cur->line_wrap_offset > 0) cur->line_wrap_offset--; } } cur= cur->next; } while(cur); } void regexp_error_popup(int rc, regex_t *pre) { char popup_buffer[4096] = { 0 }; char *error = convert_regexp_error(rc, pre); if (error) { int len = strlen(popup_buffer); if (strlen(error) > 58) { memcpy(&popup_buffer[len], error, 58); len += 58; popup_buffer[len++] = '\n'; strncat(popup_buffer, &error[58], max(1, sizeof(popup_buffer) - len)); popup_buffer[sizeof(popup_buffer) - 1] = 0x00; } else { strncat(popup_buffer, error, sizeof(popup_buffer)-strlen(popup_buffer)-1); } myfree(error); } error_popup("regexp_error_popup: failed to compile regular expression!", HELP_COMPILE_REGEXP_FAILED, popup_buffer); } void draw_gui_window_header(proginfo *last_changed_window) { char *out = NULL; int out_size = 0; int out_fill_size = 0; int pos = 0; int str_len; static char loadstr[16]; struct utsname uinfo; if (!set_title) return; str_len = strlen(set_title); for(;pos < str_len;) { if (set_title[pos] == '%') { char *repl_str = NULL; switch(set_title[++pos]) { case 'f': /* changed file */ repl_str = last_changed_window?last_changed_window -> filename:""; break; case 'h': /* hostname */ if (uname(&uinfo) == -1) error_exit("uname() failed\n"); repl_str = uinfo.nodename; break; case 'l': /* current load */ { double v1, v2, v3; get_load_values(&v1, &v2, &v3); snprintf(loadstr, sizeof(loadstr), "%f", v1); repl_str = loadstr; } break; case 'm': repl_str = mail?"New mail!":""; break; case 'u': /* username */ repl_str = getusername(); break; case 't': /* atime */ if (last_changed_window) { char *dummy; time_t lastevent = last_changed_window -> statistics.lastevent; repl_str = ctime(&lastevent); dummy = strchr(repl_str, '\n'); if (dummy) *dummy = 0x00; } else { repl_str = ""; } break; case '%': /* % */ repl_str = "%"; break; } if (repl_str) { int repl_str_len = strlen(repl_str); /* +1: 0x00 */ int req_size = out_fill_size + repl_str_len; if (req_size > 0) { grow_mem_if_needed(&out, &out_size, req_size); memcpy(&out[out_fill_size], repl_str, repl_str_len); out_fill_size += repl_str_len; } } } else { grow_mem_if_needed(&out, &out_size, out_fill_size + 1); out[out_fill_size++] = set_title[pos]; } pos++; } /* terminate string */ grow_mem_if_needed(&out, &out_size, out_fill_size + 1); out[out_fill_size] = 0x00; gui_window_header(out); myfree(out); } void send_signal_failed(proginfo *cur) { NEWWIN *mywin = create_popup(6, 60); color_on(mywin, find_colorpair(COLOR_RED, -1, 0)); mvwprintw(mywin -> win, 2, 2, "Error sending signal to %s", cur -> filename); mvwprintw(mywin -> win, 3, 2, "%s", strerror(errno)); color_off(mywin, find_colorpair(COLOR_RED, -1, 0)); (void)wait_for_keypress(HELP_SEND_SIGNAL_FAILED, 0, mywin, 0); delete_popup(mywin); } int send_signal_select_signal(void) { int sig = -1; NEWWIN *mywin = create_popup(22, 40); int cursor_index = 0; for(;;) { int loop, c; werase(mywin -> win); win_header(mywin, "Select signal to send"); mvwprintw(mywin -> win, 20, 2, "Press ^g to abort"); for(loop=1; loop<=31; loop++) { if (loop == (cursor_index + 1)) ui_inverse_on(mywin); if (loop > 16) mvwprintw(mywin -> win, 2 + loop - 16, 18, "%s", sigs[loop]); else mvwprintw(mywin -> win, 2 + loop , 2, "%s", sigs[loop]); if (loop == (cursor_index + 1)) ui_inverse_off(mywin); } draw_border(mywin); mydoupdate(); c = toupper(wait_for_keypress(HELP_SELECT_SIGNAL, 0, mywin, 1)); if (c == KEY_UP) { if (cursor_index) cursor_index--; else wrong_key(); } else if (c == KEY_DOWN) { if (cursor_index < 30) cursor_index++; else wrong_key(); } else if (c == KEY_LEFT) { if (cursor_index > 16) cursor_index -= 16; else wrong_key(); } else if (c == KEY_RIGHT) { if (cursor_index < 16) { cursor_index += 16; if (cursor_index > 30) cursor_index = 30; } else wrong_key(); } else if (c == 'Q' || c == 'X' || c == abort_key) { sig = -1; break; } else if (c == ' ' || c == 13) { sig = cursor_index + 1; break; } else wrong_key(); } delete_popup(mywin); return sig; } void send_signal(void) { int f_index = 0; proginfo *cur = NULL; if (nfd > 1) f_index = select_window(HELP_SEND_SIGNAL_SELECT_WINDOW, "Select window to send signal to"); if (f_index != -1) { char all = 1; if (pi[f_index].next) { NEWWIN *mywin = create_popup(13, 24); win_header(mywin, "Send signal to window"); escape_print(mywin, 3, 2, "To all? (^y^/^n^)"); mydoupdate(); all = ask_yes_no(HELP_SEND_SIGNAL_WINDOW_SEND_TO_ALL_SUBWIN, mywin); delete_popup(mywin); } if (all == 1) /* pressed 'Y' */ { int sig = send_signal_select_signal(); cur = &pi[f_index]; while(cur && sig != -1) { if (kill(cur -> pid, sig) == -1) { send_signal_failed(cur); break; } cur = cur -> next; } } else if (all == 0) /* pressed 'N' */ { proginfo *cur = select_subwindow(f_index, HELP_SEND_SIGNAL_SELECT_SUBWINDOW, "Select subwindow"); if (cur) { int sig = send_signal_select_signal(); if (sig != -1 && kill(cur -> pid, sig) == -1) send_signal_failed(cur); } } } } void screendump_do(WINDOW *win) { char *filename; NEWWIN *mywin = create_popup(7, 30); win_header(mywin, "Screendump"); mvwprintw(mywin -> win, 3, 2, "Select file to write to:"); filename = edit_string(mywin, 4, 2, 38, find_path_max(), 0, "screendump.txt", HELP_SCREENDUMP_SELECT_FILE, -1, &cmdfile_h, NULL); if (filename) { FILE *fh = fopen(filename, "wb"); int maxy = 0, maxx = 0; int y, x; getmaxyx(win, maxy, maxx); for(y=0; y=0; curmaxx--) { if ((mvwinch(win, y, curmaxx) & A_CHARTEXT) != 32) break; } curmaxx++; for(x=0; x win); } } void truncate_file(void) { int f_index = 0; proginfo *cur = NULL; if (nfd > 1) f_index = select_window(HELP_TRUNCATE_FILE_SELECT_WINDOW, "Select window to truncate"); if (f_index != -1) { if (pi[f_index].next) cur = select_subwindow(f_index, HELP_TRUNCATE_FILE_SELECT_SUBWINDOW, "Select subwindow"); else cur = &pi[f_index]; if (cur) { if (cur -> wt == WT_FILE) { NEWWIN *mywin = create_popup(8, 50); color_on(mywin, find_colorpair(COLOR_RED, -1, 0)); win_header(mywin, "Truncate file"); mvwprintw(mywin -> win, 3, 2, "Truncate file %s", shorten_filename(cur -> filename, 31)); mvwprintw(mywin -> win, 4, 2, "Are you sure? (y/n)"); color_off(mywin, find_colorpair(COLOR_RED, -1, 0)); mydoupdate(); if (wait_for_keypress(HELP_TRUNCATE_AREYOUSURE, 0, mywin, 0) == 'y') { if (truncate(cur -> filename, 0) == -1) error_popup("Truncate failed", HELP_TRUNCATE_FAILED, "Error: %s", strerror(errno)); else { color_on(mywin, find_colorpair(COLOR_YELLOW, -1, 0)); escape_print(mywin, 5, 2, "_File truncated. Press any key._"); color_off(mywin, find_colorpair(COLOR_YELLOW, -1, 0)); mydoupdate(); (void)wait_for_keypress(-1, 0, mywin, 0); } } delete_popup(mywin); } else error_popup("Truncate failed", HELP_TRUNCATE_ONLY_LOGFILES, "Only logFILES can be truncated."); } } } void draw_marker_line(NEWWIN *win, char *string, proginfo *marker_type) { int mx = getmaxx(win -> win); int len = strlen(string); int left_dot = (mx / 2) - (len / 2); int loop; myattr_t attrs = { -1, -1 }; char dot = '-'; if (marker_type == MARKER_REGULAR) { attrs = markerline_attrs; dot = markerline_char; } else if (marker_type == MARKER_CHANGE) { attrs = changeline_attrs; dot = changeline_char; } else if (marker_type == MARKER_IDLE) { attrs = idleline_attrs; dot = idleline_char; } else if (marker_type == MARKER_MSG) { attrs = msgline_attrs; dot = msgline_char; } myattr_on(win, attrs); for(loop=0; loop win, dot); if (len < mx) wprintw(win -> win, "%s", string); else wprintw(win -> win, "%s", shorten_filename(string, mx)); for(loop=0; loop win, dot); myattr_off(win, attrs); } void search_in_all_windows(void) { char *find; NEWWIN *mywin = create_popup(8, 44); mybool_t case_insensitive = re_case_insensitive; win_header(mywin, "Find"); mvwprintw(mywin -> win, 3, 2, "^u empty line, ^g abort"); find = edit_string(mywin, 5, 2, 40, 80, 0, reuse_searchstring?global_find:NULL, HELP_SEARCH_IN_ALL_WINDOWS, -1, &search_h, &case_insensitive); delete_popup(mywin); myfree(global_find); global_find = find; if (find) merged_scrollback_with_search(find, case_insensitive); } void highlight_in_all_windows(void) { char *find; NEWWIN *mywin = create_popup(5, 44); mybool_t case_insensitive = re_case_insensitive; win_header(mywin, "Global highlight"); find = edit_string(mywin, 3, 2, 40, 80, 0, reuse_searchstring?global_highlight_str:NULL, HELP_HIGHLIGHT_IN_ALL_WINDOWS, -1, &search_h, &case_insensitive); delete_popup(mywin); if (global_highlight_str) { regfree(&global_highlight_re); myfree(global_highlight_str); global_highlight_str = NULL; } if (find) { int rc; if ((rc = regcomp(&global_highlight_re, find, REG_EXTENDED | (case_insensitive == MY_TRUE?REG_ICASE:0)))) { regexp_error_popup(rc, &global_highlight_re); myfree(find); } else { global_highlight_str = find; } } } void toggle_regexp_case_insensitive(void) { NEWWIN *mywin = create_popup(6, 40); re_case_insensitive = !re_case_insensitive; mvwprintw(mywin -> win, 2, 2, "Default searche case now is %s.", re_case_insensitive == MY_TRUE?"insensitive":"sensitive"); escape_print(mywin, 4, 2, "_Press any key to exit this screen_"); mydoupdate(); (void)wait_for_keypress(-1, 0, mywin, 0); delete_popup(mywin); } void inverse_on(NEWWIN *win) { wattron(win -> win, inverse_attrs); } void inverse_off(NEWWIN *win) { wattroff(win -> win, inverse_attrs); } void select_conversionschemes(void) { int f_index = 0; proginfo *cur = NULL; if (nfd > 1) f_index = select_window(HELP_TRUNCATE_FILE_SELECT_WINDOW, "Select window"); if (f_index != -1) { if (pi[f_index].next) cur = select_subwindow(f_index, HELP_TRUNCATE_FILE_SELECT_SUBWINDOW, "Select subwindow"); else cur = &pi[f_index]; if (cur) { (void)select_schemes(conversions, n_conversions, SCHEME_CONVERSION, &cur -> conversions); } } } void toggle_subwindow_nr(void) { show_subwindow_id = 1 - show_subwindow_id; } void clear_a_buffer(void) { int f_index = 0; if (nfd > 1) f_index = select_window(HELP_CLEAR_BUFFER, "Select window"); if (f_index != -1) { delete_be_in_buffer(&lb[f_index]); werase(pi[f_index].data -> win); mydoupdate(); } } void clear_all_buffers(void) { int loop; for(loop=0; loop win); mydoupdate(); } } void ask_case_insensitive(mybool_t *pcase_insensitive) { NEWWIN *mywin = create_popup(6, 40); mvwprintw(mywin -> win, 2, 2, "Search case insensitive?"); mvwprintw(mywin -> win, 3, 12, "(Y/N)"); mydoupdate(); for(;;) { int c = toupper(wait_for_keypress(-1, 0, mywin, 0)); if (c == 'Q' || c == abort_key) break; if (c == 'Y') { *pcase_insensitive = MY_TRUE; break; } else if (c == 'N') { *pcase_insensitive = MY_FALSE; break; } wrong_key(); } delete_popup(mywin); } multitail-6.0/makefile.icc0000644000175000017500000000101612245202637015517 0ustar folkertfolkertinclude version CONFIG_FILE=$(DESTDIR)/etc/multitail.conf CC=icc DEBUG=-g # -D_DEBUG LDFLAGS=-lpanel -lncurses -lutil -lm $(DEBUG) CFLAGS+=-D$(shell uname) -O2 -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail clean: rm -f $(OBJS) multitail core gmon.out *.da multitail-6.0/stripstring.h0000644000175000017500000000010412245202637016020 0ustar folkertfolkertint edit_strippers(void); char * do_strip(proginfo *cur, char *in);