Cross Platform Data File Access - c

I want a distribute both a data file and a program coded in C which opens the data file and then closes it, and have it portable. The program source would look something like this:
#include <stdio.h>
int main(int argc, char** argv)
{
fclose(fopen("data.dat", "rb"));
return 0;
}
I'm also using Autotools:
$ ls -R
.:
configure.ac
dat
Makefile.am
src
./dat:
data.dat
Makefile.am
./src:
hello.c
Makefile.am
In Linux when installing software the files usually go into specific directories, e.g. hello would go in /usr/local/bin and data.dat would go in /usr/local/share, and the installer can adjust these directories. Therefore the program would have to be adapt to a change in the path of the data file, specifically the datadir variable.
#src/Makefile.am
AM_CPPFLAGS=-DDATADIR='"$(datadir)"'
...
.
//src/hello.c
...
fclose(fopen(DATADIR "/data.dat", "rb+"));
...
However in Windows, software is not installed this way, and all of the different files are usually installed into one directory. To do this, the bindir and datadir could be made to / when running configure, however that would make the fopen argument invalid.
Is there any way to adjust my setup so that the program would refer to the correct path without using #ifdefs?

You can set -DDATADIR='.' on Windows to get the desired behavior. You can use configure.ac to check whether you are compiling on Windows. Here is one way to do it, adapted from the GTK source code:
AC_CANONICAL_HOST
AC_MSG_CHECKING([for native Win32])
case "$host" in
*-*-mingw*)
os_win32=yes
;;
*)
os_win32=no
;;
esac
AC_MSG_RESULT([$os_win32])
if test "$os_win32" = "yes"; then
DATADEF='-DDATADIR=.'
else
DATADEF="-DDATADIR=$daatadir"
fi
AC_SUBST(DATADEF)
Then, add #DATADEF# to your myprogram_CPPFLAGS in Makefile.am.

Related

TCL: call TCL package while embedded in 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.)

Creating CLI apps in C

I am wondering how to make a CLI app in C that "constantly runs". What i mean by that us for example i want to just open terminal and type a keyword and a function in a program executes. Like what "ls" is.
When you type ls it lists contents of the current dir. Likewise i want to make a program that when compiled it executes a certant stuff given the keyword is invoken. I dont want to run the executable with ./example, but rather have the command always available.The compiler i use is gcc. I have read that object file needs to be created but i dont know how to use that.
Thanks
What i mean by that us for example i want to just open terminal and type a keyword and a function in a program executes.
Your program doesn't have to "constantly run" in order for you to be able to invoke it without specifying the path... you just have to make sure that the program is located in one of the directories in your PATH, or conversely, that your PATH environment variable includes the directory where your program is located.
So let's say you want to compile hello.c into a command called hello. Here's the code:
#include <stdio.h>
int main(void)
{
printf("Hello, world!\n");
return 0;
}
So you'd compile that like:
> gcc -o hello hello.c
Now you should have an executable file called hello in your current directory, and you can run it like:
> ./hello
But you just want to type hello anywhere, right? So, you can add the current directory to your PATH environment variable, e.g.:
> export PATH=$PATH:$PWD
That adds the value of PWD, an environment variable that contains the current directory, to PATH, which is an environment variable that contains a list of directories where the shell will look for executable programs.
If you want to make that change permanent, you'll need to modify one of the scripts that runs when you start up whatever shell you're using. That's a little beyond the scope of this answer, and there's plenty of advice about how to set up your PATH online, so I'll leave that to you.
Another option, instead of adding the directory that contains hello to your PATH, is to move hello to one of the directories already listed in PATH. You can see the full list by doing this:
> echo $PATH
You haven't said what OS you're using, but if it's anything Unix-like there's probably a /usr/local/bin listed in there. .../bin directories generally hold executable programs, and /usr/local is the directory sub-tree where local additions to the OS go. So you could put your program in /usr/local/bin, and then (assuming /usr/local/bin is in your PATH), hello would always be available.

Problems with linking a library with a c program in linux

