#!/usr/bin/env perl
# $Id: analyze-curses-symbols,v 1.44 2023/08/19 15:52:55 tom Exp $
# -----------------------------------------------------------------------------
# Copyright 2015-2021,2023 by Thomas E. Dickey
#
#                         All Rights Reserved
#
# 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, 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 LISTED COPYRIGHT HOLDER(S) 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.
# -----------------------------------------------------------------------------
#
# Count symbol usage for the given executables, and summarize their usage with
# categories for these types:
#       ncurses (if using ncurses extension)
#       curses (if not using ncurses extension)
#       wide(curses/ncurses)
#       termcap
#       terminfo
#       slang (perhaps check on wide/whatever)
#
# TODO
# + check and give percentage of total applications from Debian apt using the
# given library.
# + give percentage of total symbols from the library
# + give percentage of applications using no-libs, curses-libs or X-libs
# + distinguish between wide/normal
# + count all curses calls, rather than just detecting initscr/newterm
# + "-l" option should show symbol type in first column
# + flag curses calls when no initscr/newterm found
# + add COLORS, etc., for data rather than just functions

use warnings;
use strict;

$| = 1;

use Getopt::Std;

our ( $opt_d, $opt_l, $opt_q, $opt_r, $opt_s, $opt_v );

our %all_libs;
our %count_by_type;
our $ldd_tool  = "ldd";
our $assume_tc = 0;

# tc = conventional termcap
# t0 = antique terminfo, perhaps SVr2, used for Unix vi
# ti = conventional terminfo, e.g., SVr4
# t5 = symbol from ncurses5
# t6 = symbol from ncurses6
our %tc_symbols = qw(
  PC                            tc
  UP                            tc
  BC                            tc
  ospeed                        tc
  tgetent                       tc
  tgetflag                      tc
  tgetnum                       tc
  tgetstr                       tc
  tgoto                         tc
  tputs                         tc
);

# There are two tables because tgoto is in both interfaces.
# Most of the symbols for functions which use the terminal database are
# shown as curses symbols because they also were introduced in different
# epochs of curses/terminfo.
our %ti_symbols = qw(
  resetterm                     t0
  fixterm                       t0
  saveterm                      t0
  restartterm                   t0
  delterm                       t0
  termerr                       t0
  tinputfd                      t0
  setupterm                     ti
  setterm                       ti
  set_curterm                   ti
  del_curterm                   ti
  restartterm                   ti
  tgoto                         ti
  tparm                         ti
  tputs                         ti
  putp                          ti
  vidputs                       ti
  vidattr                       ti
  vid_puts                      ti
  vid_attr                      ti
  mvcur                         ti
  tigetflag                     ti
  tigetnum                      ti
  tigetstr                      ti
  tiparm                        ti
  define_key                    t5
  has_key                       t5
  key_defined                   t5
  keybound                      t5
  keyok                         t5
  new_prescr                    t5
  use_extended_names            t5
  exit_terminfo                 t6
  is_cbreak                     t6
  is_echo                       t6
  is_nl                         t6
  is_raw                        t6
  tiparm_s                      t6
  tiscan_s                      t6
);

# 4.2BSD dropped putpad (b2)
# SVr4 adds a lot of symbols (c4)
our %bsd_curses_symbols = qw(
  box                           b0
  delwin                        b0
  endwin                        b0
  gettmode                      b0
  initscr                       b0
  longname                      b0
  mvcur                         b0
  mvprintw                      b0
  mvscanw                       b0
  mvwin                         b0
  mvwprintw                     b0
  mvwscanw                      b0
  newwin                        b0
  overlay                       b0
  overwrite                     b0
  plod                          b0
  plodput                       b0
  printw                        b0
  putpad                        b0
  scanw                         b0
  scroll                        b0
  setterm                       b0
  subwin                        b0
  touchwin                      b0
  waddch                        b0
  waddstr                       b0
  wclear                        b0
  wclrtobot                     b0
  wclrtoeol                     b0
  wdeleteln                     b0
  werase                        b0
  wgetch                        b0
  wgetstr                       b0
  winsertln                     b0
  wmove                         b0
  wprintw                       b0
  wrefresh                      b0
  wscanw                        b0
  wstandend                     b0
  wstandout                     b0

  getcap                        b1
  wdelch                        b1
  winsch                        b1

  tabcol                        b2

  fullname                      b3
  idlok                         b3
  touchline                     b3
  touchoverlap                  b3
);

