*
* Copyright (C) 2019 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*/
#ifndef airscan_h
#define airscan_h
#include <avahi-common/address.h>
#include <avahi-common/strlst.h>
#include <avahi-common/watch.h>
#include <sane/sane.h>
#include <sane/saneopts.h>
#include <ctype.h>
#include <math.h>
#include <pthread.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <netinet/in.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
*/
#define CONFIG_PATH_ENV "SANE_CONFIG_DIR"
*/
#ifndef CONFIG_SANE_CONFIG_DIR
# define CONFIG_SANE_CONFIG_DIR "/etc/sane.d/"
#endif
*/
#define CONFIG_AIRSCAN_CONF "airscan.conf"
#define CONFIG_AIRSCAN_D "airscan.d"
*
* CONFIG_ENV_AIRSCAN_DEBUG - if set to "true" or non-zero number,
* enables writing debug messages to stderr
*
* SANE_AIRSCAN_DEVICE - allows to forcibly set the target device.
* See sane-airscan(5) for detains.
*/
#define CONFIG_ENV_AIRSCAN_DEBUG "SANE_DEBUG_AIRSCAN"
#define CONFIG_ENV_AIRSCAN_DEVICE "SANE_AIRSCAN_DEVICE"
*/
#define CONFIG_DEFAULT_RESOLUTION 300
* attempts, if previous sane_start was failed
*/
#define CONFIG_START_RETRY_INTERVAL 2500
*/
#define CONFIG_DEFAULT_SOCKET_DIR "/var/run"
*/
typedef struct log_ctx log_ctx;
*/
typedef struct http_uri http_uri;
* its known member
*/
#define OUTER_STRUCT(member_p,struct_t,field) \
((struct_t*)((char*)(member_p) - ((ptrdiff_t) &(((struct_t*) 0)->field))))
* Data nodes are embedded into the corresponding data structures:
* struct data {
* ll_node chain; // Linked list chain
* ...
* };
*
* Use OUTER_STRUCT() macro to obtain pointer to containing
* structure from the pointer to the list node
*/
typedef struct ll_node ll_node;
struct ll_node {
ll_node *ll_prev, *ll_next;
};
* ll_head must be initialized before use with ll_init() function
*/
typedef struct {
ll_node node;
} ll_head;
*/
static inline void
ll_init (ll_head *head)
{
head->node.ll_next = head->node.ll_prev = &head->node;
}
*/
static inline bool
ll_empty (const ll_head *head)
{
return head->node.ll_next == &head->node;
}
* by its head node
*/
static inline void
ll_push_end (ll_head *head, ll_node *node)
{
node->ll_prev = head->node.ll_prev;
node->ll_next = &head->node;
head->node.ll_prev->ll_next = node;
head->node.ll_prev = node;
}
* by its head node
*/
static inline void
ll_push_beg (ll_head *head, ll_node *node)
{
node->ll_next = head->node.ll_next;
node->ll_prev = &head->node;
head->node.ll_next->ll_prev = node;
head->node.ll_next = node;
}
*/
static inline void
ll_del (ll_node *node)
{
ll_node *p = node->ll_prev, *n = node->ll_next;
p->ll_next = n;
n->ll_prev = p;
node->ll_next = node->ll_prev = node;
}
* Returns NULL if list is empty
*/
static inline ll_node*
ll_pop_beg (ll_head *head)
{
ll_node *node, *next;
node = head->node.ll_next;
if (node == &head->node) {
return NULL;
}
next = node->ll_next;
next->ll_prev = &head->node;
head->node.ll_next = next;
node->ll_next = node->ll_prev = node;
return node;
}
* Returns NULL if list is empty
*/
static inline ll_node*
ll_pop_end (ll_head *head)
{
ll_node *node, *prev;
node = head->node.ll_prev;
if (node == &head->node) {
return NULL;
}
prev = node->ll_prev;
prev->ll_next = &head->node;
head->node.ll_prev = prev;
node->ll_next = node->ll_prev = node;
return node;
}
* the list. Returns NULL, if end of list is reached
*/
static inline ll_node*
ll_next (const ll_head *head, const ll_node *node)
{
ll_node *next = node->ll_next;
return next == &head->node ? NULL : next;
}
* the list. Returns NULL, if end of list is reached
*/
static inline ll_node*
ll_prev (const ll_head *head, const ll_node *node)
{
ll_node *prev = node->ll_prev;
return prev == &head->node ? NULL : prev;
}
* Returns NULL if list is empty
*/
static inline ll_node*
ll_first (const ll_head *head)
{
return ll_next(head, &head->node);
}
* Returns NULL if list is empty
*/
static inline ll_node*
ll_last (const ll_head *head)
{
return ll_prev(head, &head->node);
}
* list1 += list2
* list2 = empty
*/
static inline void
ll_cat (ll_head *list1, ll_head *list2)
{
if (ll_empty(list2)) {
return;
}
list2->node.ll_prev->ll_next = &list1->node;
list2->node.ll_next->ll_prev = list1->node.ll_prev;
list1->node.ll_prev->ll_next = list2->node.ll_next;
list1->node.ll_prev = list2->node.ll_prev;
ll_init(list2);
}
* Usage:
* for (LL_FOR_EACH(node, list)) {
* // do something with the node
* }
*/
#define LL_FOR_EACH(node,list) \
node = ll_first(list); node != NULL; node = ll_next(list, node)
*/
#define mem_new(T,len) ((T*) __mem_alloc(len, 0, sizeof(T), true))
* capacity at least of `len' + `extra'
*
* If p is NULL, new memory block will be allocated. Otherwise,
* existent memory block will be resized, new pointer is returned,
* while old becomes invalid (similar to how realloc() works).
*
* This function never returns NULL, it panics in a case of
* memory allocation error.
*/
#define mem_resize(p,len,extra) \
((__typeof__(p)) __mem_resize(p,len,extra,sizeof(*p),true))
* return NULL if memory allocation failed.
*/
#define mem_try_resize(p,len,extra) __mem_resize(p,len,extra,sizeof(*p),false)
*/
void
mem_trunc (void *p);
*/
#define mem_shrink(p,len) __mem_shrink(p,len, sizeof(*p))
* `p' can be NULL
*/
void
mem_free (void *p);
* For NULL pointer return 0
*/
size_t mem_len_bytes (const void *p);
size_t mem_cap_bytes (const void *p);
* For NULL pointer return 0
*/
#define mem_len(v) (mem_len_bytes(v) / sizeof(*v))
#define mem_cap(v) (mem_cap_bytes(v) / sizeof(*v))
*/
void* __attribute__ ((__warn_unused_result__))
__mem_alloc (size_t len, size_t extra, size_t elsize, bool must);
void* __attribute__ ((__warn_unused_result__))
__mem_resize (void *p, size_t len, size_t cap, size_t elsize, bool must);
void
__mem_shrink (void *p, size_t len, size_t elsize);
*/
static inline char*
str_new (void) {
char *s = mem_resize((char*) NULL, 0, 1);
*s = '\0';
return s;
}
*/
static inline char*
str_dup (const char *s1)
{
size_t len = strlen(s1);
char *s = mem_resize((char*) NULL, len, 1);
memcpy(s, s1, len + 1);
return s;
}
*/
static inline size_t
str_len (const char *s)
{
return mem_len(s);
}
*/
char*
str_dup_tolower (const char *s1);
*/
char*
str_printf (const char *format, ...);
*/
char*
str_vprintf (const char *format, va_list ap);
*/
static inline void
str_trunc (char *s)
{
mem_trunc(s);
*s = '\0';
}
*
* s1 must be previously created by some of str_XXX functions,
* s1 will be consumed and the new pointer will be returned
*/
static inline char*
str_resize (char *s, size_t len)
{
s = mem_resize(s, len, 1);
s[len] = '\0';
return s;
}
* s1 += s2[:l2]
*
* s1 must be previously created by some of str_XXX functions,
* s1 will be consumed and the new pointer will be returned
*/
static inline char*
str_append_mem (char *s1, const char *s2, size_t l2)
{
size_t l1 = str_len(s1);
s1 = mem_resize(s1, l1 + l2, 1);
memcpy(s1 + l1, s2, l2);
s1[l1+l2] = '\0';
return s1;
}
* s1 += s2
*
* s1 must be previously created by some of str_XXX functions,
* s1 will be consumed and the new pointer will be returned
*/
static inline char*
str_append (char *s1, const char *s2)
{
return str_append_mem(s1, s2, strlen(s2));
}
* s1 += c
*
* `s' must be previously created by some of str_XXX functions,
* `s' will be consumed and the new pointer will be returned
*/
static inline char*
str_append_c (char *s, char c)
{
return str_append_mem(s, &c, 1);
}
*
* `s' must be previously created by some of str_XXX functions,
* `s' will be consumed and the new pointer will be returned
*/
char*
str_append_printf (char *s, const char *format, ...);
*/
char*
str_append_vprintf (char *s, const char *format, va_list ap);
*
* `s1' must be previously created by some of str_XXX functions,
* `s1' will be consumed and the new pointer will be returned
*/
static inline char*
str_assign (char *s1, const char *s2)
{
mem_trunc(s1);
return str_append(s1, s2);
}
* The returned pointer must be eventually freed by mem_free
*/
char*
str_concat (const char *s, ...);
* if string is not empty and the last character is not `c`,
* append `c' to the string
*
* `s' must be previously created by some of str_XXX functions,
* `s' will be consumed and the new pointer will be returned
*/
static inline char*
str_terminate (char *s, char c)
{
if (s[0] != '\0' && s[str_len(s) - 1] != c) {
s = str_append_c(s, c);
}
return s;
}
*/
bool
str_has_prefix (const char *s, const char *prefix);
*/
bool
str_has_suffix (const char *s, const char *suffix);
* This function modifies string in place, and returns pointer
* to original string, for convenience
*/
char*
str_trim (char *s);
*/
#define ptr_array_new(T) mem_resize((T*) NULL, 0, 1)
* Returns new, potentially reallocated array
*/
#define ptr_array_append(a,p) \
((__typeof__(a)) __ptr_array_append((void**)a, p))
*/
#define ptr_array_trunc(a) \
do { \
mem_trunc(a); \
a[0] = NULL; \
} while(0)
* Return non-negative index if pointer was found, -1 otherwise
*/
#define ptr_array_find(a,p) __ptr_array_find((void**) a, p)
* Returns value of deleted pointer or NULL, if index is out of range
*/
#define ptr_array_del(a,i) \
((__typeof__(*a)) __ptr_array_del((void**) a, i))
*/
static inline void**
__ptr_array_append (void **a, void *p)
{
size_t len = mem_len(a) + 1;
a = mem_resize(a, len, 1);
a[len - 1] = p;
a[len] = NULL;
return a;
}
*/
static inline int
__ptr_array_find (void **a, void *p)
{
size_t len = mem_len(a), i;
for (i = 0; i < len; i ++) {
if (a[i] == p) {
return (int) i;
}
}
return -1;
}
*/
static inline void*
__ptr_array_del (void **a, int i)
{
size_t len = mem_len(a);
void *p;
if (i < 0 || i >= (int) len) {
return NULL;
}
len --;
p = a[i];
memmove(&a[i], &a[i + 1], sizeof(void*) * (len - i));
mem_shrink(a, len);
a[len] = NULL;
return p;
}
#define safe_isspace(c) isspace((unsigned char) c)
#define safe_isxdigit(c) isxdigit((unsigned char) c)
#define safe_iscntrl(c) iscntrl((unsigned char) c)
#define safe_isprint(c) isprint((unsigned char) c)
#define safe_toupper(c) toupper((unsigned char) c)
#define safe_tolower(c) tolower((unsigned char) c)
* has a particular features:
*
* OS_HAVE_EVENTFD - Linux-like eventfd (2)
* OS_HAVE_RTNETLINK - Linux-like rtnetlink (7)
* OS_HAVE_AF_ROUTE - BSD-like AF_ROUTE
* OS_HAVE_LINUX_PROCFS - Linux-style procfs
* OS_HAVE_IP_MREQN - OS defines struct ip_mreqn
* OS_HAVE_ENDIAN_H - #include <endian.h> works
* OS_HAVE_SYS_ENDIAN_H - #include <sys/endian.h> works
*/
#ifdef __linux__
# define OS_HAVE_EVENTFD 1
# define OS_HAVE_RTNETLINK 1
# define OS_HAVE_LINUX_PROCFS 1
# define OS_HAVE_IP_MREQN 1
# define OS_HAVE_ENDIAN_H 1
#endif
#ifdef BSD
# define OS_HAVE_AF_ROUTE 1
# ifdef __FreeBSD__
# define OS_HAVE_SYS_ENDIAN_H 1
# else
# define OS_HAVE_ENDIAN_H 1
# endif
#endif
* free the returned string
*
* May return NULL in a case of error
*/
const char *
os_homedir (void);
* There is no need to free the returned string
*
* May return NULL in a case of error
*/
const char*
os_progname (void);
*/
int
os_mkdir (const char *path, mode_t mode);
* which indicates "no error" condition, or some opaque
* non-null pointer, which can be converted to string
* with textual description of the error, using the ESTRING()
* function
*
* Caller should not attempt to free the memory, referred
* by error or string, obtained from an error using the
* ESTRING() function
*/
typedef struct error_s *error;
*/
extern error ERROR_ENOMEM;
*/
static inline error
ERROR (const char *s)
{
return (error) s;
}
*/
static inline const char*
ESTRING (error err)
{
return (const char*) err;
}
*/
typedef enum {
ID_PROTO_UNKNOWN = -1,
ID_PROTO_ESCL,
ID_PROTO_WSD,
NUM_ID_PROTO
} ID_PROTO;
* For unknown ID returns NULL
*/
const char*
id_proto_name (ID_PROTO proto);
* For unknown name returns ID_PROTO_UNKNOWN
*/
ID_PROTO
id_proto_by_name (const char* name);
*/
typedef enum {
ID_SOURCE_UNKNOWN = -1,
ID_SOURCE_PLATEN,
ID_SOURCE_ADF_SIMPLEX,
ID_SOURCE_ADF_DUPLEX,
NUM_ID_SOURCE
} ID_SOURCE;
* For unknown ID returns NULL
*/
const char*
id_source_sane_name (ID_SOURCE id);
* For unknown name returns ID_SOURCE_UNKNOWN
*/
ID_SOURCE
id_source_by_sane_name (const char *name);
* This value exposed to the SANE API as a couple of read-only
* options, separate for width and height justification.
* Not all scanners provide this information
*/
typedef enum {
ID_JUSTIFICATION_UNKNOWN = -1,
ID_JUSTIFICATION_LEFT,
ID_JUSTIFICATION_CENTER,
ID_JUSTIFICATION_RIGHT,
ID_JUSTIFICATION_TOP,
ID_JUSTIFICATION_BOTTOM,
NUM_ID_JUSTIFICATION
} ID_JUSTIFICATION;
* For unknown ID returns NULL
*/
const char*
id_justification_sane_name (ID_JUSTIFICATION id);
*/
typedef enum {
ID_COLORMODE_UNKNOWN = -1,
ID_COLORMODE_COLOR,
ID_COLORMODE_GRAYSCALE,
ID_COLORMODE_BW1,
NUM_ID_COLORMODE
} ID_COLORMODE;
* For unknown ID returns NULL
*/
const char*
id_colormode_sane_name (ID_COLORMODE id);
* For unknown name returns ID_COLORMODE_UNKNOWN
*/
ID_COLORMODE
id_colormode_by_sane_name (const char *name);
*/
typedef enum {
ID_FORMAT_UNKNOWN = -1,
ID_FORMAT_JPEG,
ID_FORMAT_TIFF,
ID_FORMAT_PNG,
ID_FORMAT_PDF,
ID_FORMAT_BMP,
NUM_ID_FORMAT
} ID_FORMAT;
*/
const char*
id_format_mime_name (ID_FORMAT id);
* For unknown name returns ID_FORMAT_UNKNOWN
*/
ID_FORMAT
id_format_by_mime_name (const char *name);
*/
const char*
id_format_short_name (ID_FORMAT id);
*
* Intent hints scanner on a purpose of requested scan, which may
* imply carious parameters tweaks depending on that purpose.
*
* Intent maps to the eSCL Intent (see Mopria eSCL Technical Specification, 5)
* and WSD ContentType. The semantics of these two parameters looks very
* similar.
*
* Please note, eSCL defines also the ContentType parameter, but after
* some thinking and discussion we came to conclusion that Intent better
* maps our need.
*
* Dee discussion at: https://github.com/alexpevzner/sane-airscan/pull/351
*/
typedef enum {
ID_SCANINTENT_UNKNOWN = -1,
ID_SCANINTENT_UNSET,
ID_SCANINTENT_AUTO,
ID_SCANINTENT_DOCUMENT,
ID_SCANINTENT_TEXTANDGRAPHIC,
ID_SCANINTENT_PHOTO,
ID_SCANINTENT_PREVIEW,
ID_SCANINTENT_OBJECT,
ID_SCANINTENT_BUSINESSCARD,
ID_SCANINTENT_HALFTONE,
NUM_ID_SCANINTENT
} ID_SCANINTENT;
* For unknown ID returns NULL
*/
const char*
id_scanintent_sane_name (ID_SCANINTENT id);
* For unknown name returns ID_SCANINTENT_UNKNOWN
*/
ID_SCANINTENT
id_scanintent_by_sane_name (const char *name);
*/
unsigned int
devid_alloc (void);
*/
void
devid_free (unsigned int id);
* Note, it doesn't free already allocated IDs, only restarts
* counter from the beginning.
*/
void
devid_restart (void);
*/
void
devid_init (void);
*/
void
rand_bytes (void *buf, size_t n);
*/
SANE_Status
rand_init (void);
*/
void
rand_cleanup (void);
*
* It is wrapped into struct, so it can be returned
* by value, without need to mess with memory allocation
*/
typedef struct {
char text[sizeof("urn:uuid:ede05377-460e-4b4a-a5c0-423f9e02e8fa")];
} uuid;
*/
static inline bool
uuid_valid (uuid u)
{
return u.text[0] != '\0';
}
* urn:uuid:ede05377-460e-4b4a-a5c0-423f9e02e8fa
*/
uuid
uuid_rand (void);
* urn:uuid: prefix and so on, and takes only hexadecimal digits
* into considerations
*
* Check the returned uuid with uuid_valid() for possible parse errors
*/
uuid
uuid_parse (const char *in);
*/
uuid
uuid_hash (const char *s);
*/
static inline bool
uuid_equal (uuid u1, uuid u2)
{
return !strcmp(u1.text, u2.text);
}
*/
typedef enum {
INIFILE_SECTION,
INIFILE_VARIABLE,
INIFILE_COMMAND,
INIFILE_SYNTAX
} INIFILE_RECORD;
*/
typedef struct {
INIFILE_RECORD type;
const char *section;
const char *variable;
const char *value;
const char **tokv;
unsigned int tokc;
const char *file;
unsigned int line;
} inifile_record;
*/
typedef struct {
const char *file;
unsigned int line;
FILE *fp;
bool tk_open;
char *tk_buffer;
unsigned int *tk_offsets;
unsigned int tk_count;
char *buffer;
char *section;
char *variable;
char *value;
inifile_record record;
} inifile;
*/
inifile*
inifile_open (const char *name);
*/
void
inifile_close (inifile *file);
*/
const inifile_record*
inifile_read (inifile *file);
* - match is case-insensitive
* - difference in amount of free space is ignored
* - leading and trailing space is ignored
*/
bool
inifile_match_name (const char *n1, const char *n2);
* be passed by value
*/
typedef struct {
char text[109];
} ip_straddr;
* af must be AF_INET or AF_INET6
*/
ip_straddr
ip_straddr_from_ip (int af, const void *addr);
* AF_INET, AF_INET6, and AF_UNIX are supported
*
* If `withzone' is true, zone suffix will be appended, when appropriate
*/
ip_straddr
ip_straddr_from_sockaddr(const struct sockaddr *addr, bool withzone);
* AF_INET, AF_INET6, and AF_UNIX are supported
*
* Port will not be appended, if it matches provided default port
*
* If `withzone' is true, zone suffix will be appended, when appropriate
*
* If `withlocalhost` is true and address is 127.0.0.1 or ::1,
* "localhost" will be used instead of the IP address literal
*/
ip_straddr
ip_straddr_from_sockaddr_dport (const struct sockaddr *addr,
int dport, bool withzone, bool withlocalhost);
* af must be AF_INET or AF_INET6
*/
bool
ip_is_linklocal (int af, const void *addr);
*/
bool
ip_sockaddr_is_linklocal (const struct sockaddr *addr);
* af must be AF_INET or AF_INET6
*/
bool
ip_is_loopback (int af, const void *addr);
*/
typedef struct {
int af;
int ifindex;
union {
struct in_addr v4;
struct in6_addr v6;
} ip;
} ip_addr;
*/
static inline ip_addr
ip_addr_make (int ifindex, int af, const void *addr)
{
ip_addr ip_addr;
memset(&ip_addr, 0, sizeof(ip_addr));
ip_addr.af = af;
switch (ip_addr.af) {
case AF_INET:
memcpy(&ip_addr.ip.v4, addr, 4);
break;
case AF_INET6:
memcpy(&ip_addr.ip, addr, 16);
if (ip_is_linklocal(AF_INET6, &ip_addr.ip.v6)) {
ip_addr.ifindex = ifindex;
}
break;
}
return ip_addr;
}
*/
static inline ip_addr
ip_addr_from_sockaddr (const struct sockaddr *sockaddr)
{
ip_addr addr;
memset(&addr, 0, sizeof(addr));
addr.af = sockaddr->sa_family;
switch (addr.af) {
case AF_INET:
addr.ip.v4 = ((struct sockaddr_in*) sockaddr)->sin_addr;
break;
case AF_INET6:
addr.ip.v6 = ((struct sockaddr_in6*) sockaddr)->sin6_addr;
if (ip_is_linklocal(AF_INET6, &addr.ip.v6)) {
addr.ifindex = ((struct sockaddr_in6*) sockaddr)->sin6_scope_id;
}
break;
}
return addr;
}
*/
ip_straddr
ip_addr_to_straddr (ip_addr addr, bool withzone);
*/
static inline bool
ip_addr_equal (ip_addr a1, ip_addr a2)
{
if (a1.af != a2.af) {
return false;
}
switch (a1.af) {
case AF_INET:
return a1.ip.v4.s_addr == a2.ip.v4.s_addr;
case AF_INET6:
return a1.ifindex == a2.ifindex &&
!memcmp(a1.ip.v6.s6_addr, a2.ip.v6.s6_addr, 16);
}
return false;
}
*/
typedef struct {
ip_addr addr;
int mask;
} ip_network;
*/
ip_straddr
ip_network_to_straddr (ip_network net);
*/
bool
ip_network_contains (ip_network net, ip_addr addr);
*/
typedef struct ip_addrset ip_addrset;
*/
ip_addrset*
ip_addrset_new (void);
*/
void
ip_addrset_free (ip_addrset *addrset);
*/
bool
ip_addrset_lookup (const ip_addrset *addrset, ip_addr addr);
* actually added, false if it was already in the set
*/
bool
ip_addrset_add (ip_addrset *addrset, ip_addr addr);
*/
void
ip_addrset_add_unsafe (ip_addrset *addrset, ip_addr addr);
*/
void
ip_addrset_del (ip_addrset *addrset, ip_addr addr);
*/
void
ip_addrset_purge (ip_addrset *addrset);
* addrset += addrset2
*/
void
ip_addrset_merge (ip_addrset *addrset, const ip_addrset *addrset2);
*/
const ip_addr*
ip_addrset_addresses (const ip_addrset *addrset, size_t *count);
*/
bool
ip_addrset_is_intersect (const ip_addrset *set, const ip_addrset *set2);
* given network
*/
bool
ip_addrset_on_network (const ip_addrset *set, ip_network net);
* address family
*/
bool
ip_addrset_has_af (const ip_addrset *set, int af);
* in the ip_addrset:
* * addresses are sorted, IP4 addresses goes first
* * link-local addresses are skipped, if there are non-link-local ones
*
* Caller must use mem_free to release the returned string when
* it is not needed anymore
*/
char*
ip_addrset_friendly_str (const ip_addrset *set, char *s);
* it can be passed by value
*/
typedef struct {
char text[32];
} netif_name;
*/
typedef struct netif_addr netif_addr;
struct netif_addr {
netif_addr *next;
int ifindex;
netif_name ifname;
bool ipv6;
void *data;
char straddr[64];
union {
struct in_addr v4;
struct in6_addr v6;
} ip;
};
*/
typedef enum {
NETIF_DISTANCE_LOOPBACK,
NETIF_DISTANCE_DIRECT,
NETIF_DISTANCE_ROUTED
} NETIF_DISTANCE;
*/
NETIF_DISTANCE
netif_distance_get (const struct sockaddr *addr);
* of particular address family
*/
bool
netif_has_non_link_local_addr (int af, int ifindex);
* <0, if addr1 is closer that addr2
* >0, if addr2 is farther that addr2
* 0 if distance is equal
*/
static inline int
netif_distance_cmp (const struct sockaddr *addr1, const struct sockaddr *addr2)
{
int d1 = (int) netif_distance_get(addr1);
int d2 = (int) netif_distance_get(addr2);
return d1 - d2;
}
* The returned list is sorted
*/
netif_addr*
netif_addr_list_get (void);
*/
void
netif_addr_list_free (netif_addr *list);
* lists of network interface addresses
*/
typedef struct {
netif_addr *added, *removed;
netif_addr *preserved;
} netif_diff;
*
* It works by tossing nodes between 3 output lists:
* * if node is present in list2 only, it is moved
* to netif_diff.added
* * if node is present in list1 only, it is moved
* to netif_diff.removed
* * if node is present in both lists, node from
* list1 is moved to preserved, and node from
* list2 is released
*
* It assumes, both lists are sorted, as returned
* by netif_addr_get(). Returned lists are also sorted
*/
netif_diff
netif_diff_compute (netif_addr *list1, netif_addr *list2);
*
* Input lists are consumed and new list is created.
*
* Input lists are assumed to be sorted, and output
* list will be sorted as well
*/
netif_addr*
netif_addr_list_merge (netif_addr *list1, netif_addr *list2);
*/
typedef struct netif_notifier netif_notifier;
*/
netif_notifier*
netif_notifier_create (void (*callback) (void*), void *data);
*/
void
netif_notifier_free (netif_notifier *notifier);
*/
SANE_Status
netif_init (void);
*/
void
netif_cleanup (void);
*/
#define CONF_DEVICE_DISABLE "disable"
*/
typedef struct conf_device conf_device;
struct conf_device {
unsigned int devid;
const char *name;
ID_PROTO proto;
http_uri *uri;
conf_device *next;
};
*/
typedef enum {
WSDD_FAST,
WSDD_FULL,
WSDD_OFF
} WSDD_MODE;
*/
typedef struct conf_blacklist conf_blacklist;
struct conf_blacklist {
const char *model;
const char *name;
ip_network net;
conf_blacklist *next;
};
*/
typedef struct {
bool dbg_enabled;
const char *dbg_trace;
bool dbg_hexdump;
conf_device *devices;
bool discovery;
bool model_is_netname;
bool proto_auto;
WSDD_MODE wsdd_mode;
const char *socket_dir;
conf_blacklist *blacklist;
bool pretend_local;
} conf_data;
#define CONF_INIT { \
.dbg_enabled = false, \
.dbg_trace = NULL, \
.dbg_hexdump = false, \
.devices = NULL, \
.discovery = true, \
.model_is_netname = true, \
.proto_auto = true, \
.wsdd_mode = WSDD_FAST, \
.socket_dir = NULL, \
.pretend_local = false \
}
extern conf_data conf;
*/
void
conf_load (void);
* data into initial state
*/
void
conf_unload (void);
*
* Pollable events allow to wait until some event happens
* and can be used in combination with select()/poll()
* system calls
*/
typedef struct pollable pollable;
*/
pollable*
pollable_new (void);
*/
void
pollable_free (pollable *p);
*
* When pollable event becomes "ready", this file descriptor
* becomes readable from the select/poll point of view
*/
int
pollable_get_fd (pollable *p);
*/
void
pollable_signal (pollable *p);
*/
void
pollable_reset (pollable *p);
*/
void
pollable_wait (pollable *p);
*/
typedef int64_t timestamp;
*/
static inline timestamp
timestamp_now (void)
{
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return (timestamp) t.tv_sec * 1000 + (timestamp) t.tv_nsec / 1000000;
}
*/
SANE_Status
eloop_init (void);
*/
void
eloop_cleanup (void);
* on a event loop thread context, once when event
* loop is started, and second time when it is stopped
*
* Start callbacks are called in the same order as
* they were added. Stop callbacks are called in a
* reverse order
*/
void
eloop_add_start_stop_callback (void (*callback) (bool start));
*/
void
eloop_thread_start (void);
*/
void
eloop_thread_stop (void);
*/
void
eloop_mutex_lock (void);
*/
void
eloop_mutex_unlock (void);
*/
void
eloop_cond_wait (pthread_cond_t *cond);
*/
const AvahiPoll*
eloop_poll_get (void);
* the eloop_call().
*
* It is safe to use ELOOP_CALL_BADID as parameter to eloop_call_cancel().
* Calling eloop_call_cancel(ELOOP_CALL_BADID) is guaranteed to do nothing.
*/
#define ELOOP_CALL_BADID (~(uint64_t) 0)
* The returned value can be supplied as a `callid'
* parameter for the eloop_call_cancel() function
*/
uint64_t
eloop_call (void (*func)(void*), void *data);
*
* This is safe to cancel already finished call (at this
* case nothing will happen)
*/
void
eloop_call_cancel (uint64_t callid);
* of event loop thread, when event is triggered. This is
* safe to trigger the event from a context of any thread
* or even from a signal handler
*/
typedef struct eloop_event eloop_event;
*/
eloop_event*
eloop_event_new (void (*callback)(void *), void *data);
*/
void
eloop_event_free (eloop_event *event);
*/
void
eloop_event_trigger (eloop_event *event);
* interval
*/
typedef struct eloop_timer eloop_timer;
*/
eloop_timer*
eloop_timer_new (int timeout, void (*callback)(void *), void *data);
*
* Caller SHOULD NOT cancel expired timer (timer with called
* callback) -- this is done automatically
*/
void
eloop_timer_cancel (eloop_timer *timer);
* readable, writable or both, depending on its
* event mask
*/
typedef struct eloop_fdpoll eloop_fdpoll;
*/
typedef enum {
ELOOP_FDPOLL_READ = (1 << 0),
ELOOP_FDPOLL_WRITE = (1 << 1),
ELOOP_FDPOLL_BOTH = ELOOP_FDPOLL_READ | ELOOP_FDPOLL_WRITE
} ELOOP_FDPOLL_MASK;
*/
const char*
eloop_fdpoll_mask_str (ELOOP_FDPOLL_MASK mask);
*
* Callback will be called, when file will be ready for read/write/both,
* depending on mask
*
* Initial mask value is 0, and it can be changed, using
* eloop_fdpoll_set_mask() function
*/
eloop_fdpoll*
eloop_fdpoll_new (int fd,
void (*callback) (int, void*, ELOOP_FDPOLL_MASK), void *data);
*/
void
eloop_fdpoll_free (eloop_fdpoll *fdpoll);
*/
ELOOP_FDPOLL_MASK
eloop_fdpoll_set_mask (eloop_fdpoll *fdpoll, ELOOP_FDPOLL_MASK mask);
* in the memory, owned by the event loop
*
* Caller should not free returned string. This is safe
* to use the returned string as an argument to the
* subsequent eloop_eprintf() call.
*
* The returned string remains valid until next call
* to eloop_eprintf(), which makes it usable to
* report errors up by the stack. However, it should
* not be assumed, that the string will remain valid
* on a next eloop roll, so don't save this string
* anywhere, if you need to do so, create a copy!
*/
error
eloop_eprintf(const char *fmt, ...);
*/
http_uri*
http_uri_new (const char *str, bool strip_fragment);
*/
http_uri*
http_uri_clone (const http_uri *old);
* true, scheme, host and port are taken from the
* base URI
*/
http_uri*
http_uri_new_relative (const http_uri *base, const char *path,
bool strip_fragment, bool path_only);
*/
void
http_uri_free (http_uri *uri);
*/
const char*
http_uri_str (http_uri *uri);
*/
const struct sockaddr*
http_uri_addr (const http_uri *uri);
* if host address is not literal
*/
static inline int
http_uri_af (const http_uri *uri)
{
const struct sockaddr *addr = http_uri_addr(uri);
return addr ? addr->sa_family : AF_UNSPEC;
}
*/
static inline bool
http_uri_is_literal (const http_uri *uri)
{
return http_uri_addr(uri) != NULL;
}
*/
static inline bool
http_uri_is_loopback (const http_uri *uri)
{
const struct sockaddr *addr = http_uri_addr(uri);
const void *ip = NULL;
if (addr == NULL) {
return false;
}
switch (addr->sa_family) {
case AF_INET:
ip = &(((struct sockaddr_in*) addr)->sin_addr);
break;
case AF_INET6:
ip = &(((struct sockaddr_in6*) addr)->sin6_addr);
break;
}
if (ip != NULL) {
return ip_is_loopback(addr->sa_family, ip);
}
return false;
}
*
* Note, if URL has empty path (i.e., "http://1.2.3.4"), the
* empty string will be returned
*/
const char*
http_uri_get_path (const http_uri *uri);
*/
void
http_uri_set_path (http_uri *uri, const char *path);
* not included.
*
* IPv6 literal addresses are returned in square brackets
* (i.e., [fe80::217:c8ff:fe7b:6a91%4])
*
* Note, the subsequent modifications of URI, such as http_uri_fix_host(),
* http_uri_fix_ipv6_zone() etc, may make the returned string invalid,
* so if you need to keep it for a long time, better make a copy
*/
const char*
http_uri_get_host (const http_uri *uri);
* specified string.
*
* It does its best to compare domain names correctly, taking
* in account only significant difference (for example, the difference
* in upper/lower case * in domain names is not significant).
*/
bool
http_uri_host_is (const http_uri *uri, const char *host);
* IP address
*/
bool
http_uri_host_is_literal (const http_uri *uri);
*/
void
http_uri_set_host_addr (http_uri *uri, ip_addr addr);
* replace uri's host and port with values taken from the base_uri
*/
void
http_uri_fix_host (http_uri *uri, const http_uri *base_uri, const char *match);
*/
void
http_uri_fix_ipv6_zone (http_uri *uri, int ifindex);
*
* If address is not IPv6 or doesn't have zone suffix, it is
* not changed
*/
void
http_uri_strip_zone_suffux (http_uri *uri);
*/
void
http_uri_fix_end_slash (http_uri *uri);
*/
bool
http_uri_equal (const http_uri *uri1, const http_uri *uri2);
*/
typedef struct {
const char *content_type;
const void *bytes;
size_t size;
} http_data;
*/
http_data*
http_data_ref (http_data *data);
*/
void
http_data_unref (http_data *data);
*/
typedef struct http_data_queue http_data_queue;
*/
http_data_queue*
http_data_queue_new (void);
*/
void
http_data_queue_free (http_data_queue *queue);
*/
void
http_data_queue_push (http_data_queue *queue, http_data *data);
*/
http_data*
http_data_queue_pull (http_data_queue *queue);
*/
int
http_data_queue_len (const http_data_queue *queue);
*/
static inline bool
http_data_queue_empty (const http_data_queue *queue)
{
return http_data_queue_len(queue) == 0;
}
*/
void
http_data_queue_purge (http_data_queue *queue);
*/
typedef struct http_client http_client;
*/
http_client*
http_client_new (log_ctx *log, void *ptr);
*/
void
http_client_free (http_client *client);
* in a case of transport error it will be called instead
* of the http_query callback
*/
void
http_client_onerror (http_client *client,
void (*callback)(void *ptr, error err));
*/
void
http_client_cancel (http_client *client);
*/
void
http_client_timeout (http_client *client, int timeout);
*/
bool
http_client_has_pending (const http_client *client);
*/
typedef struct http_query http_query;
*
* Newly created http_query takes ownership on uri and body (if not NULL).
* The method and content_type assumed to be constant strings.
*/
http_query*
http_query_new (http_client *client, http_uri *uri, const char *method,
char *body, const char *content_type);
*
* Newly created http_query takes ownership on uri and body (if not NULL).
* The method and content_type assumed to be constant strings.
*/
http_query*
http_query_new_len (http_client *client, http_uri *uri, const char *method,
void *body, size_t body_len, const char *content_type);
*
* Newly created http_query takes ownership on body (if not NULL).
* The method and content_type assumed to be constant strings.
*/
http_query*
http_query_new_relative(http_client *client,
const http_uri *base_uri, const char *path,
const char *method, char *body, const char *content_type);
*
* This function may be called multiple times (each subsequent call overrides
* a previous one)
*/
void
http_query_timeout (http_query *q, int timeout);
*
* This flag notifies, that http_query issued is only interested
* in the HTTP response headers, not body
*
* If this flag is set, after successful reception of response
* HTTP header, errors in fetching response body is ignored
*/
void
http_query_no_need_response_body (http_query *q);
*
* This function may be called multiple times (each subsequent call overrides
* a previous one).
*/
void
http_query_force_port(http_query *q, bool force_port);
* set by http_client_onerror()
*
* If canllback is NULL, the completion callback, specified on a
* http_query_submit() call, will be used even in a case of
* transport error.
*/
void
http_query_onerror (http_query *q, void (*onerror)(void *ptr, error err));
* redirect and may modify the supplied URI
*/
void
http_query_onredir (http_query *q,
void (*onredir)(void *ptr, http_uri *uri, const http_uri *orig_uri));
* is completed
*/
void
http_query_onrxhdr (http_query *q, void (*onrxhdr)(void *ptr, http_query *q));
*
* When query is finished, callback will be called. After return from
* callback, memory, owned by http_query will be invalidated
*/
void
http_query_submit (http_query *q, void (*callback)(void *ptr, http_query *q));
* submitted. And this function should not be called before
* http_query_submit()
*/
timestamp
http_query_timestamp (const http_query *q);
* Completion callback may later use http_query_get_uintptr()
* to fetch this value
*/
void
http_query_set_uintptr (http_query *q, uintptr_t u);
*/
uintptr_t
http_query_get_uintptr (http_query *q);
*
* Both transport errors and erroneous HTTP response codes
* considered as errors here
*/
error
http_query_error (const http_query *q);
*
* Only transport errors considered errors here
*/
error
http_query_transport_error (const http_query *q);
* with error
*/
int
http_query_status (const http_query *q);
*/
const char*
http_query_status_string (const http_query *q);
*
* It works as http_query_orig_uri() before query is submitted
* or after it is completed, and as http_query_real_uri() in
* between
*
* This function is deprecated, use http_query_orig_uri()
* or http_query_real_uri() instead
*/
http_uri*
http_query_uri (const http_query *q);
*/
http_uri*
http_query_orig_uri (const http_query *q);
* in a case of HTTP redirection
*/
http_uri*
http_query_real_uri (const http_query *q);
*/
const char*
http_query_method (const http_query *q);
*/
void
http_query_set_request_header (http_query *q, const char *name,
const char *value);
*/
const char*
http_query_get_request_header (const http_query *q, const char *name);
*/
const char*
http_query_get_response_header (const http_query *q, const char *name);
*
* You need to http_data_ref(), if you want data to remain valid
* after query end of life
*/
http_data*
http_query_get_request_data (const http_query *q);
*
* You need to http_data_ref(), if you want data to remain valid
* after query end of life
*/
http_data*
http_query_get_response_data (const http_query *q);
*/
int
http_query_get_mp_response_count (const http_query *q);
*
* You need to http_data_ref(), if you want data to remain valid
* after query end of life
*/
http_data*
http_query_get_mp_response_data (const http_query *q, int n);
*/
void
http_query_foreach_request_header (const http_query *q,
void (*callback)(const char *name, const char *value, void *ptr),
void *ptr);
*/
void
http_query_foreach_response_header (const http_query *q,
void (*callback)(const char *name, const char *value, void *ptr),
void *ptr);
* This function is intended for testing purposes, not for regular use
*/
error
http_query_test_decode_response (http_query *q, const void *data, size_t size);
*/
typedef enum {
HTTP_SCHEME_UNSET = -1,
HTTP_SCHEME_HTTP,
HTTP_SCHEME_HTTPS,
HTTP_SCHEME_UNIX
} HTTP_SCHEME;
*/
#ifndef NO_HTTP_STATUS
enum {
HTTP_STATUS_OK = 200,
HTTP_STATUS_CREATED = 201,
HTTP_STATUS_NOT_FOUND = 404,
HTTP_STATUS_GONE = 410,
HTTP_STATUS_SERVICE_UNAVAILABLE = 503
};
#endif
*/
SANE_Status
http_init (void);
*/
void
http_cleanup (void);
* file
*/
typedef struct trace trace;
*/
SANE_Status
trace_init (void);
*/
void
trace_cleanup (void);
*/
trace*
trace_open (const char *device_name);
*/
trace*
trace_ref (trace *t);
*/
void
trace_unref (trace *t);
*/
void
trace_http_query_hook (trace *t, http_query *q);
*/
void
trace_printf (trace *t, const char *fmt, ...);
*/
void
trace_error (trace *t, error err);
*/
void
trace_dump_body (trace *t, http_data *data);
* Each line is prefixed with the `prefix` character
*/
void
trace_hexdump (trace *t, char prefix, const void *data, size_t size);
*/
static inline SANE_Word*
sane_word_array_new (void)
{
return mem_new(SANE_Word,1);
}
*/
static inline void
sane_word_array_free (SANE_Word *a)
{
mem_free(a);
}
*/
static inline void
sane_word_array_reset (SANE_Word **a)
{
(*a)[0] = 0;
}
*/
static inline size_t
sane_word_array_len (const SANE_Word *a)
{
return (size_t) a[0];
}
*/
static inline SANE_Word*
sane_word_array_append (SANE_Word *a, SANE_Word w)
{
size_t len = sane_word_array_len(a) + 1;
a = mem_resize(a, len + 1, 0);
a[0] = len;
a[len] = w;
return a;
}
*/
void
sane_word_array_bound (SANE_Word *a, SANE_Word min, SANE_Word max);
*/
void
sane_word_array_sort (SANE_Word *a);
*/
SANE_Word*
sane_word_array_intersect_sorted ( const SANE_Word *a1, const SANE_Word *a2);
*/
static inline SANE_String*
sane_string_array_new (void)
{
return ptr_array_new(SANE_String);
}
*/
static inline void
sane_string_array_free (SANE_String *a)
{
mem_free(a);
}
*/
static inline void
sane_string_array_reset (SANE_String *a)
{
ptr_array_trunc(a);
}
*/
static inline size_t
sane_string_array_len (const SANE_String *a)
{
return mem_len(a);
}
*/
static inline SANE_String*
sane_string_array_append(SANE_String *a, SANE_String s)
{
return ptr_array_append(a, s);
}
*/
size_t
sane_string_array_max_strlen(const SANE_String *a);
*/
static inline const SANE_Device**
sane_device_array_new (void)
{
return ptr_array_new(const SANE_Device*);
}
*/
static inline void
sane_device_array_free (const SANE_Device **a)
{
mem_free(a);
}
*/
static inline size_t
sane_device_array_len (const SANE_Device * const *a)
{
return mem_len(a);
}
*/
static inline const SANE_Device**
sane_device_array_append(const SANE_Device **a, SANE_Device *d)
{
return ptr_array_append(a, d);
}
*
* For XML writer namespaces are simply added to the root
* node attributes
*
* XML reader performs prefix substitutions
*
* If namespace substitution is enabled, for each note, if its
* namespace matches the pattern, will be reported with name prefix
* defined by substitution rule, regardless of prefix actually used
* in the document
*
* Example:
* <namespace:nodes xmlns:namespace="http://www.example.com/namespace">
* <namespace:node1/>
* <namespace:node2/>
* <namespace:node3/>
* </namespace:nodes>
*
* rule: {"ns", "http://www.example.com/namespace"}
*
* With this rule set, all nodes will be reported as if they
* had the "ns" prefix, though actually their prefix in document
* is different
*
* XML reader interprets namespace uri as a glob-style pattern,
* as used by fnmatch (3) function with flags = 0
*/
typedef struct {
const char *prefix;
const char *uri;
} xml_ns;
*
* Attributes are supported by XML writer. Array of attributes
* is terminated by the {NULL, NULL} attribute
*/
typedef struct {
const char *name;
const char *value;
} xml_attr;
*/
typedef struct xml_rd xml_rd;
* starting from the root node
*
* The 'ns' argument, if not NULL, points to array of substitution
* rules. Last element must have NULL prefix and url
*
* Array of rules considered to be statically allocated
* (at least, it can remain valid during reader life time)
*
* On success, saves newly constructed reader into
* the xml parameter.
*/
error
xml_rd_begin (xml_rd **xml, const char *xml_text, size_t xml_len,
const xml_ns *ns);
*/
void
xml_rd_finish (xml_rd **xml);
*/
unsigned int
xml_rd_depth (xml_rd *xml);
*/
bool
xml_rd_end (xml_rd *xml);
*/
void
xml_rd_next (xml_rd *xml);
*
* If depth > 0, it will not return from nested nodes
* upper the specified depth
*/
void
xml_rd_deep_next (xml_rd *xml, unsigned int depth);
*/
void
xml_rd_enter (xml_rd *xml);
*/
void
xml_rd_leave (xml_rd *xml);
*
* The returned string remains valid, until reader is cleaned up
* or current node is changed (by set/next/enter/leave operations).
* You don't need to free this string explicitly
*/
const char*
xml_rd_node_name (xml_rd *xml);
*/
const char*
xml_rd_node_path (xml_rd *xml);
*/
bool
xml_rd_node_name_match (xml_rd *xml, const char *pattern);
*
* The returned string remains valid, until reader is cleaned up
* or current node is changed (by set/next/enter/leave operations).
* You don't need to free this string explicitly
*/
const char*
xml_rd_node_value (xml_rd *xml);
*/
error
xml_rd_node_value_uint (xml_rd *xml, SANE_Word *val);
*/
typedef struct xml_wr xml_wr;
*
* The ns parameter must be terminated by {NULL, NULL} structure
*/
xml_wr*
xml_wr_begin (const char *root, const xml_ns *ns);
* Caller must g_free() this string after use
*/
char*
xml_wr_finish (xml_wr *xml);
* of XML (without indentation and new lines)
*/
char*
xml_wr_finish_compact (xml_wr *xml);
*/
void
xml_wr_add_text (xml_wr *xml, const char *name, const char *value);
*/
void
xml_wr_add_text_attr (xml_wr *xml, const char *name, const char *value,
const xml_attr *attrs);
*/
void
xml_wr_add_uint (xml_wr *xml, const char *name, unsigned int value);
*/
void
xml_wr_add_uint_attr (xml_wr *xml, const char *name, unsigned int value,
const xml_attr *attrs);
*/
void
xml_wr_add_bool (xml_wr *xml, const char *name, bool value);
*/
void
xml_wr_add_bool_attr (xml_wr *xml, const char *name, bool value,
const xml_attr *attrs);
*/
void
xml_wr_enter (xml_wr *xml, const char *name);
*/
void
xml_wr_enter_attr (xml_wr *xml, const char *name, const xml_attr *attrs);
*/
void
xml_wr_leave (xml_wr *xml);
* and returns true, or fails, writes nothing to file and returns false
*/
bool
xml_format (FILE *fp, const char *xml_text, size_t xml_len);
*/
enum {
OPT_NUM_OPTIONS,
OPT_GROUP_STANDARD,
OPT_SCAN_RESOLUTION,
OPT_SCAN_COLORMODE,
OPT_SCAN_INTENT,
OPT_SCAN_SOURCE,
OPT_GROUP_GEOMETRY,
OPT_SCAN_TL_X,
OPT_SCAN_TL_Y,
OPT_SCAN_BR_X,
OPT_SCAN_BR_Y,
OPT_GROUP_ENHANCEMENT,
OPT_BRIGHTNESS,
OPT_CONTRAST,
OPT_SHADOW,
OPT_HIGHLIGHT,
OPT_GAMMA,
OPT_NEGATIVE,
OPT_JUSTIFICATION_X,
OPT_JUSTIFICATION_Y,
NUM_OPTIONS
};
* (missed from sane/sameopt.h)
*/
#define OPTVAL_SOURCE_PLATEN "Flatbed"
#define OPTVAL_SOURCE_ADF_SIMPLEX "ADF"
#define OPTVAL_SOURCE_ADF_DUPLEX "ADF Duplex"
#define OPTVAL_JUSTIFICATION_LEFT "left"
#define OPTVAL_JUSTIFICATION_CENTER "center"
#define OPTVAL_JUSTIFICATION_RIGHT "right"
#define OPTVAL_JUSTIFICATION_TOP "top"
#define OPTVAL_JUSTIFICATION_BOTTOM "bottom"
#define SANE_NAME_ADF_JUSTIFICATION_X "adf-justification-x"
#define SANE_TITLE_ADF_JUSTIFICATION_X SANE_I18N("ADF Width Justification")
#define SANE_DESC_ADF_JUSTIFICATION_X \
SANE_I18N("ADF width justification (left/right/center)")
#define SANE_NAME_ADF_JUSTIFICATION_Y "adf-justification-y"
#define SANE_TITLE_ADF_JUSTIFICATION_Y SANE_I18N("ADF Height Justification")
#define SANE_DESC_ADF_JUSTIFICATION_Y \
SANE_I18N("ADF height justification (top/bottom/center)")
*/
static inline bool
opt_is_enhancement (int opt)
{
return OPT_BRIGHTNESS <= opt && opt <= OPT_NEGATIVE;
}
*/
enum {
DEVCAPS_SOURCE_INTENT_DOCUMENT = (1 << 3),
DEVCAPS_SOURCE_INTENT_TXT_AND_GRAPH = (1 << 4),
DEVCAPS_SOURCE_INTENT_PHOTO = (1 << 5),
DEVCAPS_SOURCE_INTENT_PREVIEW = (1 << 6),
DEVCAPS_SOURCE_INTENT_ALL =
DEVCAPS_SOURCE_INTENT_DOCUMENT |
DEVCAPS_SOURCE_INTENT_TXT_AND_GRAPH |
DEVCAPS_SOURCE_INTENT_PHOTO |
DEVCAPS_SOURCE_INTENT_PREVIEW,
DEVCAPS_SOURCE_RES_DISCRETE = (1 << 7),
DEVCAPS_SOURCE_RES_RANGE = (1 << 8),
DEVCAPS_SOURCE_RES_ALL =
DEVCAPS_SOURCE_RES_DISCRETE |
DEVCAPS_SOURCE_RES_RANGE,
DEVCAPS_SOURCE_HAS_SIZE = (1 << 12),
derivatives are valid */
DEVCAPS_SOURCE_PWG_DOCFMT = (1 << 13),
DEVCAPS_SOURCE_SCAN_DOCFMT_EXT = (1 << 14),
};
*/
#define DEVCAPS_FORMATS_SUPPORTED \
((1 << ID_FORMAT_JPEG) | \
(1 << ID_FORMAT_PNG) | \
(1 << ID_FORMAT_TIFF) | \
(1 << ID_FORMAT_BMP))
*
* Note, currently the only image format we support is JPEG
* With JPEG, ID_COLORMODE_BW1 cannot be supported
*/
#define DEVCAPS_COLORMODES_SUPPORTED \
((1 << ID_COLORMODE_COLOR) | \
(1 << ID_COLORMODE_GRAYSCALE))
*/
typedef struct {
unsigned int flags;
unsigned int colormodes;
unsigned int formats;
unsigned int scanintents;
SANE_Word min_wid_px, max_wid_px;
SANE_Word min_hei_px, max_hei_px;
SANE_Word *resolutions;
SANE_Range res_range;
SANE_Range win_x_range_mm;
SANE_Range win_y_range_mm;
} devcaps_source;
*/
devcaps_source*
devcaps_source_new (void);
*/
void
devcaps_source_free (devcaps_source *src);
*/
devcaps_source*
devcaps_source_clone (const devcaps_source *src);
* only capabilities, supported by two input sources
*
* Returns NULL, if sources cannot be merged
*/
devcaps_source*
devcaps_source_merge (const devcaps_source *s1, const devcaps_source *s2);
*/
typedef struct {
const char *protocol;
SANE_Word units;
bool compression_ok;
SANE_Range compression_range;
SANE_Word compression_norm;
devcaps_source *src[NUM_ID_SOURCE];
ID_JUSTIFICATION justification_x;
ID_JUSTIFICATION justification_y;
} devcaps;
*/
void
devcaps_init (devcaps *caps);
*/
void
devcaps_cleanup (devcaps *caps);
*/
void
devcaps_reset (devcaps *caps);
*
* The 3rd parameter, 'trace' configures the debug level
* (log_debug vs log_trace) of the generated output
*/
void
devcaps_dump (log_ctx *log, devcaps *caps, bool trace);
*/
typedef struct {
devcaps caps;
SANE_Option_Descriptor desc[NUM_OPTIONS];
ID_SOURCE src;
ID_COLORMODE colormode_emul;
ID_COLORMODE colormode_real;
ID_SCANINTENT scanintent;
SANE_Word resolution;
SANE_Fixed tl_x, tl_y;
SANE_Fixed br_x, br_y;
SANE_Parameters params;
SANE_String *sane_sources;
SANE_String *sane_colormodes;
SANE_String *sane_scanintents;
SANE_Fixed brightness;
SANE_Fixed contrast;
SANE_Fixed shadow;
SANE_Fixed highlight;
SANE_Fixed gamma;
bool negative;
} devopt;
*/
void
devopt_init (devopt *opt);
*/
void
devopt_cleanup (devopt *opt);
* devopt.caps needs to be properly filled.
*/
void
devopt_set_defaults (devopt *opt);
*/
SANE_Status
devopt_set_option (devopt *opt, SANE_Int option, void *value, SANE_Word *info);
*/
SANE_Status
devopt_get_option (devopt *opt, SANE_Int option, void *value);
* of device IP addresses are independent between IPv4/IPv6 protocols
* and between different network interfaces
*
* It means that some of device addresses may be already discovered,
* while others still pending
*
* From another hand, some of addresses that we hope to discover may
* be not available at all. For example, device may have IPv4 address
* but IPv6 address may be missed.
*
* So once we have at least one address discovered, we limit discovery
* of another addresses by this constant.
*
* This parameter is common for both MDNS and WSDD worlds
*
* The timeout is in milliseconds
*/
#define ZEROCONF_PUBLISH_DELAY 1000
*/
extern log_ctx *zeroconf_log;
*/
typedef struct zeroconf_device zeroconf_device;
*/
typedef struct zeroconf_endpoint zeroconf_endpoint;
struct zeroconf_endpoint {
ID_PROTO proto;
http_uri *uri;
zeroconf_endpoint *next;
};
* The same device may be discovered using multiple methods
*/
typedef enum {
* scanner presence in the network
*/
ZEROCONF_MDNS_HINT,
* scanner endpoints
*/
ZEROCONF_USCAN_TCP,
ZEROCONF_USCANS_TCP,
ZEROCONF_WSD,
NUM_ZEROCONF_METHOD
} ZEROCONF_METHOD;
* Multiple findings can point to the same device, and even
* endpoints may duplicate between findings (say, if the same
* device found using multiple network interfaces or using various
* discovery methods)
*
* zeroconf_finding are bound to method and interface index
*/
typedef struct {
ZEROCONF_METHOD method;
const char *name;
const char *model;
WSDD non-scanner devices */
uuid uuid;
ip_addrset *addrs;
int ifindex;
zeroconf_endpoint *endpoints;
* and should not be used by discovery providers
*/
zeroconf_device *device;
ll_node list_node;
} zeroconf_finding;
* by index+name, for qsort
*/
int
zeroconf_finding_qsort_by_index_name (const void *p1, const void *p2);
*
* Memory, referred by the finding, remains owned by
* caller, and caller is responsible to keep this
* memory valid until zeroconf_finding_withdraw()
* is called
*
* The 'endpoinds' field may be NULL. This mechanism is
* used by WS-Discovery to notify zeroconf that scanning
* for particular UUID has been finished, though without
* success.
*/
void
zeroconf_finding_publish (zeroconf_finding *finding);
*/
void
zeroconf_finding_withdraw (zeroconf_finding *finding);
* for the method is done
*/
void
zeroconf_finding_done (ZEROCONF_METHOD method);
*/
typedef struct {
const char *ident;
const char *name;
const char *model;
zeroconf_endpoint *endpoints;
} zeroconf_devinfo;
*/
SANE_Status
zeroconf_init (void);
*/
void
zeroconf_cleanup (void);
*/
const SANE_Device**
zeroconf_device_list_get (void);
*/
void
zeroconf_device_list_free (const SANE_Device **dev_list);
* by zeroconf_device_list_get())
*
* Caller becomes owner of resources (name and list of endpoints),
* referred by the returned zeroconf_devinfo
*
* Caller must free these resources, using zeroconf_devinfo_free()
*/
zeroconf_devinfo*
zeroconf_devinfo_lookup (const char *ident);
* The format "protocol:name:url" is accepted to directly specify a device
* without listing it in the config or finding it with autodiscovery. Try
* to parse an identifier as that format. On success, returns a newly allocated
* zeroconf_devinfo that the caller must free with zeroconf_devinfo_free(). On
* failure, returns NULL.
*/
zeroconf_devinfo*
zeroconf_parse_devinfo_from_ident(const char *ident);
*/
void
zeroconf_devinfo_free (zeroconf_devinfo *devinfo);
* takes ownership of uri string
*/
zeroconf_endpoint*
zeroconf_endpoint_new (ID_PROTO proto, http_uri *uri);
*/
void
zeroconf_endpoint_free_single (zeroconf_endpoint *endpoint);
*/
zeroconf_endpoint*
zeroconf_endpoint_list_copy (const zeroconf_endpoint *list);
*/
void
zeroconf_endpoint_list_free (zeroconf_endpoint *list);
*/
zeroconf_endpoint*
zeroconf_endpoint_list_sort (zeroconf_endpoint *list);
*/
zeroconf_endpoint*
zeroconf_endpoint_list_sort_dedup (zeroconf_endpoint *list);
* endpoint (i.e., endpoint with the same URI and protocol)
*/
bool
zeroconf_endpoint_list_contains (const zeroconf_endpoint *list,
const zeroconf_endpoint *endpoint);
* of the specified address family
*/
bool
zeroconf_endpoint_list_has_non_link_local_addr (int af,
const zeroconf_endpoint *list);
*/
void
mdns_initscan_timer_expired (void);
*/
SANE_Status
mdns_init (void);
*/
void
mdns_cleanup (void);
*/
typedef struct mdns_resolver mdns_resolver;
*/
typedef struct mdns_query mdns_query;
*/
mdns_resolver*
mdns_resolver_new (int ifindex);
* by mdns_resolver_new()
*/
void
mdns_resolver_free (mdns_resolver *resolver);
*/
void
mdns_resolver_cancel (mdns_resolver *resolver);
*/
bool
mdns_resolver_has_pending (mdns_resolver *resolver);
* name. When resolving is done, successfully or not, callback will be
* called
*
* The ptr parameter is passed to the callback without any interpretation
* as a user-defined argument
*
* Answer is a set of discovered IP addresses. It is owned by resolver,
* callback should not free it and should not assume that it is still
* valid after return from callback
*/
mdns_query*
mdns_query_submit (mdns_resolver *resolver,
const char *name,
void (*callback)(const mdns_query *query),
void *ptr);
* be released and callback will not be called
*
* Note, mdns_query pointer is valid when obtained from mdns_query_sumbit
* and until canceled or return from callback.
*/
void
mdns_query_cancel (mdns_query *query);
* when query was submitted
*/
const char*
mdns_query_get_name (const mdns_query *query);
*/
const ip_addrset*
mdns_query_get_answer (const mdns_query *query);
* with query when it was submitted
*/
void*
mdns_query_get_ptr (const mdns_query *query);
* with model names matching the specified parent.
*
* Several instances of the same device (i.e. printer vs scanner) are
* counted only once per network interface.
*
* WSDD uses this function to decide when to use extended discovery
* time (some devices are known to be hard for WD-Discovery)
*
* Pattern is the glob-style expression, applied to the model name
* of discovered devices.
*/
unsigned int
mdns_device_count_by_model (int ifindex, const char *pattern);
*/
void
wsdd_initscan_timer_expired (void);
*/
void
wsdd_send_directed_probe (int ifindex, int af, const void *addr);
*/
SANE_Status
wsdd_init (void);
*/
void
wsdd_cleanup (void);
*/
typedef struct device device;
*/
device*
device_open (const char *name, SANE_Status *status);
* If log_msg is not NULL, it is written to the device log as late as possible
*/
void
device_close (device *dev, const char *log_msg);
*/
log_ctx*
device_log_ctx (device *dev);
*/
const SANE_Option_Descriptor*
device_get_option_descriptor (device *dev, SANE_Int option);
*/
SANE_Status
device_get_option (device *dev, SANE_Int option, void *value);
*/
SANE_Status
device_set_option (device *dev, SANE_Int option, void *value, SANE_Word *info);
*/
SANE_Status
device_get_parameters (device *dev, SANE_Parameters *params);
SANE_Status
device_start (device *dev);
*/
void
device_cancel (device *dev);
*/
SANE_Status
device_set_io_mode (device *dev, SANE_Bool non_blocking);
*/
SANE_Status
device_get_select_fd (device *dev, SANE_Int *fd);
*/
SANE_Status
device_read (device *dev, SANE_Byte *data, SANE_Int max_len, SANE_Int *len);
*/
SANE_Status
device_management_init (void);
*/
void
device_management_cleanup (void);
*/
typedef struct filter filter;
struct filter {
filter *next;
void (*dump) (filter *f,
log_ctx *log);
void (*free) (filter *f);
void (*apply) (filter *f,
uint8_t *line, size_t size);
};
*/
void
filter_chain_free (filter *chain);
* following options:
* - brightness
* - contrast
* - negative
*
* Returns updated chain
*/
filter*
filter_chain_push_xlat (filter *old_chain, const devopt *opt);
*/
void
filter_chain_dump (filter *chain, log_ctx *log);
*/
void
filter_chain_apply (filter *chain, uint8_t *line, size_t size);
*/
typedef enum {
PROTO_OP_NONE,
PROTO_OP_PRECHECK,
PROTO_OP_SCAN,
PROTO_OP_LOAD,
PROTO_OP_CHECK,
PROTO_OP_CLEANUP,
PROTO_OP_FINISH
} PROTO_OP;
*/
const char*
proto_op_name (PROTO_OP op);
*/
typedef struct {
int x_off, y_off;
int wid, hei;
int x_res, y_res;
ID_SOURCE src;
ID_COLORMODE colormode;
ID_SCANINTENT scanintent;
ID_FORMAT format;
} proto_scan_params;
*/
typedef struct {
log_ctx *log;
struct proto_handler *proto;
const zeroconf_devinfo *devinfo;
const devcaps *devcaps;
PROTO_OP op;
http_client *http;
http_uri *base_uri;
http_uri *base_uri_nozone;
proto_scan_params params;
const char *location;
unsigned int images_received;
const http_query *query;
PROTO_OP failed_op;
int failed_http_status;
int failed_attempt;
ID_FORMAT format_detected;
} proto_ctx;
*/
typedef struct {
PROTO_OP next;
int delay;
SANE_Status status;
error err;
union {
const char *location;
http_data *image;
} data;
} proto_result;
*/
typedef struct proto_handler proto_handler;
struct proto_handler {
const char *name;
*/
void (*free) (proto_handler *proto);
*/
http_query* (*devcaps_query) (const proto_ctx *ctx);
error (*devcaps_decode) (const proto_ctx *ctx, devcaps *caps);
* These callback are optional, set to NULL, if
* they are not implemented by the protocol
* handler
*/
http_query* (*precheck_query) (const proto_ctx *ctx);
proto_result (*precheck_decode) (const proto_ctx *ctx);
* On success, scan_decode must set ctx->data.location
*/
http_query* (*scan_query) (const proto_ctx *ctx);
proto_result (*scan_decode) (const proto_ctx *ctx);
* On success, load_decode must set ctx->data.image
*/
http_query* (*load_query) (const proto_ctx *ctx);
proto_result (*load_decode) (const proto_ctx *ctx);
*/
http_query* (*status_query) (const proto_ctx *ctx);
proto_result (*status_decode) (const proto_ctx *ctx);
*/
http_query* (*cleanup_query) (const proto_ctx *ctx);
*/
http_query* (*cancel_query) (const proto_ctx *ctx);
*/
error (*test_decode_devcaps) (proto_handler *proto,
const void *xml_text, size_t xms_size,
devcaps *caps);
};
*/
proto_handler*
proto_handler_escl_new (void);
*/
proto_handler*
proto_handler_wsd_new (void);
*/
static inline proto_handler*
proto_handler_new (ID_PROTO proto)
{
switch (proto) {
case ID_PROTO_ESCL:
return proto_handler_escl_new();
case ID_PROTO_WSD:
return proto_handler_wsd_new();
default:
return NULL;
}
}
* created by proto_handler_new/proto_handler_escl_new/
* proto_handler_wsd_new functions
*/
static inline void
proto_handler_free (proto_handler *proto)
{
proto->free(proto);
}
*
* Note, all sizes and coordinates are in pixels
*/
typedef struct {
int x_off, y_off;
int wid, hei;
} image_window;
*/
typedef struct image_decoder image_decoder;
struct image_decoder {
const char *content_type;
void (*free) (image_decoder *decoder);
error (*begin) (image_decoder *decoder, const void *data, size_t size);
void (*reset) (image_decoder *decoder);
int (*get_bytes_per_pixel) (image_decoder *decoder);
void (*get_params) (image_decoder *decoder, SANE_Parameters *params);
error (*set_window) (image_decoder *decoder, image_window *win);
error (*read_line) (image_decoder *decoder, void *buffer);
};
*/
ID_FORMAT
image_format_detect (const void *data, size_t size);
*/
image_decoder*
image_decoder_jpeg_new (void);
*/
image_decoder*
image_decoder_png_new (void);
*/
image_decoder*
image_decoder_tiff_new (void);
*/
image_decoder*
image_decoder_bmp_new (void);
*/
static inline void
image_decoder_free (image_decoder *decoder)
{
decoder->free(decoder);
}
*/
static inline const char*
image_content_type (image_decoder *decoder)
{
return decoder->content_type;
}
* buffer remains valid during a whole decoding cycle
*/
static inline error
image_decoder_begin (image_decoder *decoder, const void *data, size_t size)
{
return decoder->begin(decoder, data, size);
}
* another image can be started
*/
static inline void
image_decoder_reset (image_decoder *decoder)
{
decoder->reset(decoder);
}
*/
static inline int
image_decoder_get_bytes_per_pixel (image_decoder *decoder)
{
return decoder->get_bytes_per_pixel(decoder);
}
* image_decoder_begin() and image_decoder_reset()
*
* Decoder must return an actual image parameters, regardless
* of clipping window set by image_decoder_set_window()
*/
static inline void
image_decoder_get_params (image_decoder *decoder, SANE_Parameters *params)
{
decoder->get_params(decoder, params);
}
* window needs to be decoded. Decoder may assume that window is
* always within the actual image boundaries
*
* Note, if decoder cannot handle exact window boundaries, it
* it must update window to keep actual values
*
* In particular, if decoder doesn't implement image clipping
* at all, it is safe that decoder will simply set window boundaries
* to contain an entire image
*/
static inline error
image_decoder_set_window (image_decoder *decoder, image_window *win)
{
return decoder->set_window(decoder, win);
}
* buffer is big enough to keep the entire line
*/
static inline error
image_decoder_read_line (image_decoder *decoder, void *buffer)
{
return decoder->read_line(decoder, buffer);
}
* and fills array of decoders, indexed by ID_FORMAT
*
* Note, it is not guaranteed, that for all ID_FORMAT
* decoder will be created. Missed entries will be set
* to NULL. Be aware when using the filled array!
*/
static inline void
image_decoder_create_all (image_decoder *decoders[NUM_ID_FORMAT])
{
int i;
*/
for (i = 0; i < NUM_ID_FORMAT; i ++) {
decoders[i] = NULL;
}
*/
decoders[ID_FORMAT_BMP] = image_decoder_bmp_new();
decoders[ID_FORMAT_JPEG] = image_decoder_jpeg_new();
decoders[ID_FORMAT_PNG] = image_decoder_png_new();
decoders[ID_FORMAT_TIFF] = image_decoder_tiff_new();
}
* created by image_decoder_create_all
*/
static inline void
image_decoder_free_all (image_decoder *decoders[NUM_ID_FORMAT])
{
int i;
for (i = 0; i < NUM_ID_FORMAT; i ++) {
image_decoder *decoder = decoders[i];
if (decoder != NULL) {
image_decoder_free(decoder);
decoders[i] = NULL;
}
}
}
*/
SANE_Word
math_gcd (SANE_Word x, SANE_Word y);
*/
SANE_Word
math_lcm (SANE_Word x, SANE_Word y);
*/
static inline SANE_Word
math_min (SANE_Word a, SANE_Word b)
{
return a < b ? a : b;
}
*/
static inline SANE_Word
math_max (SANE_Word a, SANE_Word b)
{
return a > b ? a : b;
}
*/
static inline SANE_Word
math_bound (SANE_Word x, SANE_Word min, SANE_Word max)
{
if (x < min) {
return min;
} else if (x > max) {
return max;
} else {
return x;
}
}
*/
static inline double
math_bound_double (double x, double min, double max)
{
if (x < min) {
return min;
} else if (x > max) {
return max;
} else {
return x;
}
}
* and integer overflow
*/
static inline SANE_Word
math_muldiv (SANE_Word x, SANE_Word mul, SANE_Word div)
{
int64_t tmp;
tmp = (int64_t) x * (int64_t) mul;
tmp += div / 2;
tmp /= div;
return (SANE_Word) tmp;
}
*/
bool
math_range_merge (SANE_Range *out, const SANE_Range *r1, const SANE_Range *r2);
*/
SANE_Word
math_range_fit (const SANE_Range *r, SANE_Word i);
*/
static inline SANE_Fixed
math_px2mm_res (SANE_Word px, SANE_Word res)
{
return SANE_FIX((double) px * 25.4 / res);
}
*/
static inline SANE_Word
math_mm2px_res (SANE_Fixed mm, SANE_Word res)
{
return (SANE_Word) roundl(SANE_UNFIX(mm) * res / 25.4);
}
*/
char*
math_fmt_mm (SANE_Word mm, char buf[]);
*/
uint32_t
math_rand_u32 (void);
*/
uint32_t
math_rand_max (uint32_t max);
*/
uint32_t
math_rand_range (uint32_t min, uint32_t max);
*/
static inline unsigned int
math_popcount (unsigned int n)
{
unsigned int count = (n & 0x55555555) + ((n >> 1) & 0x55555555);
count = (count & 0x33333333) + ((count >> 2) & 0x33333333);
count = (count & 0x0F0F0F0F) + ((count >> 4) & 0x0F0F0F0F);
count = (count & 0x00FF00FF) + ((count >> 8) & 0x00FF00FF);
return (count & 0x0000FFFF) + ((count >> 16) & 0x0000FFFF);
}
*
* No log messages should be generated before this call
*/
void
log_init (void);
*
* No log messages should be generated after this call
*/
void
log_cleanup (void);
* logger can configure itself
*
* This is safe to generate log messages before log_configure()
* is called. These messages will be buffered, and after
* logger is configured, either written or abandoned, depending
* on configuration
*/
void
log_configure (void);
* If parent != NULL, new logging context will have its own prefix,
* but trace file will be inherited from parent
*/
log_ctx*
log_ctx_new (const char *name, log_ctx *parent);
*/
void
log_ctx_free (log_ctx *log);
*/
trace*
log_ctx_trace (log_ctx *log);
*/
void
log_debug (log_ctx *log, const char *fmt, ...);
*/
void
log_trace (log_ctx *log, const char *fmt, ...);
*/
void
log_trace_data (log_ctx *log, const char *content_type,
const void *bytes, size_t size);
*/
void
log_panic (log_ctx *log, const char *fmt, ...);
*/
#define log_assert(log,expr) \
do { \
if (!(expr)) { \
log_panic(log,"file %s: line %d (%s): assertion failed: (%s)",\
__FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \
__builtin_unreachable(); \
} \
} while (0)
*/
#define log_internal_error(log) \
do { \
log_panic(log,"file %s: line %d (%s): internal error", \
__FILE__, __LINE__, __PRETTY_FUNCTION__); \
__builtin_unreachable(); \
} while (0)
*
* These flags are mostly used for testing
*/
typedef enum {
AIRSCAN_INIT_NO_CONF = (1 << 0),
AIRSCAN_INIT_NO_THREAD = (1 << 1)
} AIRSCAN_INIT_FLAGS;
* If log_msg is not NULL, it is written to the log early
*/
SANE_Status
airscan_init (AIRSCAN_INIT_FLAGS flags, const char *log_msg);
* If log_msg is not NULL, it is written to the log as late as possible
*/
void
airscan_cleanup (const char *log_msg);
*/
AIRSCAN_INIT_FLAGS
airscan_get_init_flags (void);
#ifdef __cplusplus
};
#endif
#endif
*/