Skip to content

Commit

Permalink
Added interface for curl_easy_setopt() to accept c_funptr arguments. …
Browse files Browse the repository at this point in the history
…Added DICT example.
  • Loading branch information
interkosmos committed Jan 30, 2021
1 parent a9b9b6f commit 45dd924
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 22 deletions.
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ LDLIBS = -lcurl
ARFLAGS = rcs
TARGET = libfortran-curl.a

DICT = dict
DOWNLOAD = download
GOPHER = gopher
HTTP = http
IMAP = imap
SMTP = smtp
VERSION = version

.PHONY: all clean download gopher http imap smtp version
.PHONY: all clean dict download gopher http imap smtp version

all: $(TARGET)

Expand All @@ -30,6 +31,9 @@ $(TARGET):
$(FC) $(FFLAGS) -c src/curl.f90
$(AR) $(ARFLAGS) $(TARGET) curl.o curlv.o

dict: $(TARGET)
$(FC) $(FFLAGS) $(LDFLAGS) -o $(DICT) examples/dict/dict.f90 $(TARGET) $(LDLIBS)

download: $(TARGET)
$(FC) $(FFLAGS) $(LDFLAGS) -o $(DOWNLOAD) examples/download/download.f90 $(TARGET) $(LDLIBS)

Expand All @@ -52,6 +56,7 @@ clean:
if [ `ls -1 *.mod 2>/dev/null | wc -l` -gt 0 ]; then rm *.mod; fi
if [ `ls -1 *.o 2>/dev/null | wc -l` -gt 0 ]; then rm *.o; fi
if [ -e $(TARGET) ]; then rm $(TARGET); fi
if [ -e $(DICT) ]; then rm $(DICT); fi
if [ -e $(DOWNLOAD) ]; then rm $(DOWNLOAD); fi
if [ -e $(GOPHER) ]; then rm $(GOPHER); fi
if [ -e $(HTTP) ]; then rm $(HTTP); fi
Expand Down
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Link your Fortran application with `libfortran-curl.a -lcurl`.
## Examples
Examples are provided in directory `examples/`:

