2020# Author: Massimo Loschi <ccfedevel@gmail.com>
2121#
2222
23+ use v5.36; # strict + warnings + modern features (M7 Phase 6 capstone)
2324use Curses;
2425use Sys::Hostname;
2526use File::Basename;
5455use CCFE::Context (); # explicit run-state container (M7 de-globalisation)
5556use FindBin (); # to locate the program at runtime (see the path block below)
5657
58+ # M7 Phase 6 (capstone): this legacy script predates `use strict`. Its package
59+ # globals -- constants, lookup tables, the search-path arrays, message strings
60+ # and the few intentionally-global runtime vars ($cpid/$tmpfh, owned by the
61+ # signal handlers) -- are declared here so the file runs under strict. The
62+ # de-globalisation (Phases 1-5) moved the mutable per-screen/config/run state
63+ # onto lexicals and $ctx; what remains global is genuinely program-wide.
64+ # # BEGIN-OUR (formatted; scalars, then arrays, then hashes)
65+ our (
66+ $ALL_FIELDS_IDS_TAG , $ASKS_FIELD_PAD , $ASKS_FIELD_SIZE , $ASKS_WIN_COLS ,
67+ $ASKS_WIN_FTR_ROWS , $ASKS_WIN_ROWS , $BAD_SHELL_MSG , $BAD_SHELL_TITLE ,
68+ $BFIELD_DEFAULT , $BFIELD_NO , $BFIELD_NO_DESCR , $BFIELD_NULL ,
69+ $BFIELD_NULL_DESCR , $BFIELD_YES , $BFIELD_YES_DESCR , $BIG_OUTPUT_MSG ,
70+ $BIG_OUTPUT_TITLE , $BINDIR , $BOOLEAN , $BOOLEAN_FIELD_SIZE , $CALLNAME ,
71+ $CALL_SHELL_MSG , $CALL_SYS_ES_MSG , $CALL_SYS_MSG , $CONFIRM_DESCR_NO ,
72+ $CONFIRM_DESCR_YES , $CONFIRM_TITLE , $CURSES_ACTIVE , $DEBUG , $DESCR ,
73+ $DMENU_DEF_FNAME , $EMPTY_LIST_MSG , $EMPTY_LIST_TITLE ,
74+ $ERR_EMPTY_FIELD_MSG , $ERR_EMPTY_FIELD_TITLE , $ERR_LOAD_INITIAL_OBJ ,
75+ $ES_CANCEL , $ES_EXIT , $ES_FOPEN_ERR , $ES_FOPEN_ERR_MSG , $ES_NOT_FOUND ,
76+ $ES_NOT_FOUND_MSG , $ES_NO_ERR , $ES_NO_ERR_MSG , $ES_NO_ITEMS ,
77+ $ES_NO_ITEMS_MSG , $ES_SYNTAX_ERR , $ES_SYNTAX_ERR_MSG , $ES_USER_REQ ,
78+ $ETCDIR , $FALSE , $FIELD_LMARGIN , $FIELD_RMARGIN , $FIELD_VALUE_GAP ,
79+ $FORMEXT , $FORM_ARGV_ID , $FORM_ERR_TITLE , $FOUND_NONE_MSG ,
80+ $FOUND_NONE_TITLE , $FSEP_ID_PRFX , $FS_BOTTOM_ROWS , $FS_HEADER_ROWS ,
81+ $FS_TOP_ROWS , $HAS_COLOR , $HOSTNAME , $HTAB_COLS , $INIT_DISABLE_FIELDS ,
82+ $INIT_ENABLE_FIELDS , $INIT_FORM_ERR_MSG , $INIT_REMOVE_FIELDS ,
83+ $KEY_ENTER_LABEL , $KEY_F10_LABEL , $KEY_F1_LABEL , $KEY_F2_LABEL ,
84+ $KEY_F3_LABEL , $KEY_F4_LABEL , $KEY_F5_LABEL , $KEY_F6_LABEL , $KEY_F7_LABEL ,
85+ $KEY_F8_LABEL , $KEY_F9_LABEL , $KEY_FIND_LABEL , $KEY_FNEXT_LABEL ,
86+ $KEY_INTR_LABEL , $KEY_SELALL_LABEL , $KEY_UNSELALL_LABEL , $LANG_ID ,
87+ $LEGACY_DIR , $LIBDIR , $LIST_CMD_ERR_MSG , $LIST_CMD_ERR_TITLE ,
88+ $LOAD_FORM_ERR_MSG , $LOAD_MENU_ERR_MSG , $LOGDIR , $LOG_ACTION_CMD ,
89+ $LOG_ACTION_OUT , $LOG_DATE , $LOG_DEFAULT_CMD , $LOG_FIELDS_VAL ,
90+ $LOG_INITFORM_OUT , $LOG_LIST_CMD , $LOG_MENU_CHOICE , $LOG_NORMAL ,
91+ $LOG_REQUESTED , $LOG_SCAN_PATHS , $LOG_SYSCALL_ENV , $LOG_WRITE_ERROR_MSG ,
92+ $LOG_WRITE_ERROR_TITLE , $LW_COLS , $LW_FOOTER_ROWS , $LW_PAD_COLS , $LW_ROW0 ,
93+ $MAIN_PATH , $MARK_PRIV_SHCUTS , $MENUEXT , $MENU_ERR_TITLE ,
94+ $MIN_ITEMS_FOR_FIND , $MOUSE_ON , $MSGDIR , $MSG_WIN_BMSG , $MSG_WIN_ROWS ,
95+ $MSG_WIN_TITLE , $MS_BOTTOM_ROWS , $MS_HEADER_ROWS , $MS_TOP_ROWS , $NO ,
96+ $NORMAL , $NULLBOOLEAN , $NULL_FACTION_MSG , $NULL_FACTION_TITLE ,
97+ $NULL_LIST_MSG , $NULL_LIST_TITLE , $NUMERIC , $OBJDIR , $OFF , $ON , $PERS_DIR ,
98+ $PERS_WRITE_ERROR_MSG , $PERS_WRITE_ERROR_TITLE , $PREFIX , $PRIV_DIR ,
99+ $RB_FAILED_MSG , $RB_LINES_MSG , $RB_OK_MSG , $RB_RUNNING_MSG , $RB_TIME_MSG ,
100+ $RB_TITLE , $REALNAME , $RESTRICTED_MSG , $RESTRICTED_TITLE , $RS_BOTTOM_ROWS ,
101+ $RS_HEADER_ROWS , $RS_INFO_ID , $RS_STDERR_ID , $RS_STDOUT_ID , $RS_TOP_ROWS ,
102+ $SAVE_DETAILED , $SAVE_DETAILED_DESCR , $SAVE_ERROR_MSG , $SAVE_ERROR_TITLE ,
103+ $SAVE_FIELDVAL_MSG , $SAVE_FIELDVAL_TITLE , $SAVE_FNAME_PROMPT ,
104+ $SAVE_FNAME_TITLE , $SAVE_SCRIPT , $SAVE_SCRIPT_DESCR , $SAVE_SIMPLE ,
105+ $SAVE_SIMPLE_DESCR , $SAVE_TYPE_TITLE , $SEARCH_PTRN_PROMPT ,
106+ $SEARCH_PTRN_TITLE , $SEPARATOR , $SEP_LINE , $SEP_LINE_DOUBLE , $SEP_TEXT ,
107+ $SEP_TEXT_CENTER , $SHOW_ACTION_TITLE , $SIMPLE , $SR_BUFF_SIZE , $STRING ,
108+ $THEMEDIR , $TRUE , $UCSTRING , $USERNAME , $USR_CFG , $USR_OBJ , $VERSION ,
109+ $VERSION_DATE , $VERSION_YEAR , $WAIT_MSG_MSG , $WRKDIR , $YES , $attrk ,
110+ $attrv , $called_form , $ch , $choice , $cpid , $descr , $es , $exec_hh ,
111+ $exec_mm , $exec_ss , $i , $id , $lflags_size , $mlmargin , $mwin , $mwinr , $opt ,
112+ $out , $ovl_mode , $p , $pad_lines , $path , $pid , $prev_wdir , $res ,
113+ $rflags_size , $s , $scan , $search_string , $shcut_type , $text , $tmpfh ,
114+ $twin ,
115+ @CONFIRM_ITEMS , @ERR_LITTLE_SCREEN , @ERR_WRONG_FPATH , @FORM_TOP_MSG ,
116+ @FSKeys , @LW_DISPLAY_TOP_MSG , @LW_MULTIVAL_TOP_MSG , @LW_SINGLEVAL_TOP_MSG ,
117+ @MENU_TOP_MSG , @MSKeys , @RSKeys , @cnf_path , @es_str , @flist ,
118+ @fn_key_functions , @lines , @mf_path ,
119+ %bool_vals , %layout_vals , %options , %sep_type_vals , %type_vals ,
120+ );
121+ # # END-OUR
122+
57123# Optional display-width support. In a UTF-8 locale a label/title can occupy
58124# fewer screen columns than it has bytes (e.g. "caf\xc3\xa9" is 5 bytes, 4
59125# columns) and a CJK glyph occupies two columns; ncursesw already lays the
179245$BFIELD_NO = ' NO' ;
180246$BFIELD_NULL = ' ' ;
181247$BFIELD_DEFAULT = $BFIELD_NO ;
248+ # Defaults so a headless load_form (e.g. --dump/-k, before load_msgs) does not
249+ # read these undef; load_msgs overrides them with the localised descriptions.
250+ $BFIELD_YES_DESCR = ' Yes' ;
251+ $BFIELD_NO_DESCR = ' No' ;
252+ $BFIELD_NULL_DESCR = ' Not set' ;
182253$MIN_ITEMS_FOR_FIND = 5;
183254
184255$INIT_REMOVE_FIELDS = ' CCFE_REMOVE_FIELDS' ;
@@ -422,6 +493,7 @@ sub harden_child_env {
422493
423494sub trace {
424495 my ( $msg , $log_level ) = @_ ;
496+ $log_level //= 0; # no level given -> 0 (logged only under DEBUG, as before)
425497 my @months = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ) ;
426498 my @buff = localtime (time );
427499 my $now = ' ' ;
@@ -767,7 +839,7 @@ sub init_footer {
767839 # chgat() takes the colour pair as a separate argument, so extract
768840 # it from $ka (0 = no colour, i.e. the monochrome default).
769841 chgat( $win , $y , $x , length ( $ctx -> {cfg }{keys }{ $keysList [$i ] }{key } ),
770- $ka , PAIR_NUMBER($ka ), NULL );
842+ $ka , PAIR_NUMBER($ka ), 0 ); # 0 = NULL opts pointer
771843 }
772844 $x += $labelSize ;
773845 }
@@ -846,7 +918,7 @@ sub disp_msg {
846918 addstr( $win , 1, 2 + int ( ( $width - disp_width($msg ) ) / 2 ), $msg );
847919 addstr( $win , 3, 2 + int ( ( $width - disp_width($MSG_WIN_BMSG ) ) / 2 ),
848920 $MSG_WIN_BMSG );
849- chgat( $win , 3, 1, $width + 2, $bottom_attr , NULL, NULL );
921+ chgat( $win , 3, 1, $width + 2, $bottom_attr , 0, 0 );
850922 refresh($win );
851923 $ch = getch($win );
852924 del_panel($panel );
@@ -1071,7 +1143,7 @@ sub disp_page {
10711143 $ctx -> {cfg }{SHOW_SCREEN_NAME } ? $screen_name : ' ' ,
10721144 $ovl_flag , $pos );
10731145 addstr( $win , 0, $COLS - length ($buff ), $buff );
1074- chgat( $win , 0, $COLS - length ($pos ) - 4, 3, A_REVERSE, NULL, NULL )
1146+ chgat( $win , 0, $COLS - length ($pos ) - 4, 3, A_REVERSE, 0, 0 )
10751147 if $ovl_flag and $ctx -> {cfg }{LAYOUT } == $NORMAL ;
10761148 move( $win , $saveY , $saveX );
10771149}
@@ -1202,13 +1274,18 @@ sub load_form {
12021274 # Separator label formatting depends on $COLS, so the pure
12031275 # parser defers it to here: centre the text, or draw a rule
12041276 # line across the field area.
1277+ # Clamp to >= 0: headless paths (--dump/-k) run before
1278+ # initscr, so $COLS is 0 and the width goes negative; a
1279+ # negative `x` repeat is an empty string either way.
12051280 my $line_width = $COLS - $FIELD_LMARGIN - $FIELD_RMARGIN ;
1281+ $line_width = 0 if $line_width < 0;
12061282 foreach my $f ( @{ $form {fields } } ) {
12071283 next unless ( $f -> {type } // 0 ) == $SEPARATOR ;
12081284 next unless defined $f -> {sep_type };
12091285 if ( $f -> {sep_type } == $SEP_TEXT_CENTER ) {
12101286 my $lblanks =
12111287 ( $line_width - length ( $f -> {label } ) ) / 2;
1288+ $lblanks = 0 if $lblanks < 0;
12121289 $f -> {label } =
12131290 sprintf ( " %s%s " , ' ' x ($lblanks ), $f -> {label } );
12141291 }
@@ -3920,7 +3997,7 @@ sub do_form {
39203997 elsif ( $ch == $ctx -> {cfg }{keys }{show_action }{code } ) {
39213998 $sync_fields_val -> ();
39223999 my $args = $form {action };
3923- $args =~ s / ^(\b * [a-zA-Z]+)\( ?([a-zA-Z_,]*)\) ?/ $1 / ;
4000+ $args =~ s / ^([a-zA-Z]+)\( ?([a-zA-Z_,]*)\) ?/ $1 / ;
39244001 if ($args ) {
39254002 $prepare_action -> ( \$args );
39264003 my @cmd = split /\n/, $args ;
@@ -4117,11 +4194,11 @@ sub run_browse {
41174194 if ( $pos0 >= 0 ) {
41184195 $nfound ++;
41194196 chgat( $p , $row , $pos0 , length ($search_string ), A_REVERSE,
4120- NULL, NULL );
4197+ 0, 0 );
41214198 if ( $pos0 + length ($search_string ) >= $COLS ) {
41224199 chgat( $p , $row + 1, 0,
41234200 $pos0 + length ($search_string ) - $COLS ,
4124- A_REVERSE, NULL, NULL );
4201+ A_REVERSE, 0, 0 );
41254202 }
41264203 }
41274204 } while ( $pos0 >= 0 );
@@ -4137,7 +4214,8 @@ sub run_browse {
41374214
41384215 move( $p , 0, 0 );
41394216 seek ( $tmpfh , 0, 0 );
4140- while ( $buff = <$tmpfh > and $c <= $ctx -> {state }{pad_lines } ) {
4217+ while ( defined ( $buff = <$tmpfh > ) and $c <= $ctx -> {state }{pad_lines } )
4218+ {
41414219 $c ++;
41424220 ( $src , $buff ) = split /:/, $buff , 2;
41434221 if ( length ($buff ) == $COLS + 1 ) {
@@ -4214,7 +4292,7 @@ sub run_browse {
42144292 eval { $cpid = open3( $infh , $outfh , $errfh , $ctx -> {cfg }{OPEN3_SHELL }, ' -c' , $cmd ); };
42154293 fatal($@ ) if $@ ;
42164294 trace(" successfully forked child PID $cpid " );
4217- $sel = new IO::Select;
4295+ $sel = IO::Select-> new ;
42184296 $sel -> add( $outfh , $errfh );
42194297
42204298 $tmpfh = tempfile( ' ccfeXXXXX' , DIR => ' /tmp' );
0 commit comments