I want to run serial commands from a Bealgebone to a 4Dsystems display. Therefore I copied the c library found here into a directory and created a test program main.c:
#include "Picaso_const4D.h"
#include "Picaso_Serial_4DLibrary.h"
int main(int argc,char *argv[])
{
OpenComm("/dev/ttyUSB0", B115200); // Matches with the display "Comms" rate
gfx_BGcolour(0xFFFF);
gfx_Cls();
gfx_CircleFilled(120,160,80,BLUE);
while (1) {}
}
Now when I do gcc -o main main.c its says
main.c:2:37: fatal error: Picaso_Serial_4DLibrary.h: No such file or
directory
So I try linking it:
gcc main.c -L. -lPICASO_SERIAL_4DLIBRARY
which gives me the same error. Then I tried to create a static library:
gcc -Wall -g -c -o PICASO_SERIAL_4DLIBRARY PICASO_SERIAL_4DLIBRARY.C
which gives me this:
PICASO_SERIAL_4DLIBRARY.C:1:21: fatal error: windows.h: No such file
or directory compilation terminated.
What am I doing wrong? the git page clearly says this library is created for people who do not run windows.
Thanks in advance!
You're not getting a linker error; you're getting a preprocessor error. Specifically, your preprocessor can't find Picaso_Serial_4DLibrary.h. Make sure that it's in your include path; you can add directories to your include path using the -I argument to gcc.
You've had two problems. First was the picaso_whatever.h file that couldn't be found. You fixed that with the -I you added. But, now, the picaso.h wants windows.h
What are you building on? WinX or BSD/Linux?
If you're compiling on WinX, you need to install the "platform sdk" for visual studio.
If you're using mingw or cygwin, you need to do something else.
If on WinX, cd to the C: directory. Do find . -type f -name windows.h and add a -I for the containing directory.
If under Linux, repeat the find at the source tree top level. Otherwise, there is probably some compatibility cross-build library that you need to install.
Or, you'll have to find WinX that has it as Picaso clearly includes it. You could try commenting out one or more of the #include's for it and see if things are better or worse.
If you can't find a real one, create an empty windows.h and add -I to it and see how bad [or good] things are.
You may need the mingw cross-compiler. See https://forums.wxwidgets.org/viewtopic.php?t=7729
UPDATE:
Okay ... Wow ... You are on the right track and close, but this is, IMO, ugly WinX stuff.
The primary need of Picaso is getting a serial comm port connection, so the need from within windows.h is [thankfully] minimal. It needs basic boilerplate definitions for WORD, DWORD, etc.
mingw or cygwin will provide their own copies of windows.h. These are "clean room" reimplementations, so no copyright issues.
mingw is a collection of compile/build tools that let you use gcc/ld/make build utilities.
cygwin is more like: I'd like a complete shell-like environment similar to BSD/Linux. You get bash, ls, gcc, tar, and just about any GNU utility you want.
Caveat: I use cygwin, but have never used mingw. The mingw version of windows.h [and a suite of .h files that it includes underneath], being open source, can be reused by other projects (e.g. cygwin, wine).
Under Linux, wine (windows emulator) is a program/suite that attempts to allow you to run WinX binaries under Linux (e.g. wine mywinpgm).
I git cloned the Picaso library and after some fiddling, I was able to get it to compile after pointing it to wine's version of windows.h
Picaso's OpenComm is doing CreateFile [a win32 API call]. So, you'll probably need cygwin. You're opening /dev/ttyUSB0. /dev/* implies cygwin. But, /dev/ttyUSB0 is a Linux-like name. You may need some WinX-style name like "COM:" or whatever. Under the cygwin terminal [which gives you a bash prompt], do ls /dev and see what's available.
You can get cygwin from: http://cygwin.com/ If you have a 64 bit system, be sure to use the 64 bit version of the installer: setup-x86_64.exe It's semi-graphical and will want two directories, one for the "root" FS and one to store packages. On my system, I use C:\cygwin64 and C:\cygwin64_packages--YMMV.
Note that the installer won't install gcc by default. You can [graphically] select which packages to install. You may also need some "devel" packages. They have libraries and .h files that a non-developer wouldn't need. As, docs mention, you can rerun the installer as often as you need. You can add packages that you forgot to specify or even remove ones that you installed that you don't need anymore.
Remember that you'll need to adjust makefile -I and/or -L option appropriately. Also, when building the picaso library, gcc generated a ton of warnings about overflow of a "large integer". The code was doing:
#define control_code -279
unsigned char buf[2];
buf[0] = control_code >> 8;
buf[1] = control_code;
The code is okay, and the warning is correct [because the code is sloppy]. If the code had done:
#define control_code -279
unsigned char buf[2];
buf[0] = (unsigned) control_code >> 8;
buf[1] = (unsigned) control_code;
it probably would have been silent. Use -Wno-overflow in your Makefile to get rid of the warnings rather that edit 50 or so lines

