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);
Related
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.
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'
How should I set my_printf, so it would do what printf("%p") does + without using printf.
void my_printf(char * format, ...)
{
va_list ap;
va_start(ap, format);
if(!strcmp(format,"%p"))
{
void *address= va_arg (ap, void*);
char *arr=malloc(sizeof(address));
arr=address;
arr[strlen(arr)]='\0';
write(1,arr,strlen(arr));
}
va_end (ap);
//it has to print an address in hexadecimal format.
}
In :
char *arr=malloc(sizeof(address));
arr=address;
the allocated block is lost, this is a memory leak
To do :
arr[strlen(arr)]='\0';
has no sense, if you are able to use strlen that means the null character is already present.
But it is not sure at all you have a string so you cannot use strlen on that pointer.
You do not know if you can modify it for a lot of reasons, and in fact it is useless to do that.
In
write(1,arr,strlen(arr));
again supposes you have a string, which can be wrong, and your goal is not to write the contain of the pointed value but its address.
A way to do is :
#include <stdint.h>
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h> /* for malloc use in main */
void my_printf(char * format, ...)
{
va_list ap;
va_start(ap, format);
if (!strcmp(format,"%p"))
{
void * address= va_arg(ap, void*);
uintptr_t u = (uintptr_t) address;
if (u == 0)
fputs("(nil)", stdout);
else {
char s[2 * ((sizeof(u) * CHAR_BIT + 7) / 8) + 3];
int i = sizeof(s) - 1;
s[i] = 0;
do {
s[--i] = ((u & 0xf) < 10) ? ('0' + (u & 0xf)) : ('a' + (u & 0xf) - 10);
u >>= 4;
} while(u);
s[--i] = 'x'; /* can also putchar('0') */
s[--i] = '0'; /* can also putchar('x') */
fputs(s+i, stdout);
}
/* check */
printf("\n%p\n", address);
}
va_end (ap);
}
int main()
{
void * p = malloc(1);
my_printf("%p", 0);
my_printf("%p", &main);
my_printf("%p", &p);
my_printf("%p", p);
free(p);
return 0;
}
Compilation and execution :
pi#raspberrypi:/tmp $ gcc -Wall p.c
pi#raspberrypi:/tmp $ ./a.out
(nil)
(nil)
0x106b8
0x106b8
0xbec2d26c
0xbec2d26c
0x10b1558
0x10b1558
pi#raspberrypi:/tmp $
Note your printf is very limited and only manage the simple format %p
This function should return a string that contains string representations of the second and subsequent arguments, each to two decimal places and separated by commas. The first argument is a count of the number of arguments that follow. I did some research and found out about va_list and tried to work with it. Unfortunately the program is crashing and I have no idea why. I'm posting this hoping that someone could maybe spot an obvious mistake or something like that.
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
char *to_string(int count,...)
{
int i,x,k=0;
float tmp;
va_list valist;
va_start(valist, count+1);
char comma=',';
char buffer[count*6];
for(i=0;i<count;i++)
{
tmp=va_arg(valist, double)*100;
x=tmp/100.0;
k+=4;
snprintf(buffer + k, "%C",comma);
k++;
snprintf(buffer + 1, "%.2f", x);
}
va_end(valist);
printf("%s", buffer);
return buffer;
}
int main()
{
to_string(2,3.14876,6.123243);
}
As noted in the comments, you need to read the manual pages for va_start() and snprintf() in particular, and also pay attention to memory management (you cannot safely return a pointer to a local (non-static) variable).
Here's a revamped piece of code that cleans up the issues identified and that doesn't crash (for me on my Mac running macOS Sierra 10.12.1 and GCC 6.2.0).
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
static char *to_string(char *buffer, size_t buflen, int count, ...)
{
va_list valist;
va_start(valist, count);
char *data = buffer;
for (int i = 0; i < count; i++)
{
int x = va_arg(valist, double) * 100;
double tmp = x / 100.0 + 0.005;
int n = snprintf(data, buflen, ",%.2f", tmp);
if (n > (int)(buflen - 1))
{
fprintf(stderr, "%s: buffer not big enough\n", __func__);
break;
}
data += n;
buflen -= n;
}
va_end(valist);
printf("%s: [%s]\n", __func__, buffer);
return buffer;
}
int main(void)
{
char buffer[80];
printf("main: [%s]\n", to_string(buffer, sizeof(buffer), 2, 3.14876, 6.123243));
return 0;
}
Example output:
to_string: [,3.15,6.12]
main: [,3.15,6.12]
Note that there's the leading comma which you probably don't want. That can be fixed like this:
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
static char *to_string(char *buffer, size_t buflen, int count, ...)
{
va_list valist;
va_start(valist, count);
char *data = buffer;
const char *pad = "";
for (int i = 0; i < count; i++)
{
int x = va_arg(valist, double) * 100;
double tmp = x / 100.0 + 0.005;
int n = snprintf(data, buflen, "%s%.2f", pad, tmp);
if (n > (int)(buflen - 1))
{
fprintf(stderr, "%s: buffer not big enough\n", __func__);
break;
}
pad = ",";
data += n;
buflen -= n;
}
va_end(valist);
printf("%s: [%s]\n", __func__, buffer);
return buffer;
}
int main(void)
{
char buffer[80];
printf("main: [%s]\n", to_string(buffer, sizeof(buffer), 2, 3.14876, 6.123243));
printf("main: [%s]\n", to_string(buffer, sizeof(buffer), 20,
6.67, 0.04, 8.81, 8.49, 3.50, 1.20, 4.28, 0.67, 1.93, 5.63,
5.30, 8.43, 1.99, 4.62, 5.54, 7.21, 9.43, 2.02, 4.77, 0.29));
return 0;
}
Example output:
to_string: buffer not big enough
to_string: [3.15,6.12]
main: [3.15,6.12]
to_string: [6.67,0.04,8.82,8.50,3.50,1.20,4.29,0.68,1.93,5.63,5.30,8.44,1.99,4.62,5.54,7.21]
main: [6.67,0.04,8.82,8.50,3.50,1.20,4.29,0.68,1.93,5.63,5.30,8.44,1.99,4.62,5.54,7.21]
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);