Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some effort to compile on windows #7

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,53 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
#

ifeq ($(OS),Windows_NT)
detected_OS := Windows
SHARED_LIB_EXT := dll
EXTRA_OBJS := wcwidth.o
else
detected_OS := $(shell uname)
SHARED_LIB_EXT := so
EXTRA_OBJS :=
endif
include Makefile.cfg

export CC = gcc -pthread
export CFLAGS += -I. -Wall -Os -ggdb -D_GNU_SOURCE -fPIC
export LDLIBS += -lncursesw

SONAME := libstfl.so.0
export LDLIBS += $(shell pkg-config --libs ncursesw iconv)
export CFLAGS += $(shell pkg-config --cflags ncursesw iconv)
Minoru marked this conversation as resolved.
Show resolved Hide resolved
VERSION := 0.24
ifeq ($(detected_OS),Windows)
SHARED_LIB_NAME := libstfl.$(VERSION).$(SHARED_LIB_EXT)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SHARED_LIB_EXT is only used here. How about hard-coding it to dll here and removing the variable?

CFLAGS += -DNCURSES_STATIC
else
SHARED_LIB_NAME := libstfl.so.$(VERSION)
SONAME := libstfl.so.0
endif

all: libstfl.so.$(VERSION) libstfl.a example
all: $(SHARED_LIB_NAME) libstfl.a example

example: libstfl.a example.o