* **dict** queries a [DICT](https://en.wikipedia.org/wiki/DICT) server on TCP port 2628.
* **download** fetches a remote file.
* **gopher** prints the contents of a remote Gopher map file.
* **http** makes an HTTP GET request.
Expand All @@ -50,15 +51,15 @@ $ make <name>
```

## Coverage
| C Function Name | Fortran Interface Name | Bound |
|-----------------------|-----------------------------------------|-------|
| `curl_easy_init` | `curl_easy_init` ||
| `curl_easy_perform` | `curl_easy_perform` ||
| `curl_easy_cleanup` | `curl_easy_cleanup` ||
| `curl_easy_setopt` | `curl_easy_setopt`, `curl_easy_setopt_` ||
| `curl_slist_append` | `curl_slist_append` ||
| `curl_slist_free_all` | `curl_slist_free_all` ||
| `curl_version_info` | `curl_version_info` ||
| C Function Name | Fortran Interface Name | Bound |
|-----------------------|---------------------------------------------------------------------------|-------|
| `curl_easy_init` | `curl_easy_init` ||
| `curl_easy_perform` | `curl_easy_perform` ||
| `curl_easy_cleanup` | `curl_easy_cleanup` ||
| `curl_easy_setopt` | `curl_easy_setopt`, `curl_easy_setopt_c_ptr`, `curl_easy_setopt_c_funptr` ||
| `curl_slist_append` | `curl_slist_append` ||
| `curl_slist_free_all` | `curl_slist_free_all` ||
| `curl_version_info` | `curl_version_info` ||

| C Constant Name | Fortran Interface Name | Bound |
|---------------------|------------------------|-------|
Expand Down
79 changes: 79 additions & 0 deletions examples/dict/dict.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
! dict.f90
!
! Basic dict protocol example, that looks up the definitions of the given words in
! the dictionaries at dict://dict.org/ on port 2628. The program is equal to
! running
!
! $ curl dict://dict.org/d:FORTRAN
!
! on the command-line.
!
! Author: John S. Urban, Philipp Engel
! Licence: ISC
module callback_dict
use :: curl, only: c_f_str_ptr
implicit none
private
public :: response_callback
contains
! static size_t callback(char *ptr, size_t size, size_t nmemb, void *data)
function response_callback(ptr, size, nmemb, data) bind(c)
!! Callback function for `CURLOPT_WRITEFUNCTION` that prints the
!! response to standard output.
!!
!! This callback function might be called several times by libcurl,
!! passing in more chunks of the response.
use, intrinsic :: iso_c_binding, only: c_associated, c_f_pointer, c_ptr, c_size_t
type(c_ptr), intent(in), value :: ptr !! C pointer to a chunk of the response.
integer(kind=c_size_t), intent(in), value :: size !! Always 1.
integer(kind=c_size_t), intent(in), value :: nmemb !! Size of the response chunk.
type(c_ptr), intent(in), value :: data !! C pointer to argument passed by caller.
integer(kind=c_size_t) :: response_callback !! Function return value.
character(len=:), allocatable :: response

response_callback = int(0, kind=c_size_t)

if (.not. c_associated(ptr)) return

allocate (character(len=nmemb) :: response)
call c_f_str_ptr(ptr, response)
write (*, '(a)', advance='no') response
deallocate (response)

response_callback = nmemb
end function response_callback
end module callback_dict

program main
use, intrinsic :: iso_c_binding
use :: curl
use :: callback_dict
implicit none

character(len=*), parameter :: DEFAULT_PROTOCOL = 'dict'
character(len=*), parameter :: DEFAULT_URL = 'dict://dict.org/'
character(len=*), parameter :: DICT_QUERY = 'd:FORTRAN'
type(c_ptr) :: curl_ptr
integer :: rc

curl_ptr = curl_easy_init()

if (.not. c_associated(curl_ptr)) then
stop 'Error: curl_easy_init() failed'
end if

! Set curl options.
rc = curl_easy_setopt(curl_ptr, CURLOPT_DEFAULT_PROTOCOL, DEFAULT_PROTOCOL // c_null_char)
rc = curl_easy_setopt(curl_ptr, CURLOPT_URL, DEFAULT_URL // DICT_QUERY // c_null_char)
rc = curl_easy_setopt(curl_ptr, CURLOPT_TIMEOUT, int(10, kind=8))
rc = curl_easy_setopt(curl_ptr, CURLOPT_NOSIGNAL, int( 1, kind=8))
rc = curl_easy_setopt(curl_ptr, CURLOPT_CONNECTTIMEOUT, int(10, kind=8))
rc = curl_easy_setopt(curl_ptr, CURLOPT_WRITEFUNCTION, c_funloc(response_callback))

! Send request.
if (curl_easy_perform(curl_ptr) /= CURLE_OK) then
print '(a)', 'Error: curl_easy_perform() failed'
end if

call curl_easy_cleanup(curl_ptr)
end program main
11 changes: 8 additions & 3 deletions fpm.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
name = "fortran-curl"
version = "0.1.0"
version = "0.1.1"
license = "ISC"
author = "Philipp Engel"
maintainer = "@interkosmos"
copyright = "Copyright (c) 2020, Philipp Engel"
copyright = "Copyright (c) 2021, Philipp Engel"
description = "Fortran 2008 ISO C binding interfaces to libcurl"
keywords = ["curl", "libcurl", "http", "gopher", "smtp", "imap"]
keywords = ["curl", "libcurl", "http", "gopher", "smtp", "imap", "dict"]

[build]
link = "curl"

[[example]]
name = "dict"
source-dir = "examples/dict"
main = "dict.f90"

[[example]]
name = "download"
source-dir = "examples/download"
Expand Down
28 changes: 19 additions & 9 deletions src/curl.f90
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ module curl
public :: curl_easy_perform
public :: curl_easy_cleanup
public :: curl_easy_setopt
public :: curl_easy_setopt_
public :: curl_easy_setopt_c_ptr
public :: curl_easy_setopt_c_funptr
public :: curl_slist_append
public :: curl_slist_free_all
public :: curl_version_info
Expand Down Expand Up @@ -404,13 +405,22 @@ function curl_easy_perform(curl) bind(c, name='curl_easy_perform')
end function curl_easy_perform

! CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...)
function curl_easy_setopt_(curl, option, parameter) bind(c, name='curl_easy_setopt')
function curl_easy_setopt_c_ptr(curl, option, parameter) bind(c, name='curl_easy_setopt')
import :: c_int, c_ptr
type(c_ptr), intent(in), value :: curl
integer(kind=c_int), intent(in), value :: option
type(c_ptr), intent(in), value :: parameter
integer(kind=c_int) :: curl_easy_setopt_
end function curl_easy_setopt_
integer(kind=c_int) :: curl_easy_setopt_c_ptr
end function curl_easy_setopt_c_ptr

! CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...)
function curl_easy_setopt_c_funptr(curl, option, parameter) bind(c, name='curl_easy_setopt')
import :: c_funptr, c_int, c_ptr
type(c_ptr), intent(in), value :: curl
integer(kind=c_int), intent(in), value :: option
type(c_funptr), intent(in), value :: parameter
integer(kind=c_int) :: curl_easy_setopt_c_funptr
end function curl_easy_setopt_c_funptr

! struct curl_slist *curl_slist_append(struct curl_slist *list, const char *string)
function curl_slist_append(list, string) bind(c, name='curl_slist_append')
Expand Down Expand Up @@ -470,7 +480,7 @@ function curl_easy_setopt_char(curl, option, parameter)
character(len=*), target, intent(in) :: parameter
integer :: curl_easy_setopt_char

curl_easy_setopt_char = curl_easy_setopt_(curl, option, c_loc(parameter))
curl_easy_setopt_char = curl_easy_setopt_c_ptr(curl, option, c_loc(parameter))
end function curl_easy_setopt_char

! CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...)
Expand All @@ -480,7 +490,7 @@ function curl_easy_setopt_fptr(curl, option, parameter)
type(c_funptr), intent(in) :: parameter
integer :: curl_easy_setopt_fptr

curl_easy_setopt_fptr = curl_easy_setopt_(curl, option, parameter)
curl_easy_setopt_fptr = curl_easy_setopt_c_funptr(curl, option, parameter)
end function curl_easy_setopt_fptr

! CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...)
Expand All @@ -490,7 +500,7 @@ function curl_easy_setopt_int(curl, option, parameter)
integer(kind=4), target, intent(in) :: parameter
integer :: curl_easy_setopt_int

curl_easy_setopt_int = curl_easy_setopt_(curl, option, c_loc(parameter))
curl_easy_setopt_int = curl_easy_setopt_c_ptr(curl, option, c_loc(parameter))
end function curl_easy_setopt_int

! CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...)
Expand All @@ -500,7 +510,7 @@ function curl_easy_setopt_long(curl, option, parameter)
integer(kind=8), target, intent(in) :: parameter
integer :: curl_easy_setopt_long

curl_easy_setopt_long = curl_easy_setopt_(curl, option, c_loc(parameter))
curl_easy_setopt_long = curl_easy_setopt_c_ptr(curl, option, c_loc(parameter))
end function curl_easy_setopt_long

! CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...)
Expand All @@ -510,7 +520,7 @@ function curl_easy_setopt_ptr(curl, option, parameter)
type(c_ptr), intent(in) :: parameter
integer :: curl_easy_setopt_ptr

curl_easy_setopt_ptr = curl_easy_setopt_(curl, option, parameter)
curl_easy_setopt_ptr = curl_easy_setopt_c_ptr(curl, option, parameter)
end function curl_easy_setopt_ptr

! curl_version_info_data *curl_version_info(CURLversion age)
Expand Down

0 comments on commit 45dd924

Please sign in to comment.