3

I need to generate three different types of paths on runtime:

  1. /sys/class/gpio/gpio%d
  2. /sys/class/gpio/gpio%d/value
  3. /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

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
bodokaiser
  • 15,122
  • 22
  • 97
  • 140

4 Answers4

4

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
$
Aif
  • 11,015
  • 1
  • 30
  • 44
4

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);
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
2

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); 
Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
2

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 */
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278