The strnlen() C Function Is Not Available

Both of the following statements work on an Arduino Uno WiFi Rev2 board but the second statement does not work on the new Arduino Uno R4 WiFi board.

Serial.print("strlen = ");  Serial.println(strlen(string));
Serial.print("strnlen = ");  Serial.println(strnlen(string, 10));  // error: 'strnlen' was not declared in this scope

The strlen() function is defined, but the strnlen() function is not defined. Anybody know why this is the case and perhaps how to fix it?

You can always add code for strnlen() to the .ino file. It is very short.

There are a few functions missing from the standard C library included with this core. Another is strdup(). They may be optional according to the standards documentation.

@jremington and @sharp5, thank you for your replies.

I created a strnlen() macro that works within a sketch, but the problem I am having is that the strnlen() function is used within a library, specifically the PubSubClient library. I already submitted an issue for that library, but I thought it would be wise to also acknowledge that lack of the strnlen() function here and see if anybody knew of a work-a-around.

You are welcome to make a copy of the library, and fix that problem for yourself. The issue you posted on the Github site will make others aware of the problem.

Neither strdup nor strnlen is defined in the ISO C standard, but they are included in other standards like POSIX.1.

They are also both present in AVR libc, used by the original Uno R3 core. Users, and library authors, will have become used to their availability. I guess that makes them de facto standards.

To get the string length have you tried using length() ?

String mystring = "abcdefghijklmo";
Serial.print("strlen = ");  Serial.println(mystring.length());

For the second line you could try

Serial.print("strnlen = ");  Serial.println(mystring.substring(0, 10));

Not sure if it is what you are looking for but worth a shot.

Does this mean that the M4 build is NOT using "newlib"?

Fixed that for you
(The OP isn't using a String, so it isn't what they're looking for)

1 Like

In order to make all relevant information available to any who are interested in this subject, I'll share a link to @woolsey's issue report here:

Thanks @ptillisch.

Something is weird here. The compiler has strnlen in

Library/Arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/arm-none-eabi/include

just where it is supposed to be. There is a standard string.h with

size_t _EXFUN(strnlen,(const char *, size_t));

Looks like something is overriding this.

Meanwhile, my quick and dirty fix was to add to PubSubClient.h

#ifdef ARDUINO_UNOR4_WIFI
size_t strnlen(const char*, size_t);
#endif

and to PubSubClient.cpp

#ifdef ARDUINO_UNOR4_WIFI
size_t strnlen(const char* b, size_t s) { size_t i; for(i=0; b[i]!=0 && i<s; i++); return i; }
#endif

This is not very beautiful but works.

You left out the context:

#if __POSIX_VISIBLE >= 200809
size_t	 _EXFUN(strnlen,(const char *, size_t));
#endif
1 Like

Hmm. It's inside conditionals:

#if __POSIX_VISIBLE >= 200809
size_t	 _EXFUN(strnlen,(const char *, size_t));
#endif

Alas, tracing why __POSIX_VISIBLE gets set to a particular value seems to be a non-trivial task (or at least requires tool knowledge that I don't have.)
SAMD compiles, which theoretically use the same toolchain, work fine.
(and indeed, R4 compiles have #define __POSIX_VISIBLE 199209, while SAMD has 200809)

Ahh. A bit of brute force says that __POSIX_VISIBLE gets set based on _POSIX_C_SOURCE, which defaults to 200809 unless _XOPEN_SOURCE is set less than 700.

For some reason, the R4 build line includes a -D_XOPEN_SOURCE (SAMD doesn't define it at all.)

I don't know if that's intentional, or if someone thought that it means "Open Source" in the sense of OSSW. (I dont think that that's what it means!) (I can't find anything in the Arduino R4 core, the Arduino-api core, or the Renesas fsp code, that actually references _XOPEN_SOURCE

This does imply that the problem is in the #include files, and the function is actually present in the C library that is linked in. So a workaround is simply to provide an explicit prototype like:

extern "C" size_t _EXFUN(strnlen,(const char *, size_t));
2 Likes

Heh. _XOPEN_SOURCE seems to be related to X/Open - Wikipedia

Great analysis! I tried now to add the prototype and it works.

Even more amazing, if you leave away _XOPEN_SOURCE in platform.txt then the original PubSubClient complies without the prototype.

What is broken then is the buildin RTC. It doesn't compile any more because it redefines the struct timeval. After removing this in RTC.h everything works fine.

Nice work. I was dreading going through examples and trying to figure out whether undefining _XOPEN_SOURCE would break anything.

1 Like

I compiled with RTC and WiFiS3 now and they seem to work. Can be that some of the other libraries cause problems because they also redefine structures. Will check this a little more over the next couple of days. If there is no big thing coming, then the obvious solution would be a new platform.txt file in the next version of the code.

Apparently there is a separate issue for defining _XOPEN_SOURCE with a more correct value (which will solve this as well.) platform: properly define _XOPEN_SOURCE by facchinm · Pull Request #87 · arduino/ArduinoCore-renesas · GitHub

1 Like