chicken-users
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: cannot open file if name contains accented characters


From: felix . winkelmann
Subject: Re: cannot open file if name contains accented characters
Date: Tue, 26 Dec 2023 21:44:52 +0100

> Thanks for the responses. I tried what I could, but it still doesn't work.
> I wrote some code to test if I can open and close a file. The C code works
> but the Chicken code doesn't.
>
> (import (chicken foreign))
>
> (foreign-declare "#include <stdio.h>")
> (foreign-declare "#include <wchar.h>")
>
> (define chicken_wfopen
>     (foreign-lambda (c-pointer "FILE") "_wfopen" (c-pointer "wchar_t")
> (c-pointer "wchar_t")))
> (define chicken_fclose
>     (foreign-lambda int "fclose" (c-pointer "FILE")))
>
> (let ([file-handle (chicken_wfopen "c:\\temp\\íűőúöüóéá.txt" "r")])
>     (print "File has been opened at: " (number->string file-handle))
>     (print "Closing file.")
>     (chicken_fclose file-handle))
>
> The Chicken code fails with the error message: Error: unbound variable:
> It doesn't say the name of the variable, and it returns the error code 70.
> Note that in C the string needs to be prefixed with an L to make it wide
> character. In Chicken I don't know how to do that.
> Does anybody have a clue which variable is unbound?

Ugh. Sorry, I don't know what variable is meant here. I also gave wrong
information: _wfopen returns a FILE* (as you correctly implement above).
The returned pointer will not be a number, though.

Anyway: the strings you pass are Scheme strings, passes as char *
to the FFI code, this is wrong, as you want UTF-16 Windows strings
(wchar_t *) here. The proper way would be (I think) to do

;; warning: totally untested

(define wfopen
  (foreign-lambda* bool ((c-string str) (c-string mode) (scheme-object port))
   "int sz1 = 4 * (strlen(str) + 1), sz2 = 4 * (strlen(mode) + 1);
     wchar_t *buf1 = malloc(sz1), buf2 = malloc(sz2);
     FILE *fp;
     MultiByteToWideChar(CP_UTF8, 0, str, -1, buf1, sz1);
     MultiByteToWideChar(CP_UTF8, 0, mode, -1, buf2, sz2);
     fp = _wfopen(buf1, buf2);
     free(buf1); free(buf2);
     if(fp == NULL) return(0);
     C_set_block_item(port, 0, (C_word)fp);
     return(1);"))

(define (fopen-utf16 fname mode input?)
  (let ((p (##sys#make-port (if input? 1 2) ##sys#stream-port-class name 
'stream)))
    (if (wfopen fname mode)
        p
        (error "error opening file" fname mode))))

the ##sys#make-port thing above creates a proper port to be used in Scheme,
the input? flag is unfortunately needed to distinguish between input and output
ports. Sorry if all this is a bit confusing.


felix





reply via email to

[Prev in Thread] Current Thread [Next in Thread]