Thursday, January 31, 2013

Get WAN IP in one line command

If the local host is behind firewall and/or there is NAT in between, ifconfig may not report the correct IP address which is seen externally.

The following (one line) command prints out the WAN side IP.

# curl -s http://checkip.dyndns.org | sed -e 's/.*: //;s/<.*//'

Sunday, January 13, 2013

Expect: interact with external program with script

The following script calls an external program and interact with this external program by controlling the inputs and outputs of it.

The script first create call the external program 'dc', which is a postfix based calculator.
Then it send the string "4p\r" as input to the 'dc' program.
In the expect block, the script try to match two patterns: "0\r" and any digit followed by a carry return.
If the output of 'dc' is 0, then the script terminates it by sending the command "q".
If the output of 'dc' is any digit larger then 0 (starting from 4 in this example), the value will be decreased by sending "1-p" to 'dc'.


#!/usr/bin/expect -f

spawn dc      
send "4p\r"   

expect {

  "0\r" {
    puts "Expect: matched $expect_out(0,string)";
    send "q\r"
    puts "Expect: exit";
  }

  -re {[1-9]\r} {
    puts "Expect: matched $expect_out(0,string)";
    send "1-p\r";
    puts "Expect: reduce by 1";
    exp_continue
  }

}

Running the script will generated the following output on screen.


$ ./script.exp
spawn dc
4p
4
Expect: matched 4
Expect: reduce by 1
1-p
3
Expect: matched 3
Expect: reduce by 1
1-p
2
Expect: matched 2
Expect: reduce by 1
1-p
1
Expect: matched 1
Expect: reduce by 1
1-p
0
Expect: matched 0
Expect: exit


Notes:

1. Except captures all output from the external program including '\r'.
2. Variable $expect_out(0,string) stores the matched string from the immediate previous matching.
3. Variable $expect_out(buffer) stores the remaining output of the external program by removing all characters up to (and including) the matched string from the immediate previous matching.

Tuesday, January 08, 2013

Interface between C and TCL : Case III

Extending TCL script by C functions. The C functions are compiled as dynamic loaded library and is called from within a TCL script. The script can be interpreted by a standard TCL shell.

The C library is listed below.

#include <stdio.h>
#include <tcl.h>

int my_cmd1 (ClientData cdata, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[]) {
  printf("Hello, world!\n");
  return TCL_OK;
}

int my_cmd2 (ClientData cdata, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[]) {
  printf("Hello, again!\n");
  return TCL_OK;
}

// The function name must matches the dynamic library name.
// With the first letter in capital form and a "_Init" postfix.
int My_cmd_Init (Tcl_Interp *interp) {

  if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL)
    return TCL_ERROR;

  Tcl_CreateObjCommand(interp, "my_cmd1", my_cmd1,
      (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL );

  Tcl_CreateObjCommand(interp, "my_cmd2", my_cmd2,
      (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL );

  return TCL_OK;
}

To compile this file into a dynamic library (in Mac OS X 10.8.2), run the following line:

gcc -Wall -shared -o my_cmd.dylib my_cmd.c \
  -undefined dynamic_lookup -rdynamic

The expected output is:

$ tclsh
% load ./my_cmd.dylib
dlsym(0x7fb5dbe02360, My_cmd_SafeInit): symbol not founddlsym(0x7fb5dbe02360, My_cmd_Unload): symbol not founddlsym(0x7fb5dbe02360, My_cmd_SafeUnload): symbol not found
% my_cmd1
Hello, world!
% my_cmd2
Hello, again!
% exit

The "symbol not found" error can be safely ignore for this simple example.

Interface between C and TCL : Case II

C main function create an interactive TCL shell. The program terminates after the TCL shell is terminated and will not return control to C.

The C main function is listed below.

#include <stdio.h>
#include <tcl.h>

// A dummy initialisation.
int Tcl_AppInit(Tcl_Interp *interp) { return 0; }

int main(int argc, char *argv[]) {

  printf("In C start\n");
  Tcl_Main(argc, argv, Tcl_AppInit);
  printf("In C end\n");   // This line will never be executed.

  return 0;
}

The program is compiled by:

gcc -Wall -o main main.c -ltcl

The expected output is:

$ ./main
In C start
% puts "hello"
hello
% exit

Interface between C and TCL : Case I

C main program calls TCL interpreter to run an external TCL script. C and TCL communicate through variable values. TCL can call C function.

The C main program is listed below.

#include <stdio.h>
#include <tcl.h>

// a function to be called from within the TCL script
int my_func (ClientData data, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[]);

int main(void) {

  Tcl_Interp *interp = NULL;

  interp = Tcl_CreateInterp();
  if (interp)
    printf("In C: TCL interpretor started.\n");

  Tcl_CreateObjCommand(interp, "my_cmd", my_func, 
      (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL );

  printf("In C: TCL script begin.\n");
  if (Tcl_EvalFile(interp, "simple.tcl") == TCL_OK)
    printf("In C: TCL script end.\n");

  printf("C says: Hello, %s!\n", Tcl_GetVar(interp, "name", 0));

  Tcl_DeleteInterp(interp);
  if (Tcl_InterpDeleted(interp))
    printf("In C: TCL interpretor stopped.\n");

  return 0;

}


int my_func (ClientData data, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[]) {
  int i;

  printf("In C: my_func() started.\n");
  printf("  obj[0] = %s.\n", Tcl_GetString(objv[0]));
  printf("  obj[1] = %s.\n", Tcl_GetString(objv[1]));
  if (Tcl_GetIntFromObj(interp, objv[2], &i) == TCL_OK)
    printf("  obj[2] = %d.\n", i);
  else
    printf("  obj[2] is not a integer!\n");
  printf("  obj[3] = %s.\n", Tcl_GetString(objv[3]));
  printf("In C: my_func() ended.\n");

  return TCL_OK;
}

The TCL script is listed below.

#!/usr/bin/tclsh

puts "Tcl calls: my_cmd (implemented as a C function)"
my_cmd abc +123 xyz

puts "----"

puts "Tcl asks: What is your name? "
gets stdin name

The program is compiled by (in Mac OS X 10.8.2) :

gcc -Wall -o main main.c -ltcl

The expected output is (in Mac OS X 10.8.2) :

$ ./main 
In C: TCL interpretor started.
In C: TCL script begin.
Tcl calls: my_cmd (implemented as a C function)
In C: my_func() started.
  obj[0] = my_cmd.
  obj[1] = abc.
  obj[2] = 123.
  obj[3] = xyz.
In C: my_func() ended.
----
Tcl asks: What is your name? 
Brittle
In C: TCL script end.
C says: Hello, Brittle!
In C: TCL interpretor stopped.