#!/bin/sh ############################################################ IDENT(1) # # $Title: Script to search p4d log entries $ # ############################################################ CONFIGURATION # # Local perforce server settings # NB: Taken from rc.conf(5) on FreeBSD # P4D_ROOT=$( sysrc -n p4d_root 2> /dev/null ) P4D_LOG=$( sysrc -n p4d_log 2> /dev/null ) # # Sensible defaults (i.e., Linux) # : ${P4D_ROOT:=/perforce} : ${P4D_LOG:=$P4D_ROOT/logs/p4log} unset P4D_ROOT ############################################################ GLOBALS pgm="${0##*/}" # Program basename # # Global exit status # SUCCESS=0 FAILURE=1 # # Command-line options # AFTER=0 # -A NUM BEFOR=0 # -B NUM COLOR=1 # -n (disable) or -c (always enabled even to non-terminal) LIMIT=0 # -NUM LOGFILE= # -f file # # Miscellaneous # REGEX= # first non-flag argument ############################################################ FUNCTIONS usage() { local fmt="$1" exec >&2 if [ "$fmt" ]; then shift 1 # fmt printf "%s: $fmt\n" "$pgm" "$@" fi local optfmt="\t%-8s %s\n" printf "Usage: %s [OPTIONS] regex\n" "$pgm" printf "OPTIONS:\n" printf "$optfmt" "regex" \ "awk(1) regular expression for matching log entries." printf "$optfmt" "-NUM" \ "Limit output to at-most NUM mathing entries." printf "$optfmt" "-A NUM" \ "Show NUM lines of context following matched entries." printf "$optfmt" "-B NUM" \ "Show NUM lines of context leading up to matched entries." printf "$optfmt" "-c" \ "Always enable color, even to non-terminals (e.g., pipes)." printf "$optfmt" "-f file" \ "Read file (\`-' for stdin; Default $P4D_LOG)." printf "$optfmt" "-n" \ "Disable the use of color highlighting on terminals." exit $FAILURE } setopt() { local __var_to_set="$1" __value="$2" __retval=0 # NB: Caller sets $arg case "$arg" in -??*) __retval=1 arg="-${arg#-?}" ;; -?) __retval=0 arg= ;; *) return $FAILURE esac eval $__var_to_set='"$__value"' return $__retval # caller: 0 = shift (-X); 1 = continue (-Xv => -v) } setarg() { local __var_to_set="$1" __value="$2" __retval=0 # NB: Caller sets $arg case "$arg" in -??*) eval __retval=0 $__var_to_set='${arg#-?}' ;; -?) eval __retval=1 $__var_to_set='"$__value"' ;; esac arg= return $__retval # caller: 0 = shift 1 (-Xarg); 1 = shift 2 (-X arg) } ############################################################ MAIN # # Process command-line arguments # while [ $# -gt 0 ]; do : ${arg:="$1"} case "$arg" in -[0-9]*) LIMIT="${arg#-}" LIMIT="${LIMIT%%[!0-9]*}" arg="${arg#-$LIMIT}" arg="${arg:+-}$arg" [ "$arg" ] && continue ;; -A*) setarg AFTER "$2" || shift ;; -B*) setarg BEFOR "$2" || shift ;; -c*) setopt COLOR 2 || continue ;; -f*) setarg LOGFILE "$2" || shift ;; -n*) setopt COLOR 0 || continue ;; -*) usage "invalid option -- %s" "${1#-}" ;; *) break # non-option argument encountered esac shift done REGEX="$1" # # Validate command-line arguments # [ "$REGEX" ] || usage [ "$BEFOR" = "${BEFOR%%[!0-9]*}" ] || usage "invalid \`-B NUM' argument" [ "$LIMIT" = "${LIMIT%%[!0-9]*}" ] || usage "invalid \`-NUM' argument" [ "$AFTER" = "${AFTER%%[!0-9]*}" ] || usage "invalid \`-A NUM' argument" P4D_LOG="${LOGFILE:-$P4D_LOG}" # set after last-call to usage() # # For `-n' (COLOR=0) and `-c' (COLOR=2), keep the value of COLOR as-is. In the # default case (COLOR=1), reset COLOR to zero if stdout is a non-terminal # (e.g., when stdout has been redirected to a file/pipe). # [ $COLOR -ne 1 ] || [ -t 1 ] || COLOR=0 # # Wield awk(1) to display matching paragraphs from the p4d log file # awk -v REGEX="$REGEX" -v COLOR=$COLOR \ -v LIMIT=$LIMIT -v BEFOR=$BEFOR -v AFTER=$AFTER ' ######################################## AWK(1) FUNCTIONS function buf_print() { print buf } function color_print() { pbuf = buf if (COLOR) { cbuf = pbuf pbuf = "" while (match(cbuf, REGEX)) { pbuf = sprintf("%s%s%c[43;30m%s%c[49;39m", pbuf, substr(cbuf, 0, RSTART - 1), 27, substr(cbuf, RSTART, RLENGTH), 27) cbuf = substr(cbuf, RSTART + RLENGTH) } pbuf = pbuf cbuf if (BEFOR || AFTER) pbuf = sprintf("%c[7m%s%c[27m", 27, pbuf, 27) } print pbuf } function bstack_add() { if (A) { # Print/Skip when consuming AFTER-elements buf_print() if (!--A && !BEFOR && N) print "--" return } if (BEFOR <= 0) # feature not enabled return if (BEFOR == 1) # only one slot (easy) bstack[B = 1] = buf else { # multiple slots (tricky) if (B < BEFOR) B++ for (n = B; n > 1; n--) # make room bstack[n] = bstack[n-1] bstack[n] = buf } } function bufhandler() { if (!buf) return if (buf ~ REGEX && (LIMIT ? N < LIMIT : 1)) { if (B && N) print "--" # print hr before bstack items while (B) # print the bstack and reset B to zero print bstack[B--] delete bstack # erase the bstack A = AFTER # reset A to high watermark color_print() # dump matching buffer N++ # increment printed matches } else bstack_add() buf = "" # reset buffer } ######################################## AWK(1) MAIN /^[^[:space:]]/ { # got a line beginning with non-whitespace bufhandler() if (LIMIT > 0 && N >= LIMIT && !A) exit buf = sprintf(">>> ENTRY#%09u %s:%u: %s", C++, FILENAME, FNR, $0) next } # NOTREACHED unless line begins with whitespace { buf = buf "\n" $0 } # NOTREACHED until EOF or awk(1) exit END { bufhandler() } ######################################## AWK(1) END ' "$P4D_LOG" ################################################################################ # END ################################################################################ # # $Copyright: 2015 Devin Teske. All rights reserved. $ # # $Header: //guest/freebsdfrau/p4t/libexec/server_log#1 $ # ################################################################################