# References:
# ----------
# (SVr2)
# http://bitsavers.trailing-edge.com/pdf/att/unix/System_V_Release_2
# UNIX_Programmers_Manual_Vol_2_System_Calls_and_Library_Routines_1986.pdf
# pages 246-252
#
# (SVr3)
# UNIX_System_V_Programmers_Reference_Manual_1987.pdf
# pages 418-448
#
# c2 = SVr2 (1986)
# c3 = SVr3 (1987)
# c4 = SVr4 (1988)
our %sys5_curses_symbols = qw(
  addch                         c2
  addchnstr                     c4
  addchstr                      c4
  addnstr                       c4
  addnwstr                      c4
  addstr                        c2
  addwch                        c4
  addwchnstr                    c4
  addwchstr                     c4
  addwstr                       c4
  attroff                       c2
  attron                        c4
  attrset                       c2
  baudrate                      c2
  beep                          c3
  bkgd                          c4
  bkgdset                       c4
  boolcodes                     c3
  boolfnames                    c3
  boolnames                     c3
  border                        c4
  box                           c2
  can_change_color              c4
  cbreak                        c2
  cconvert                      c4
  cexpand                       c4
  clear                         c2
  clearok                       c2
  clrtobot                      c2
  clrtoeol                      c2
  color_content                 c4
  copywin                       c3
  crmode                        c4
  cur_term                      c3
  curs_set                      c3
  curscr                        c3
  def_prog_mode                 c3
  def_shell_mode                c3
  del_curterm                   c3
  delay_output                  c2
  delch                         c2
  deleteln                      c2
  delkey                        c4
  delkeymap                     c4
  delscreen                     c4
  delterm                       c4
  delwin                        c3
  derwin                        c4
  doupdate                      c3
  draino                        c3
  dupwin                        c4
  echo                          c2
  echochar                      c3
  echowchar                     c4
  endwin                        c2
  erase                         c2
  erasechar                     c3
  filter                        c3
  fixterm                       c2
  flash                         c2
  flushinp                      c3
  garbagedlines                 c3
  getattrs                      c4
  getbegx                       c3
  getbegy                       c3
  getbkgd                       c4
  getbmap                       c4
  getch                         c2
  getcurx                       c4
  getcury                       c4
  getmaxx                       c4
  getmaxy                       c4
  getmaxyx                      c3
  getmouse                      c4
  getnwstr                      c4
  getparx                       c4
  getpary                       c4
  getstr                        c2
  getsyx                        c3
  gettmode                      c2
  getwch                        c4
  getwin                        c4
  getwstr                       c4
  getyx                         c2
  halfdelay                     c3
  has_colors                    c4
  has_ic                        c2
  has_il                        c2
  hline                         c4
  idcok                         c4
  idlok                         c2
  iexpand                       c4
  immedok                       c4
  inch                          c2
  inchnstr                      c4
  inchstr                       c4
  init_color                    c4
  init_pair                     c4
  initscr                       c2
  innstr                        c4
  innwstr                       c4
  insch                         c2
  insdelln                      c4
  insertln                      c2
  insnstr                       c4
  insnwstr                      c4
  insstr                        c4
  instr                         c4
  inswch                        c4
  inswstr                       c4
  intrflush                     c2
  inwch                         c4
  inwchnstr                     c4
  inwchstr                      c4
  inwstr                        c4
  is_linetouched                c4
  is_wintouched                 c4
  isendwin                      c3
  keyname                       c3
  keypad                        c2
  killchar                      c2
  leaveok                       c2
  longname                      c2
  meta                          c2
  mouse_off                     c4
  mouse_on                      c4
  mouse_set                     c4
  move                          c2
  mvaddch                       c2
  mvaddchnstr                   c4
  mvaddchstr                    c4
  mvaddnstr                     c4
  mvaddnwstr                    c4
  mvaddstr                      c2
  mvaddwch                      c4
  mvaddwchnstr                  c4
  mvaddwchstr                   c4
  mvaddwstr                     c4
  mvcur                         c2
  mvdelch                       c2
  mvderwin                      c4
  mvgetch                       c2
  mvgetnwstr                    c4
  mvgetstr                      c2
  mvgetwch                      c4
  mvgetwstr                     c4
  mvhline                       c4
  mvinch                        c2
  mvinchnstr                    c4
  mvinchstr                     c4
  mvinnstr                      c4
  mvinnwstr                     c4
  mvinsch                       c2
  mvinsnstr                     c4
  mvinsnwstr                    c4
  mvinsstr                      c4
  mvinstr                       c4
  mvinswch                      c4
  mvinswstr                     c4
  mvinwch                       c4
  mvinwchnstr                   c4
  mvinwchstr                    c4
  mvinwstr                      c4
  mvprintw                      c2
  mvscanw                       c2
  mvvline                       c4
  mvwaddch                      c2
  mvwaddchnstr                  c4
  mvwaddchstr                   c4
  mvwaddnstr                    c4
  mvwaddnwstr                   c4
  mvwaddstr                     c2
  mvwaddwch                     c2
  mvwaddwchnstr                 c4
  mvwaddwchstr                  c4
  mvwaddwstr                    c4
  mvwdelch                      c2
  mvwgetch                      c2
  mvwgetnwstr                   c4
  mvwgetstr                     c2
  mvwgetwch                     c4
  mvwgetwstr                    c4
  mvwhline                      c4
  mvwin                         c2
  mvwinch                       c2
  mvwinchnstr                   c4
  mvwinchstr                    c4
  mvwinnstr                     c4
  mvwinnwstr                    c4
  mvwinsch                      c2
  mvwinsnstr                    c4
  mvwinsnwstr                   c4
  mvwinsstr                     c4
  mvwinstr                      c4
  mvwinswch                     c4
  mvwinswstr                    c4
  mvwinwch                      c4
  mvwinwchnstr                  c4
  mvwinwchstr                   c4
  mvwinwstr                     c4
  mvwprintw                     c2
  mvwscanw                      c2
  mvwvline                      c4
  napms                         c3
  newkey                        c4
  newpad                        c2
  newscreen                     c4
  newterm                       c2
  newwin                        c2
  nl                            c2
  nocbreak                      c2
  nocrmode                      c4
  nodelay                       c2
  noecho                        c2
  nonl                          c2
  noqiflush                     c4
  noraw                         c2
  notimeout                     c3
  numcodes                      c3
  numfnames                     c3
  numnames                      c3
  ospeed                        c4
  outchcount                    c4
  overlay                       c2
  overwrite                     c2
  pair_content                  c4
  pechochar                     c3
  pechowchar                    c4
  pnoutrefresh                  c2
  prefresh                      c2
  printw                        c2
  prog_istermios                c4
  progname                      c4
  putp                          c2
  putwin                        c4
  qiflush                       c4
  raw                           c2
  redrawwin                     c4
  refresh                       c2
  request_mouse_pos             c4
  reset_prog_mode               c3
  reset_shell_mode              c3
  resetterm                     c2
  resetty                       c2
  restartterm                   c3
  ripoffline                    c3
  rmpadding                     c4
  saveterm                      c2
  savetty                       c2
  scanw                         c2
  scr_dump                      c3
  scr_init                      c3
  scr_ll_dump                   c4
  scr_reset                     c4
  scr_restore                   c3
  scr_set                       c4
  scrl                          c4
  scroll                        c2
  scrollok                      c2
  set_curterm                   c3
  set_term                      c3
  setcurscreen                  c4
  setcurterm                    c4
  setkeymap                     c4
  setscrreg                     c2
  setsyx                        c3
  setterm                       c2
  setupterm                     c2
  slk_attroff                   c4
  slk_attron                    c4
  slk_attrset                   c4
  slk_clear                     c3
  slk_init                      c3
  slk_label                     c3
  slk_noutrefresh               c3
  slk_refresh                   c3
  slk_restore                   c3
  slk_set                       c3
  slk_start                     c4
  slk_touch                     c3
  standend                      c2
  standout                      c2
  start_color                   c4
  stdscr                        c2
  strcodes                      c3
  strfnames                     c3
  strnames                      c3
  subpad                        c3
  subwin                        c2
  syncok                        c4
  termattrs                     c4
  termname                      c4
  tgetch                        c4
  tgetent                       c2
  tgetflag                      c2
  tgetnum                       c2
  tgetstr                       c2
  tgetwch                       c4
  tgoto                         c2
  tifgetflag                    c4
  tifgetnum                     c4
  tifgetstr                     c4
  tigetflag                     c3
  tigetnum                      c3
  tigetstr                      c3
  timeout                       c4
  tinputfd                      c4
  touchline                     c3
  touchwin                      c2
  tparm                         c2
  tputs                         c2
  traceoff                      c2
  traceon                       c2
  ttimeout                      c4
  ttytype                       c4
  typeahead                     c2
  unctrl                        c2
  ungetch                       c4
  ungetwch                      c4
  untouchwin                    c4
  use_env                       c4
  vidattr                       c2
  vidputs                       c2
  vidupdate                     c4
  vline                         c4
  vwprintw                      c3
  vwscanw                       c3
  waddch                        c2
  waddchnstr                    c4
  waddchstr                     c4
  waddnstr                      c4
  waddnwstr                     c4
  waddstr                       c2
  waddwch                       c4
  waddwchnstr                   c4
  waddwchstr                    c4
  waddwstr                      c4
  wadjcurspos                   c4
  wattroff                      c2
  wattron                       c2
  wattrset                      c2
  wbkgd                         c4
  wbkgdset                      c4
  wborder                       c4
  wclear                        c2
  wclrtobot                     c2
  wclrtoeol                     c2
  wcscrw                        c4
  wcursyncup                    c4
  wdelch                        c2
  wdeleteln                     c2
  wechochar                     c4
  wechowchar                    c4
  werase                        c2
  wgetch                        c2
  wgetnstr                      c4
  wgetnwstr                     c4
  wgetstr                       c2
  wgetwch                       c4
  wgetwstr                      c4
  whline                        c4
  winch                         c2
  winchnstr                     c4
  winchstr                      c4
  winnstr                       c4
  winnwstr                      c4
  winsch                        c2
  winsdelln                     c4
  winsertln                     c2
  winsnstr                      c4
  winsnwstr                     c4
  winsstr                       c4
  winstr                        c4
  winswch                       c4
  winswstr                      c4
  winwch                        c4
  winwchnstr                    c4
  winwchstr                     c4
  winwstr                       c4
  wmbinch                       c4
  wmbmove                       c4
  wmouse_position               c4
  wmove                         c2
  wmovenextch                   c4
  wmoveprevch                   c4
  wnoutrefresh                  c2
  wprintw                       c2
  wredrawln                     c4
  wrefresh                      c2
  wscanw                        c2
  wscrl                         c4
  wsetscrreg                    c2
  wstandend                     c2
  wstandout                     c2
  wsyncdown                     c4
  wsyncup                       c4
  wtimeout                      c4
  wtouchln                      c4
  wvline                        c4
);

