1

I'm currently trying to compile a crc calculator I took from github and am having difficulty compiling it on visual studio 2015. I get the error expected constant expression for the line:

char paths[strlen(src) + 1 + strlen(name) + 2 + 1];

Thoughts on how I can resolve the error?

static int create_source(char *src, char *name, FILE **head, FILE **code) {
    // for error return
    *head = NULL;
    *code = NULL;

    // create the src directory if it does not exist
    int ret = _mkdir(src, 0755);
    if (ret && errno != EEXIST)
        return 1;

    // construct the path for the source files, leaving suff pointing to the
    // position for the 'h' or 'c'.
    char paths[strlen(src) + 1 + strlen(name) + 2 + 1];
    char *suff = stpcpy(path, src);
    *suff++ = '/';
    suff = stpcpy(suff, name);
    *suff++ = '.';
    suff[1] = 0;

    // create header file
    *suff = 'h';
    *head = fopen(path, "wx");
    if (*head == NULL)
        return errno == EEXIST ? 2 : 1;

    // create code file
    *suff = 'c';
    *code = fopen(path, "wx");
    if (*code == NULL) {
        int err = errno;
        fclose(*head);
        *head = NULL;
        *suff = 'h';
        unlink(path);
        return err == EEXIST ? 2 : 1;
    }

    // all good -- return handles for header and code
    return 0;
}
Pablo
  • 13,271
  • 4
  • 39
  • 59
Sean Hui
  • 35
  • 5

2 Answers2

3

Your immediate problem is you are attempting to use a VLA (Variable Length Array), introduced into the standard with C99, with a compiler that does not support VLAs. Without VLA support, arrays must be declared with an integer constant (not simply a const int). As of C11, support for VLAs is optional.

To solve your immediate problem and provide portability, simply allocate storage for paths instead with malloc. The free the memory before you return from your function (either though an error return or on success)

You can do something like:

size_t pathlen = strlen(src) + 1 + strlen(name) + 2 + 1;
char *paths = malloc (pathlen);     /* allocate storage for paths */
if (!paths) {                       /* validate EVERY allocation */
    perror ("malloc-paths");
    return 3;   /* or however you want to handle the error */ 
}
char *suff = stpcpy(path, src);

...

*head = fopen(path, "wx");
if (*head == NULL) {
    free (path);                    /* free paths */
    return errno == EEXIST ? 2 : 1;
}

...

if (*code == NULL) {
    int err = errno;
    free (path);                    /* free paths */
    fclose(*head);
    *head = NULL;
    *suff = 'h';
    unlink(path);
    return err == EEXIST ? 2 : 1;
}

free (path);                        /* free paths */
return 0;

There is a small overhead for the allocation and free, but it is negligible in your case where there is a single allocation and single free.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • `stpcpy` is not in ISO C – M.M Feb 05 '20 at 03:13
  • also `unlink(path)` causes UB as path is not null-terminated (`*suff = 'h'` was the last write to it). Assuming `path` means `paths` of course. Replacing all this guff with a `snprintf` seems like it'd be a huge improvement – M.M Feb 05 '20 at 03:15
  • Good catches -- those were just copies -- but needed to be caught. `stpcpy` is POSIX (don't know if VS15 supports it.) [man 3 stpcpy](http://man7.org/linux/man-pages/man3/stpcpy.3.html) suggest `strcpy` instead. (or the `snprintf` as suggested) – David C. Rankin Feb 05 '20 at 04:46
  • If the compiler is C11-compliant and does not support VLA, it should announce the fact by defining `__STDC_NO_VLA__` or thereabouts. – Jonathan Leffler Feb 05 '20 at 06:14
  • Prior to VS2017, the only portions of the C99 and C11 standard incorporated were just to support C++11/14, [Is there any option to switch between C99 and C11 C standards in Visual Studio?](https://stackoverflow.com/questions/48981823/is-there-any-option-to-switch-between-c99-and-c11-c-standards-in-visual-studio) – David C. Rankin Feb 05 '20 at 07:30
0

As a simple alternative to David's solution, you could also use FILENAME_MAX ...

[...] which expands to an integer constant expression that is the size needed for an array of char large enough to hold the longest file name string that the implementation guarantees can be opened; (§7.21.1, ISO C11)

like this

char paths[FILENAME_MAX];

You might like to check that you don't overflow this size, though.

the busybee
  • 10,755
  • 3
  • 13
  • 30