curl_global_init =: (LIBCURL,' curl_global_init n i') & cd
curl_global_cleanup =: (LIBCURL,' curl_global_cleanup n') & cd
NB. curl_easy_init=: [:{.(LIBCURL,' curl_easy_init * ') & cd
curl_easy_init=: (LIBCURL,' curl_easy_init x ') & cd
ccurl_easy_cleanup=: (LIBCURL,' curl_easy_cleanup n * ') & cd
curl_easy_setopt=: (LIBCURL,' curl_easy_setopt n x i x') & cd
curl_perform=: (LIBCURL,' curl_easy_perform i * ') & cd
curl_slist_append=: (LIBCURL,' curl_slist_append * * *') & cd
curl_slist_free_all=: (LIBCURL,' curl_slist_free_all n *') & cd
NB. print_slist =: (LIBJCURL,' print_slist n *') & cd
set_curl_string_opt =: (LIBJCURL,' set_curl_string_opt n x i *c') & cd
set_curl_long_opt =: (LIBJCURL,' set_curl_int_opt n * i i') & cd
set_curl_ptr_opt =: (LIBJCURL,' set_curl_ptr_opt n * i *') & cd
jcurlperform =: (LIBJCURL,' jcurlperform i x *c') & cd
free_response =: (LIBJCURL,' free_response n *') & cd
mystrdup =: (LIBJCURL,' mystrdup * *') & cd
This seems to center around the pointer I get back from curl_easy_init. I have defined the return value as a * and x as above but when I go to use that pointer in a C call the libcurl call curl_easy_perform segfaults and crashes J.
Here is the C code that is testing this stuff:
int jcurlperform(CURL* curl, char* comment) {
CURLcode res;
CURL *curl1;
FILE *file_ptr; // Declare a FILE pointer
file_ptr = fopen("jcurlget.log", "w");
// Check if the file was opened successfully
if (file_ptr == NULL) {
perror("Error opening file"); // Print a system-specific error message
return 100; // Indicate an error
}
// Write the string to the file
fprintf(file_ptr, "initialize chunk\n");
chunk = (struct memory){malloc(1), 0};
// fprintf(file_ptr,"curl ptr from J: %p \n",(void *) curl);
curl1 = curl_easy_init();
if(curl1) {
fprintf(file_ptr, "curl pointer seems valid (%p) check setup\n",(void *) curl1);
curl_easy_setopt(curl1, CURLOPT_URL,comment );
/* example.com is redirected, so we tell libcurl to follow redirection */
curl_easy_setopt(curl1, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl1, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl1, CURLOPT_WRITEDATA, (void*)&chunk);
fprintf(file_ptr,"curl write options added within C\n");
/* Perform the request, res gets the return code */
res = curl_easy_perform(curl1);
/* Check for errors */
fprintf(file_ptr,"curl_easy_perform(curl1) completed\n");
if(res != CURLE_OK) {
fprintf(file_ptr, "curl_easy_perform() failed: %d, %s\n",res,
curl_easy_strerror(res));
curl_easy_cleanup(curl1);
fclose(file_ptr);
return res;
}
/* always cleanup */
curl_easy_cleanup(curl1);
fprintf(file_ptr,"almost done log response\n");
fprintf(file_ptr,"%s\n",chunk.response);
fclose(file_ptr);
return res;
}
fprintf(file_ptr,"Problem setting up curl handle\n");
fclose(file_ptr);
return 200;
}
chat =: 3 : 0
C =: >{. curl_easy_init''
if. C = 0 do.
NULL
return.
end.
smoutput 'curl initialized'
echo C
hlibjcurl =: (15!:20) LIBJCURL
mycbp =: hlibjcurl (15!:21) 'write_callback'
chunkp =: hlibjcurl (15!:21) 'chunk'
NB. headers =: mema 4
headers =: <0
NB. headers =: {. curl_slist_append (headers);'Content-Type: application/json'
NB. headers =: {. curl_slist_append (headers);'Authorization: Bearer sk-123'
NB. set_curl_string_opt C;CURLOPT_URL;'https://example.com'
NB. set_curl_string_opt C;CURLOPT_URL;url
NB. chkstr =: >{. chkptr <<url_base
NB. echo chkstr
NB. curl_easy_setopt C;CURLOPT_URL;url_base
NB. curl_setopt C;CURLOPT_HTTPHEADER;<headers
NB. set_curl_ptr_opt C;CURLOPT_HTTPHEADER;<headers
NB. curl_setopt C;CURLOPT_POSTEFIELDS;json
NB. curl_setopt C;CURLOPT_WRITEFUNCTION;<<mycbp
NB. curl_setopt C;CURLOPT_WRITEDATA;<<chunkp
NB. curl_setopt C;CURLOPT_VERBOSE;curlverbose
NB.
NB. res =: >{.curl_perform <C
NB.
NB. curl_easy_cleanup <C
NB. curl_slist_free_all <headers
NB. res_str =: 2000$' '
echo 'jcurlperform being called'
cmtstr =: 'https://example.com'
fullres =: jcurlperform C;cmtstr
res =: >{. fullres
echo 'return from jcurlperform'
respptr =. memr chunkp,0,1,JINT
respstr =. memr respptr,0,_1,JCHAR
NB. close the handle to the DLL
NB. free_response <respptr
(15!:22) hlibjcurl
if. res ~: CURLE_OK do.
echo 'curl error'
res
return.
end.
respstr
)
To unsubscribe from this group and stop receiving emails from it, send an email to forum+un...@jsoftware.com.
load '/users/tmcguire/j9.7-user/temp/jcurl.ijs'
mchat =: 'https://example.com' conew 'JCurl'
chat__mchat''
curl initialized
jcurlperform being called
return from jcurlperform
JE has crashed, likely due to an internal bug. Please report the code which caused the crash, as well as the following printout, to the J forum.
Could not generate stack trace: no debug info in Mach-O executable (-1)
Could not generate stack trace: no debug info in Mach-O executable (-1)
Could not generate stack trace: no debug info in Mach-O executable (-1)
Could not generate stack trace: no debug info in Mach-O executable (-1)
Could not generate stack trace: no debug info in Mach-O executable (-1)
Could not generate stack trace: no debug info in Mach-O executable (-1)
Could not generate stack trace: no debug info in Mach-O executable (-1)
Could not generate stack trace: no debug info in Mach-O executable (-1)
Could not generate stack trace: no debug info in Mach-O executable (-1)
Could not generate stack trace: no debug info in Mach-O executable (-1)
Could not generate stack trace: no debug info in Mach-O executable (-1)
Could not generate stack trace: no debug info in Mach-O executable (-1)
Could not generate stack trace: no debug info in Mach-O executable (-1)
-----------------------------------------------------------------------------
Abort trap: 6
logout
set_curl_ptr_opt (<C);CURLOPT_WRITEFUNCTION;<<mycbp
set_curl_ptr_opt (<C);CURLOPT_WRITEDATA;<<chunkp
This is a simple C helper function wrapping curl_easy_setopt calls with appropriate pointer arithmetic.
I also tried the direct libcurl call:
curl_easy_setopt C;CURLOPT_WRITEFUNCTION;mycbp
curl_easy_setopt C;CURLOPT_WRITEDATA;chunkp
with the same crash.
respptr =. memr chunkp,0,1,JINT
respstr =. memr respptr,0,_1,JCHAR
I was thinking that the CURL* address I get back from curl_easy_init may need to be dereferenced in a certain way. I had tried to use ‘mema’ as a way to allocate space for a pointer. But my attempts to fill that with the pointer returned by curl_easy_init. But I run into problems with those calls as well.
Tom McGuire
curl_global_init =: (LIBCURL,' curl_global_init n i') & cd
curl_global_cleanup =: (LIBCURL,' curl_global_cleanup n') & cd
curl_easy_init=: (LIBCURL,' curl_easy_init x ') & cd
curl_easy_cleanup=: (LIBCURL,' curl_easy_cleanup n * ') & cd
curl_easy_setopt=: (LIBCURL,' curl_easy_setopt n x i x') & cd
curl_easy_perform=: (LIBCURL,' curl_easy_perform i * ') & cd
curl_slist_append=: (LIBCURL,' curl_slist_append * * *') & cd
curl_slist_free_all=: (LIBCURL,' curl_slist_free_all n *') & cd
NB. lots of constant definitions from libcurl defined here but removed for this email
runCurl =: 3 : 0
url =: y
curlverbose =: 1
curl_global_init <CURL_GLOBAL_DEFAULT
Cret =: curl_easy_init''
echo Cret
C =: {.Cret
echo C
if. C = 0 do.
NULL
return.
end.
NB. get the C write callback and data ptr
hlibjcurl =: (15!:20) LIBJCURL
mycbp =: hlibjcurl (15!:21) 'write_callback'
chunkp =: hlibjcurl (15!:21) 'chunk'
curl_easy_setopt C;CURLOPT_URL;url
curl_easy_setopt C;CURLOPT_VERBOSE;1
curl_easy_setopt C;CURLOPT_WRITEFUNCTION;<<mycbp
curl_easy_setopt C;CURLOPT_WRITEDATA;<<chunkp
res =: >{.curl_easy_perform <C
if. res ~: CURLE_OK do.
echo 'curl error'
res
return.
end.
respptr =. memr chunkp,0,1,JINT
respstr =. memr respptr,0,_1,JCHAR
echo respstr
curl_easy_cleanup <C
NB. close the handle to the DLL
(15!:22) hlibjcurl
respstr
curl_global_cleanup ''
)
Run of the above code under JQT 9.7
runCurl 'https://example.com'
┌──────────┐
└──────────┘
┌──────────┐
└──────────┘
|domain error in cd, executing dyad 15!:0
| curl_easy_setopt C;CURLOPT_URL;url
Press ENTER to inspect
[This is desk analysis, take with a grain of salt]
Cret =: curl_easy_init''
echo Cret
C =: {.Cret
echo C
(I think the echos show that C is a boxed integer)
...
curl_easy_setopt C;CURLOPT_URL;url
C is supposed to be a boxed integer (x), but it is actually a box.
cder'' will show what cd is complaining about.
Henry Rich
NULL =: <0
3 : 0''
select. UNAME
case. 'Win' do.
LIBCURL=: '"C:\Users\Thomas\Downloads\libcurl.dll" '
case. 'Darwin' do.
LIBCURL=: '/opt/homebrew/Cellar/curl/8.15.0/lib/libcurl.dylib'
LIBJCURL=: jpath '~/cppdev/jcurl/libjcurl.dylib'
case. 'Linux' do.
LIBCURL=: 'libcurl.dll '
case. do.
LIBCURL=: 'libcurl.dll '
end.
)
NB. some usage
NB. curl_global_init(CURL_GLOBAL_DEFAULT)
NB. all pointer parameters and return values use * or *c as the
NB. case may be. As suggested by Henry Rich
curl_global_init =: (LIBCURL,' curl_global_init n i') & cd
curl_global_cleanup =: (LIBCURL,' curl_global_cleanup n') & cd
curl_easy_init=: (LIBCURL,' curl_easy_init * ') & cd
curl_easy_cleanup=: (LIBCURL,' curl_easy_cleanup n * ') & cd
NB. multiple call signatures to the same curl_easy_setopt
NB. as suggested by Bill Lam
curl_easy_setopt_str=: (LIBCURL,' curl_easy_setopt n * i *c') & cd
curl_easy_setopt_ptr=: (LIBCURL,' curl_easy_setopt n * i *') & cd
curl_easy_setopt_int=: (LIBCURL,' curl_easy_setopt n * i i') & cd
curl_easy_perform=: (LIBCURL,' curl_easy_perform i * ') & cd
NB. not used yet but it's used for setting up header parameters
NB. to be used by curl_easy_perform in the http request
curl_slist_append=: (LIBCURL,' curl_slist_append * * *') & cd
curl_slist_free_all=: (LIBCURL,' curl_slist_free_all n *') & cd
runCurl =: 3 : 0
url =: y
curlverbose =: 1
curl_global_init <CURL_GLOBAL_DEFAULT
Cret =: curl_easy_init''
echo Cret
C =: {.Cret
echo C
if. C = 0 do.
NULL
return.
end.
NB. get the C write callback and data ptr
hlibjcurl =: (15!:20) LIBJCURL
mycbp =: hlibjcurl (15!:21) 'write_callback'
chunkp =: hlibjcurl (15!:21) 'chunk'
curl_easy_setopt_str C;CURLOPT_URL;url
NB. curl_easy_setopt_ptr C;CURLOPT_URL;<<mystr
curl_easy_setopt_int C;CURLOPT_VERBOSE;1
curl_easy_setopt_ptr C;CURLOPT_WRITEFUNCTION;<<mycbp
curl_easy_setopt_ptr C;CURLOPT_WRITEDATA;<<chunkp
fullres =: curl_easy_perform <C
res =: >{. fullres
if. res ~: CURLE_OK do.
echo 'curl error'
res
return.
end.
respptr =. memr chunkp,0,1,JINT
respstr =. memr respptr,0,_1,JCHAR
echo respstr
curl_easy_cleanup <C
NB. close the handle to the DLL
(15!:22) hlibjcurl
respstr
curl_global_cleanup ''
)
OUTPUT:
load '/users/tmcguire/j9.7-user/temp/curl.ijs'
runCurl 'https://example.com'
┌──────────┐
└──────────┘
┌──────────┐
└──────────┘
curl error
3
The error code corresponds to the following constant definition:
CURLE_URL_MALFORMAT=: 3
So after building up the curl handle with curl_easy_setopt calls the curl_easy_perform call doesn’t seem to be able to access anything. Yet if I wrap all these calls in a C-language wrapper I can get the curl_easy_perform to work properly in C when called from my wrapper.
This seems so straight forward and yet I can’t get it to work.
Tom McGuire
To unsubscribe from this group and stop receiving emails from it, send an email to forum+un...@jsoftware.com.
load '/users/tmcguire/j9.7-user/temp/curl.ijs'
runCurl 'https://www.example.com/'
┌──────────┐
└──────────┘
┌──────────┐
└──────────┘
curl error
3
So my understanding is that the default behavior for libcurl is to write to a file. So if I comment out the callback function and its write buffer I should be defaulting to this behavior. However, when I comment those lines and reload and rerun:
load '/users/tmcguire/j9.7-user/temp/curl.ijs'
runCurl 'https://www.example.com/'
┌──────────┐
│4773172736│
└──────────┘
┌──────────┐
│4773172736│
└──────────┘
curl error
3
Still getting the error. I suspect that curl_easy_perform returns immediately when it can’t locate a valid URL. We will be unable to even test default behavior until I can get a URL into the CURL* handle in libcurl that curl_perform_routine can access properly.
Thinking that curl_easy_setopt wanted a C string I even used the dll memory mangement routines in J to set up a string that way.
mema a location for the number of bytes in the URL
memw to write the J string into the memory as a null terminated string.
used both the curl_easy_setopt_str and curl_easy_setopt_ptr calls to see if curl_easy perform would be able to access the URL string. Unable to get that to work.
![]() | |
Begin forwarded message:
From: bill lam <bbil...@gmail.com>Subject: Re: [Jforum] libcurl FFI interfaceDate: September 9, 2025 at 10:44:01 AM EDTReply-To: fo...@jsoftware.comI haved added api/curl addon. You can get it from pacman. There are 2 examples included under the test folder.simple - write to stdoutgetinmemory - use callback and write to a noun.I tested them run ok on mac (arm64 and x86_64) and linux.untested on windows, not sure the libcurl.dll is included in windows 10/11.You must work on Apple arm64, the curl_easy_setopt is a variadic function and its ABI need special treatment.The C compiler will handle it correctly. But using FFI, it is more complicated.