2

I tried to compiler the following code(minimum example, see the edit for the whole code):

// a.c
#include <stdio.h>

#define _XOPEN_SOURCE
#include <unistd.h>

int main(int argc, char* argv[])
{
    puts((const char*) crypt("AAAA", "$6$2222"));
    return 0;
}

Using clang-7 -lcrypt a.c and it emitted the following warning:

minimum.c:8:24: warning: implicit declaration of function 'crypt' is invalid in C99 [-Wimplicit-function-declaration]
    puts((const char*) crypt("AAAA", "$6$2222"));
                       ^
minimum.c:8:10: warning: cast to 'const char *' from smaller integer type 'int' [-Wint-to-pointer-cast]
    puts((const char*) crypt("AAAA", "$6$2222"));
         ^
2 warnings generated.

But ./a.out did seem to work:

$6$2222$6GKY4KPtBqD9jAhwxIZGDqEShaBaw.pkyJxjvSlKmtygDXKQ2Q62CPY98MPIZbz2h6iMCgLTVEYplzp.naYLz1

I found out that if I remove #include <stdio.h> and puts like this:

// new_a.c
#define _XOPEN_SOURCE
#include <unistd.h>

int main(int argc, char* argv[])
{
    crypt("AAAA", "$6$2222");
    return 0;
}

Then there is no warnings.

How to fix these warnings without removing #include <stdio.h>?

Edit:

Whole program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define _X_OPEN_SOURCE
#include <unistd.h>

#include <assert.h>

void* Calloc(size_t cnt, size_t size)
{
    void *ret = calloc(cnt, size);
    assert(ret);
    return ret;
}

size_t GetSaltLen(const char *salt)
{
    size_t salt_len = strlen(salt);
    assert(salt_len >  0);
    assert(salt_len <= 16);
    return salt_len;
}

char* GetSaltAndVersion(const char version, const char *salt)
{
    size_t saltlen = GetSaltLen(salt);
    /*
     * The format of salt:
     *     $one_digit_number$up_to_16_character\0
     * For more info, check man crypt.
     */
    char *ret = (char*) Calloc(1 + 1 + 1 + saltlen + 1, sizeof(char));

    char *beg = ret;

    *beg++ = '$';
    *beg++ = version;
    *beg++ = '$';
    memcpy((void*) beg, (const void*) salt, saltlen + 1);

    return ret;
}

void crypt_and_print(const char *passwd, const char *salt_and_version)
{
    char *result = crypt(passwd, salt_and_version);
    assert(puts(result) != EOF);
}

int main(int argc, char* argv[])
{
    if (argc != 4) {
        fprintf(stderr, "argc = %d\n", argc);
        return 1;
    }

    char *salt_and_version = GetSaltAndVersion(argv[2][0], argv[3]);
    crypt_and_print(argv[1], salt_and_version);
    free(salt_and_version);

    return 0;
}

I have tried as @Andrey Akhmetov suggested and put the #define onto the first line, but the warnings did not disappear.

JiaHao Xu
  • 2,452
  • 16
  • 31
  • You should specify a value `#define _XOPEN_SOURCE 700` (for POSIX 2008, the current version) or 600 or 500 for older versions. Specifying no value leads to quasi-indeterminate behaviour; the headers might treat that as 0, and it isn't clear which version of POSIX (X/Open) is identified by a version number of 0. – Jonathan Leffler Jan 06 '19 at 04:34
  • I `#define _X_OPEN_SOURCE 700` and it doesn't work on my original code. – JiaHao Xu Jan 06 '19 at 04:37
  • 2
    You have a typo: It's `_XOPEN_SOURCE` and not `_X_OPEN_SOURCE`. – nanofarad Jan 06 '19 at 04:39

1 Answers1

5

The macro _XOPEN_SOURCE is documented in feature_test_macros(7). In particular, the manpage states:

NOTE: In order to be effective, a feature test macro must be defined before including any header files. This can be done either in the compilation command (cc -DMACRO=value) or by defining the macro within the source code before including any headers.

When you include stdio.h, you indirectly include features.h, which uses the feature test macros as defined at that point. In particular, since _XOPEN_SOURCE and friends aren't defined at that point, crypt.h does not declare crypt.

By the time you define _XOPEN_SOURCE it is too late, since features.h has an include guard preventing it from being included twice.

By swapping the order of the first two lines, the code works without raising this warning on my system:

#define _XOPEN_SOURCE
#include <stdio.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
    puts((const char*) crypt("AAAA", "$6$2222"));
    return 0;
}

Your larger example does not work for a second reason: You wrote _X_OPEN_SOURCE as the name of the macro, while the correct name is _XOPEN_SOURCE.

nanofarad
  • 40,330
  • 4
  • 86
  • 117