our %xopen_curses_symbols = qw(
  add_wch                       cx
  add_wchnstr                   cx
  add_wchstr                    cx
  addch                         cx
  addchnstr                     cx
  addchstr                      cx
  addnstr                       cx
  addnwstr                      cx
  addstr                        cx
  addwstr                       cx
  attr_get                      cx
  attr_off                      cx
  attr_on                       cx
  attr_set                      cx
  attroff                       cx
  attron                        cx
  attrset                       cx
  baudrate                      cx
  beep                          cx
  bkgd                          cx
  bkgdset                       cx
  bkgrnd                        cx
  bkgrndset                     cx
  boolcodes                     cx
  boolfnames                    cx
  boolnames                     cx
  border                        cx
  border_set                    cx
  box                           cx
  box_set                       cx
  can_change_color              cx
  cbreak                        cx
  chgat                         cx
  clear                         cx
  clearok                       cx
  clrtobot                      cx
  clrtoeol                      cx
  color_content                 cx
  color_set                     cx
  copywin                       cx
  cur_term                      cx
  curs_set                      cx
  curscr                        cx
  def_prog_mode                 cx
  def_shell_mode                cx
  del_curterm                   cx
  delay_output                  cx
  delch                         cx
  deleteln                      cx
  delscreen                     cx
  delwin                        cx
  derwin                        cx
  doupdate                      cx
  dupwin                        cx
  echo                          cx
  echo_wchar                    cx
  echochar                      cx
  endwin                        cx
  erase                         cx
  erasechar                     cx
  erasewchar                    cx
  filter                        cx
  flash                         cx
  flushinp                      cx
  get_wch                       cx
  get_wstr                      cx
  getbkgd                       cx
  getbkgrnd                     cx
  getcchar                      cx
  getch                         cx
  getn_wstr                     cx
  getnstr                       cx
  getstr                        cx
  getwin                        cx
  halfdelay                     cx
  has_colors                    cx
  has_ic                        cx
  has_il                        cx
  hline                         cx
  hline_set                     cx
  idcok                         cx
  idlok                         cx
  immedok                       cx
  in_wch                        cx
  in_wchnstr                    cx
  in_wchstr                     cx
  inch                          cx
  inchnstr                      cx
  inchstr                       cx
  init_color                    cx
  init_pair                     cx
  initscr                       cx
  innstr                        cx
  innwstr                       cx
  ins_nwstr                     cx
  ins_wch                       cx
  ins_wstr                      cx
  insch                         cx
  insdelln                      cx
  insertln                      cx
  insnstr                       cx
  insstr                        cx
  instr                         cx
  intrflush                     cx
  inwstr                        cx
  is_linetouched                cx
  is_wintouched                 cx
  isendwin                      cx
  key_name                      cx
  keyname                       cx
  keypad                        cx
  killchar                      cx
  killwchar                     cx
  leaveok                       cx
  longname                      cx
  meta                          cx
  move                          cx
  mvadd_wch                     cx
  mvadd_wchnstr                 cx
  mvadd_wchstr                  cx
  mvaddch                       cx
  mvaddchnstr                   cx
  mvaddchstr                    cx
  mvaddnstr                     cx
  mvaddnwstr                    cx
  mvaddstr                      cx
  mvaddwstr                     cx
  mvchgat                       cx
  mvcur                         cx
  mvdelch                       cx
  mvderwin                      cx
  mvget_wch                     cx
  mvget_wstr                    cx
  mvgetch                       cx
  mvgetn_wstr                   cx
  mvgetnstr                     cx
  mvgetstr                      cx
  mvhline                       cx
  mvhline_set                   cx
  mvin_wch                      cx
  mvin_wchnstr                  cx
  mvin_wchstr                   cx
  mvinch                        cx
  mvinchnstr                    cx
  mvinchstr                     cx
  mvinnstr                      cx
  mvinnwstr                     cx
  mvins_nwstr                   cx
  mvins_wch                     cx
  mvins_wstr                    cx
  mvinsch                       cx
  mvinsnstr                     cx
  mvinsstr                      cx
  mvinstr                       cx
  mvinwstr                      cx
  mvprintw                      cx
  mvscanw                       cx
  mvvline                       cx
  mvvline_set                   cx
  mvwadd_wch                    cx
  mvwadd_wchnstr                cx
  mvwadd_wchstr                 cx
  mvwaddch                      cx
  mvwaddchnstr                  cx
  mvwaddchstr                   cx
  mvwaddnstr                    cx
  mvwaddnwstr                   cx
  mvwaddstr                     cx
  mvwaddwstr                    cx
  mvwchgat                      cx
  mvwdelch                      cx
  mvwget_wch                    cx
  mvwget_wstr                   cx
  mvwgetch                      cx
  mvwgetn_wstr                  cx
  mvwgetnstr                    cx
  mvwgetstr                     cx
  mvwhline                      cx
  mvwhline_set                  cx
  mvwin                         cx
  mvwin_wch                     cx
  mvwin_wchnstr                 cx
  mvwin_wchstr                  cx
  mvwinch                       cx
  mvwinchnstr                   cx
  mvwinchstr                    cx
  mvwinnstr                     cx
  mvwinnwstr                    cx
  mvwins_nwstr                  cx
  mvwins_wch                    cx
  mvwins_wstr                   cx
  mvwinsch                      cx
  mvwinsnstr                    cx
  mvwinsstr                     cx
  mvwinstr                      cx
  mvwinwstr                     cx
  mvwprintw                     cx
  mvwscanw                      cx
  mvwvline                      cx
  mvwvline_set                  cx
  napms                         cx
  newpad                        cx
  newterm                       cx
  newwin                        cx
  nl                            cx
  nocbreak                      cx
  nodelay                       cx
  noecho                        cx
  nonl                          cx
  noqiflush                     cx
  noraw                         cx
  notimeout                     cx
  numcodes                      cx
  numfnames                     cx
  numnames                      cx
  overlay                       cx
  overwrite                     cx
  pair_content                  cx
  pecho_wchar                   cx
  pechochar                     cx
  pnoutrefresh                  cx
  prefresh                      cx
  printw                        cx
  putp                          cx
  putwin                        cx
  qiflush                       cx
  raw                           cx
  redrawwin                     cx
  refresh                       cx
  reset_prog_mode               cx
  reset_shell_mode              cx
  resetty                       cx
  restartterm                   cx
  ripoffline                    cx
  savetty                       cx
  scanw                         cx
  scr_dump                      cx
  scr_init                      cx
  scr_restore                   cx
  scr_set                       cx
  scrl                          cx
  scroll                        cx
  scrollok                      cx
  set_curterm                   cx
  set_term                      cx
  setcchar                      cx
  setscrreg                     cx
  setupterm                     cx
  slk_attr_off                  cx
  slk_attr_on                   cx
  slk_attr_set                  cx
  slk_attroff                   cx
  slk_attron                    cx
  slk_attrset                   cx
  slk_clear                     cx
  slk_color                     cx
  slk_init                      cx
  slk_label                     cx
  slk_noutrefresh               cx
  slk_refresh                   cx
  slk_restore                   cx
  slk_set                       cx
  slk_touch                     cx
  slk_wset                      cx
  standend                      cx
  standout                      cx
  start_color                   cx
  stdscr                        cx
  strcodes                      cx
  strfnames                     cx
  strnames                      cx
  subpad                        cx
  subwin                        cx
  syncok                        cx
  term_attrs                    cx
  termattrs                     cx
  termname                      cx
  tgetent                       cx
  tgetflag                      cx
  tgetnum                       cx
  tgetstr                       cx
  tgoto                         cx
  tigetflag                     cx
  tigetnum                      cx
  tigetstr                      cx
  timeout                       cx
  touchline                     cx
  touchwin                      cx
  tparm                         cx
  tputs                         cx
  typeahead                     cx
  unctrl                        cx
  unget_wch                     cx
  ungetch                       cx
  untouchwin                    cx
  use_env                       cx
  vid_attr                      cx
  vid_puts                      cx
  vidattr                       cx
  vidputs                       cx
  vline                         cx
  vline_set                     cx
  vw_printw                     cx
  vw_scanw                      cx
  vwprintw                      cx
  vwscanw                       cx
  wadd_wch                      cx
  wadd_wchnstr                  cx
  wadd_wchstr                   cx
  waddch                        cx
  waddchnstr                    cx
  waddchstr                     cx
  waddnstr                      cx
  waddnwstr                     cx
  waddstr                       cx
  waddwstr                      cx
  wattr_get                     cx
  wattr_off                     cx
  wattr_on                      cx
  wattr_set                     cx
  wattroff                      cx
  wattron                       cx
  wattrset                      cx
  wbkgd                         cx
  wbkgdset                      cx
  wbkgrnd                       cx
  wbkgrndset                    cx
  wborder                       cx
  wborder_set                   cx
  wchgat                        cx
  wclear                        cx
  wclrtobot                     cx
  wclrtoeol                     cx
  wcolor_set                    cx
  wcursyncup                    cx
  wdelch                        cx
  wdeleteln                     cx
  wecho_wchar                   cx
  wechochar                     cx
  werase                        cx
  wget_wch                      cx
  wget_wstr                     cx
  wgetbkgrnd                    cx
  wgetch                        cx
  wgetn_wstr                    cx
  wgetnstr                      cx
  wgetstr                       cx
  whline                        cx
  whline_set                    cx
  win_wch                       cx
  win_wchnstr                   cx
  win_wchstr                    cx
  winch                         cx
  winchnstr                     cx
  winchstr                      cx
  winnstr                       cx
  winnwstr                      cx
  wins_nwstr                    cx
  wins_wch                      cx
  wins_wstr                     cx
  winsch                        cx
  winsdelln                     cx
  winsertln                     cx
  winsnstr                      cx
  winsstr                       cx
  winstr                        cx
  winwstr                       cx
  wmove                         cx
  wnoutrefresh                  cx
  wprintw                       cx
  wredrawln                     cx
  wrefresh                      cx
  wscanw                        cx
  wscrl                         cx
  wsetscrreg                    cx
  wstandend                     cx
  wstandout                     cx
  wsyncdown                     cx
  wsyncup                       cx
  wtimeout                      cx
  wtouchln                      cx
  wunctrl                       cx
  wvline                        cx
  wvline_set                    cx
);

