#!/usr/bin/env perl
# $Id: tracemunch,v 1.40 2021/02/13 19:29:12 tom Exp $
##############################################################################
# Copyright 2018-2020,2021 Thomas E. Dickey                                  #
# Copyright 1998-2005,2017 Free Software Foundation, Inc.                    #
#                                                                            #
# Permission is hereby granted, free of charge, to any person obtaining a    #
# copy of this software and associated documentation files (the "Software"), #
# to deal in the Software without restriction, including without limitation  #
# the rights to use, copy, modify, merge, publish, distribute, distribute    #
# with modifications, sublicense, and/or sell copies of the Software, and to #
# permit persons to whom the Software is furnished to do so, subject to the  #
# following conditions:                                                      #
#                                                                            #
# The above copyright notice and this permission notice shall be included in #
# all copies or substantial portions of the Software.                        #
#                                                                            #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   #
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    #
# THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER      #
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    #
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        #
# DEALINGS IN THE SOFTWARE.                                                  #
#                                                                            #
# Except as contained in this notice, the name(s) of the above copyright     #
# holders shall not be used in advertising or otherwise to promote the sale, #
# use or other dealings in this Software without prior written               #
# authorization.                                                             #
##############################################################################
# tracemunch -- compactify ncurses trace logs
#
# The error logs produced by ncurses with tracing enabled can be very tedious
# to wade through.  This script helps by compacting runs of log lines that
# can be conveniently expressed as higher-level operations.

use strict;
use warnings;

$| = 1;

our $putattr =
    'PutAttrChar\(\{\{ ' . "'(.)'"
  . ' = 0[0-7]+ \}\}\) at \(([0-9]+), ([0-9]+)\)';
our $waddnstr =
'^called \{waddnstr\((0x[[:xdigit:]]+|window\d+),"((\\.|[^\"]*))",[-]?[0-9]+\)';

our %TR = qw(
  DISABLE       0x0000
  TIMES         0x0001
  TPUTS         0x0002
  UPDATE        0x0004
  MOVE          0x0008
  CHARPUT       0x0010
  ORDINARY      0x001F
  CALLS         0x0020
  VIRTPUT       0x0040
  IEVENT        0x0080
  BITS          0x0100
  ICALLS        0x0200
  CCALLS        0x0400
  DATABASE      0x0800
  ATTRS         0x1000
);

our $tracelevel = 0;

our $tSCREEN   = 1;    # SCREEN*
our $tWINDOW   = 2;    # WINDOW*
our $tTERMINAL = 3;    # TERMINAL*
our $tPANEL    = 4;    # PANEL*
our $tFIELD    = 5;    # FIELD*
our $tFORM     = 5;    # FORM*
our $tMENU     = 6;    # MENU*
our $tITEM     = 7;    # ITEM*

our %known_p1_types = (
    $tSCREEN   => "SCREEN*",
    $tWINDOW   => "WINDOW*",
    $tTERMINAL => "TERMINAL*",
    $tPANEL    => "PANEL*",
    $tFORM     => "FORM*",
    $tFIELD    => "FIELD*",
    $tMENU     => "MENU*",
    $tITEM     => "ITEM*",
);