Relative paths from binary file

I have the following folder structure:
bin/ <-binary-file is in here
include/
src/
data/
Makefile
In my code, I use relative paths to my data. So "../data/xml/xmlFile.xml". This is fine if I were executing the binary file from the bin/ folder:
brandonto#computer:~/PATH-TO-PROJECT/bin$ ./binary-file
argv[0] = ./binary-file
dirname(argv[0]) = .
But if I were executing the binary from the main folder (or any other folder that is not the bin/ folder):
brandonto#computer:~/PATH-TO-PROJECT$ bin/binary-file
argv[0] = bin/binary-file
dirname(argv[0]) = bin
The xml files would not be found because "../data" would now go up one directory from the main folder (or whatever folder you are in when executing the program).
How could I make it so that the binary file could be executed from any directory on my system?
To make the question a little more clear:
brandonto#brandonto-Aspire-S3-391:~/cpp-workspace/sdl-projects/sdl-space-shooter/bin$ ~/cpp-workspace/sdl-projects/sdl-space-shooter/bin/SpaceShooter
argv[0] = /home/brandonto/cpp-workspace/sdl-projects/sdl-space-shooter/bin/SpaceShooter
dirname(argv[0]) = /home/brandonto/cpp-workspace/sdl-projects/sdl-space-shooter/bin
brandonto#brandonto-Aspire-S3-391:~/cpp-workspace/sdl-projects/sdl-space-shooter/bin$ cd ..
brandonto#brandonto-Aspire-S3-391:~/cpp-workspace/sdl-projects/sdl-space-shooter$ ~/cpp-workspace/sdl-projects/sdl-space-shooter/bin/SpaceShooter
argv[0] = /home/brandonto/cpp-workspace/sdl-projects/sdl-space-shooter/bin/SpaceShooter
dirname(argv[0]) = /home/brandonto/cpp-workspace/sdl-projects/sdl-space-shooter/bin
Unable to load image ../data/graphics/background/darkPurple.png! SDL_image Error: Couldn't open ../data/graphics/background/darkPurple.png
Unable to load image ../data/graphics/sprites/meteorBrown_big1.png! SDL_image Error: Couldn't open ../data/graphics/sprites/meteorBrown_big1.png
Here, I executed the binary file once from inside the bin/ folder, then once from inside the main folder. The binary ran fine from inside the bin/ folder, but could not find the relative paths to the .png files from inside the main folder.
Probably you are asking a wrong question: the build system has nothing to do with program execution.
However, if you look for an answer, how to make my program to correctly use data, that is located relative to program installation, than here is an answer.
When you program main gets executed, it gets the binary path as the first parameter (index 0). That path can be relative or absolute, but in any case it allows you to find the base directory.
These are also useful links:
How do I find the location of the executable in C?
Finding current executable's path without /proc/self/exe
Here how you can use first argument:
#include <linux/limits.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>
int main(int argc, char *argv[])
{
char datadir[PATH_MAX];
strncpy(datadir, argv[0], sizeof(datadir));
dirname(datadir);
strncat(datadir, "/../data", sizeof(datadir));
printf("Data dir: %s\n", datadir);
return 0;
}
I believe that you can find your process id (pid) using the getpid command and perform functions to extract the directory in a manner similar to this question on Ask Ubuntu.
I would have the data associated in some way (organizationally) with the bin directory where the executable resides.
Then, when running the routine, if a complete path is provided (noted by checking arg[0]), then you can find the data directory. If a relative path is provided, then search the search path sequentially until you find the executable, and then you can therefore find the data directory.
No pids needed. (I think this is how Python finds its way, or at least how it used to do so.)
I usually solve this with a program setting. In the good old days I would have these settings in a .ini file which would accompany the executable. Some settings would be configurable from within the program, and all could be edited with a text editor. If the file was missing, or any setting missing, they would be created by default.
For the location of the program's data I would use its full absolute path name. For example it might be
Datapath = D:\os50k
and the program then appends individual file names to the path as necessary.
These days in Windows the System Registry is used for this purpose. However your question is tagged Linux which stores settings in various places, including the program directory.
This question, and this question describe the process more fully.
If your paths can be determined at build time, (i.e. your project will never need to be installed to another directory,) you can inject the path through the build system as a preprocessor definition. Here's an example with CMake:
file(TO_CMAKE_PATH "${PROJECT_BINARY_DIR}/resources" RESOURCE_DIR) # Normalize Windows/Linux paths
add_custom_command(
TARGET my_target POST_BUILD
COMMAND ${CMAKE_COMMAND} ARGS -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/resources ${RESOURCE_DIR}
)
target_compile_definitions(my_target PUBLIC RESOURCE_DIR=${RESOURCE_DIR})
.
#define VAL(x) #x
#define STR(x) VAL(x)
const char* my_resource = STR(RESOURCE_DIR) "/my_resource.abc";

