diff --git a/docs/man/man1/halcmd.1 b/docs/man/man1/halcmd.1 index d4bdfb7e796..95a4495752b 100644 --- a/docs/man/man1/halcmd.1 +++ b/docs/man/man1/halcmd.1 @@ -397,6 +397,10 @@ halcmd \fB\-i\fR flag. They have the following formats: \fB[SECTION]VAR\fR followed by end-of-line or whitespace .IP \fB[SECTION](VAR)\fR +.SH LINE CONTINUATION +The backslash character (\fB\\\fR) may be used to indicate the line +is extended to the next line. The backslash character must be the +last character before the newline. .SH EXAMPLES .SH HISTORY .SH BUGS diff --git a/src/hal/utils/halcmd_main.c b/src/hal/utils/halcmd_main.c index 3a9f1074131..37deac1991d 100644 --- a/src/hal/utils/halcmd_main.c +++ b/src/hal/utils/halcmd_main.c @@ -64,8 +64,12 @@ static void print_help_general(int showR); static int release_HAL_mutex(void); static int propose_completion(char *all, char *fragment, int start); -static char *prompt = ""; +static char *prompt = ""; +static char *prompt_script = "%%\n"; +static char *prompt_interactive = "halcmd: "; +static char *prompt_continue = "halcmd+: "; +#define MAX_EXTEND_LINES 20 /*********************************************************************** * LOCAL FUNCTION DEFINITIONS * @@ -223,9 +227,9 @@ int main(int argc, char **argv) if (srcfile && isatty(fileno(srcfile))) { if (scriptmode) { - prompt = "%%\n"; + prompt = prompt_script; } else { - prompt = "halcmd: "; + prompt = prompt_interactive; } } @@ -249,15 +253,53 @@ int main(int argc, char **argv) } } } else { + int extend_ct = 0; // extend lines with backslash (\) /* read command line(s) from 'srcfile' */ while (get_input(srcfile, raw_buf, MAX_CMD_LEN)) { char *tokens[MAX_TOK+1]; + char eline [(LINELEN + 2) * (MAX_EXTEND_LINES + 1)]; + char *elineptr; + char *elinenext; + int newLinePos; + halcmd_set_linenumber(linenumber++); + + newLinePos = (int)strlen(raw_buf) - 1; // interactive + if (raw_buf[newLinePos] == '\n') { newLinePos--; } // tty + + if (newLinePos > 0 && raw_buf[newLinePos] == '\\') { // backslash + raw_buf[newLinePos] = 0; + newLinePos++; + if (!extend_ct) { //first extend + if (prompt == prompt_interactive) prompt = prompt_continue; + elineptr = eline; + strncpy(elineptr,raw_buf,strlen(raw_buf)); + elinenext = elineptr + strlen(raw_buf); + } else { // subsequent extends + strncpy(elinenext,raw_buf,newLinePos); + elinenext = elinenext + strlen(raw_buf); + } + *elinenext = 0; + extend_ct++; + continue; // get next line to extend + } else { // no backslash + if (extend_ct) { // extend finished + strncpy(elinenext,raw_buf,strlen(raw_buf)); + *(eline+strlen(eline)+0)='\n'; + elinenext = elinenext + strlen(raw_buf); + *elinenext = 0; + elineptr = eline; + } + } + if (!extend_ct) { elineptr = (char*)raw_buf; } + extend_ct = 0; + if (prompt == prompt_continue) { prompt = prompt_interactive; } + /* remove comments, do var substitution, and tokenise */ - retval = halcmd_preprocess_line(raw_buf, tokens); - if(echo_mode) { - halcmd_echo("%s\n", raw_buf); - } + retval = halcmd_preprocess_line(elineptr, tokens); + if(echo_mode) { + halcmd_echo("%s\n", eline); + } if (retval == 0) { /* the "quit" command is not handled by parse_line() */ if ( ( strcasecmp(tokens[0],"quit") == 0 ) || @@ -281,7 +323,8 @@ int main(int argc, char **argv) /* exit from loop */ break; } - } + } //while get_input() + extend_ct=0; } /* all done */ halcmd_shutdown(); diff --git a/tcl/twopass.tcl b/tcl/twopass.tcl index e64df6f52bc..f7a68bc55c5 100644 --- a/tcl/twopass.tcl +++ b/tcl/twopass.tcl @@ -378,17 +378,23 @@ proc ::tp::prep_the_files {} { set f $foundfile # convert to a temporary tcl file if necessary - set suffix [filesuffix $f] - switch -exact $suffix { - tcl { + switch -exact [file extension $f] { + .tcl { + # handle .tcl files with trailing backslash + set has_backslash "" + catch {set has_backslash [exec grep {\\$} $f]} msg + if {"$has_backslash" != ""} { + set ::TP($f,tmp) /tmp/tmp_[file tail $f] + set f [handle_tcl_with_backslash $f $::TP($f,tmp)] + } if {[llength $f_argv]} { set f "$f $f_argv" ;# optional args } lappend ::TP(runfiles) $f verbose "tclfile: $f" } - hal { - set ::TP($f,tmp) /tmp/[file tail $f].tmp + .hal { + set ::TP($f,tmp) /tmp/tmp_[file tail $f] set converted_file [hal_to_tcl $f $::TP($f,tmp)] if {"$converted_file" == ""} { puts "twopass:Sourcing $f WITHOUT twopass processing" @@ -400,12 +406,51 @@ proc ::tp::prep_the_files {} { } } default {return -code error \ - "prep_the_files:unknown file type <$suffix>"} + "prep_the_files:unknown file type <$f>"} } } } } ;# prep_the_files +proc ::tp::handle_tcl_with_backslash {ifile ofile} { + if [catch {set fdin [open $ifile r] + set fdout [open $ofile w] + } msg + ] { + puts "twopass: Error: $msg" + exit 1 + } + puts $fdout "# temporary tcl file generated by twopass.tcl" + set lno 0 + while 1 { + if [eof $fdin] break + incr lno + set theline [gets $fdin] + set line [string trim $theline] + if {"$line" == ""} continue + + # handle .tcl files extended with backslash: + if {[string range $line end end] == "\\"} { + set line [string replace $line end end] ;# rm trailing backslash + if [info exists extend] { + set extend "${extend}$line" ;# subsequent extends + } else { + set extend "$line" ;# first extend + } + continue ;# get next line + } else { + if [info exists extend] { + set line "${extend}$line" + } + } + catch {unset extend} + puts $fdout "$line" + } + close $fdin + close $fdout + return $ofile +} ;# handle_tcl_with_backslash + proc ::tp::hal_to_tcl {ifile ofile} { # When hal files are specified with HAL:HALFILE, # try to make them work (preferred way is use tcl files). @@ -436,6 +481,22 @@ proc ::tp::hal_to_tcl {ifile ofile} { set line [string trim $theline] if {"$line" == ""} continue + # handle .hal files extended with backslash: + if {[string range $line end end] == "\\"} { + set line [string replace $line end end] ;# rm trailing backslash + if [info exists extend] { + set extend "${extend}$line" ;# subsequent extends + } else { + set extend "$line" ;# first extend + } + continue ;# get next line + } else { + if [info exists extend] { + set line "${extend}$line" + } + } + catch {unset extend} + # find *.hal files excluded from twopass processing: set tmpline [string map -nocase {" " ""} $line] set tmpline [string tolower $tmpline] @@ -522,7 +583,7 @@ proc ::tp::source_the_files {} { foreach file_plus_args $::TP(runfiles) { catch {unset ::argv} set f [lindex $file_plus_args 0] - if {[filesuffix $f] == "tcl"} { + if {[file extension $f] == ".tcl"} { # note: ::argv supplies the file_plus_args to the sourced file # ::argv0 is not set to maintain compatibility with # the way the linuxcnc script uses haltcl to use tcl files @@ -546,12 +607,6 @@ proc ::tp::source_the_files {} { } } ;# source_the_files -proc ::tp::filesuffix {f} { - set dot [string last . $f] - if {$dot < 0} {return -code error "filesuffix: no suffix <$f>"} - return [string range $f [expr 1 + $dot ] end] -} ;# filesuffix - proc ::tp::load_the_modules {} { if ![info exists ::TP(modules)] { # no modules unlikely, but can occur in testing diff --git a/tests/loadrt.1/expected b/tests/loadrt.1/expected index 63140c9b4c2..14558600ea4 100644 --- a/tests/loadrt.1/expected +++ b/tests/loadrt.1/expected @@ -1,2 +1,2 @@ -and2.0 m.q m.r or2.0 or2.1 or2.2 -and2.0.in0 and2.0.in1 and2.0.out and2.0.time m.q.in0 m.q.in1 m.q.out m.q.sel m.q.time m.r.in0 m.r.in1 m.r.out m.r.sel m.r.time or2.0.in0 or2.0.in1 or2.0.out or2.0.time or2.1.in0 or2.1.in1 or2.1.out or2.1.time or2.2.in0 or2.2.in1 or2.2.out or2.2.time +and2.0 d1 d2 d3 l1 l2 m.q m.r or2.0 or2.1 or2.2 xor2.0 xor2.1 +and2.0.in0 and2.0.in1 and2.0.out and2.0.time d1.in d1.out d1.time d2.in d2.out d2.time d3.in d3.out d3.time l1.and l1.in-00 l1.in-01 l1.time l2.in-00 l2.in-01 l2.in-02 l2.or l2.time m.q.in0 m.q.in1 m.q.out m.q.sel m.q.time m.r.in0 m.r.in1 m.r.out m.r.sel m.r.time or2.0.in0 or2.0.in1 or2.0.out or2.0.time or2.1.in0 or2.1.in1 or2.1.out or2.1.time or2.2.in0 or2.2.in1 or2.2.out or2.2.time xor2.0.in0 xor2.0.in1 xor2.0.out xor2.0.time xor2.1.in0 xor2.1.in1 xor2.1.out xor2.1.time diff --git a/tests/loadrt.1/test.hal b/tests/loadrt.1/test.hal index 46520e76254..2a055360f68 100644 --- a/tests/loadrt.1/test.hal +++ b/tests/loadrt.1/test.hal @@ -1,5 +1,18 @@ loadrt and2 # default count of 1 still works loadrt mux2 names=m.q,m.r # new names= works loadrt or2 count=3 # count= still works + +# backslash line extend: +loadrt xor2 \ +count=2 + +loadrt ddt names=d1\ +,d2\ +,d3 + +loadrt logic \ +names=l1,l2 \ +personality=0x102,0x203 + list funct list pin