0

Is there a clean way to open a file like this without system calls:

ID*_LogConfig.csv

I tried the following but it didn't worked.

/*Read setup file*/
getcwd(cwd, sizeof(cwd));
snprintf(source, sizeof(source),"%s/ID%*d_LogConfig.csv",cwd);
if( NULL == (input = fopen(source,"r")))
{
    snprintf(errbuffer,sizeof(errbuffer), "Could not open file %s - check existence/rights\n", source);
    exitHandler(1, errbuffer);
}

It outputs:

/mnt/dataflash/canfilter.d/ID*_LogConfig.csv not found

But with e.g. cat /mnt/dataflash/canfilter.d/ID*_LogConfig.csv it shows the file content.

My compromise solution would be a system call ll ID*_LogConfig.csv and using the output as filename.

Payoff
  • 19
  • 5
  • `ll` is not a system call. It's a program invoked by the shell. It's also the shell the preforms glob substitution. – StoryTeller - Unslander Monica Mar 16 '17 at 12:31
  • 1
    Does not `%*d` expect a width and `int` argument? Try `snprintf(source, sizeof(source),"%s/%s", ,cwd, "ID*_LogConfig.csv");` – chux - Reinstate Monica Mar 16 '17 at 12:34
  • What is `cwd`? what is `source`? what is `errbuffer` ? please add the definitions for these to your question. – wildplasser Mar 16 '17 at 12:43
  • Is the file actually named `ID*_LogConfig.csv` If it is, you spelled `ID%*d_LogConfig.csv` wrong in your snprintf call, it should be just `ID*_LogConfig.csv` . But perhaps you mean that the asterisk expands to something else, perhaps a number or a date/timestamp? This is extremely important to your question. Please write `ls -l /mnt/dataflash/canfilter.d/ID*_LogConfig.csv` so we can know exactly what your file(s) are named. – nos Mar 16 '17 at 13:31

3 Answers3

2

This line

snprintf(source, sizeof(source),"%s/ID%*d_LogConfig.csv",cwd);

likely does not produce what you think it does.

The %*d portion is an format specifier with a field-width, per the POSIX printf() documentation

A field width, or precision, or both, may be indicated by an ( '*' ). In this case an argument of type int supplies the field width or precision. Applications shall ensure that arguments specifying field width, or precision, or both appear in that order before the argument, if any, to be converted. A negative field width is taken as a '-' flag followed by a positive field width. A negative precision is taken as if the precision were omitted. In format strings containing the "%n$" form of a conversion specification, a field width or precision may be indicated by the sequence "*m$", where m is a decimal integer in the range [1,{NL_ARGMAX}] giving the position in the argument list (after the format argument) of an integer argument containing the field width or precision, for example:

printf("%1$d:%2$.*3$d:%4$.*3$d\n", hour, min, precision, sec);

So, this line

snprintf(source, sizeof(source),"%s/ID%*d_LogConfig.csv",cwd);

expects two more integer arguments to be passed. Since you don't pass them, you invoke undefined behavior.

See this answer: https://stackoverflow.com/a/19897395/4756299

Community
  • 1
  • 1
Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
0

Is there a clean way to open a file like this without system calls

No. fopen() makes use of a system call. You cannot 'open' a file without a system call.

If you're referring to the system(3) function, then you're probably in for some pain - it's best to avoid it if possible, from a performance, reliability and security point of view.

If you want to open 'all files that match the pattern', then look at glob(3), which is likely what your shell is using to handle such wildcards. You will need to iterate over each of the resulting paths, calling fopen(), fread() and fclose() on each.

Example usage of glob(3):

#include <stdio.h>
#include <glob.h>

void main(void) {
    int ret;
    int i;
    glob_t glob_info;

    ret = glob("*.csv", 0, NULL, &glob_info);
    if (ret != 0)
    {
        fprintf(stderr, "glob() failed: %d\n", ret);
        return;
    }

    for (i = 0; i < glob_info.gl_pathc; i++) {
        printf("%d: %s\n", i, glob_info.gl_pathv[i]);
    }

    globfree(&glob_info);
}

It is not really a good idea to open lots of files and treat the stream as a single 'thing' (as you are doing with your cat example).

As @Andrew has pointed out, you must be careful with your use of printf() format strings... You have provided the following: %s/ID%*d_LogConfig.csv. A % denotes the beginning of a format specifier, you have thus given the following:

  • %s - a char * (string) parameter follows
  • %*d - similar to %d, but the * means that the precision is provided as an int parameter, followed by the number itself.

For example:

printf(">%s< >%*d<\n", "Hello", 5, 3);

Will output: (note the 5 characters that the %d outputs)

>Hello< >    3<

If you are after a *, then just put a * in the format string. If you are after a %, then you need to escape the % but putting %% in the format string.

Attie
  • 6,690
  • 2
  • 24
  • 34
-2

Ok I solved the "problem" by using the following: (Processing the output of ls -t and use the newest file as config-file)

/*Search for config-file*/
FILE  *file_config;
file_config = popen("ls -t ID*_LogConfig.csv","r");
if (file_config == NULL)  {
    exitHandler(1, "Error opening date pipe.");
}   
fgets(configfile, sizeof(configfile), file_config);
if (strlen(configfile) > 0)
    configfile[strlen(configfile)-1] = '\0';
else {
    exitHandler(1, "Could not find a ID*_LogConfig.csv\n");
}   
getcwd(cwd, sizeof(cwd));
snprintf(source, sizeof(source),"%s/%s",cwd,configfile);

/*Read setup file*/
if( NULL == (input = fopen(source,"r")))
{   
    snprintf(errbuffer,sizeof(errbuffer), "Could not open file |%s| - check existence/rights\n", source);
    exitHandler(1, errbuffer);
}   

It seems that this is the only simple way.

Thanks to all.

Payoff
  • 19
  • 5
  • `configfile[strlen(configfile)-1] = '\0';` causes UB when `configfile[0] == 0`. This is a rare thing by chance, yet can also be a hacker exploit. – chux - Reinstate Monica Mar 16 '17 at 13:11
  • This is not a good solution, and you also appear to have added a requirement **use the newest file as config-file**. – Attie Mar 16 '17 at 13:40