skaarj1989 / mWebSockets

WebSockets for microcontrollers
https://skaarj1989.github.io/mWebSockets/autobahn-testsuite/servers/
MIT License
108 stars 23 forks source link

'strtok_r' was not declared in this scope, 'strcasecmp' was not declared in this scope #59

Closed gigaj0ule closed 1 year ago

gigaj0ule commented 1 year ago

Describe the bug

I am having some compilation errors:

'strtok_r' was not declared in this scope 'strcasecmp' was not declared in this scope

Manually including

#include <Arduino.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>

to WebSocketClient.cpp and WebSocketServer.cpp didn't find these functions either, so I am not sure where they could be.

Do we need to define any specific -D flags for GCC?

Apparently strcasecmp is not an iso-c function https://c-for-dummies.com/blog/?p=3863


Environment info


Expected behavior Successful compilation

Failure log

system42/libraries/mWebSockets/src/WebSocketClient.cpp: In member function 'bool net::WebSocketClient::_readResponse(const char*)':
system42/libraries/mWebSockets/src/WebSocketClient.cpp:170:24: error: 'strtok_r' was not declared in this scope; did you mean 'strtok'?
  170 |           char *header{strtok_r(rest, ":", &rest)};
      |                        ^~~~~~~~
      |                        strtok
In file included from system42/Arduino_Core_STM32/cores/arduino/WString.h:29,
                 from system42/Arduino_Core_STM32/cores/arduino/Print.h:26,
                 from system42/Arduino_Core_STM32/cores/arduino/Stream.h:26,
                 from system42/Arduino_Core_STM32/cores/arduino/HardwareSerial.h:29,
                 from system42/Arduino_Core_STM32/cores/arduino/WSerial.h:5,
                 from system42/Arduino_Core_STM32/cores/arduino/wiring.h:48,
                 from system42/Arduino_Core_STM32/cores/arduino/Arduino.h:36,
                 from system42/libraries/Ethernet/src/Ethernet.h:51,
                 from system42/libraries/mWebSockets/src/platform.h:49,
                 from system42/libraries/mWebSockets/src/utility.h:3,
                 from system42/libraries/mWebSockets/src/WebSocket.h:5,
                 from system42/libraries/mWebSockets/src/WebSocketClient.h:5,
                 from system42/libraries/mWebSockets/src/WebSocketClient.cpp:1:
system42/Arduino_Core_STM32/cores/arduino/avr/pgmspace.h:56:28: error: 'strcasecmp' was not declared in this scope; did you mean 'strcasecmp_P'?
   56 | #define strcasecmp_P(a, b) strcasecmp((a), (b))
      |                            ^~~~~~~~~~
system42/libraries/mWebSockets/src/WebSocketClient.cpp:176:15: note: in expansion of macro 'strcasecmp_P'
  176 |           if (strcasecmp_P(header, (PGM_P)F("Upgrade")) == 0) {
      |               ^~~~~~~~~~~~
make: *** [Makefile:794: /home/razor/Desktop/system42/__build/system42/libraries/mWebSockets/src/WebSocketClient.o] Error 1
make: *** Waiting for unfinished jobs....
system42/libraries/mWebSockets/src/WebSocketServer.cpp: In member function 'bool net::WebSocketServer::_handleRequest(NetClient&, char*)':
system42/libraries/mWebSockets/src/WebSocketServer.cpp:179:25: error: 'strtok_r' was not declared in this scope; did you mean 'strtok'?
  179 |           auto header = strtok_r(rest, ":", &rest);
      |                         ^~~~~~~~
      |                         strtok
system42/libraries/mWebSockets/src/WebSocketServer.cpp:278:42: error: 'strtok_r' was not declared in this scope; did you mean 'strtok'?
  278 |                                        : strtok_r(protocols, ",", &rest));
      |                                          ^~~~~~~~
      |                                          strtok