# n5 = ncurses5
# n6 = ncurses6
our %extended_symbols = qw(
  assume_default_colors         n5
  curses_version                n5
  get_escdelay                  n5
  getmouse                      n5
  has_mouse                     n5
  is_cleared                    n5
  is_idcok                      n5
  is_idlok                      n5
  is_immedok                    n5
  is_keypad                     n5
  is_leaveok                    n5
  is_nodelay                    n5
  is_notimeout                  n5
  is_pad                        n5
  is_scrollok                   n5
  is_subwin                     n5
  is_syncok                     n5
  is_term_resized               n5
  mcprint                       n5
  mouse_trafo                   n5
  mouseinterval                 n5
  mousemask                     n5
  nofilter                      n5
  resize_term                   n5
  resizeterm                    n5
  set_escdelay                  n5
  set_tabsize                   n5
  ungetmouse                    n5
  use_default_colors            n5
  use_legacy_coding             n5
  use_screen                    n5
  use_tioctl                    n5
  use_window                    n5
  wenclose                      n5
  wgetparent                    n5
  wgetscrreg                    n5
  wmouse_trafo                  n5
  wresize                       n5
  alloc_pair                    n6
  exit_curses                   n6
  extended_color_content        n6
  extended_pair_content         n6
  extended_slk_color            n6
  find_pair                     n6
  free_pair                     n6
  init_extended_color           n6
  init_extended_pair            n6
  reset_color_pairs             n6
  wgetch_events                 n6
  wgetnstr_events               n6
);