Using zlib under windows mingw

I can't seem to get zlib to do anything on mingw under windows.
I downloaded zlib # http://sourceforge.net/projects/mingw/files_beta/MinGW/zlib/zlib-1.2.3-1-mingw32/ and put the header and lib files in the right place.
Simple code like:
#include <stdlib.h>
#include <stdio.h>
#include "zlib.h"
int main(int argc, char *argv[])
{
long a;
char buffer[1024];
a = 1024;
compress(buffer,&a,"testing",7);
return 0;
}
compiled:
gcc test.c -lzlib -Wall -o test.exe
Compiles fine.
However the exe crashes at the compress function.
Any ideas?
I recommend using MSYS2 for this kind of thing. These instructions assume you want to compile a 64-bit program, but they can easily be modified for 32-bit.
After installing MSYS2, run the "MinGW-w64 Win64 Shell" shortcut in your Start Menu. Install the 64-bit toolchain by running:
pacman -S mingw-w64-x86_64-toolchain mingw-w64-x86_64-zlib
Then compile your code by running something like this:
gcc test.c -lz -o test
I did not check your code carefully, but I was able to run your code without any crashing, so your code might be OK. Your code also gives no output so it's hard to tell if it really worked.
Looking at the zlib manual it says:
ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
const Bytef *source, uLong sourceLen));
Compresses the source buffer into the
destination buffer. sourceLen is the
byte length of the source buffer. Upon
entry, destLen is the total size of
the destination buffer, which must be
at least the value returned by
compressBound(sourceLen). Upon exit,
destLen is the actual size of the
compressed buffer.
Maybe a=1024 isn't big enough? I think you need to call compressBound to get a suitable value.
I tried to use the zlib from MSYS (accessible with mingw-get) and got the same problem as described below.
The solution is to do a static link instead of using the shared library.
Just remove or rename the import library libz.dll.a to avoid the linker to do a link with the msys-z.dll.
Recompile and it will be working.
Another way is to install zlib yourself from the zlib.net website.
Remove the one from mingw-get.
Using zlib in your code is extremely simple, something that the documentation ( or the various answers on stackoverflow I found ) don't make obvious.
The following technique works for any compiler and IDE. I tested it in windows mingw using code:blocks, which is why I am posting it as an answer to this question.
Download the zlib source code from http://www.zlib.net/
Copy all the .c and .h files from the root folder of the zlib source to a folder in your compiler search path.
Add the zlib source files to the IDE project.
Add #include "zlib.h" to your source code
Call compress or uncompress
That's it. It could hardly be simpler.
All you have to be careful about is memory management, since this is c code.
To make things even simpler for myself, I have put together a c++ wrapper which you are welcome to use, like this:
/** ZLIB C++ wrapper
Usage:
<pre>
#include "cZLIB.h"
{
// compress data in bigbuffer
raven::set::cZLIB ZLIB;
ZLIB.Compress( bigbuffer, sizebigbuffer );
// use compressed buffer, before ZLIB goes out of scope
use( ZLIB.Buffer(), ZLIB.Length() );
}
...
{
// decompress data in smallbuffer
raven::set::cZLIB ZLIB;
ZLIB.Inflate( smallbuffer, sizesmallbuffer )
// use decompressed data, before ZLIB goes out of scope
use( ZLIB.Buffer(), ZLIB.Length() );
}
</pre>
Build:
Download this code ( cZLIB.h and cZLIB.cpp ) from
https://github.com/JamesBremner/raven-set
and install somewhere in your compiler search path.
Let's assume you install it in folder .../src.
Download the zlib source code from http://www.zlib.net/
Copy all the .c and .h files from the root folder of the zlib source
to a new folder .../src/zlib
Add the files cZLIB.h, cZLIB.cpp and all the files in .../src/zlib
to the IDE project.
Build.
*/
class cZLIB
...

Resources