# If the trace is complete, we can infer addresses using the return value from
# newwin, etc.  But if it is incomplete, we can still check for special cases
# such as SCREEN* and WINDOW* parameters.  In this table, the type for the
# first parameter is encoded, relying upon an ncurses programming convention:
our %known_p1 = (
    TransformLine          => $tSCREEN,
    _nc_console_read       => $tSCREEN,
    _nc_freewin            => $tWINDOW,
    _nc_initscr            => $tSCREEN,
    _nc_makenew            => $tSCREEN,
    _nc_mingw_console_read => $tSCREEN,
    _nc_reset_colors       => $tSCREEN,
    _nc_scroll_optimize    => $tSCREEN,
    _nc_tinfo              => $tSCREEN,
    _nc_tinfo_mvcur        => $tSCREEN,
    _nc_wgetch             => $tWINDOW,
    adjust_window          => $tWINDOW,
    assume_default_colors  => $tSCREEN,
    attr_get               => $tWINDOW,
    baudrate               => $tSCREEN,
    beep                   => $tSCREEN,
    border_set             => $tWINDOW,
    bottom_panel           => $tPANEL,
    bottom_panel           => $tPANEL,
    box                    => $tWINDOW,
    box_set                => $tWINDOW,
    can_change_color       => $tSCREEN,
    cbreak                 => $tSCREEN,
    ceiling_panel          => $tSCREEN,
    clearok                => $tWINDOW,
    color_content          => $tSCREEN,
    copywin                => $tWINDOW,
    current_item           => $tMENU,
    curs_set               => $tSCREEN,
    decrease_size          => $tSCREEN,
    def_prog_mode          => $tSCREEN,
    def_shell_mode         => $tSCREEN,
    define_key             => $tSCREEN,
    del_curterm            => $tSCREEN,
    del_panel              => $tPANEL,
    del_panel              => $tPANEL,
    delay_output           => $tSCREEN,
    delscreen              => $tSCREEN,
    delwin                 => $tWINDOW,
    derwin                 => $tWINDOW,
    doupdate               => $tSCREEN,
    dup_field              => $tFIELD,
    dupwin                 => $tWINDOW,
    echo                   => $tSCREEN,
    endwin                 => $tSCREEN,
    erasechar              => $tSCREEN,
    field_opts_off         => $tFIELD,
    field_opts_on          => $tFIELD,
    filter                 => $tSCREEN,
    flash                  => $tSCREEN,
    flushinp               => $tSCREEN,
    form_driver            => $tFORM,
    form_driver_w          => $tFORM,
    form_opts_off          => $tFORM,
    form_opts_on           => $tFORM,
    free_field             => $tFIELD,
    free_form              => $tFORM,
    free_item              => $tITEM,
    free_menu              => $tMENU,
    getattrs               => $tWINDOW,
    getbegx                => $tWINDOW,
    getbegy                => $tWINDOW,
    getbkgd                => $tWINDOW,
    getcurx                => $tWINDOW,
    getcury                => $tWINDOW,
    getmaxx                => $tWINDOW,
    getmaxy                => $tWINDOW,
    getmouse               => $tSCREEN,
    getparx                => $tWINDOW,
    getpary                => $tWINDOW,
    ground_panel           => $tSCREEN,
    halfdelay              => $tSCREEN,
    has_ic                 => $tSCREEN,
    has_il                 => $tSCREEN,
    has_key                => $tSCREEN,
    hide_panel             => $tPANEL,
    hide_panel             => $tPANEL,
    idcok                  => $tWINDOW,
    idlok                  => $tWINDOW,
    immedok                => $tWINDOW,
    increase_size          => $tSCREEN,
    init_color             => $tSCREEN,
    init_pair              => $tSCREEN,
    intrflush              => $tSCREEN,
    is_cleared             => $tWINDOW,
    is_idcok               => $tWINDOW,
    is_idlok               => $tWINDOW,
    is_immedok             => $tWINDOW,
    is_keypad              => $tWINDOW,
    is_leaveok             => $tWINDOW,
    is_linetouched         => $tWINDOW,
    is_nodelay             => $tWINDOW,
    is_notimeout           => $tWINDOW,
    is_pad                 => $tWINDOW,
    is_scrollok            => $tWINDOW,
    is_subwin              => $tWINDOW,
    is_syncok              => $tWINDOW,
    is_term_resized        => $tSCREEN,
    is_wintouched          => $tWINDOW,
    item_count             => $tMENU,
    item_description       => $tITEM,
    item_index             => $tITEM,
    item_init              => $tMENU,
    item_name              => $tITEM,
    item_opts              => $tITEM,
    item_opts_off          => $tITEM,
    item_opts_on           => $tITEM,
    item_term              => $tMENU,
    item_userptr           => $tITEM,
    item_value             => $tITEM,
    item_visible           => $tITEM,
    key_defined            => $tSCREEN,
    keybound               => $tSCREEN,
    keyok                  => $tSCREEN,
    keypad                 => $tWINDOW,
    killchar               => $tSCREEN,
    leaveok                => $tWINDOW,
    link_field             => $tFIELD,
    longname               => $tSCREEN,
    menu_back              => $tMENU,
    menu_driver            => $tMENU,
    menu_fore              => $tMENU,
    menu_format            => $tMENU,
    menu_grey              => $tMENU,
    menu_init              => $tMENU,
    menu_items             => $tMENU,
    menu_mark              => $tMENU,
    menu_opts              => $tMENU,
    menu_opts_off          => $tMENU,
    menu_opts_on           => $tMENU,
    menu_pad               => $tMENU,
    menu_pattern           => $tMENU,
    menu_spacing           => $tMENU,
    menu_sub               => $tMENU,
    menu_term              => $tMENU,
    menu_userptr           => $tMENU,
    menu_win               => $tMENU,
    meta                   => $tWINDOW,
    mouseinterval          => $tSCREEN,
    mousemask              => $tSCREEN,
    move_field             => $tFIELD,
    move_panel             => $tPANEL,
    move_panel             => $tPANEL,
    mvcur                  => $tSCREEN,
    mvderwin               => $tWINDOW,
    mvwadd_wch             => $tWINDOW,
    mvwadd_wchnstr         => $tWINDOW,
    mvwadd_wchstr          => $tWINDOW,
    mvwaddch               => $tWINDOW,
    mvwaddchnstr           => $tWINDOW,
    mvwaddchstr            => $tWINDOW,
    mvwaddnstr             => $tWINDOW,
    mvwaddnwstr            => $tWINDOW,
    mvwaddstr              => $tWINDOW,
    mvwaddwstr             => $tWINDOW,
    mvwchgat               => $tWINDOW,
    mvwdelch               => $tWINDOW,
    mvwget_wch             => $tWINDOW,
    mvwget_wstr            => $tWINDOW,
    mvwgetch               => $tWINDOW,
    mvwgetn_wstr           => $tWINDOW,
    mvwgetnstr             => $tWINDOW,
    mvwgetstr              => $tWINDOW,
    mvwhline               => $tWINDOW,
    mvwhline_set           => $tWINDOW,
    mvwin                  => $tWINDOW,
    mvwin_wch              => $tWINDOW,
    mvwin_wchnstr          => $tWINDOW,
    mvwin_wchstr           => $tWINDOW,
    mvwinch                => $tWINDOW,
    mvwinchnstr            => $tWINDOW,
    mvwinchstr             => $tWINDOW,
    mvwins_nwstr           => $tWINDOW,
    mvwins_wch             => $tWINDOW,
    mvwins_wstr            => $tWINDOW,
    mvwinsch               => $tWINDOW,
    mvwinsnstr             => $tWINDOW,
    mvwinsstr              => $tWINDOW,
    mvwinstr               => $tWINDOW,
    mvwinwstr              => $tWINDOW,
    mvwvline               => $tWINDOW,
    mvwvline_set           => $tWINDOW,
    new_panel              => $tWINDOW,
    new_panel              => $tWINDOW,
    newpad                 => $tSCREEN,
    newterm                => $tSCREEN,
    newwin                 => $tSCREEN,
    nl                     => $tSCREEN,
    nocbreak               => $tSCREEN,
    nodelay                => $tWINDOW,
    noecho                 => $tSCREEN,
    nofilter               => $tSCREEN,
    nonl                   => $tSCREEN,
    noqiflush              => $tSCREEN,
    noraw                  => $tSCREEN,
    notimeout              => $tWINDOW,
    overlap                => $tWINDOW,
    overlay                => $tWINDOW,
    overwrite              => $tWINDOW,
    pair_content           => $tSCREEN,
    panel_above            => $tPANEL,
    panel_above            => $tPANEL,
    panel_below            => $tPANEL,
    panel_below            => $tPANEL,
    panel_hidden           => $tPANEL,
    panel_hidden           => $tPANEL,
    panel_userptr          => $tPANEL,
    panel_userptr          => $tPANEL,
    panel_window           => $tPANEL,
    panel_window           => $tPANEL,
    pecho_wchar            => $tWINDOW,
    pechochar              => $tWINDOW,
    pnoutrefresh           => $tWINDOW,
    pos_form_cursor        => $tFORM,
    pos_menu_cursor        => $tMENU,
    post_form              => $tFORM,
    post_menu              => $tMENU,
    putwin                 => $tWINDOW,
    qiflush                => $tSCREEN,
    raw                    => $tSCREEN,
    redrawwin              => $tWINDOW,
    replace_panel          => $tPANEL,
    replace_panel          => $tPANEL,
    reset_prog_mode        => $tSCREEN,
    reset_shell_mode       => $tSCREEN,
    resetty                => $tSCREEN,
    resize_term            => $tSCREEN,
    resizeterm             => $tSCREEN,
    restartterm            => $tSCREEN,
    ripoffline             => $tSCREEN,
    savetty                => $tSCREEN,
    scale_menu             => $tMENU,
    scr_init               => $tSCREEN,
    scr_restore            => $tSCREEN,
    scr_set                => $tSCREEN,
    scroll                 => $tWINDOW,
    scrollok               => $tWINDOW,
    set_current_field      => $tFORM,
    set_current_item       => $tMENU,
    set_curterm            => $tTERMINAL,
    set_field_back         => $tFIELD,
    set_field_buffer       => $tFIELD,
    set_field_fore         => $tFIELD,
    set_field_init         => $tFORM,
    set_field_just         => $tFIELD,
    set_field_opts         => $tFIELD,
    set_field_pad          => $tFIELD,
    set_field_status       => $tFIELD,
    set_field_term         => $tFORM,
    set_field_type         => $tFIELD,
    set_field_userptr      => $tFIELD,
    set_form_fields        => $tFORM,
    set_form_init          => $tFORM,
    set_form_opts          => $tFORM,
    set_form_page          => $tFORM,
    set_form_sub           => $tFORM,
    set_form_term          => $tFORM,
    set_form_userptr       => $tFORM,
    set_form_win           => $tFORM,
    set_item_init          => $tMENU,
    set_item_opts          => $tITEM,
    set_item_term          => $tMENU,
    set_item_userptr       => $tITEM,
    set_item_value         => $tITEM,
    set_menu_back          => $tMENU,
    set_menu_fore          => $tMENU,
    set_menu_format        => $tMENU,
    set_menu_grey          => $tMENU,
    set_menu_init          => $tMENU,
    set_menu_items         => $tMENU,
    set_menu_mark          => $tMENU,
    set_menu_opts          => $tMENU,
    set_menu_pad           => $tMENU,
    set_menu_pattern       => $tMENU,
    set_menu_spacing       => $tMENU,
    set_menu_sub           => $tMENU,
    set_menu_term          => $tMENU,
    set_menu_userptr       => $tMENU,
    set_menu_win           => $tMENU,
    set_new_page           => $tFIELD,
    set_panel_userptr      => $tPANEL,
    set_panel_userptr      => $tPANEL,
    set_term               => $tSCREEN,
    set_top_row            => $tMENU,
    show_panel             => $tPANEL,
    show_panel             => $tPANEL,
    slk_attr               => $tSCREEN,
    slk_attr_set           => $tSCREEN,
    slk_attroff            => $tSCREEN,
    slk_attron             => $tSCREEN,
    slk_attrset            => $tSCREEN,
    slk_clear              => $tSCREEN,
    slk_color              => $tSCREEN,
    slk_init               => $tSCREEN,
    slk_label              => $tSCREEN,
    slk_noutrefresh        => $tSCREEN,
    slk_refresh            => $tSCREEN,
    slk_restore            => $tSCREEN,
    slk_set                => $tSCREEN,
    slk_touch              => $tSCREEN,
    start_color            => $tSCREEN,
    subwin                 => $tWINDOW,
    syncok                 => $tWINDOW,
    termattrs              => $tSCREEN,
    termname               => $tSCREEN,
    tgetflag               => $tSCREEN,
    tgetnum                => $tSCREEN,
    tigetflag              => $tSCREEN,
    tigetnum               => $tSCREEN,
    tigetstr               => $tSCREEN,
    tinfo                  => $tSCREEN,
    top_panel              => $tPANEL,
    top_panel              => $tPANEL,
    top_row                => $tMENU,
    touchline              => $tWINDOW,
    touchwin               => $tWINDOW,
    typeahead              => $tSCREEN,
    unfocus_current_field  => $tFORM,
    unget_wch              => $tSCREEN,
    ungetch                => $tSCREEN,
    ungetmouse             => $tSCREEN,
    unpost_form            => $tFORM,
    unpost_menu            => $tMENU,
    untouchwin             => $tWINDOW,
    update_panels_sp       => $tSCREEN,
    use_default_colors     => $tSCREEN,
    use_env                => $tSCREEN,
    use_legacy_coding      => $tSCREEN,
    use_screen             => $tSCREEN,
    use_tioctl             => $tSCREEN,
    use_window             => $tWINDOW,
    vidattr                => $tSCREEN,
    vidputs                => $tSCREEN,
    vw_printw              => $tWINDOW,
    vwprintw               => $tWINDOW,
    wadd_wch               => $tWINDOW,
    wadd_wchnstr           => $tWINDOW,
    wadd_wchstr            => $tWINDOW,
    waddch                 => $tWINDOW,
    waddchnstr             => $tWINDOW,
    waddchstr              => $tWINDOW,
    waddnstr               => $tWINDOW,
    waddnwstr              => $tWINDOW,
    waddstr                => $tWINDOW,
    waddwstr               => $tWINDOW,
    wattr_get              => $tWINDOW,
    wattr_off              => $tWINDOW,
    wattr_on               => $tWINDOW,
    wattr_set              => $tWINDOW,
    wattroff               => $tWINDOW,
    wattron                => $tWINDOW,
    wattrset               => $tWINDOW,
    wbkgd                  => $tWINDOW,
    wbkgdset               => $tWINDOW,
    wborder                => $tWINDOW,
    wborder_set            => $tWINDOW,
    wchgat                 => $tWINDOW,
    wclear                 => $tWINDOW,
    wclrtobot              => $tWINDOW,
    wclrtoeol              => $tWINDOW,
    wcolor_set             => $tWINDOW,
    wcursyncup             => $tWINDOW,
    wdelch                 => $tWINDOW,
    wdeleteln              => $tWINDOW,
    wechochar              => $tWINDOW,
    wenclose               => $tWINDOW,
    werase                 => $tWINDOW,
    wget_wch               => $tWINDOW,
    wget_wstr              => $tWINDOW,
    wgetbkgrnd             => $tWINDOW,
    wgetch                 => $tWINDOW,
    wgetch_events          => $tWINDOW,
    wgetdelay              => $tWINDOW,
    wgetn_wstr             => $tWINDOW,
    wgetnstr               => $tWINDOW,
    wgetparent             => $tWINDOW,
    wgetscrreg             => $tWINDOW,
    wgetstr                => $tWINDOW,
    whline                 => $tWINDOW,
    whline_set             => $tWINDOW,
    win_wch                => $tWINDOW,
    win_wchnstr            => $tWINDOW,
    win_wchstr             => $tWINDOW,
    winch                  => $tWINDOW,
    winchnstr              => $tWINDOW,
    winchstr               => $tWINDOW,
    winnstr                => $tWINDOW,
    winnwstr               => $tWINDOW,
    wins_nwstr             => $tWINDOW,
    wins_wch               => $tWINDOW,
    wins_wstr              => $tWINDOW,
    winsch                 => $tWINDOW,
    winsdelln              => $tWINDOW,
    winsertln              => $tWINDOW,
    winsnstr               => $tWINDOW,
    winsstr                => $tWINDOW,
    winstr                 => $tWINDOW,
    winwstr                => $tWINDOW,
    wmouse_trafo           => $tWINDOW,
    wmove                  => $tWINDOW,
    wnoutrefresh           => $tWINDOW,
    wprintw                => $tWINDOW,
    wredrawln              => $tWINDOW,
    wrefresh               => $tWINDOW,
    wresize                => $tWINDOW,
    wscrl                  => $tWINDOW,
    wsetscrreg             => $tWINDOW,
    wstandend              => $tWINDOW,
    wstandout              => $tWINDOW,
    wsyncdown              => $tWINDOW,
    wsyncup                => $tWINDOW,
    wtimeout               => $tWINDOW,
    wtouchln               => $tWINDOW,
    wvline                 => $tWINDOW,
);