sub read_pipe($) {
    my $pipe = shift;
    my $fh;
    my @data;
    printf "read_pipe(%s)\n", $pipe if ($opt_d);
    if ( open( $fh, "$pipe|" ) ) {
        @data = <$fh>;
        close $fh;
    }
    else {
        warn "can't read from $pipe: $!";
    }
    return @data;
}

sub is_executable($) {
    my $path   = shift;
    my $result = 0;
    if ( -x $path ) {
        my @info = &read_pipe("file \"$path\"");
        if ( $#info == 0 ) {
            $result = 1 unless ( $info[0] =~ /script/ );
        }
    }
    return $result;
}

# using "nm", obtain a list of unresolved symbols from the given application.
sub read_app_syms($) {
    my $path = shift;
    my %counts;
    if ( -r $path ) {
        my $opts = "-P";

        $opts .= "D" if ( $^O eq "linux" );
        $opts .= "D" if ( $^O eq "solaris" );

        printf "...reading %s\n", $path if ($opt_v);
        my @data = &read_pipe("nm $opts $path 2>/dev/null");
        my $k    = 0;
        for my $n ( 0 .. $#data ) {
            my $value = $data[$n];
            chomp $value;
            next unless ( $value =~ /\b[BCU]\b/ );
            $value =~ s/@.*// if ( $^O eq "linux" );
            $value =~ s/^_//  if ( $^O eq "darwin" );
            if ( $value =~ /\|/ ) {
                $value =~ s/^.*\|//;
            }
            else {
                $value =~ s/\s.*//;
            }
            $counts{$value} = 0;
        }
    }
    return %counts;
}

