TCL: call TCL package while embedded in C - c

From this question I learnt that we can embed TCL in C as easy as the folloiwng
#include <stdio.h>
#include <tcl.h>
void main ()
{
Tcl_Interp *myinterp;
char *action = "set a [expr 5 * 8]; puts $a";
int status;
printf ("Your Program will run ... \n");
myinterp = Tcl_CreateInterp();
status = Tcl_Eval(myinterp,action);
printf ("Your Program has completed\n");
getch();
}
And to compile it, we need to define path to the tcl libraries:
gcc -o test.exe test.c -Ic:/tcl/include /mingw64/bin/tcl86.dll
My question is: If my tcl script is calling another package (for example: package require Img), How to include this package (for example "Img") in the created test.exe.
I am using mingw64 on windows to compile my C code, but when running the resulted test.exe, it gives me TCL error that {can't find package Img while executing "package require Img"}
BTW, I have Img is installed in and when I run my TCL script using tclsh, I have no errors.

You should extend the list in the global auto_path variable with the path to the location (i.e., the directory) of the extra libraries you want to be able to access.
Tcl_SetVar(interp, "::auto_path", "/path/to/directory", TCL_APPEND_VALUE | TCL_LIST_ELEMENT);
Do this after you create the interpreter but before you evaluate any scripts in it. This is safe against characters like spaces in the pathname. On Windows, you can use \ as a separator if you prefer. If you have multiple locations, put several calls to Tcl_SetVar() in. (How you work out the correct directory or directories is up to you; the value gets copied immediately.)

Related

Compile and Link to .com file with Turbo C

I'm trying to compile and link a simple program to a DOS .com file using Turbo C compiler and linker. By that I try the simplest C-program I can think of.
void main()
{}
Are there command line arguments to link to com files in the Turbo C Linker?
The Error Message I get from the Linker is the following:
"Fatal: Cannot generate COM file: invalid entry point address"
I know that com files need entry point to be at 100h. Does Turbo C have an option to set this address?
It has been a long time since I have genuinely tried to use Turbo-C for this kind of thing. If you are compiling and linking on the command line separately with TCC.EXE and TLINK.EXE then this may work for you.
To compile and link to a COM file you can do this for each one of your C source files creating an OBJ file for each:
tcc -IF:\TURBOC3\INCLUDE -c -mt file1.c
tcc -IF:\TURBOC3\INCLUDE -c -mt file2.c
tcc -IF:\TURBOC3\INCLUDE -c -mt file3.c
tlink -t -LF:\TURBOC3\LIB c0t.obj file1.obj file2.obj file3.obj,myprog.com,myprog.map,cs.lib
Each C file is compiled individually using -mt (tiny memory model) to a corresponding OBJ file. The -I option specifies the path of the INCLUDE directory in your environment (change accordingly). The -c option tell TCC to compile to a OBJ file only.
When linking -t tells the linker to generate a COM program (and not an EXE), -LF:\TURBOC3\LIB is the path to the library directory in your environment (change accordingly). C0T.OBJ is the C runtime file for the tiny memory model. This includes the main entry point that you are missing. You then list all the other OBJ files separated by a space. After the first comma is the output file name. If using -t option name the program with a COM extension. After the second comma is the MAP file name (you can leave the file name blank if you don't want a MAP file). After the third comma is the list of libraries separated by spaces. With the tiny model you want to use the small model libraries. The C library for the small memory model is called CS.LIB .
As an example if we have a single source file called TEST.C that looks like:
#include<stdio.h>
int main()
{
printf("Hello, world!\n");
return 0;
}
If we want to compile and link this the commands would be:
tcc -IF:\TURBOC3\INCLUDE -c -mt test.c
tlink -t -LF:\TURBOC3\LIB c0t.obj test.obj,test.com,test.map,cs.lib
You will have to use the paths for your own environment. These commands should produce a program called TEST.COM. When run it should print:
Hello, world!
You can generate COM file while still using IDE to generate EXE. Following worked on TC 2.01. Change memory model to Tiny in the options, then compile the program and generate EXE file, then go to command prompt, and run EXE2BIN PROG.EXE PROG.COM. Replace PROG with your program name.
Your problem is about "entry point"
some compiler or linker can recognize void main() like entry point omiting a return value but no all of them.
You shoud use int main() entry point instead for better control of app and compiler can recognize main function as entry point
example:
int main() {
/* some compiler return 0 when you don't for main,
they can ask for return value */
}
from geekforgeeks:
A conforming implementation may provide more versions of main(), but they must all have return type int. The int returned by main() is a way for a program to return a value to “the system” that invokes it. On systems that doesn’t provide such a facility the return value is ignored, but that doesn’t make “void main()” legal C++ or legal C. Even if your compiler accepts “void main()” avoid it, or risk being considered ignorant by C and C++ programmers.
In C++, main() need not contain an explicit return statement. In that case, the value returned is 0, meaning successful execution.
source: https://www.geeksforgeeks.org/fine-write-void-main-cc/

Tcl-C - can't require specific package when running Tcl Interperter from C

I want to extend my C/C++ application with Tcl.
I wrote the following code that should create Tcl Interpreter but I want it to load user package that is located in ./user/tcl8.6.6/lib .
The problem is that my program exists without loading anything.
So after I Debug the program I saw that the result in the interpreter after running the line that should require my package is: *myinterp = {result = 0x647d58 "can't find package user", freeProc = 0x0, errorLine = 1}
I assume that the Tcl Interperter that is invoked by this line is located somewhere that doesn't have this package under it's lib subfolder.
So my question is how can I see what is the path to the shell that is invoked (maybe it is something related to TCL_LIBRARY or other environment variables?) and how can I update it or add to it the location to the folder contains my user package from my C code??
This is the code I wrote:
int main() {
Tcl_Interp *myinterp;
int status;
printf ("Your Program will run ... \n");
myinterp = Tcl_CreateInterp();
std::string test = "package require user";
status = Tcl_Eval(myinterp,test.c_str());
std::string test = "set a 5";
status = Tcl_Eval(myinterp,test.c_str());
printf ("Your Program has completed\n");
return 0;
}
Tcl initialises where to load packages from from the TCLLIBPATH environment variable, which if defined must contain a Tcl list of directories to search for pkgIndex.tcl files (which define how to load the package implementations). However, within your application if you know where the library package is, you should instead append that location to the list in the auto_path global variable:
std::string libLocation = "/some/directory/I/know";
Tcl_SetVar(interp, "auto_path", libLocation.c_str(),
TCL_GLOBAL_ONLY | TCL_APPEND_VALUE | TCL_LIST_ELEMENT);
Do that after creating the interpreter but before evaluating your script.
Don't set TCL_LIBRARY if you can avoid it. It defines where Tcl gets its internal library scripts from. I guess you might need to set it if you're embedding Tcl and aren't installing it separately, but that's not where non-built-in packages are to be found.

extract library version from binary with CMake

I am writing a FindXXX.cmake script for an external C library. I would like my script to provide information about the library version. However, the library only provides this information in the form of a function that returns the version number as a string.
I thought I could extract the version number by having FindXXX.cmake compile the following C program on the fly:
#include <stdio.h>
#include "library.h"
int main() {
char version[256];
get_version(version);
puts(version);
return 0;
}
In order for this to work, CMake should compile and run the program above at configure time, and use the information it prints as the version identifier. I know how to do the latter (execute_process), and I almost know how to do the former: CheckCSourceRuns comes to mind, but I do not know how to capture the stdout of the generated executable.
TL;DR: is there a way to compile a program, run it and capture its stdout from CMake at generation time?
You may use try_run for that purpose (it is assumed that your source file is named as foo_get_version.c):
try_run(foo_run_result foo_compile_result
foo_try_run ${CMAKE_CURRENT_LIST_DIR}/foo_get_version.c
RUN_OUTPUT_VARIABLE foo_run_output)
if(NOT foo_compile_result)
# ... Failed to compile
endif()
if(NOT foo_run_result EQUAL "0")
# ... Failed to run
endif()
# Now 'foo_run_output' variable contains output of your program.
Note, that try_run isn't executed when cross-compiling. Instead, CMake expects that the user will set cache variables foo_run_result and foo_run_result__TRYRUN_OUTPUT.

Tcl interpreter undefined reference error while compiling with gcc

I am new to Tcl scripting and would like to use C to embed Tcl codes.
This is the code that I have copied from a website to test the Tcl-C working.
test.c
#include <stdio.h>
#include <tcl.h>
void main ()
{
Tcl_Interp *myinterp;
char *action = "set a [expr 5 * 8]; puts $a";
int status;
printf ("Your Program will run ... \n");
myinterp = Tcl_CreateInterp();
status = Tcl_Eval(myinterp,action);
printf ("Your Program has completed\n");
getch();
}
I am using MinGW to compile this file.
I have copied the contents of the C:\Tcl\include folder into the C:\MinGW\include folder as well.
My gcc command for compiling :
gcc -o test.exe test.c
The error message shown :
C:\Users\user\AppData\Local\Temp\ccEHJKCb.o:tcl_connection_test.c:(.text+0x23): undefined reference to `_imp__Tcl_CreateInterp'
C:\Users\user\AppData\Local\Temp\ccEHJKCb.o:tcl_connection_test.c:(.text+0x3d): undefined reference to `_imp__Tcl_Eval'
c:/mingw/bin/../lib/gcc/mingw32/4.8.1/../../../../mingw32/bin/ld.exe: C:\Users\user\AppData\Local\Temp\ccEHJKCb.o: bad reloc address 0x20 in section `.eh_frame'
c:/mingw/bin/../lib/gcc/mingw32/4.8.1/../../../../mingw32/bin/ld.exe: final link failed: Invalid operation
collect2.exe: error: ld returned 1 exit status
I don't seem to have any libtcl file in the Tcl folder.
The Tcl version is ActiveTcl 8.5.15.0.297577.
Any help would be really appreciated.
Your example how to embed Tcl is outdated, and you are missing certain things in your link line (-ltcl85 for example). If you simply add -ltcl85 to your link line it should start to work.
It does not work in your case, because you installed the x64 (64-Bit version) of ActiveTcl, which provides x64 dlls, not 32-Bit ones. But the standard mingw gcc only works with 32-Bit libraries.
So to get this to work:
Download the 32-Bit ActiveTcl distribution
Compile your code with gcc -o test.exe test.c -Lc:/tcl/lib -Ic:/tcl/include -ltcl86
Adjust your path so the c:\tcl\bin\tcl86.dll is found in PATH, make also sure Tcl finds its libdir (set TCL_LIBRARY=c:\tcl\lib\tcl8.6)
run your program
But for more complex examples, you still need to initialise the library and a do some boilerplate code, so please call Tcl_FindExecutable(argv[0]); before the call to Tcl_CreateInterp() otherwise a few commands (e.g. clock might just not work as expected).
Have a look at http://www.tcl.tk/cgi-bin/tct/tip/66.html for some more details. Also have a look at the Tcl source distribution and the source for the tclsh shell.
You're very close to getting it right.
The Tcler's Wiki has a few examples, some of which are very confusing to be frank, but this one from this page is the best I've spotted recently. (The comments are mine.)
#include <stdlib.h>
#include <tcl.h>
int main (int argc, char *argv[]) {
Tcl_Interp *interp;
const char *script = "proc p1 a { puts $a }";
// Initialize the Tcl library; ***STRONGLY RECOMMENDED***
Tcl_FindExecutable(argv[0]);
// Create the interpreter, the execution context
interp = Tcl_CreateInterp();
// Initialise the interpreter
if (TCL_OK != Tcl_Init(interp)) {
fprintf(stderr, "Tcl_Init error: %s\n", Tcl_GetStringResult(interp));
exit(EXIT_FAILURE);
}
// Define a procedure
Tcl_Eval(interp, script);
fprintf(stderr, "res 1: %s\n", Tcl_GetStringResult(interp));
// Check if the procedure exists
Tcl_Eval(interp, "puts [info commands p*]");
fprintf(stderr, "res 2: %s\n", Tcl_GetStringResult(interp));
// Call the procedure
Tcl_Eval(interp, "p1 abc");
fprintf(stderr, "res 3: %s\n", Tcl_GetStringResult(interp));
// We could use Tcl_DeleteInterpreter to clean up here, but why bother?
return EXIT_SUCCESS;
}
What else were you missing? Simple. You forgot to tell the C compiler to use the Tcl library when building the executable; the compiler (or, more strictly, the linker) is in places a stupid piece of code. The exact option to use to get the linker to add the library in will depend on your system configuration, but is probably going to be -ltcl, -ltcl8.5 or -ltcl8.6; which it is depends on the filename and all sorts of things that we can't check exactly without being on your system. The names do fit a simple pattern though.
It's also possible that you might need to pass the -L option in to tell the linker about additional library locations. (There's an equivalent -I for telling the compiler where to find include files, so you don't have to copy everything into one gigantic unmanageable directory.)
The order of arguments can matter. Libraries should be listed after the source file:
gcc -o test.exe test.c -L/mingw/path/to/library/directory -ltcl86
(If you're using old, unsupported versions of Tcl — why would you do that?! — then the code above won't work because Tcl_Eval then took a writable string. But that was fixed many years ago and upgrading to a current version is the fix.)

run c program - stdio.h where do i get it?

Looking into learning C. As I understand it when I say #include <stdio.h> it grabs stdio.h from the default location...usually a directory inside your working directory called include. How do I actually get the file stdio.h? Do I need to download a bunch of .h files and move them from project to project inside the include directory? I did the following in a test.c file. I then ran make test and it outputted a binary. When I ran ./test I did not see hello print onto my screen. I thought I wasn't seeing output maybe because it doesn't find the stdio.h library. But then again if I remove the greater than or less than signs in stdio the compiler gives me an error. Any ideas?
I'm on a Mac running this from the command line. I am using: GNU Make 3.81. This program built for i386-apple-darwin10.0
#include <stdio.h>
main()
{
printf("hello");
}
Edit: I have updated my code to include a datatype for the main function and to return 0. I still get the same result...compiles without error and when I run the file ./test it doesn't print anything on screen.
#include <stdio.h>
int main()
{
printf("hello");
return 0;
}
Update:
If I add a \n inside of the printf it works! so this will work:
#include <stdio.h>
int main()
{
printf("hello\n");
return 0;
}
Your code should have preferably
printf("hello\n");
or
puts("hello");
If you want to know where does the standard header file <stdio.h> comes from, you could run your compiler with appropriate flags. If it is gcc, try compiling with
gcc -H -v -Wall hello.c -o hello
Pedantically, a standard header file is even not required to exist as a file; the standard permits an implementation which would process the #include <stdio.h> without accessing the file system (but e.g. by retrieving internal resources inside the compiler, or from a database...). Few compilers behave that way, most really access something in the file system.
If you didn't have the file, you'd get a compilation error.
My guess is the text was printed, but the console closed before you got the chance to see it.
Also, main returns an int, and you should return 0; to signal successful completion.
#include <header.h>, with angle brackets, searches in standard system locations, known to the compiler-- not in your project's subdirectories. In Unix systems (including your Mac, I believe), stdio.h is typically in /usr/include. If you use #include "header.h", you're searching subdirectories first and then the same places as with <header.h>.
But you don't need to find or copy the header to run your program. It is read at compilation time, so your ./test doesn't need it at all. Your program looks like it should have worked. Is it possible that you just typed "test", not "./test", and got the system command "test"? (Suggestion: Don't name your programs "test".)
Just going to leave this here : STILL! in 2018, December... Linux Mint 18.3
has no support for C development.
innocent / # cc ThoseSorts.c
ThoseSorts.c:1:19: fatal error: stdio.h: No such file or directory
compilation terminated.
innocent / # gcc ThoseSorts.c
ThoseSorts.c:1:19: fatal error: stdio.h: No such file or directory
compilation terminated.
innocent / # apt show libc6
(Abbreviated)::
Package: libc6
Version: 2.23-0ubuntu10
Priority: required
Section: libs
Source: glibc
Origin: Ubuntu
Installed-Size: 11.2 MB
Depends: libgcc1
Homepage: http://www.gnu.org/software/libc/libc.html
Description: GNU C Library: Shared libraries
Contains the standard libraries that are used by nearly all programs on
the system. This package includes shared versions of the standard C library
and the standard math library, as well as many others.
innocent / # apt-get install libc6-dev libc-dev
So, magic... and a minute later they are all installed on the
computer and then things work as they should.
Not all distros bundle up all the C support libs in each ISO.
Hunh.
hardlyinnocent / # gcc ThoseSorts.c
hardlyinnocent / # ./a.out
20
18
17
16
... ... ...

Resources