system42/libraries/mWebSockets/src/WebSocketServer.cpp: In member function 'bool net::WebSocketServer::_isValidGET(char*)':
system42/libraries/mWebSockets/src/WebSocketServer.cpp:297:22: error: 'strtok_r' was not declared in this scope; did you mean 'strtok'?
  297 |     const auto pch = strtok_r(rest, " ", &rest);
      |                      ^~~~~~~~
      |                      strtok
system42/libraries/mWebSockets/src/WebSocketServer.cpp: In member function 'bool net::WebSocketServer::_isValidConnection(char*)':
system42/libraries/mWebSockets/src/WebSocketServer.cpp:330:18: error: 'strtok_r' was not declared in this scope; did you mean 'strtok'?
  330 |   while ((item = strtok_r(rest, ",", &rest))) {
      |                  ^~~~~~~~
      |                  strtok
make: *** [Makefile:794: /home/razor/Desktop/system42/__build/system42/libraries/mWebSockets/src/WebSocketServer.o] Error 1
gigaj0ule commented 1 year ago

Manually including the following file as "strcasecmp.h" solves at least one compilation error.

It may be a good idea to include it in the library since strcasecmp is not iso-c

/* Copyright (C) 1991-2022 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.
   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <ctype.h>
#include <string.h>
#ifndef _LIBC
# define __strcasecmp strcasecmp
# define TOLOWER(Ch) tolower (Ch)
#else
# include <locale/localeinfo.h>
# ifdef USE_IN_EXTENDED_LOCALE_MODEL
#  define __strcasecmp __strcasecmp_l
# endif
# define TOLOWER(Ch) __tolower_l ((Ch), loc)
#endif
#ifdef USE_IN_EXTENDED_LOCALE_MODEL
# define LOCALE_PARAM , locale_t loc
#else
# define LOCALE_PARAM
#endif
/* Compare S1 and S2, ignoring case, returning less than, equal to or
   greater than zero if S1 is lexicographically less than,
   equal to or greater than S2.  */
static int
__strcasecmp (const char *s1, const char *s2 LOCALE_PARAM)
{
#if defined _LIBC && !defined USE_IN_EXTENDED_LOCALE_MODEL
  locale_t loc = _NL_CURRENT_LOCALE;
#endif
  const unsigned char *p1 = (const unsigned char *) s1;
  const unsigned char *p2 = (const unsigned char *) s2;
  int result;
  if (p1 == p2)
    return 0;
  while ((result = TOLOWER (*p1) - TOLOWER (*p2++)) == 0)
    if (*p1++ == '\0')
      break;
  return result;
}

#ifndef __strcasecmp
libc_hidden_def (__strcasecmp)
weak_alias (__strcasecmp, strcasecmp)
#endif

#undef strcasecmp_P
#define strcasecmp_P(a, b) __strcasecmp((a), (b))

I still don't know what needs to be -Defined to get the re-entrant strtok_r()

skaarj1989 commented 1 year ago

I have never used gcc with make hence my support will be limited. I checked SO and it looks like it's related to -std option. What are your compiler flags?

gigaj0ule commented 1 year ago

I tried disabling the strict -std=c99 check without success.

My flags as of present:

-DARDUINO=110035 
-DSTM32 -DARDUINO_GENERIC_F405RGTX 
-DSTM32F405xx 
-DINCLUDE_MCU_NETWORKING 
-DMCU_NETWORKING_USING_W5500 
-DMCU_NETWORKING_USING_CDC0 
-DINCLUDE_MCU_NETWORKING 
-DMCU_NETWORKING_USING_CDC0 
-DINCLUDE_MCU_NETWORKING 
-DMCU_NETWORKING_USING_UART2 
-DARDUINO_ARCH_STM32 
-DVARIANT_H="<variant_generic.h>" 
-D_REENTRANT 
-DUSBCON  
-DHAL_UART_MODULE_ENABLED  
-DHAL_PCD_MODULE_ENABLED 
-DUSE_FULL_LL_DRIVER 
-DUSBD_USE_CDC  
-DUSBD_CDC_USE_SINGLE_BUFFER 
-D__STM32F4__ 
-DBOARD_NAME="\"DISCO_F407VG\""  
-D'__UNALIGNED_UINT32_READ(addr)=(*((const __packed uint32_t *)(addr)))' 
-D'__UNALIGNED_UINT32_WRITE(addr, val)=((*((__packed uint32_t *)(addr))) = (val))' 

-mthumb 
-fno-exceptions 
-ffunction-sections 
-fdata-sections 
-MMD 
-ffast-math
-fno-math-errno 
-Werror=return-type 
-Wall
-Wdouble-promotion  
-Wfloat-conversion 
-Os  
-mcpu=cortex-m4 
-mfpu=fpv4-sp-d16 
-mfloat-abi=hard 
-fno-elide-constructors  
-fno-exceptions  
-fext-numeric-literals 
-std=c++17 
-Wno-register  
-Wno-bool-compare 
-g

I'm not sure yet what I need to do to find strtok_r, as the -D_REENTRANT didn't seem to have any effect.

This person claims strtok_r is also not a standard C function:

https://stackoverflow.com/questions/23961147/implicit-declaration-of-function-strtok-r-despite-including-string-h

adding

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

#define BUFFER_SIZE 1024

extern char *strtok_r(char *, const char *, char **);

to the top of WebSocketServer.cpp and WebSocketClient.cpp seems to help the compiler find it

Edit: Nevermind, all I did was push the error to the linker:

undefined reference to `strtok_r(char*, char const*, char**)'
skaarj1989 commented 1 year ago

Is it possible for you to use Arduino IDE? are you really limited to make/gcc?

gigaj0ule commented 1 year ago

It's very not possible to use the arduino IDE unfortunately, but, the IDE just bundles MAKE/GCC under its hood anyway so the environment I am using is technically the same.

If I fix this bug, your library will be more portable and compliant with ISO-C.

I think I got it building!

First, we must use -D_POSIX_C_SOURCE=200112L as a compiler flag, because strtok_r is not an iso-c function (it is a posix GNU function).

This is not a full fix, because it will only work with environments that use GNU C compiler like the Arduino IDE.

Then, we have to #include into WebSocketServer.cpp and WebSocketClient.cpp , to define the non-ISO-C functions.

/* Copyright (C) 1991-2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>.  */

#ifndef _EXTENDEDLIBC_H
    #define _EXTENDEDLIBC_H

    namespace net {

        #ifdef HAVE_CONFIG_H
        # include <config.h>
        #endif
        #include <ctype.h>
        #include <string.h>
        #ifndef _LIBC
        # define __strcasecmp strcasecmp
        # define TOLOWER(Ch) tolower (Ch)
        #else
        # include <locale/localeinfo.h>
        # ifdef USE_IN_EXTENDED_LOCALE_MODEL
        #  define __strcasecmp __strcasecmp_l
        # endif
        # define TOLOWER(Ch) __tolower_l ((Ch), loc)
        #endif
        #ifdef USE_IN_EXTENDED_LOCALE_MODEL
        # define LOCALE_PARAM , locale_t loc
        #else
        # define LOCALE_PARAM
        #endif

        /* Compare S1 and S2, ignoring case, returning less than, equal to or
        greater than zero if S1 is lexicographically less than,
        equal to or greater than S2.  */
        static int __strcasecmp (const char *s1, const char *s2 LOCALE_PARAM)
        {
            #if defined _LIBC && !defined USE_IN_EXTENDED_LOCALE_MODEL
            locale_t loc = _NL_CURRENT_LOCALE;
            #endif
            const unsigned char *p1 = (const unsigned char *) s1;
            const unsigned char *p2 = (const unsigned char *) s2;
            int result;
            if (p1 == p2)
                return 0;
            while ((result = TOLOWER (*p1) - TOLOWER (*p2++)) == 0)
                if (*p1++ == '\0')
                break;
            return result;
        }

        #ifndef __strcasecmp
        libc_hidden_def (__strcasecmp)
        weak_alias (__strcasecmp, strcasecmp)
        #endif
    }

    #undef strcasecmp_P
    #define strcasecmp_P(a, b) __strcasecmp((a), (b))

    extern char *strtok_r(char *, const char *, char **);

#endif

Now, the library will compile and seems to run successfully.

If I can make a static definition of strtok_r, then we won't need to rely on GNU C anymore and the library will be -ansi compliant.

This looks promising: https://github.com/lattera/glibc/blob/master/string/strtok_r.c

gigaj0ule commented 1 year ago

This seems to compile...

We no longer need to use -D_POSIX_C_SOURCE=200112L and the library compiles without needing the nonstandard GNU libc

/* Copyright (C) 1991-2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>.  */

#ifndef _EXTENDEDLIBC_H
    #define _EXTENDEDLIBC_H

    namespace net {

        #ifdef HAVE_CONFIG_H
        # include <config.h>
        #endif

        #include <ctype.h>
        #include <string.h>

        #ifndef _LIBC
            # define __strcasecmp strcasecmp
            # define TOLOWER(Ch) tolower (Ch)
        #else
            # include <locale/localeinfo.h>
            # ifdef USE_IN_EXTENDED_LOCALE_MODEL
            #  define __strcasecmp __strcasecmp_l
            # endif
            # define TOLOWER(Ch) __tolower_l ((Ch), loc)
        #endif

        #ifdef USE_IN_EXTENDED_LOCALE_MODEL
            # define LOCALE_PARAM , locale_t loc
        #else
            # define LOCALE_PARAM
        #endif

        /* Compare S1 and S2, ignoring case, returning less than, equal to or
        greater than zero if S1 is lexicographically less than,
        equal to or greater than S2.  */
        static int __strcasecmp (const char *s1, const char *s2 LOCALE_PARAM)
        {
            #if defined _LIBC && !defined USE_IN_EXTENDED_LOCALE_MODEL
            locale_t loc = _NL_CURRENT_LOCALE;
            #endif
            const unsigned char *p1 = (const unsigned char *) s1;
            const unsigned char *p2 = (const unsigned char *) s2;
            int result;
            if (p1 == p2)
                return 0;
            while ((result = TOLOWER (*p1) - TOLOWER (*p2++)) == 0)
                if (*p1++ == '\0')
                break;
            return result;
        }

        #ifndef __strcasecmp
            libc_hidden_def (__strcasecmp)
            weak_alias (__strcasecmp, strcasecmp)
        #endif

        #undef strcasecmp_P
        #define strcasecmp_P(a, b) __strcasecmp((a), (b))

        /* Parse S into tokens separated by characters in DELIM.
        If S is NULL, the saved pointer in SAVE_PTR is used as
        the next starting point.  For example:
            char s[] = "-abc-=-def";
            char *sp;
            x = strtok_r(s, "-", &sp);  // x = "abc", sp = "=-def"
            x = strtok_r(NULL, "-=", &sp);  // x = "def", sp = NULL
            x = strtok_r(NULL, "=", &sp);   // x = NULL
                // s = "abc\0-def\0"
        */

        #ifndef _LIBC
            #define __strtok_r strtok_r
        #endif

        static char * __strtok_r (char *s, const char *delim, char **save_ptr)
        {
            char *end;

            if (s == NULL)
                s = *save_ptr;

            if (*s == '\0')
                {
                *save_ptr = s;
                return NULL;
                }

            /* Scan leading delimiters.  */
            s += strspn (s, delim);
            if (*s == '\0')
                {
                *save_ptr = s;
                return NULL;
                }

            /* Find the end of the token.  */
            end = s + strcspn (s, delim);
            if (*end == '\0')
                {
                *save_ptr = end;
                return s;
                }

            /* Terminate the token and make *SAVE_PTR point past it.  */
            *end = '\0';
            *save_ptr = end + 1;
            return s;
        }

        #ifndef __strtok_r
            libc_hidden_def (__strtok_r)
            weak_alias (__strtok_r, strtok_r)
        #endif
    }

#endif

Let me make sure the library isn't broken now...

Edit: Drat... the static strtok_r doesn't work. Let me dig into understanding why...

stale[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

gigaj0ule commented 1 year ago

I found a better solution:

See: https://github.com/skaarj1989/mWebSockets/issues/62