# using "nm", obtain a list of symbols from the given library.
sub read_lib_syms($) {
    my $path = shift;
    my %counts;
    if ( -r $path ) {
        my $opts = "-P";

        $opts .= "D" if ( $^O eq "linux" );
        $opts .= "D" if ( $^O =~ /^.*bsd$/ );
        $opts .= "D" if ( $^O eq "solaris" );

        if (%all_libs) {
            %counts = %{ $all_libs{$path} } if ( $all_libs{$path} );
        }
        if ( not %counts ) {
            printf "...reading %s\n", $path if ($opt_v);
            my @data = &read_pipe("nm $opts $path 2>/dev/null");
            my $k    = 0;
            for my $n ( 0 .. $#data ) {
                my $value = $data[$n];
                chomp $value;
                next unless ( $value =~ /\b[TDBC]\b/ );
                $value =~ s/\s.*//;
                $counts{$value} = 0;
            }
            $all_libs{$path} = \%counts;
        }
    }
    return %counts;
}

# obtain a list of shared libraries used by the given application.
sub read_ldd($) {
    my $path = shift;

    if ( $^O eq "solaris" ) {
        return unless ( -x $path );
    }
    return unless ( -r $path );

    my @data  = &read_pipe("$ldd_tool $path");
    my $state = 0;
    for my $n ( 0 .. $#data ) {
        chomp $data[$n];
        if ( $ldd_tool eq "ldd" ) {
            if ( $data[$n] =~ /=>\s+\// ) {
                $data[$n] =~ s/^.*=>\s+//;
                $data[$n] =~ s/\s\(.*$//;
            }
            elsif ( $data[$n] =~ /^\s+\/.*/ ) {
                $data[$n] =~ s/\(.*$//;
            }
            else {
                $data[$n] = "";
            }
        }
        elsif ( $ldd_tool eq "otool -l" ) {
            if ( $data[$n] =~ /^\s*cmd\s+LC_(LOAD|REEXPORT)_\w*DYLIB/ ) {
                $state = 1;
            }
            elsif ( $state == 1 && $data[$n] =~ /^\s*name\s+/ ) {
                $data[$n] =~ s/^\s*name\s+//;
                $data[$n] =~ s/\s+\(.*//;
                $state = 0;
            }
            else {
                $state = 0 if ( $data[$n] =~ /^Load command/ );
                $data[$n] = "";
            }
        }
        else {
            $data[$n] = "";
        }
    }
    return @data;
}

sub is_curses($) {
    my $sym    = shift;
    my $result = "?";
    if ( $bsd_curses_symbols{$sym} ) {
        $result = $bsd_curses_symbols{$sym};
    }
    elsif ( $sys5_curses_symbols{$sym} ) {
        $result = $sys5_curses_symbols{$sym};
    }
    elsif ( $xopen_curses_symbols{$sym} ) {
        $result = $xopen_curses_symbols{$sym};
    }
    return $result;
}

sub is_extended($) {
    my $sym    = shift;
    my $result = "?";
    if ( $extended_symbols{$sym} ) {
        $result = $extended_symbols{$sym};
    }
    elsif ( $sym =~ /_sp$/ ) {
        $sym =~ s/_sp$//;
        $result = "nc" if ( &is_curses($sym) );
    }
    return $result;
}

sub analyze_sym($$) {
    my $sym    = shift;
    my $tc     = shift;
    my $result = "?";
    if ( $sym =~ /^_nc_/ ) {
        $result = "*n";
    }
    elsif ( $sym =~ /^SL/ ) {
        $result = "s";
    }
    elsif ( $tc == 0 and $ti_symbols{$sym} ) {
        $result = $ti_symbols{$sym};
    }
    elsif ( $tc != 0 and $tc_symbols{$sym} ) {
        $result = $tc_symbols{$sym};
    }
    return $result;
}

sub sym_is_terminal($) {
    my $sym    = shift;
    my $result = 0;
    if (   $ti_symbols{$sym}
        or $tc_symbols{$sym}
        or &is_curses($sym) ne "?"
        or &is_extended($sym) ne "?" )
    {
        $result = 1;
    }
    return $result;
}

sub lib_is_terminal($) {
    my $lib    = shift;
    my $result = 0;
    if (   $lib =~ /lib([nx])?curses/
        or $lib =~ /libtinfo/
        or $lib =~ /libterm/
        or $lib =~ /libslang/ )
    {
        $result = 1;
    }
    printf "...lib is for terminal: $lib\n" if ( $result and $opt_d );
    return $result;
}

sub lib_depends_on_terminal($) {
    my $path   = shift;
    my $result = 0;
    my @libs   = &read_ldd($path);
    for my $n ( 0 .. $#libs ) {
        if ( &lib_is_terminal( $libs[$n] ) ) {
            $result = 1;
            last;
        }
    }
    printf "...lib depends on terminal: $path\n" if ( $result and $opt_d );
    return $result;
}

sub lookup_sym($) {
    my $sym  = shift;
    my $tc   = 0;
    my $test = &analyze_sym( $sym, $assume_tc );
    $test = &is_curses($sym)   if ( $test eq "?" );
    $test = &is_extended($sym) if ( $test eq "?" );
    return $test;
}

sub merge_syms($$) {
    my %current = %{ $_[0] };
    my %updates = %{ $_[1] };
    my %result;
    foreach my $sym ( keys %current ) {
        $result{$sym} = $current{$sym};
    }
    foreach my $sym ( sort keys %updates ) {
        printf "+\t$sym (%s)\n", &lookup_sym($sym)
          if (  $opt_d
            and not defined( $result{$sym} )
            and &sym_is_terminal($sym) );
        $result{$sym} = $updates{$sym};
    }
    return %result;
}

sub analyze_file($$) {
    my $path  = shift;
    my $recur = shift;
    if ( -d $path ) {
        if ( -l $path ) {
            printf "link:%s\n", $path if ($opt_v);
        }
        elsif ( $opt_r or ( $recur == 0 ) ) {
            &analyze_dir( $path, $recur + 1 );
        }
    }
    elsif ( &is_executable($path) ) {
        printf "file:%s\n", $path if ($opt_v);
        my @libs = &read_ldd($path);
        my %libs;
        my %syms;
        my $type;
        my $found = 0;
        for my $n ( 0 .. $#libs ) {
            my $lib = $libs[$n];
            if ( $lib !~ /^\/.+\/lib.+/ ) {

                # ignore
            }
            elsif ( &lib_is_terminal($lib) ) {
                my %counts = &read_lib_syms($lib);
                $libs{$lib} = \%counts;
                $found++;
            }
            elsif ( &lib_depends_on_terminal($lib) ) {
                my %counts = &read_app_syms($lib);
                %syms = &merge_syms( \%syms, \%counts );
                $found++;
            }
            if ( $lib =~ /libncurses/ ) {
                $type = "ncurses";
            }
            elsif ( $lib =~ /libslang/ ) {
                $type = "slang" unless ($type);
            }
        }
        if ($found) {
            my %counts = &read_app_syms($path);
            my %levels;
            %counts = &merge_syms( \%syms, \%counts );

            $type = "";
            if ( defined $counts{"initscr"} or defined $counts{"newterm"} ) {
                $type = "c";
            }
            $assume_tc = 0;
            $assume_tc = 1 if ( defined $counts{"tgetent"} );
            if ( defined $counts{"tgetent"} ) {
                $type .= "+" if ( $type ne "" );
                $type .= "tc";
            }
            for my $sym ( keys %counts ) {
                my $test = &analyze_sym( $sym, $assume_tc );
                $test          = &is_curses($sym)   if ( $test eq "?" );
                $test          = &is_extended($sym) if ( $test eq "?" );
                $levels{$test} = 1                  if ( $test ne "?" );
            }
            my $apptype = "";
            for my $level ( sort keys %levels ) {
                $apptype .= "+" if ( $apptype ne "" );
                $apptype .= $level;
            }

            # get rid of repeats (we only want the highest levels)
            $apptype =~ s/\b[bc]\d.*(\bn\d)/$1/;
            $apptype =~ s/\b[bc]\d.*(\bc[\dx])/$1/;
            $apptype =~ s/^\*n+.*(n\d)/$1*/;
            $apptype =~ s/\bt.*(t\d)/$1*/;
            $apptype =~ s/\b(t\d).*ti/$1*/;
            if ( $type eq "" ) {
                for my $sym ( keys %counts ) {
                    my $test = &analyze_sym( $sym, $assume_tc );
                    if ( $test ne "?" ) {
                        $type = $test;
                        last;
                    }
                }
            }
            if ( $type ne "" ) {
                for my $sym ( keys %counts ) {
                    my $test = &is_extended($sym);
                    if ( $test ne "?" ) {
                        $type =~ s/\bc\b/nc/;
                        last;
                    }
                }
            }
            $type = "i" if ( $type eq "" );
            $count_by_type{$type} += 1;
            printf "%s\t%s\n", $apptype, $path unless ($opt_q);

            foreach my $sym ( keys %counts ) {
                foreach my $lib ( keys %libs ) {
                    my %data = %{ $libs{$lib} };
                    if ( defined $data{$sym} ) {
                        $data{$sym} += 1;
                        $libs{$lib}     = \%data;
                        $all_libs{$lib} = \%data;
                        last;
                    }
                }
            }
        }
        else {
            printf "%s\t%s\n", "?", $path unless ($opt_q);
            $count_by_type{"?"} += 1;
        }
    }
}

sub analyze_dir($$) {
    my $path  = shift;
    my $recur = shift;
    printf "dir:%s\n", $path if ($opt_v);
    if ( opendir( my $dh, $path ) ) {
        my @entries = sort readdir($dh);
        closedir $dh;
        for my $n ( 0 .. $#entries ) {
            next if ( $entries[$n] =~ /^\.\.?$/ );
            &analyze_file( $path . "/" . $entries[$n], $recur + 1 );
        }
    }
    else {
        warn "can't opendir $path: $!";
    }
}

sub main::HELP_MESSAGE() {
    printf STDERR <<EOF
Usage: $0 [options]

Options:

-d         debug, shows parsed values
-l         show report of symbols used from libraries
-q         quiet, do not show filenames and their types
-r         recur into subdirectories
-s         show summary of application types
-v         verbose, shows files opened
EOF
      ;
    exit;
}

$ldd_tool = "ldd";
$ldd_tool = "otool -l" if ( $^O eq "darwin" );

&getopts('dlqrsv') || main::HELP_MESSAGE;

if ( $#ARGV >= 0 ) {
    while ( $#ARGV >= 0 ) {
        &analyze_file( shift @ARGV, 0 );
    }
}
else {
    while ( !eof(STDIN) ) {
        last unless defined( $_ = <STDIN> );
        chomp $_;
        &analyze_file( $_, 0 );
    }
}

if ($opt_l) {
    for my $lib ( sort keys %all_libs ) {
        printf "lib: %s\n", $lib;
        my %counts = %{ $all_libs{$lib} };
        for my $sym ( sort keys %counts ) {
            printf "\t%d\t%s\n", $counts{$sym}, $sym if ( $counts{$sym} > 0 );
        }
    }
}

if ($opt_s) {
    my @types = ( sort keys %count_by_type );
    my $count = 0;
    for my $n ( 0 .. $#types ) {
        $count += $count_by_type{ $types[$n] };
    }
    printf "%d files\n", $count;
    for my $n ( 0 .. $#types ) {
        printf "%d\t%s\n", $count_by_type{ $types[$n] }, $types[$n];
    }
}

1;
