What is the best way to generate a path in C? - c

I need to generate three different types of paths on runtime:
/sys/class/gpio/gpio%d
/sys/class/gpio/gpio%d/value
/sys/class/gpio/gpio%d/direction
Currently I generate these by doing the following:
#define GPIO_PATH_BASE "/sys/class/gpio/gpio"
#define GPIO_PATH_DIRECTION "/direction"
#define GPIO_PATH_VALUE "/value"
int open_gpio(const char * port) {
char * base_path = (char *) malloc(sizeof(GPIO_PATH_BASE) + sizeof(port));
strcpy(base_path, GPIO_PATH_BASE);
strcat(base_path, port);
char * value_path = (char *) malloc(sizeof(base_path) + sizeof(GPIO_PATH_VALUE));
strcpy(value_path, (const char *) base_path);
strcat(value_path, GPIO_PATH_VALUE);
char * dir_path = (char *) malloc(sizeof(base_path) + sizeof(GPIO_PATH_DIRECTION));
strcpy(dir_path, (const char *) base_path);
strcat(dir_path, GPIO_PATH_DIRECTION);
}
I am actually quite unhappy with this approach. Is there a possibility to let a macro this stuff for me or should I create a helper function?
Bodo

You can make a function that takes two parts, allocates the space, and concatenates them. This should reduce the code duplication, while keeping your code readable:
static char *concat(const char* prefix, const char* suffix) {
size_t len = strlen(prefix) + strlen(suffix) + 1;
char *res = malloc(len);
strcpy(res, prefix);
strcat(res, suffix);
return res;
}
Now you can use this function as follows:
char * base_path = concat(GPIO_PATH_BASE, port);
char * value_path = concat(base_path, GPIO_PATH_VALUE);
char * dir_path = concat(base_path, GPIO_PATH_DIRECTION);

