Refactoring: code split to many modules

This commit is contained in:
Alexander Pevzner
2019-10-26 14:14:36 +03:00
parent 25f066f7c5
commit b8849a9e6e
7 changed files with 603 additions and 304 deletions
+5 -2
View File
@@ -1,9 +1,12 @@
SRC = airscan.c airscan-array.c airscan-xml.c sane_strstatus.c
CFLAGS = -O2 -g -W -Wall -fPIC
CFLAGS += `pkg-config --cflags --libs avahi-client`
CFLAGS += `pkg-config --cflags --libs avahi-glib`
CFLAGS += `pkg-config --cflags --libs libjpeg`
CFLAGS += `pkg-config --cflags --libs libsoup-2.4`
CFLAGS += `pkg-config --cflags --libs libxml-2.0`
CFLAGS += -Wl,--version-script=airscan.sym
# This magic is a workaround for libsoup bug.
#
@@ -22,9 +25,9 @@ CFLAGS += -Wl,-z,nodelete
all: libsane-airscan.so test
libsane-airscan.so: Makefile airscan.c
libsane-airscan.so: Makefile $(SRC) airscan.h airscan.sym
@ctags -R .
gcc -o libsane-airscan.so -shared ${CFLAGS} airscan.c
gcc -o libsane-airscan.so -shared ${CFLAGS} $(SRC)
test: libsane-airscan.so test.c
#gcc -o test test.c -l sane
+125
View File
@@ -0,0 +1,125 @@
/* AirScan (a.k.a. eSCL) backend for SANE
*
* Copyright (C) 2019 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*/
#include "airscan.h"
#include <glib.h>
/******************** Typed Arrays ********************/
/* Initial capacity of arrays
*/
#define ARRAY_INITIAL_CAPACITY 4
/* Initialize array of SANE_Word
*/
void
array_of_word_init (SANE_Word **a)
{
*a = g_new0(SANE_Word, ARRAY_INITIAL_CAPACITY);
}
/* Cleanup array of SANE_Word
*/
void
array_of_word_cleanup (SANE_Word **a)
{
g_free(*a);
*a = NULL;
}
/* Get length of the SANE_Word array
*/
size_t
array_of_word_len (SANE_Word **a)
{
return (size_t) (*a)[0];
}
/* Append word to array
*/
void
array_of_word_append(SANE_Word **a, SANE_Word w)
{
size_t sz = array_of_word_len(a) + 1;
/* If sz reached the power-of-2, reallocate the array, doubling its size */
if (sz >= ARRAY_INITIAL_CAPACITY && (sz & (sz - 1)) == 0) {
*a = g_renew(SANE_Word, (*a), sz + sz);
}
(*a)[sz] = w;
(*a)[0] ++;
}
/* Compare function for array_of_word_sort
*/
int
array_of_word_sort_cmp(const void *p1, const void *p2)
{
return *(SANE_Word*) p1 - *(SANE_Word*) p2;
}
/* Sort array of SANE_Word in increasing order
*/
void
array_of_word_sort(SANE_Word **a)
{
SANE_Word len = (*a)[0];
if (len) {
qsort((*a) + 1, len, sizeof(SANE_Word), array_of_word_sort_cmp);
}
}
/* Initialize array of SANE_String
*/
void
array_of_string_init (SANE_String **a)
{
*a = g_new0(SANE_String, ARRAY_INITIAL_CAPACITY);
}
/* Cleanup array of SANE_String
*/
void
array_of_string_cleanup (SANE_String **a)
{
g_free(*a);
*a = NULL;
}
/* Get length of the SANE_Word array
*/
size_t
array_of_string_len (SANE_String **a)
{
size_t sz;
for (sz = 0; (*a)[sz]; sz ++)
;
return sz;
}
/* Append string to array
*/
void
array_of_string_append(SANE_String **a, SANE_String s)
{
size_t sz = array_of_string_len(a) + 1;
/* If sz reached the power-of-2, reallocate the array, doubling its size */
if (sz >= ARRAY_INITIAL_CAPACITY && (sz & (sz - 1)) == 0) {
*a = g_renew(SANE_String, (*a), sz + sz);
}
/* Append string */
(*a)[sz - 1] = s;
(*a)[sz] = NULL;
}
/* vim:ts=8:sw=4:et
*/
+188
View File
@@ -0,0 +1,188 @@
/* AirScan (a.k.a. eSCL) backend for SANE
*
* Copyright (C) 2019 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*/
#include "airscan.h"
#include <glib.h>
#include <libxml/tree.h>
/******************** XML utilities ********************/
/* Static initializer for the XML iterator
*/
#define XML_ITER_INIT {NULL, NULL, NULL, NULL, NULL}
/* Skip dummy nodes. This is internal function, don't call directly
*/
static void
__xml_iter_skip_dummy (xml_iter *iter)
{
xmlNode *node = iter->node;
while (node != NULL &&
(node->type == XML_COMMENT_NODE || xmlIsBlankNode (node))) {
node = node->next;
}
iter->node = node;
}
/* Invalidate cached data. This is internal function, don't call directly
*/
static void
__xml_iter_invalidate_cache (xml_iter *iter)
{
g_free((void*) iter->name);
xmlFree((xmlChar*) iter->text);
g_free((void*) iter->err);
iter->name = NULL;
iter->text = NULL;
iter->err = NULL;
}
/* Initialize iterator to iterate starting from the given node
*/
void
xml_iter_init (xml_iter *iter, xmlNode *node)
{
iter->node = node;
__xml_iter_skip_dummy(iter);
__xml_iter_invalidate_cache(iter);
iter->parent = iter->node ? iter->node->parent : NULL;
}
/* Cleanup XML iterator
*/
void
xml_iter_cleanup (xml_iter *iter)
{
__xml_iter_invalidate_cache(iter);
iter->node = NULL;
iter->parent = NULL;
}
/* Check for end-of-document condition
*/
SANE_Bool
xml_iter_end (xml_iter *iter)
{
return iter->node == NULL;
}
/* Shift to the next node
*/
void
xml_iter_next (xml_iter *iter)
{
if (iter->node) {
iter->node = iter->node->next;
__xml_iter_skip_dummy(iter);
__xml_iter_invalidate_cache(iter);
}
}
/* Enter the current node - iterate its children
*/
void
xml_iter_enter (xml_iter *iter)
{
if (iter->node) {
iter->parent = iter->node;
iter->node = iter->node->children;
__xml_iter_skip_dummy(iter);
__xml_iter_invalidate_cache(iter);
}
}
/* Leave the current node - return to its parent
*/
void
xml_iter_leave (xml_iter *iter)
{
iter->node = iter->parent;
if (iter->node) {
iter->parent = iter->node->parent;
}
__xml_iter_invalidate_cache(iter);
}
/* Get name of the current node.
*
* The returned string remains valid, until iterator 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_iter_node_name (xml_iter *iter)
{
const char *prefix = NULL;
if (iter->name == NULL && iter->node != NULL) {
if (iter->node->ns != NULL) {
prefix = (const char*) iter->node->ns->prefix;
}
if (prefix != NULL) {
iter->name = g_strconcat(prefix, ":", iter->node->name, NULL);
} else {
iter->name = g_strdup((const char*) iter->node->name);
}
}
return iter->name;
}
/* Match name of the current node against the pattern
*/
SANE_Bool
xml_iter_node_name_match (xml_iter *iter, const char *pattern)
{
return !g_strcmp0(xml_iter_node_name(iter), pattern);
}
/* Get value of the current node as text
*
* The returned string remains valid, until iterator 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_iter_node_value (xml_iter *iter)
{
if (iter->text == NULL && iter->node != NULL) {
iter->text = xmlNodeGetContent(iter->node);
g_strstrip((char*) iter->text);
}
return (const char*) iter->text;
}
/* Get value of the current node as unsigned integer
* Returns error string, NULL if OK
*/
const char*
xml_iter_node_value_uint (xml_iter *iter, SANE_Word *val)
{
const char *s = xml_iter_node_value(iter);
char *end;
unsigned long v;
g_assert(s != NULL);
v = strtoul(s, &end, 10);
if (end == s || *end || v != (unsigned long) (SANE_Word) v) {
g_free((char*) iter->err);
iter->err = g_strdup_printf("%s: invalid numerical value",
xml_iter_node_name(iter));
return iter->err;
}
*val = (SANE_Word) v;
return NULL;
}
/* vim:ts=8:sw=4:et
*/
+2 -302
View File
@@ -4,7 +4,7 @@
* See LICENSE for license terms and conditions
*/
#include <sane/sane.h>
#include "airscan.h"
#include <stdio.h>
#include <sys/time.h>
@@ -17,7 +17,6 @@
#include <glib.h>
#include <libsoup/soup.h>
#include <libxml/tree.h>
/******************** Constants *********************/
/* Service type to look for
@@ -43,305 +42,6 @@
/******************** Debugging ********************/
#define DBG(level, msg, args...) printf("airscan: " msg, ##args)
/******************** XML utilities ********************/
/* XML iterator
*/
typedef struct {
xmlNode *node;
xmlNode *parent;
const char *name;
const xmlChar *text;
const char *err;
} xml_iter;
/* Static initializer for the XML iterator
*/
#define XML_ITER_INIT {NULL, NULL, NULL, NULL, NULL}
/* Skip dummy nodes. This is internal function, don't call directly
*/
static void
__xml_iter_skip_dummy (xml_iter *iter)
{
xmlNode *node = iter->node;
while (node != NULL &&
(node->type == XML_COMMENT_NODE || xmlIsBlankNode (node))) {
node = node->next;
}
iter->node = node;
}
/* Invalidate cached data. This is internal function, don't call directly
*/
static void
__xml_iter_invalidate_cache (xml_iter *iter)
{
g_free((void*) iter->name);
xmlFree((xmlChar*) iter->text);
g_free((void*) iter->err);
iter->name = NULL;
iter->text = NULL;
iter->err = NULL;
}
/* Set current node to iterate from
*/
static void
xml_iter_set (xml_iter *iter, xmlNode *node)
{
iter->node = node;
__xml_iter_skip_dummy(iter);
__xml_iter_invalidate_cache(iter);
iter->parent = iter->node ? iter->node->parent : NULL;
}
/* Cleanup XML iterator
*/
static void
xml_iter_cleanup (xml_iter *iter)
{
__xml_iter_invalidate_cache(iter);
iter->node = NULL;
iter->parent = NULL;
}
/* Check for end-of-document condition
*/
static SANE_Bool
xml_iter_end (xml_iter *iter)
{
return iter->node == NULL;
}
/* Shift to the next node
*/
static void
xml_iter_next (xml_iter *iter)
{
if (iter->node) {
iter->node = iter->node->next;
__xml_iter_skip_dummy(iter);
__xml_iter_invalidate_cache(iter);
}
}
/* Enter the current node - iterate its children
*/
static void
xml_iter_enter (xml_iter *iter)
{
if (iter->node) {
iter->parent = iter->node;
iter->node = iter->node->children;
__xml_iter_skip_dummy(iter);
__xml_iter_invalidate_cache(iter);
}
}
/* Leave the current node - return to its parent
*/
static void
xml_iter_leave (xml_iter *iter)
{
iter->node = iter->parent;
if (iter->node) {
iter->parent = iter->node->parent;
}
__xml_iter_invalidate_cache(iter);
}
/* Get name of the current node.
*
* The returned string remains valid, until iterator is cleaned up
* or current node is changed (by set/next/enter/leave operations).
* You don't need to free this string explicitly
*/
static const char*
xml_iter_node_name (xml_iter *iter)
{
const char *prefix = NULL;
if (iter->name == NULL && iter->node != NULL) {
if (iter->node->ns != NULL) {
prefix = (const char*) iter->node->ns->prefix;
}
if (prefix != NULL) {
iter->name = g_strconcat(prefix, ":", iter->node->name, NULL);
} else {
iter->name = g_strdup((const char*) iter->node->name);
}
}
return iter->name;
}
/* Match name of the current node against the pattern
*/
static SANE_Bool
xml_iter_node_name_match (xml_iter *iter, const char *pattern)
{
return !g_strcmp0(xml_iter_node_name(iter), pattern);
}
/* Get value of the current node as text
*
* The returned string remains valid, until iterator is cleaned up
* or current node is changed (by set/next/enter/leave operations).
* You don't need to free this string explicitly
*/
static const char*
xml_iter_node_value (xml_iter *iter)
{
if (iter->text == NULL && iter->node != NULL) {
iter->text = xmlNodeGetContent(iter->node);
g_strstrip((char*) iter->text);
}
return (const char*) iter->text;
}
/* Get value of the current node as unsigned integer
* Returns error string, NULL if OK
*/
static const char*
xml_iter_node_value_uint (xml_iter *iter, SANE_Word *val)
{
const char *s = xml_iter_node_value(iter);
char *end;
unsigned long v;
g_assert(s != NULL);
v = strtoul(s, &end, 10);
if (end == s || *end || v != (unsigned long) (SANE_Word) v) {
g_free((char*) iter->err);
iter->err = g_strdup_printf("%s: invalid numerical value",
xml_iter_node_name(iter));
return iter->err;
}
*val = (SANE_Word) v;
return NULL;
}
/******************** Types Arrays ********************/
/* Initial capacity of arrays
*/
#define ARRAY_INITIAL_CAPACITY 4
/* Initialize array of SANE_Word
*/
static void
array_of_word_init (SANE_Word **a)
{
*a = g_new0(SANE_Word, ARRAY_INITIAL_CAPACITY);
}
/* Cleanup array of SANE_Word
*/
static void
array_of_word_cleanup (SANE_Word **a)
{
g_free(*a);
*a = NULL;
}
/* Get length of the SANE_Word array
*/
static size_t
array_of_word_len (SANE_Word **a)
{
return (size_t) (*a)[0];
}
/* Append word to array
*/
static void
array_of_word_append(SANE_Word **a, SANE_Word w)
{
size_t sz = array_of_word_len(a) + 1;
/* If sz reached the power-of-2, reallocate the array, doubling its size */
if (sz >= ARRAY_INITIAL_CAPACITY && (sz & (sz - 1)) == 0) {
*a = g_renew(SANE_Word, (*a), sz + sz);
}
(*a)[sz] = w;
(*a)[0] ++;
}
/* Compare function for array_of_word_sort
*/
static int
array_of_word_sort_cmp(const void *p1, const void *p2)
{
return *(SANE_Word*) p1 - *(SANE_Word*) p2;
}
/* Sort array of SANE_Word in increasing order
*/
static void
array_of_word_sort(SANE_Word **a)
{
SANE_Word len = (*a)[0];
if (len) {
qsort((*a) + 1, len, sizeof(SANE_Word), array_of_word_sort_cmp);
}
}
/* Initialize array of SANE_String
*/
static void
array_of_string_init (SANE_String **a)
{
*a = g_new0(SANE_String, ARRAY_INITIAL_CAPACITY);
}
/* Cleanup array of SANE_String
*/
static void
array_of_string_cleanup (SANE_String **a)
{
g_free(*a);
*a = NULL;
}
/* Get length of the SANE_Word array
*/
static size_t
array_of_string_len (SANE_String **a)
{
size_t sz;
for (sz = 0; (*a)[sz]; sz ++)
;
return sz;
}
/* Append string to array
*/
static void
array_of_string_append(SANE_String **a, SANE_String s)
{
size_t sz = array_of_string_len(a) + 1;
/* If sz reached the power-of-2, reallocate the array, doubling its size */
if (sz >= ARRAY_INITIAL_CAPACITY && (sz & (sz - 1)) == 0) {
*a = g_renew(SANE_String, (*a), sz + sz);
}
/* Append string */
(*a)[sz - 1] = s;
(*a)[sz] = NULL;
}
/******************** Device Capabilities ********************/
/* Source flags
*/
@@ -664,7 +364,7 @@ devcaps_parse (devcaps *caps, xmlDoc *xml)
xml_iter iter = XML_ITER_INIT;
/* Parse capabilities XML */
xml_iter_set(&iter, xmlDocGetRootElement(xml));
xml_iter_init(&iter, xmlDocGetRootElement(xml));
if (!xml_iter_node_name_match(&iter, "scan:ScannerCapabilities")) {
err = "XML: missed scan:ScannerCapabilities";
goto DONE;
+141
View File
@@ -0,0 +1,141 @@
/* AirScan (a.k.a. eSCL) backend for SANE
*
* 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 <sane/sane.h>
#include <libxml/tree.h>
/******************** Typed Arrays ********************/
/* Initialize array of SANE_Word
*/
void
array_of_word_init (SANE_Word **a);
/* Cleanup array of SANE_Word
*/
void
array_of_word_cleanup (SANE_Word **a);
/* Get length of the SANE_Word array
*/
size_t
array_of_word_len (SANE_Word **a);
/* Append word to array
*/
void
array_of_word_append(SANE_Word **a, SANE_Word w);
/* Compare function for array_of_word_sort
*/
int
array_of_word_sort_cmp(const void *p1, const void *p2);
/* Sort array of SANE_Word in increasing order
*/
void
array_of_word_sort(SANE_Word **a);
/* Initialize array of SANE_String
*/
void
array_of_string_init (SANE_String **a);
/* Cleanup array of SANE_String
*/
void
array_of_string_cleanup (SANE_String **a);
/* Get length of the SANE_Word array
*/
size_t
array_of_string_len (SANE_String **a);
/* Append string to array
*/
void
array_of_string_append(SANE_String **a, SANE_String s);
/******************** XML utilities ********************/
/* XML iterator
*/
typedef struct {
xmlNode *node;
xmlNode *parent;
const char *name;
const xmlChar *text;
const char *err;
} xml_iter;
/* Static initializer for the XML iterator
*/
#define XML_ITER_INIT {NULL, NULL, NULL, NULL, NULL}
/* Initialize iterator to iterate starting from the given node
*/
void
xml_iter_init (xml_iter *iter, xmlNode *node);
/* Cleanup XML iterator
*/
void
xml_iter_cleanup (xml_iter *iter);
/* Check for end-of-document condition
*/
SANE_Bool
xml_iter_end (xml_iter *iter);
/* Shift to the next node
*/
void
xml_iter_next (xml_iter *iter);
/* Enter the current node - iterate its children
*/
void
xml_iter_enter (xml_iter *iter);
/* Leave the current node - return to its parent
*/
void
xml_iter_leave (xml_iter *iter);
/* Get name of the current node.
*
* The returned string remains valid, until iterator 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_iter_node_name (xml_iter *iter);
/* Match name of the current node against the pattern
*/
SANE_Bool
xml_iter_node_name_match (xml_iter *iter, const char *pattern);
/* Get value of the current node as text
*
* The returned string remains valid, until iterator 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_iter_node_value (xml_iter *iter);
/* Get value of the current node as unsigned integer
* Returns error string, NULL if OK
*/
const char*
xml_iter_node_value_uint (xml_iter *iter, SANE_Word *val);
#endif
/* vim:ts=8:sw=4:et
*/
+33
View File
@@ -0,0 +1,33 @@
{
global:
sane_airscan_cancel;
sane_airscan_close;
sane_airscan_control_option;
sane_airscan_exit;
sane_airscan_get_devices;
sane_airscan_get_option_descriptor;
sane_airscan_get_parameters;
sane_airscan_get_select_fd;
sane_airscan_init;
sane_airscan_open;
sane_airscan_read;
sane_airscan_set_io_mode;
sane_airscan_start;
sane_cancel;
sane_close;
sane_control_option;
sane_exit;
sane_get_devices;
sane_get_option_descriptor;
sane_get_parameters;
sane_get_select_fd;
sane_init;
sane_open;
sane_read;
sane_set_io_mode;
sane_start;
sane_strstatus;
local:
*;
};
+109
View File
@@ -0,0 +1,109 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 1996, 1997 David Mosberger-Tang and Andreas Beck
This file is part of the SANE package.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA.
As a special exception, the authors of SANE give permission for
additional uses of the libraries contained in this release of SANE.
The exception is that, if you link a SANE library with other files
to produce an executable, this does not by itself cause the
resulting executable to be covered by the GNU General Public
License. Your use of that executable is in no way restricted on
account of linking the SANE library code into it.
This exception does not, however, invalidate any other reasons why
the executable file might be covered by the GNU General Public
License.
If you submit changes to SANE to the maintainers to be included in
a subsequent release, you agree by submitting the changes that
those changes may be distributed with this exception intact.
If you write modifications of your own for SANE, it is your choice
whether to permit this exception to apply to your modifications.
If you do not wish that, delete this exception notice.
This file implements the backend-independent parts of SANE. */
#include <stdio.h>
#include <sane/sane.h>
#ifndef SANE_I18N
#define SANE_I18N(text) text
#endif
SANE_String_Const
sane_strstatus (SANE_Status status)
{
static char buf[80];
switch (status)
{
case SANE_STATUS_GOOD:
return SANE_I18N("Success");
case SANE_STATUS_UNSUPPORTED:
return SANE_I18N("Operation not supported");
case SANE_STATUS_CANCELLED:
return SANE_I18N("Operation was canceled");
case SANE_STATUS_DEVICE_BUSY:
return SANE_I18N("Device busy");
case SANE_STATUS_INVAL:
return SANE_I18N("Invalid argument");
case SANE_STATUS_EOF:
return SANE_I18N("End of file reached");
case SANE_STATUS_JAMMED:
return SANE_I18N("Document feeder jammed");
case SANE_STATUS_NO_DOCS:
return SANE_I18N("Document feeder out of documents");
case SANE_STATUS_COVER_OPEN:
return SANE_I18N("Scanner cover is open");
case SANE_STATUS_IO_ERROR:
return SANE_I18N("Error during device I/O");
case SANE_STATUS_NO_MEM:
return SANE_I18N("Out of memory");
case SANE_STATUS_ACCESS_DENIED:
return SANE_I18N("Access to resource has been denied");
#ifdef SANE_STATUS_WARMING_UP
case SANE_STATUS_WARMING_UP:
return SANE_I18N("Lamp not ready, please retry");
#endif
#ifdef SANE_STATUS_HW_LOCKED
case SANE_STATUS_HW_LOCKED:
return SANE_I18N("Scanner mechanism locked for transport");
#endif
default:
/* non-reentrant, but better than nothing */
sprintf (buf, "Unknown SANE status code %d", status);
return buf;
}
}