our $fld_nums = 0;
our $frm_nums = 0;
our $itm_nums = 0;
our $mnu_nums = 0;
our $pan_nums = 0;
our $scr_nums = 0;
our $thr_nums = 0;
our $trm_nums = 0;
our $try_nums = 0;
our $usr_nums = 0;
our $win_nums = 0;

our $curscr = "";
our $newscr = "";
our $stdscr = "";

our %fld_addr;    # FIELD*
our %frm_addr;    # FORM*
our %itm_addr;    # ITEM*
our %mnu_addr;    # MENU*
our %pan_addr;    # PANEL*
our %scr_addr;    # SCREEN*
our %thr_addr;    # thread-id
our %trm_addr;    # TERMINAL*
our %try_addr;    # tries-number
our %usr_addr;    # user-pointer
our %win_addr;    # WINDOW*

sub has_addr($) {
    my $value  = shift;
    my $result = 0;
    $result = 1 if ( $value =~ /\b0x[[:xdigit:]]+\b/i );
    return $result;
}

sub transaddr($) {
    my $arg = shift;
    my $n;

    $arg =~ s/\b$curscr\b/curscr/g if ($curscr);
    $arg =~ s/\b$newscr\b/newscr/g if ($newscr);
    $arg =~ s/\b$stdscr\b/stdscr/g if ($stdscr);
    if ( &has_addr($arg) ) {
        foreach my $addr ( keys %fld_addr ) {
            $n = $fld_addr{$addr};
            $arg =~ s/\b$addr\b/field$n/g if ( defined $n );
        }
    }
    if ( &has_addr($arg) ) {
        foreach my $addr ( keys %frm_addr ) {
            $n = $frm_addr{$addr};
            $arg =~ s/\b$addr\b/form$n/g if ( defined $n );
        }
    }
    if ( &has_addr($arg) ) {
        foreach my $addr ( keys %itm_addr ) {
            $n = $itm_addr{$addr};
            $arg =~ s/\b$addr\b/item$n/g if ( defined $n );
        }
    }
    if ( &has_addr($arg) ) {
        foreach my $addr ( keys %mnu_addr ) {
            $n = $mnu_addr{$addr};
            $arg =~ s/\b$addr\b/menu$n/g if ( defined $n );
        }
    }
    if ( &has_addr($arg) ) {
        foreach my $addr ( keys %pan_addr ) {
            $n = $pan_addr{$addr};
            $arg =~ s/\b$addr\b/panel$n/g if ( defined $n );
        }
    }
    if ( &has_addr($arg) ) {
        foreach my $addr ( keys %scr_addr ) {
            $n = $scr_addr{$addr};
            $arg =~ s/\b$addr\b/screen$n/g if ( defined $n );
        }
    }
    if ( &has_addr($arg) ) {
        foreach my $addr ( keys %thr_addr ) {
            $n = $thr_addr{$addr};
            $arg =~ s/\b$addr\b/thread$n/g if ( defined $n );
        }
    }
    if ( &has_addr($arg) ) {
        foreach my $addr ( keys %trm_addr ) {
            $n = $trm_addr{$addr};
            $arg =~ s/\b$addr\b/terminal$n/g if ( defined $n );
        }
    }
    if ( &has_addr($arg) ) {
        foreach my $addr ( keys %try_addr ) {
            $n = $try_addr{$addr};
            $arg =~ s/\b$addr\b/tries_$n/g if ( defined $n );
        }
    }
    if ( &has_addr($arg) ) {
        foreach my $addr ( keys %usr_addr ) {
            $n = $usr_addr{$addr};
            $arg =~ s/\b$addr\b/user_ptr$n/g if ( defined $n );
        }
    }
    if ( &has_addr($arg) ) {
        foreach my $addr ( keys %win_addr ) {
            $n = $win_addr{$addr};
            $arg =~ s/\b$addr\b/window$n/g if ( defined $n );
        }
    }
    if ( &has_addr($arg) ) {
        if ( $arg =~ /add_wch\((window\d+,)?0x[[:xdigit:]]+\)/i ) {
            $arg =~ s/(0x[[:xdigit:]]+)[)]/\&wch)/i;
        }
        elsif (
            $arg =~ /color_content\((screen\d+,)?\d+(,0x[[:xdigit:]]+){3}/i )
        {
            $arg =~ s/(,0x[[:xdigit:]]+){3}[)]/,\&r,\&g,\&b)/i;
        }
        elsif ( $arg =~ /pair_content\((screen\d+,)?\d+(,0x[[:xdigit:]]+){2}/i )
        {
            $arg =~ s/(,0x[[:xdigit:]]+){2}[)]/,\&fg,\&bg)/i;
        }
    }
    if ( &has_addr($arg) and $arg =~ /called\s+\{/ ) {
        my $func = $arg;
        chomp $func;
        $func =~ s/^.*called\s+\{([[:alnum:]_]+)\(.*$/$1/;
        if ( defined $known_p1{$func} ) {
            my $addr = $arg;
            my $type = $known_p1{$func};
            chomp $addr;
            $addr =~ s/^[^(]+\((0x[[:xdigit:]]+).*$/$1/i;
            if ( $addr !~ /^0x[[:xdigit:]]+$/i ) {
                if ( $type == $tSCREEN and $addr =~ /^[^(]+\(screen\d+[,)]/ ) {

                    # ignore
                }
                elsif ( $type == $tWINDOW
                    and $addr =~
                    /^[^(]+\((stdscr|newscr|curscr|window\d+)[,)]/ )
                {

                    # ignore
                }
                elsif ( $type == $tTERMINAL
                    and $addr =~ /^[^(]+\(terminal\d+[,)]/ )
                {
                    # ignore
                }
                elsif ( $type == $tPANEL and $addr =~ /^[^(]+\(panel\d+[,)]/ ) {

                    # ignore
                }
                elsif ( $type == $tFIELD and $addr =~ /^[^(]+\(field\d+[,)]/ ) {

                    # ignore
                }
                elsif ( $type == $tMENU and $addr =~ /^[^(]+\(menu\d+[,)]/ ) {

                    # ignore
                }
                elsif ( $type == $tITEM and $addr =~ /^[^(]+\(item\d+[,)]/ ) {

                    # ignore
                }
                else {
                    printf "OOPS - expected type \"%s\", skipping\n>>$addr\n",
                      $known_p1_types{$type};
                }
            }
            elsif ( $type == $tSCREEN ) {
                $scr_addr{$addr} = ++$scr_nums;
                $arg = &transaddr($arg);
            }
            elsif ( $type == $tWINDOW ) {
                $win_addr{$addr} = ++$win_nums;
                $arg = &transaddr($arg);
            }
            elsif ( $type == $tTERMINAL ) {
                $trm_addr{$addr} = ++$trm_nums;
                $arg = &transaddr($arg);
            }
            elsif ( $type == $tPANEL ) {
                $pan_addr{$addr} = ++$pan_nums;
                $arg = &transaddr($arg);
            }
            elsif ( $type == $tFIELD ) {
                $fld_addr{$addr} = ++$fld_nums;
                $arg = &transaddr($arg);
            }
            elsif ( $type == $tFORM ) {
                $frm_addr{$addr} = ++$frm_nums;
                $arg = &transaddr($arg);
            }
            elsif ( $type == $tMENU ) {
                $mnu_addr{$addr} = ++$mnu_nums;
                $arg = &transaddr($arg);
            }
            elsif ( $type == $tITEM ) {
                $itm_addr{$addr} = ++$itm_nums;
                $arg = &transaddr($arg);
            }
        }
    }

    return $arg;
}

sub muncher($) {
    my $STDIN = shift;

    while (<$STDIN>) {
        my $addr;
        my $n;
        my $awaiting = "";

      CLASSIFY: {

            next unless $_;

            # just in case someone tries a file with cr/lf line-endings:
            $_ =~ s/\r\n/\n/g;
            $_ =~ s/\r/\n/g;

            if ( $_ =~
                /^TRACING NCURSES version.*\(tracelevel=(0x[[:xdigit:]]+)\)/ )
            {
                $tracelevel = hex $1;
                print;
                next;
            }

            my $thread = "";
            if ( $_ =~ /^(0x[[:xdigit:]]+):/ ) {
                $thr_addr{$1} = ++$thr_nums unless defined $thr_addr{$1};
                $thread = "thread" . $thr_addr{$1} . ":";
                $_ =~ s/^[^:]*://;
            }

            # Transform window pointer addresses so it's easier to compare logs
            $awaiting = "curscr" if ( $_ =~ /creating curscr/ );
            $awaiting = "newscr" if ( $_ =~ /creating newscr/ );
            $awaiting = "stdscr" if ( $_ =~ /creating stdscr/ );
            $awaiting = "screen" if ( $_ =~ /^(\+ )*called \{new_prescr\(\)/ );
            if ( $_ =~ /^create :window 0x([[:xdigit:]]+)/ ) {
                $addr = "0x$1";
                if ( $awaiting eq "curscr" ) {
                    $curscr = $addr;
                }
                elsif ( $awaiting eq "newscr" ) {
                    $newscr = $addr;
                }
                elsif ( $awaiting eq "stdscr" ) {
                    $stdscr = $addr;
                }
                else {
                    $win_addr{$addr} = $win_nums++;
                }
                $awaiting = "";
            }
            elsif ( $_ =~ /^create :(root|new)_panel 0x([[:xdigit:]]+)/ ) {
                $addr            = "0x$2";
                $pan_addr{$addr} = $pan_nums++;
                $_               = &transaddr($_);
            }
            elsif ( $_ =~ /^create :user_ptr 0x([[:xdigit:]]+)/ ) {
                $addr            = "0x$1";
                $usr_addr{$addr} = $usr_nums++;
                $_               = &transaddr($_);
            }
            elsif ( $_ =~ /^create :field 0x([[:xdigit:]]+)/ ) {
                $addr            = "0x$1";
                $fld_addr{$addr} = $fld_nums++;
                $_               = &transaddr($_);
            }
            elsif ( $_ =~ /^create :form 0x([[:xdigit:]]+)/ ) {
                $addr            = "0x$1";
                $frm_addr{$addr} = $frm_nums++;
                $_               = &transaddr($_);
            }
            elsif ( $_ =~ /^create :menu 0x([[:xdigit:]]+)/ ) {
                $addr            = "0x$1";
                $mnu_addr{$addr} = $mnu_nums++;
                $_               = &transaddr($_);
            }
            elsif ( $_ =~ /^create :item 0x([[:xdigit:]]+)/ ) {
                $addr            = "0x$1";
                $itm_addr{$addr} = $itm_nums++;
                $_               = &transaddr($_);
            }
            elsif ( $_ =~ /^(\+ )*called \{set_curterm\((0x[[:xdigit:]]+)\)/ ) {
                $trm_addr{$2} = ++$trm_nums unless defined $trm_addr{$2};
            }
            elsif ( $_ =~ /^(\+ )*called \{_nc_add_to_try\((0x[[:xdigit:]]+),/ )
            {
                $try_addr{$2} = ++$try_nums unless defined $try_addr{$2};
            }
            elsif ( $_ =~ /^(\+ )*_nc_alloc_screen_sp 0x([[:xdigit:]]+)/ ) {
                $addr = "0x$2";
                $scr_addr{$addr} = ++$scr_nums unless ( $scr_addr{$addr} );
                $awaiting = "";
            }
            elsif ( $_ =~ /^(\+ )*return }0x([[:xdigit:]]+)/ ) {
                $addr = "0x$2";
                if ( $awaiting eq "screen" ) {
                    $scr_addr{$addr} = ++$scr_nums unless ( $scr_addr{$addr} );
                }
            }
            elsif ( $_ =~ /^\.\.\.deleted win=0x([[:xdigit:]]+)/ ) {
                $addr = "0x$1";
                $_    = &transaddr($_);
                if ( $addr eq $curscr ) {
                    $curscr = "";
                }
                elsif ( $addr eq $newscr ) {
                    $newscr = "";
                }
                elsif ( $addr eq $stdscr ) {
                    $stdscr = "";
                }
                else {
                    undef $win_addr{$addr};
                }
            }
            elsif ( $_ =~ /^\.\.\.deleted pan=\"0x([[:xdigit:]]+)\"/ ) {
                $addr = "0x$1";
                $_    = &transaddr($_);
                undef $pan_addr{$addr};
            }
            elsif ( $_ =~ /^([+ ])*called \{free_field\(0x([[:xdigit:]]+)\)/ ) {
                $addr = "0x$2";
                $_    = &transaddr($_);
                undef $fld_addr{$addr};
            }
            elsif ( $_ =~ /^([+ ])*called \{free_form\(0x([[:xdigit:]]+)\)/ ) {
                $addr = "0x$2";
                $_    = &transaddr($_);
                undef $frm_addr{$addr};
            }
            elsif ( $_ =~ /^([+ ])*called \{free_menu\(0x([[:xdigit:]]+)\)/ ) {
                $addr = "0x$2";
                $_    = &transaddr($_);
                undef $mnu_addr{$addr};
            }
            elsif ( $_ =~ /^([+ ])*called \{free_item\(0x([[:xdigit:]]+)\)/ ) {
                $addr = "0x$2";
                $_    = &transaddr($_);
                undef $itm_addr{$addr};
            }

            # Compactify runs of PutAttrChar
            if ( ( ( $tracelevel & $TR{CHARPUT} ) != 0 ) and $_ =~ /$putattr/ )
            {
                my $putattr_chars = $1;
                my $starty        = $2;
                my $startx        = $3;
                while (<$STDIN>) {
                    if ( $_ =~ /$putattr/ ) {
                        $putattr_chars .= $1;
                    }
                    else {
                        next if ( $_ =~ /^PUTC 0x[[:xdigit:]]+.*/ );
                        next if ( $_ =~ /^\.\.\.skip.*/ );
                        next if ( $_ =~ /^forced to blank.*/ );
                        last;
                    }
                }
                print "RUN of PutAttrChar()s:"
                  . " \"$putattr_chars\" from ${starty}, ${startx}\n";
                redo CLASSIFY;
            }

            # Compactify runs of waddnstr calls
            if ( ( ( $tracelevel & $TR{CALLS} ) != 0 ) and $_ =~ /$waddnstr/ ) {
                my $waddnstr_chars = $2;
                my $winaddr        = $1;
                while (<$STDIN>) {
                    next if ( $_ =~ /^return \}0/ );
                    if ( $_ =~ /$waddnstr/ && $1 eq $winaddr ) {
                        $waddnstr_chars .= $2;
                    }
                    else {
                        last;
                    }
                }
                my $winaddstr = &transaddr($winaddr);
                print "RUN of waddnstr()s:"
                  . " $winaddstr, \"$waddnstr_chars\"\n";
                redo CLASSIFY;
            }

            # More transformations can go here

            # Repeated runs of anything
            my $anyline     = &transaddr($_);
            my $repeatcount = 1;
            while (<$STDIN>) {
                if ( &transaddr($_) eq $anyline ) {
                    $repeatcount++;
                }
                else {
                    last;
                }
            }
            if ( $repeatcount > 1 ) {
                print "${repeatcount} REPEATS OF $anyline";
            }
            else {
                print $thread . $anyline;
            }
            redo CLASSIFY if $_;

        }    # :CLASSIFY
    }
}

for my $tr ( keys %TR ) {
    $TR{$tr} = hex $TR{$tr};
}

if ( $#ARGV >= 0 ) {
    while ( $#ARGV >= 0 ) {
        my $file = shift @ARGV;
        open my $ifh, "<", $file or die $!;
        &muncher($ifh);
    }
}
else {
    &muncher( \*STDIN );
}

# tracemunch ends here