I find sprintf (or snprintf as mentionned by #larsmans) more suitable for string manipulations, especially if you want to add decimal values in your string.
I would use PATH_MAX (defined in limits.h) to have a statically allocated buffer like:
#include <limits.h>
#include <stdio.h>
unsigned char path[PATH_MAX];
#define GPIO_PATH_BASE "/sys/class/gpio/gpio"
#define GPIO_PATH_VALUE "/value"
int main(void)
{
snprintf(path, PATH_MAX, "%s/%d%s", GPIO_PATH_BASE, 42, GPIO_PATH_VALUE);
puts(path);
return 0;
}
$ make main
cc main.c -o main
$ ./main
/sys/class/gpio/gpio/42/value
$

If your system supports it, then asprintf() is the easiest mechanism:
#define GPIO_PATH_BASE "/sys/class/gpio/gpio"
#define GPIO_PATH_DIRECTION "/direction"
#define GPIO_PATH_VALUE "/value"
int open_gpio(const char * port)
{
char *base_path = asprintf("%s%s", GPIO_PATH_BASE, port);
char *value_path = asprintf("%s%s", base_path, GPIO_PATH_VALUE);
char *dir_path = asprintf("%s%s", base_path, GPIO_PATH_DIRECTION);
//...do some work with these...or return them...
//...should check for failed allocations, too...
}
If your system doesn't support asprintf(), you can 'fake' it with:
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
/* Should be in a header */
#ifndef HAVE_ASPRINTF
extern int asprintf(char **ret, const char *format, ...);
extern int vasprintf(char **ret, const char *format, va_list args);
#endif
#ifndef HAVE_ASPRINTF
int vasprintf(char **ret, const char *format, va_list args)
{
va_list copy;
va_copy(copy, args);
/* Make sure return pointer is determinate */
*ret = 0;
int count = vsnprintf(NULL, 0, format, args);
if (count >= 0)
{
char* buffer = malloc(count + 1);
if (buffer != NULL)
{
count = vsnprintf(buffer, count + 1, format, copy);
if (count < 0)
{
free(buffer);
buffer = 0;
}
*ret = buffer;
}
}
va_end(copy);
return count;
}
int asprintf(char **ret, const char *format, ...)
{
va_list args;
va_start(args, format);
int count = vasprintf(ret, format, args);
va_end(args);
return(count);
}
#endif /* HAVE_ASPRINTF */

I quite like to use sprintf to generate strings like this. But that assumes that you have a reasonable maximum size that you know won't be exceeded.
Of course, that's at least a little better than sizeof(base_path), which is completely wrong.
Using malloc also seems like a bad idea for variables that aren't being visible outside the module.
If we assume that port is the correct string, something like this:
char base_path[100];
sprintf(base_path, "%s/%s", GPIO_PATH_BASE, port);

Related

undefined reference to `concatf'

I have gone through similar questions and seen answers given, where given and none of those points me to what I am doing wrong. I keep getting undefined reference to 'concatf' while the function is properly defined in the header file.
concatf.c
#include "concatf.h"
/*
* vscprintf:
* MSVC implements this as _vscprintf, thus we just 'symlink' it here
* GNU-C-compatible compilers do not implement this, thus we implement it here
*/
#ifdef _MSC_VER
#define vscprintf _vscprintf
#endif
#ifdef __GNUC__
int vscprintf(const char *format, va_list ap)
{
va_list ap_copy;
va_copy(ap_copy, ap);
int retval = vsnprintf(NULL, 0, format, ap_copy);
va_end(ap_copy);
return retval;
}
#endif
int vasprintf(char **strp, const char *format, va_list ap)
{
int len = vscprintf(format, ap);
if (len == -1)
return -1;
char *str = (char*)malloc((size_t) len + 1);
if (!str)
return -1;
int retval = vsnprintf(str, len + 1, format, ap);
if (retval == -1) {
free(str);
return -1;
}
*strp = str;
return retval;
}
int concatf(char **strp, const char *format, ...)
{
va_list ap;
va_start(ap, format);
int retval = vasprintf(strp, format, ap);
va_end(ap);
return retval;
}
concatf.h
#ifndef CONCATF_H
#define CONCATF_H
#if defined(__GNUC__) && ! defined(_GNU_SOURCE)
#define _GNU_SOURCE /* needed for (v)asprintf, affects '#include <stdio.h>' */
#endif
#include <stdio.h> /* needed for vsnprintf */
#include <stdlib.h> /* needed for malloc, free */
#include <stdarg.h> /* needed for va_* */
int concatf(char **strp, const char *format, ...);
#endif // CONCATF_H
build.c
#include "concatf.h"
#include <stdio.h>
#include <stdlib.h>
extern char _binary_script_php_start;
extern char _binary_script_php_end;
int main(int argc, char *argv[]) {
// EXTRACT OUR RESOURCE OBJECT INTO /tmp/test.php
char *p = &_binary_script_php_start;
FILE *fp = fopen("/tmp/test.php","wb");
while ( p != &_binary_script_php_end ) {
fputc(*p++,fp);
}
fclose(fp);
// NOW READ IN OUR STANDARD ARGUMENTS AND LAUNCH OUR COMMAND
int i = 1;
char *cmd = "php /tmp/test.php";
char *s = NULL;
// asprintf(&s, "%s",cmd);
// for(i = 1; i < argc; i++) {
// asprintf(&s, "%s \"%s\"",s,argv[i]);
// }
// concatf("%s",cmd);
// for(i = 1; i < argc; i++) {
// concatf("%s \"%s\"",s,argv[i]);
// }
concatf(&s, "%s",cmd);
for(i = 1; i < argc; i++) {
concatf(&s, "%s \"%s\"",s,argv[i]);
}
system(s);
free(s);
unlink("/tmp/test.php"); // comment me out for debugging if you want
}
These codes run on my environment but fail to run on the second windows environment and those of my colleagues, I can't seem to point why that is the case.
To run it you will need a php file named script.php run ld -r -b binary script.php data.o to complie your php file and gcc build.c data.o -o runme to link the php file with the build function. You can find the sample project here https://github.com/Sammiiie/C_php_http.

Can I know the file and function names from where my function is called if it's from another .c?

I'm developing a library and I would like to know some data about the caller of one of the functions I'm offering. In particular, I would need to know the file name, function name and line where my function (a redefined malloc) is being called.
EDIT: Here's a minimum working example where I can detect when a user calls malloc and "redirect" him to my own malloc function:
main.c:
#include <stdio.h>
#include <stdlib.h>
#include "myLib.h"
int main(){
printf("Inside main, asking for memory\n");
int *p = malloc(sizeof(int));
*p = 3;
free(p);
return 0;
}
myLib.c:
#include "myLib.h"
void * myAlloc (size_t size){
void * p = NULL;
fprintf(stderr, "Inside my own malloc\n");
p = (malloc)(size);
return p;
}
#undef malloc
#define malloc(size) myAlloc(size)
myLib.h:
#ifndef MYLIB_H
#define MYLIB_H
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define malloc(size) myAlloc(size)
void * myAlloc(size_t size);
#endif
I've tried using _FILE_ _func_ and _LINE_ keywords, but I can't make it work since it's in a different module.
You could:
//mylib.h
#ifndef MYLIB_H
#define MYLIB_H
#include <stdlib.h>
// replace malloc in case it's already a macro
#ifdef malloc
#undef malloc
#endif
// I believe that from the standards point of view, this is undefined behavior
#define malloc(size) my_alloc(size, __FILE__, __LINE__, __func__)
#ifdef __GNUC__
// Allow compiler to do static checking.
__attribute__((__alloc_size__(1), __malloc__))
#endif
void *my_alloc(size_t size, const char *file, int line, const char *func);
// ^^^^^^^^ I do not like camelCase case - one snake case to rule them all.
#endif
// mylib.c
#include "mylib.h" // do not ever mix uppercase and lowercase in filenames
#undef malloc // undef malloc so we don't call ourselves recursively
#include <stdio.h>
void *my_alloc(size_t size, const char *file, int line, const char *func){
fprintf(stderr, "Och my god, you wouldn't believe it!\n"
"A function %s in file %s at line %d called malloc!\n",
func, file, line);
return malloc(size);
}
You might also see how assert does it. If you are aiming at glibc, read glibc docs replacing malloc.
Still as you discovered a user may do (malloc)(size) cicumvent macro expansion. You could do:
void *my_alloc(size_t size, const char *file, int line, const char *func);
static inline void *MY_ALLOC(size_t size) {
return my_alloc(size, NULL, 0, NULL);
}
#define MY_ALLOC(size) my_alloc(size, __FILE__, __LINE__, __func__)
// if called with `malloc()` then MY_ALLOC is expanded
// if called as `(malloc)`, then just expands to MY_ALLOC.
#define malloc MY_ALLOC
int main() {
malloc(10); // calls my_alloc(10, "main.c", 62, "main");
(malloc)(20); // calls my_alloc(20, NULL, 0, NULL);
}
GLIBC defines hidden symbols for malloc(), free()... which are called __libc_malloc(), __libc_free()...
So, you can tremendously simplify your debug macros.
In m.h, just define the following:
#if DEBUG_LEVEL > 0
extern void *__libc_malloc (size_t bytes);
extern void *myMalloc(size_t size, const char *filename, const char *funcname, int line);
#define malloc(size) myMalloc(size, __FILE__, __FUNCTION__, __LINE__)
#endif
Then you can write a program defining myMalloc() as follow (e.g. file name is m.c):
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "m.h"
#if DEBUG_LEVEL > 0
void *myMalloc(
size_t size,
const char *filename,
const char *funcname,
int line
) {
fprintf(stderr, "malloc(%zu) called from %s/%s()#%d\n", size, filename, funcname, line);
return __libc_malloc(size);
}
#endif
char *dup_str(char *string) {
char *str = malloc(strlen(string) + 1);
strcpy(str, string);
return str;
}
int main(int ac, char *av[]) {
char *str;
if (av[1]) {
str = dup_str(av[1]);
} else {
str = dup_str("NULL");
}
printf("String = '%s'\n", str);
free(str);
return 0;
}
When you compile this example program in non debug mode:
$ gcc m.c -DDEBUG_LEVEL=0
$ ./a.out azerty
String = 'azerty'
When you compile your program in debug mode:
$ gcc m.c -DDEBUG_LEVEL=1
$ ./a.out azerty
malloc(7) called from m.c/dup_str()#27
String = 'azerty'

Is it possible to pass 2 functions which have different signature as an argument to another function?

Currently, I have the following 2 functions:
void write_to_file(FILE *fp)
{
fprintf(fp, "stuff here: %d", 10);
/* 1000s of similar lines below */
}
and
void write_to_string(char *str)
{
sprintf(str, "stuff here: %d", 10);
/* 1000s of similar lines below */
}
I'd like to poly morph it into a single function.
I'd thought about something like:
void write_somewhere(void *ptr, int to_file)
{
if (to_file) {
typedef fprintf myprintf;
} else {
typedef sprintf myprintf;
}
myprintf(ptr, "stuff here: %d", 10);
}
This doesn't work and looks ugly.
Since the signature of fprintf and sprintf are different and as follows,
int fprintf(FILE *stream, const char *format, …);
int sprintf(char *buffer, const char *format, …);
Is it possible to do something like,
void write_somewhere(void *ptr, void *func)
{
func(ptr, "stuff here: %d", 10);
}
EDIT:
Based on Alter's answer below, this is what I have but it doesn't quite work as expected and prints out garbage value when trying to print out values in write_somewhere() function:
#include <stdio.h>
#include <stdarg.h>
typedef int (*myprintf_t) (void *, const char *, ...);
int myfprintf(void *ptr, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = vfprintf(ptr, format, args);
va_end(args);
return ret;
}
int mysprintf(void *ptr, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = vsprintf(ptr, format, args);
va_end(args);
return ret;
}
void write_somewhere(void *ptr, myprintf_t myprintf, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = myprintf(ptr, format, args);
va_end(args);
return ret;
}
int main(void)
{
char s[100];
int i = 100;
/* This works */
write_somewhere(stdout, myprintf, "Hello world");
/* This prints out garbage */
write_somewhere(stdout, myprintf, "Hello world, I am %d", i);
write_somewhere(s, mysprintf);
return 0;
}
Jen‘s answer is the correct one, but in this case you can redirect ptr to v*printf using a pointer to function:
#include <stdio.h>
#include <stdarg.h>
int myfprintf(void *ptr, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = vfprintf(ptr, format, args);
va_end(args);
return ret;
}
int mysprintf(void *ptr, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = vsprintf(ptr, format, args);
va_end(args);
return ret;
}
void write_somewhere(void *ptr, int (*myprintf)(void *, const char *, ...))
{
myprintf(ptr, "stuff here");
}
int main(void)
{
char s[100];
write_somewhere(stdout, myfprintf);
write_somewhere(s, mysprintf);
return 0;
}
For your last edit:
It seems that you want to pass some extras parameters to write_somewhere, in this case I suggest:
#include <stdio.h>
#include <stdarg.h>
#define TO_FILE 0
#define TO_STRING 1
void write_somewhere(int where, void *ptr, const char *format, ...)
{
#define myprintf(ptr, ...) \
(where == TO_FILE ? vfprintf(ptr, __VA_ARGS__) : vsprintf(ptr, __VA_ARGS__))
va_list args;
va_start(args, format);
myprintf(ptr, format, args);
/* more stuff */
va_end(args);
#undef myprintf
}
int main(void)
{
char s[100];
write_somewhere(TO_FILE, stdout, "%u\n", 10);
write_somewhere(TO_STRING, s, "Hello");
printf("%s\n", s);
return 0;
}
The C language guarantees that all function pointers have the same representation. Your polymorphic functions simply needs to be prototyped as accepting any function pointer, say, a void (*funcptr)(void). Note that a ptr-to-void is not a function pointer (it's an object pointer) and may not be able to hold a function pointer.
Of course you can only call the function if you know which of the several types it is. So you need some way to discriminate, much like printf does by looking at the format. If you call a function with arguments not matching its prototype, the behavior is undefined.
Not an answer to your exact question, but instead of writing write_something() as you have, you could change the structure slightly:
void write_somewhere(void *ptr, int to_file)
{
if (to_file) {
fprintf( (FILE*) ptr, "stuff here");
} else {
sprintf( (char*) ptr, "stuff here");
}
}
However, for a strict answer to your question...
As you've found, the typedef line that you've attempted doesn't work. typedef is a compile time operation, not a runtime operation.
What you could do, though, is to define a type for a function pointer that matches both the fprintf() and sprintf() functions:
typedef int (*someprintf_ptr)(FILE *stream, const char *format, …);
The write_somewhere() would then look like:
void write_somewhere(void *ptr, someprintf_ptr func)
{
func(ptr, "stuff here");
}
/* with calls looking like... */
write_somewhere( (void *)a_file_ptr, (someprintf_ptr)(fprintf));
Your write_something function would have to be something like:
void write_something(void (*function)(), int to_file)
{
....
}
Try making myprintf a function
void write_somewhere(void *ptr, int to_file)
{
myprintf(to_file, ptr, "stuff here");
// do stuff
/* 1000s of similar lines below */
}
void myprintf( int to_file, void *ptr, char *output )
{
if (to_file)
fprintf( ptr, output );
else
sprintf( ptr, output );
}
Let me try:
struct target {
int (*tgtfunction)();
void* ptr;
}
struct target mktarget_file(FILE * fp) {
struct target tgt = { .tgtfuntion = vfprintf, .ptr = fp };
return tgt;
}
struct target mktarget_string(char * str) {
struct target tgt = { .tgtfuntion = vsprintf; .ptr = str };
return tgt;
}
void tgtprintf(struct target * target, char * fmt, ...) {
va_list ap;
va_start(ap, target);
int ret = target.tgtfunction(target.ptr, fmt, ap);
va_end(ap);
return ret;
}
void write_stuff(struct target * target)
{
tgtprintf(target, "stuff here");
/* 1000s of similar lines below */
}
should do what you want: create a struct target for your wanted target, and just call write_stuff to write your stuff there.
Be aware that the sprintf stuff might need to be refined, as each string is written to the same place instead of appended, and there is no check for free space. But the general concept could start like this.

vsprintf on OS X: EXC_BAD_ACCESS

I've discovered weird behavior of vsprintf on OSX.
This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#if defined(WIN32)
#include <windows.h>
#define VSNPRINTF _vsnprintf
#elif defined(LINUX) || defined (DARWIN)
#define VSNPRINTF vsnprintf
#include <sys/types.h>
#include <unistd.h>
#endif
char *f(const char *fmt, ...)
{
char *out = NULL;
const int step = 32;
int n = -1, lout = step;
va_list arg;
if(fmt!=NULL)
{
va_start(arg, fmt);
do
{
if(!out)
{
free(out);
out = NULL;
}
out = (char*)malloc(lout + 1);
if(!out) break;
memset(out, 0, lout + 1);
n = VSNPRINTF(out, lout, fmt, arg);
if(n == -1 || n + 1 > lout)
{
lout += step;
n = -1;
}
}while(n == -1);
va_end(arg);
}
return out;
}
int main()
{
char *msg = NULL;
unsigned long x = 0xDEADBEEF;
msg = f("%X%X%X%X", x, x, x, x);
if(!msg) return -1;
puts(msg);
return 0;
}
The function shall return allocated string (char*) containing formatted text. It works correctly on Linux and Windows. It returns badly formatted text on OSX and sometimes it leads to Segmentation fault (EXC_BAD_ACCESS). Btw, I know that I can use vasprintf.
What can be the problem?
Your problem is most likely that you're calling vsnprintf with the same va_list multiple times. This doesn't work in some ABIs.
Look up the man page of va_copy. The short version is to do something like this:
va_list c;
va_copy(c, arg);
n = VSNPRINTF(out, lout, fmt, c);
va_end(c);

Concatenation in C

I have a program which does concatenation.
its like char *testConc(int a,..)
Where a indicates number of arguments are being passed for concatenation.
As legth keeps on changing is there is anything like constructor overloading in C
or any simple syntax which implements the functionality
Yes, there are varadic functions
#include <stdio.h>
#include <stdarg.h>
/* print all non-negative args one at a time;
all args are assumed to be of int type */
void printargs(int arg1, ...)
{
va_list ap;
int i;
va_start(ap, arg1);
for (i = arg1; i >= 0; i = va_arg(ap, int))
printf("%d ", i);
va_end(ap);
putchar('\n');
}
int main(void)
{
printargs(5, 2, 14, 84, 97, 15, 24, 48, -1);
printargs(84, 51, -1);
printargs(-1);
printargs(1, -1);
return 0;
}
C does not have function overloading capabilities. The syntax you have is called a variadic function, which can be used to perform what you asked.
The textConc function would look something like this:
char *textConc(int argc, ...)
{
va_list args;
char *str = NULL;
size_t len = 0;
va_start(args, argc);
while (argc--)
{
/* next string */
const char *temp = va_arg(args, const char *);
size_t size = strlen(temp);
/* make room and copy over */
str = realloc(str, len+size+1);
memcpy(str+len, temp, size+1);
/* new length */
len += size;
}
va_end(args);
return str;
}
int main(int argc, char **argv)
{
char *example = textConc(4, "Hello", "All", "good", "morning");
puts(example);
free(example);
return 0;
}
If you use GCC, we can fake overloading completely, using a little help of macros.
Rename textConc to textConcN and use the following macros:
#define ARGCOUNT(...) (sizeof((const char *[]){__VA_ARGS__})/sizeof(const char *))
#define textConc(...) textConcN(ARGCOUNT(__VA_ARGS__), __VA_ARGS__)
int main(int argc, char **argv)
{
/* notice, no more need for the number of arguments */
char *example = textConc("Hello", "All", "good", "morning");
puts(example);
free(example);
return 0;
}
Functions can't be overloaded in C.
You could rewrite your function as char *testConc(const char *s, ...), where you mark the end of the list with NULL:
testConc("foo", "bar", "baz", "quux", (char *)0);
This makes adding changing the number of actual arguments easier. If you have a C99 compiler, you can even write a wrapping macro that adds the NULL for you:
#define TESTCONC(...) testConc(__VA_ARGS__, (char *)0)

Resources