libstfl.a: public.o base.o parser.o dump.o style.o binding.o iconv.o \
libstfl.a: public.o base.o parser.o dump.o style.o binding.o iconv.o $(EXTRA_OBJS) \
$(patsubst %.c,%.o,$(wildcard widgets/*.c))
rm -f $@
ar qc $@ $^
ranlib $@

libstfl.so.$(VERSION): public.o base.o parser.o dump.o style.o binding.o iconv.o \
$(patsubst %.c,%.o,$(wildcard widgets/*.c))
$(CC) -shared -Wl,-soname,$(SONAME) -o $@ $(LDLIBS) $^
$(SHARED_LIB_NAME): $(EXTRA_OBJS) public.o base.o parser.o dump.o style.o binding.o iconv.o \
$(patsubst %.c,%.o,$(wildcard widgets/*.c))
ifeq ($(detected_OS),Windows)
$(CC) -shared -o $@ $(CFLAGS) $^ $(LDLIBS)
else
$(CC) -shared -Wl,-soname,$(SONAME) -o $@ $(CFLAGS) $^ $(LDLIBS)
endif

ifeq ($(detected_OS),Windows)
wcwidth.o: wcwidth.c
$(CC) $(CFLAGS) -c -o $@ $^
endif

clean:
rm -f libstfl.a example core core.* *.o Makefile.deps
Expand Down
26 changes: 21 additions & 5 deletions example.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,32 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <langinfo.h>
#include <locale.h>

#ifdef _WIN32
#include <windows.h>
#else
#include <langinfo.h>
#endif

int main()
{
if (!setlocale(LC_ALL,""))
fprintf(stderr, "WARING: Can't set locale!\n");
if (!setlocale(LC_ALL,""))
fprintf(stderr, "WARNING: Can't set locale!\n");

struct stfl_ipool *ipool;

struct stfl_ipool *ipool = stfl_ipool_create(nl_langinfo(CODESET));
#ifdef _WIN32
// Windows specific initialization
// Get the current Windows ANSI code page
UINT codePage = GetACP();
wchar_t codePageStr[10];
swprintf(codePageStr, 10, L"%u", codePage);
ipool = stfl_ipool_create(codePageStr);
Comment on lines +47 to +49
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After looking at this again, I wonder how it even compiles. Here, we're passing wchar_t* into stfl_ipool_create() which expects char* — why isn't it a compilation error to do so?

Assuming wchar_t* simply gets casted to char* somehow, this code is broken: wchar_t for a digit would contain zeroes (digits are part of ASCII, many encodings are extensions of ASCII, including all of Unicode encodings). Thus stfl_ipool_create() will only use the first digit of the codepage, if even that (zeroes might be at the very start of the string).

Here's a test program to verify that a string can be converted from UTF-8 to WCHAR_T and then to UTF-32:

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

// "Hello" in Ukrainian, in UTF-8
char INPUT[] = {
    0xd0, 0x9f, 0xd1, 0x80, 0xd0, 0xb8, 0xd0, 0xb2, 0xd1, 0x96, 0xd1, 0x82, 0x0 };
// Same string in UTF-32
char EXPECTED_UTF32_OUTPUT[] = {
    0xff, 0xfe, 0x00, 0x00, 0x1f, 0x04, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x38, 0x04,
    0x00, 0x00, 0x32, 0x04, 0x00, 0x00, 0x56, 0x04, 0x00, 0x00, 0x42, 0x04, 0x00, 0x00
};

int main() {
    const char input_code[] = "UTF-8";
    iconv_t conv = iconv_open("WCHAR_T", input_code);
    if(conv == (iconv_t)-1) {
        perror("opening iconv for conversion from UTF-8 to WCHAR_T failed");
        exit(1);
    }

    char* input = INPUT;
    char* input_ptr = input;
    size_t inputbytesleft = strlen(input_ptr);

    const size_t OUT_BUFFER_SIZE = 4096; // should be enough for any encoding under the Sun
    char* wc_string = malloc(OUT_BUFFER_SIZE);
    if(wc_string == NULL) {
        perror("allocating a buffer for the wide string failed");
        exit(1);
    }

    char* output_ptr = wc_string;
    size_t outbytesleft = OUT_BUFFER_SIZE;

    size_t chars_converted =
        iconv(
            conv,
            &input_ptr,
            &inputbytesleft,
            &output_ptr,
            &outbytesleft);
    if(chars_converted == (size_t)-1) {
        free(wc_string);
        perror("conversion from UTF-8 to WCHAR_T failed");
        exit(1);
    }

    if(wcslen((wchar_t*)wc_string) == 0) {
        free(wc_string);
        printf("Expected WCHAR_T string to contain some bytes, but it is empty\n");
        exit(1);
    }

    if(iconv_close(conv) == -1) {
        free(wc_string);
        perror("closing iconv for conversion from UTF-8 to WCHAR_T failed");
        exit(1);
    }

    const char output_code[] = "UTF-32";
    conv = iconv_open(output_code, "WCHAR_T");
    if(conv == (iconv_t)-1) {
        free(wc_string);
        perror("opening iconv for conversion from WCHAR_T to UTF-32 failed");
        exit(1);
    }

    input_ptr = wc_string;
    inputbytesleft = OUT_BUFFER_SIZE - outbytesleft;

    char* utf32_string = malloc(OUT_BUFFER_SIZE);
    if(utf32_string == NULL) {
        free(wc_string);
        perror("allocating a buffer for the UTF-32 string failed");
        exit(1);
    }

    output_ptr = utf32_string;
    outbytesleft = OUT_BUFFER_SIZE;

    chars_converted =
        iconv(
            conv,
            &input_ptr,
            &inputbytesleft,
            &output_ptr,
            &outbytesleft);
    if(chars_converted == (size_t)-1) {
        free(utf32_string);
        free(wc_string);
        perror("conversion from WCHAR_T to UTF-32 failed");
        exit(1);
    }

    if(iconv_close(conv) == -1) {
        free(utf32_string);
        free(wc_string);
        perror("closing iconv for conversion from WCHAR_T to UTF-32 failed");
        exit(1);
    }

    const size_t utf32_string_len = OUT_BUFFER_SIZE - outbytesleft;
    const size_t EXPECTED_UTF32_STRING_LEN = 28;
    if(utf32_string_len != EXPECTED_UTF32_STRING_LEN) {
        printf("Expected UTF-32 string to contain %lu bytes, but it contains %lu\n",
                EXPECTED_UTF32_STRING_LEN,
                utf32_string_len);
        free(utf32_string);
        free(wc_string);
        exit(1);
    }

    for(size_t i = 0; i < EXPECTED_UTF32_STRING_LEN; ++i) {
        if(utf32_string[i] != EXPECTED_UTF32_OUTPUT[i]) {
            printf("UTF-32 output differs from expected at position %lu: expected %u, found %u\n",
                    i,
                    EXPECTED_UTF32_OUTPUT[i],
                    utf32_string[i]);
        }
    }

    free(utf32_string);
    free(wc_string);
}

Can you please modify it to run on Windows? In the process you'll figure out all the kinks of how to initialize iconv, and then you can just copy that code here.

#else
// POSIX specific initialization
ipool = stfl_ipool_create(nl_langinfo(CODESET));
#endif
struct stfl_form *f = stfl_create(L"<example.stfl>");

stfl_set(f, L"value_a", L"This is a little");
Expand Down
7 changes: 7 additions & 0 deletions stfl.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
#define STFL_H 1

#include <wchar.h>
//in case of windows use wcwdith.c
#ifdef _WIN32
#include "wcwidth.h"
#define wcwidth mk_wcwidth
#define wcswidth mk_wcswidth
#endif


#ifdef __cplusplus
extern "C" {
Expand Down
Loading