mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
bitkeeper revision 1.1026.1.2 (40e15b09oBOK6QWmjoE3IKmAR5xlVA)
Merge http://xen.bkbits.net:8080/xeno-unstable.bk into gandalf.hpl.hp.com:/var/bk/xeno-unstable.bk
This commit is contained in:
commit
3cffa5ecd2
62 files changed
+8369
-695
No files matched your search
@@ -164,6 +164,44 @@
|
||||
40c9c468pXANclL7slGaoD0kSrIwoQ tools/examples/xm_dom_create.py
|
||||
40cf2937oKlROYOJTN8GWwWM5AmjBg tools/examples/xmdefaults
|
||||
40dfd40auJwNnb8NoiSnRkvZaaXkUg tools/examples/xmnetbsd
|
||||
40e033325Sjqs-_4TuzeUEprP_gYFg tools/lib/allocate.c
|
||||
40e03332KYz7o1bn2MG_KPbBlyoIMA tools/lib/allocate.h
|
||||
40e03332IyRttYoXKoJla5qCC514SQ tools/lib/debug.h
|
||||
40e03332qV5tJ-GJZjo-LBCeGuEjJA tools/lib/enum.c
|
||||
40e03332wwMVxfobgA1PSMTSAGLiCw tools/lib/enum.h
|
||||
40e03332p5Dc_owJQRuN72ymJZddFQ tools/lib/file_stream.c
|
||||
40e03332jWfB2viAhLSkq1WK0r_iDQ tools/lib/file_stream.h
|
||||
40e03332rUjNMGg11n2rN6V4DCrvOg tools/lib/gzip_stream.c
|
||||
40e033321O5Qg22haLoq5lpmk4tooQ tools/lib/gzip_stream.h
|
||||
40e03332QrTR96tc6yS2rMBpd2mq1A tools/lib/hash_table.c
|
||||
40e033325KoIb0d_uy8s7b5DUR9fPQ tools/lib/hash_table.h
|
||||
40e03332ihnBGzHykVwZnFmkAppb4g tools/lib/iostream.c
|
||||
40e03332UGwbLR4wsw4ft14p0Yw5pg tools/lib/iostream.h
|
||||
40e0333245DLDzJemeSVBLuutHtzEQ tools/lib/kernel_stream.c
|
||||
40e03332aK0GkgpDdc-PVTkWKTeOBg tools/lib/kernel_stream.h
|
||||
40e03332HJ0cDcZDKDUUT-tEiBWOZw tools/lib/lexis.c
|
||||
40e03332tnH9Ggzxbfi3xY9Vh2hUlg tools/lib/lexis.h
|
||||
40e03332aYIW0BNBh6wXuKKn_P7Yyg tools/lib/lzi_stream.c
|
||||
40e0333233voTffE4cJSMGJARfiSSQ tools/lib/lzi_stream.h
|
||||
40e03332FXuMoUnfsAKSgV8X4rFbYQ tools/lib/lzo_stream.c
|
||||
40e03332InJaiLfpDcIXBy2fI0RFGQ tools/lib/lzo_stream.h
|
||||
40e03332a5SCuRsejHStTuWzMQNv8Q tools/lib/marshal.c
|
||||
40e03332TwKyJrZQiiQfNq4vc2hpgw tools/lib/marshal.h
|
||||
40e033328ccHlJuTR1FswYL_EC6LFA tools/lib/socket_stream.c
|
||||
40e03332P0KVQGkmahj47aafo1X0nA tools/lib/socket_stream.h
|
||||
40e03332KT_tnnoAMbPVAZBB7kSOAQ tools/lib/string_stream.c
|
||||
40e03332-VtK6_OZa1vMHXFil8uq6w tools/lib/string_stream.h
|
||||
40e03332dDtczi6YX7_mMxhYjJeAdQ tools/lib/sxpr.c
|
||||
40e03332QPuyNKDOTIYVvkwK5qO-vg tools/lib/sxpr.h
|
||||
40e03332Pi0_osJ3XPBi38ADPqdl4A tools/lib/sxpr_parser.c
|
||||
40e033324v5QFMvWEXXzv38uUT9kHg tools/lib/sxpr_parser.h
|
||||
40e03332gKUInsqtxQOV4mPiMqf_dg tools/lib/sys_ctype.h
|
||||
40e03332Rkvq6nn_UNjzAAK_Tk9v1g tools/lib/sys_net.c
|
||||
40e03332lQHvQHw4Rh7VsT1_sui29A tools/lib/sys_net.h
|
||||
40e033321smklZd7bDSdWvQCeIshtg tools/lib/sys_string.c
|
||||
40e03332h5V611rRWURRLqb1Ekatxg tools/lib/sys_string.h
|
||||
40e03332u4q5kgF0N7RfqB4s0pZVew tools/lib/xdr.c
|
||||
40e03332hY16nfRXF4gGd5S1aUJUBw tools/lib/xdr.h
|
||||
3f776bd2Xd-dUcPKlPN2vG89VGtfvQ tools/misc/Makefile
|
||||
40ab2cfawIw8tsYo0dQKtp83h4qfTQ tools/misc/fakei386xen
|
||||
3f6dc136ZKOjd8PIqLbFBl_v-rnkGg tools/misc/miniterm/Makefile
|
||||
@@ -185,6 +223,8 @@
|
||||
3fbba6dbasJQV-MVElDC0DGSHMiL5w tools/xc/lib/xc_domain.c
|
||||
40278d99BLsfUv3qxv0I8C1sClZ0ow tools/xc/lib/xc_elf.h
|
||||
403e0977Bjsm_e82pwvl9VvaJxh8Gg tools/xc/lib/xc_evtchn.c
|
||||
40e03333Eegw8czSWvHsbKxrRZJjRA tools/xc/lib/xc_io.c
|
||||
40e03333vrWGbLAhyJjXlqCHaJt7eA tools/xc/lib/xc_io.h
|
||||
3fbba6dbNCU7U6nsMYiXzKkp3ztaJg tools/xc/lib/xc_linux_build.c
|
||||
3fbba6dbl267zZOAVHYLOdLCdhcZMw tools/xc/lib/xc_linux_restore.c
|
||||
3fbba6db7li3FJiABYtCmuGxOJxEGw tools/xc/lib/xc_linux_save.c
|
||||
|
||||
+21
-2
@@ -3,8 +3,8 @@
|
||||
*.pyc
|
||||
*.so
|
||||
*.so.*
|
||||
*.tar.gz
|
||||
*.tar.bz2
|
||||
*.tar.gz
|
||||
*~
|
||||
BitKeeper/*/*
|
||||
PENDING/*
|
||||
@@ -12,15 +12,34 @@ TAGS
|
||||
extras/mini-os/h/hypervisor-ifs
|
||||
install
|
||||
install/*
|
||||
patches/*
|
||||
linux-*-xen*/*
|
||||
linux-2.4.26-xen/*
|
||||
linux-xen-sparse
|
||||
patches/*
|
||||
tools/*/build/lib*/*.py
|
||||
tools/balloon/balloon
|
||||
tools/misc/miniterm/miniterm
|
||||
tools/misc/xen_cpuperf
|
||||
tools/misc/xen_log
|
||||
tools/misc/xen_read_console
|
||||
tools/xc/lib/.allocate.o.d
|
||||
tools/xc/lib/.file_stream.o.d
|
||||
tools/xc/lib/.gzip_stream.o.d
|
||||
tools/xc/lib/.iostream.o.d
|
||||
tools/xc/lib/.sys_net.o.d
|
||||
tools/xc/lib/.sys_string.o.d
|
||||
tools/xc/lib/.xc_atropos.o.d
|
||||
tools/xc/lib/.xc_bvtsched.o.d
|
||||
tools/xc/lib/.xc_domain.o.d
|
||||
tools/xc/lib/.xc_evtchn.o.d
|
||||
tools/xc/lib/.xc_io.o.d
|
||||
tools/xc/lib/.xc_linux_build.o.d
|
||||
tools/xc/lib/.xc_linux_restore.o.d
|
||||
tools/xc/lib/.xc_linux_save.o.d
|
||||
tools/xc/lib/.xc_misc.o.d
|
||||
tools/xc/lib/.xc_netbsd_build.o.d
|
||||
tools/xc/lib/.xc_physdev.o.d
|
||||
tools/xc/lib/.xc_private.o.d
|
||||
tools/xentrace/xentrace
|
||||
xen/drivers/pci/classlist.h
|
||||
xen/drivers/pci/devlist.h
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
# This file expects the variable 'vmid' to be set.
|
||||
#============================================================================
|
||||
|
||||
import sys
|
||||
import xenctl.ip
|
||||
|
||||
try:
|
||||
vmid = int(vmid) # convert to integer
|
||||
except:
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "allocate.h"
|
||||
|
||||
/** @file
|
||||
* Support for allocating memory.
|
||||
* Usable from user code or kernel code (with __KERNEL__ defined).
|
||||
* In user code will use GC if USE_GC is defined.
|
||||
*/
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/*----------------------------------------------------------------------------*/
|
||||
# include <linux/config.h>
|
||||
# include <linux/slab.h>
|
||||
# include <linux/string.h>
|
||||
# include <linux/types.h>
|
||||
|
||||
# define DEFAULT_TYPE 0
|
||||
# define MALLOC(n, type) kmalloc(n, type)
|
||||
# define FREE(ptr) kfree(ptr)
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#else /* ! __KERNEL__ */
|
||||
|
||||
# include <stdlib.h>
|
||||
# include <string.h>
|
||||
|
||||
# define DEFAULT_TYPE 0
|
||||
|
||||
#ifdef USE_GC
|
||||
# include "gc.h"
|
||||
# define MALLOC(n, typ) GC_malloc(n)
|
||||
# define FREE(ptr) (ptr=NULL)
|
||||
//typedef void *GC_PTR;
|
||||
//GC_PTR (*GC_oom_fn)(size_t n);
|
||||
#else
|
||||
# define MALLOC(n, type) malloc(n)
|
||||
# define FREE(ptr) free(ptr)
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#endif
|
||||
|
||||
/** Function to call when memory cannot be allocated. */
|
||||
AllocateFailedFn *allocate_failed_fn = NULL;
|
||||
|
||||
/** Allocate memory and zero it.
|
||||
* The type is only relevant when calling from kernel code,
|
||||
* from user code it is ignored.
|
||||
* In kernel code the values accepted by kmalloc can be used:
|
||||
* GFP_USER, GFP_ATOMIC, GFP_KERNEL.
|
||||
*
|
||||
* @param size number of bytes to allocate
|
||||
* @param type memory type to allocate (kernel only)
|
||||
* @return pointer to the allocated memory or zero
|
||||
* if malloc failed
|
||||
*/
|
||||
void *allocate_type(int size, int type){
|
||||
void *p = MALLOC(size, type);
|
||||
if(p){
|
||||
memzero(p, size);
|
||||
} else if(allocate_failed_fn){
|
||||
allocate_failed_fn(size, type);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/** Allocate memory and zero it.
|
||||
*
|
||||
* @param size number of bytes to allocate
|
||||
* @return pointer to the allocated memory or zero
|
||||
* if malloc failed
|
||||
*/
|
||||
void *allocate(int size){
|
||||
return allocate_type(size, DEFAULT_TYPE);
|
||||
}
|
||||
|
||||
/** Free memory allocated by allocate().
|
||||
* No-op if 'p' is null.
|
||||
*
|
||||
* @param p memory to free
|
||||
*/
|
||||
void deallocate(void *p){
|
||||
if(p){
|
||||
FREE(p);
|
||||
}
|
||||
}
|
||||
|
||||
/** Set bytes to zero.
|
||||
* No-op if 'p' is null.
|
||||
*
|
||||
* @param p memory to zero
|
||||
* @param size number of bytes to zero
|
||||
*/
|
||||
void memzero(void *p, int size){
|
||||
if(p){
|
||||
memset(p, 0, (size_t)size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _XEN_LIB_ALLOCATE_H_
|
||||
#define _XEN_LIB_ALLOCATE_H_
|
||||
|
||||
/** Allocate memory for a given type, and cast. */
|
||||
#define ALLOCATE(ctype) (ctype *)allocate(sizeof(ctype))
|
||||
|
||||
/** Allocate memory for a given type, and cast. */
|
||||
#define ALLOCATE_TYPE(ctype, type) (ctype *)allocate(sizeof(ctype))
|
||||
|
||||
extern void *allocate_type(int size, int type);
|
||||
extern void *allocate(int size);
|
||||
extern void deallocate(void *);
|
||||
extern void memzero(void *p, int size);
|
||||
|
||||
typedef void AllocateFailedFn(int size, int type);
|
||||
extern AllocateFailedFn *allocate_failed_fn;
|
||||
|
||||
#endif /* _XEN_LIB_ALLOCATE_H_ */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _XEN_LIB_DEBUG_H_
|
||||
#define _XEN_LIB_DEBUG_H_
|
||||
|
||||
#ifndef MODULE_NAME
|
||||
#define MODULE_NAME ""
|
||||
#endif
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#define dprintf(fmt, args...) printk(KERN_DEBUG "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
|
||||
#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
|
||||
#define iprintf(fmt, args...) printk(KERN_INFO "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
|
||||
#define eprintf(fmt, args...) printk(KERN_ERR "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
|
||||
|
||||
#else
|
||||
|
||||
#define dprintf(fmt, args...) do {} while(0)
|
||||
#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME fmt, ##args)
|
||||
#define iprintf(fmt, args...) printk(KERN_INFO "[INF] " MODULE_NAME fmt, ##args)
|
||||
#define eprintf(fmt, args...) printk(KERN_ERR "[ERR] " MODULE_NAME fmt, ##args)
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#define dprintf(fmt, args...) fprintf(stdout, "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
|
||||
#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
|
||||
#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
|
||||
#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
|
||||
|
||||
#else
|
||||
|
||||
#define dprintf(fmt, args...) do {} while(0)
|
||||
#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME fmt, ##args)
|
||||
#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME fmt, ##args)
|
||||
#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME fmt, ##args)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/** Print format for an IP address.
|
||||
* See NIPQUAD(), HIPQUAD()
|
||||
*/
|
||||
#define IPFMT "%u.%u.%u.%u"
|
||||
|
||||
#endif /* ! _XEN_LIB_DEBUG_H_ */
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2002, 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of the
|
||||
* License, or (at your option) any later version. This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/errno.h>
|
||||
#else
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "sys_string.h"
|
||||
#include "enum.h"
|
||||
|
||||
/** Map an enum name to its value using a table.
|
||||
*
|
||||
* @param name enum name
|
||||
* @param defs enum definitions
|
||||
* @return enum value or -1 if not known
|
||||
*/
|
||||
int enum_name_to_val(char *name, EnumDef *defs){
|
||||
int val = -1;
|
||||
for(; defs->name; defs++){
|
||||
if(!strcmp(defs->name, name)){
|
||||
val = defs->val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/** Map an enum value to its name using a table.
|
||||
*
|
||||
* @param val enum value
|
||||
* @param defs enum definitions
|
||||
* @param defs_n number of definitions
|
||||
* @return enum name or NULL if not known
|
||||
*/
|
||||
char *enum_val_to_name(int val, EnumDef *defs){
|
||||
char *name = NULL;
|
||||
for(; defs->name; defs++){
|
||||
if(val == defs->val){
|
||||
name = defs->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2002, 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of the
|
||||
* License, or (at your option) any later version. This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _XEN_LIB_ENUM_H_
|
||||
#define _XEN_LIB_ENUM_H_
|
||||
|
||||
/** Mapping of an enum value to a name. */
|
||||
typedef struct EnumDef {
|
||||
int val;
|
||||
char *name;
|
||||
} EnumDef;
|
||||
|
||||
extern int enum_name_to_val(char *name, EnumDef *defs);
|
||||
extern char *enum_val_to_name(int val, EnumDef *defs);
|
||||
|
||||
#endif /* _XEN_LIB_ENUM_H_ */
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* An IOStream implementation using FILE*.
|
||||
*/
|
||||
#ifndef __KERNEL__
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "allocate.h"
|
||||
#include "file_stream.h"
|
||||
|
||||
static int file_read(IOStream *s, void *buf, size_t n);
|
||||
static int file_write(IOStream *s, const void *buf, size_t n);
|
||||
static int file_error(IOStream *s);
|
||||
static int file_close(IOStream *s);
|
||||
static void file_free(IOStream *s);
|
||||
static int file_flush(IOStream *s);
|
||||
|
||||
/** Methods used by a FILE* IOStream. */
|
||||
static const IOMethods file_methods = {
|
||||
read: file_read,
|
||||
write: file_write,
|
||||
error: file_error,
|
||||
close: file_close,
|
||||
free: file_free,
|
||||
flush: file_flush,
|
||||
};
|
||||
|
||||
/** IOStream for stdin. */
|
||||
static IOStream _iostdin = {
|
||||
methods: &file_methods,
|
||||
data: (void*)1,
|
||||
};
|
||||
|
||||
/** IOStream for stdout. */
|
||||
static IOStream _iostdout = {
|
||||
methods: &file_methods,
|
||||
data: (void*)2,
|
||||
};
|
||||
|
||||
/** IOStream for stderr. */
|
||||
static IOStream _iostderr = {
|
||||
methods: &file_methods,
|
||||
data: (void*)3,
|
||||
};
|
||||
|
||||
/** IOStream for stdin. */
|
||||
IOStream *iostdin = &_iostdin;
|
||||
|
||||
/** IOStream for stdout. */
|
||||
IOStream *iostdout = &_iostdout;
|
||||
|
||||
/** IOStream for stderr. */
|
||||
IOStream *iostderr = &_iostderr;
|
||||
|
||||
/** Get the underlying FILE*.
|
||||
*
|
||||
* @param s file stream
|
||||
* @return the stream s wraps
|
||||
*/
|
||||
static inline FILE *get_file(IOStream *s){
|
||||
switch((long)s->data){
|
||||
case 1: s->data = stdin; break;
|
||||
case 2: s->data = stdout; break;
|
||||
case 3: s->data = stderr; break;
|
||||
}
|
||||
return (FILE*)s->data;
|
||||
}
|
||||
|
||||
/** Control buffering on the underlying stream, like setvbuf().
|
||||
*
|
||||
* @param io file stream
|
||||
* @param buf buffer
|
||||
* @param mode buffering mode (see man setvbuf())
|
||||
* @param size buffer size
|
||||
* @return 0 on success, non-zero otherwise
|
||||
*/
|
||||
int file_stream_setvbuf(IOStream *io, char *buf, int mode, size_t size){
|
||||
return setvbuf(get_file(io), buf, mode, size);
|
||||
}
|
||||
|
||||
/** Write to the underlying stream using fwrite();
|
||||
*
|
||||
* @param stream input
|
||||
* @param buf where to put input
|
||||
* @param n number of bytes to write
|
||||
* @return number of bytes written
|
||||
*/
|
||||
static int file_write(IOStream *s, const void *buf, size_t n){
|
||||
return fwrite(buf, 1, n, get_file(s));
|
||||
}
|
||||
|
||||
/** Read from the underlying stream using fread();
|
||||
*
|
||||
* @param stream input
|
||||
* @param buf where to put input
|
||||
* @param n number of bytes to read
|
||||
* @return number of bytes read
|
||||
*/
|
||||
static int file_read(IOStream *s, void *buf, size_t n){
|
||||
return fread(buf, 1, n, get_file(s));
|
||||
}
|
||||
|
||||
/** Fush the underlying stream using fflush().
|
||||
*
|
||||
* @param s file stream
|
||||
* @return 0 on success, error code otherwise
|
||||
*/
|
||||
static int file_flush(IOStream *s){
|
||||
return fflush(get_file(s));
|
||||
}
|
||||
|
||||
/** Check if a stream has an error.
|
||||
*
|
||||
* @param s file stream
|
||||
* @return 1 if has an error, 0 otherwise
|
||||
*/
|
||||
static int file_error(IOStream *s){
|
||||
return ferror(get_file(s));
|
||||
}
|
||||
|
||||
/** Close a file stream.
|
||||
*
|
||||
* @param s file stream to close
|
||||
* @return result of the close
|
||||
*/
|
||||
static int file_close(IOStream *s){
|
||||
return fclose(get_file(s));
|
||||
}
|
||||
|
||||
/** Free a file stream.
|
||||
*
|
||||
* @param s file stream
|
||||
*/
|
||||
static void file_free(IOStream *s){
|
||||
// Do nothing - fclose does it all?
|
||||
}
|
||||
|
||||
/** Create an IOStream for a stream.
|
||||
*
|
||||
* @param f stream to wrap
|
||||
* @return new IOStream using f for i/o
|
||||
*/
|
||||
IOStream *file_stream_new(FILE *f){
|
||||
IOStream *io = ALLOCATE(IOStream);
|
||||
if(io){
|
||||
io->methods = &file_methods;
|
||||
io->data = (void*)f;
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
/** IOStream version of fopen().
|
||||
*
|
||||
* @param file name of the file to open
|
||||
* @param flags giving the mode to open in (as for fopen())
|
||||
* @return new stream for the open file, or 0 if failed
|
||||
*/
|
||||
IOStream *file_stream_fopen(const char *file, const char *flags){
|
||||
IOStream *io = 0;
|
||||
FILE *fin = fopen(file, flags);
|
||||
if(fin){
|
||||
io = file_stream_new(fin);
|
||||
if(!io){
|
||||
fclose(fin);
|
||||
//free(fin); // fclose frees ?
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
/** IOStream version of fdopen().
|
||||
*
|
||||
* @param fd file descriptor
|
||||
* @param flags giving the mode to open in (as for fdopen())
|
||||
* @return new stream for the open file, or 0 if failed
|
||||
*/
|
||||
IOStream *file_stream_fdopen(int fd, const char *flags){
|
||||
IOStream *io = 0;
|
||||
FILE *fin = fdopen(fd, flags);
|
||||
if(fin){
|
||||
io = file_stream_new(fin);
|
||||
}
|
||||
return io;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _XEN_LIB_FILE_STREAM_H_
|
||||
#define _XEN_LIB_FILE_STREAM_H_
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#include "iostream.h"
|
||||
#include <stdio.h>
|
||||
|
||||
extern IOStream *file_stream_new(FILE *f);
|
||||
extern IOStream *file_stream_fopen(const char *file, const char *flags);
|
||||
extern IOStream *file_stream_fdopen(int fd, const char *flags);
|
||||
extern IOStream get_stream_stdout(void);
|
||||
extern IOStream get_stream_stderr(void);
|
||||
extern IOStream get_stream_stdin(void);
|
||||
|
||||
extern int file_stream_setvbuf(IOStream *io, char *buf, int mode, size_t size);
|
||||
#endif
|
||||
#endif /* !_XEN_LIB_FILE_STREAM_H_ */
|
||||
@@ -0,0 +1,171 @@
|
||||
/* $Id: gzip_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $ */
|
||||
/*
|
||||
* Copyright (C) 2003 Hewlett-Packard Company.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* An IOStream implementation using zlib gzFile to provide
|
||||
* compression and decompression.
|
||||
*/
|
||||
#ifndef __KERNEL__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
#include "allocate.h"
|
||||
#include "gzip_stream.h"
|
||||
|
||||
static int gzip_read(IOStream *s, void *buf, size_t n);
|
||||
static int gzip_write(IOStream *s, const void *buf, size_t n);
|
||||
static int gzip_error(IOStream *s);
|
||||
static int gzip_close(IOStream *s);
|
||||
static void gzip_free(IOStream *s);
|
||||
static int gzip_flush(IOStream *s);
|
||||
|
||||
/** Methods used by a gzFile* IOStream. */
|
||||
static const IOMethods gzip_methods = {
|
||||
read: gzip_read,
|
||||
write: gzip_write,
|
||||
error: gzip_error,
|
||||
close: gzip_close,
|
||||
free: gzip_free,
|
||||
flush: gzip_flush,
|
||||
};
|
||||
|
||||
/** Get the underlying gzFile*.
|
||||
*
|
||||
* @param s gzip stream
|
||||
* @return the stream s wraps
|
||||
*/
|
||||
static inline gzFile get_gzfile(IOStream *s){
|
||||
return (gzFile)s->data;
|
||||
}
|
||||
|
||||
/** Write to the underlying stream.
|
||||
*
|
||||
* @param stream destination
|
||||
* @param buf data
|
||||
* @param n number of bytes to write
|
||||
* @return number of bytes written
|
||||
*/
|
||||
static int gzip_write(IOStream *s, const void *buf, size_t n){
|
||||
return gzwrite(get_gzfile(s), (void*)buf, n);
|
||||
}
|
||||
|
||||
/** Read from the underlying stream.
|
||||
*
|
||||
* @param stream input
|
||||
* @param buf where to put input
|
||||
* @param n number of bytes to read
|
||||
* @return number of bytes read
|
||||
*/
|
||||
static int gzip_read(IOStream *s, void *buf, size_t n){
|
||||
return gzread(get_gzfile(s), buf, n);
|
||||
}
|
||||
|
||||
/** Flush the underlying stream.
|
||||
*
|
||||
* @param s gzip stream
|
||||
* @return 0 on success, error code otherwise
|
||||
*/
|
||||
static int gzip_flush(IOStream *s){
|
||||
//return gzflush(get_gzfile(s), Z_NO_FLUSH);
|
||||
return gzflush(get_gzfile(s), Z_SYNC_FLUSH);
|
||||
//return gzflush(get_gzfile(s), Z_FULL_FLUSH);
|
||||
}
|
||||
|
||||
/** Check if a stream has an error.
|
||||
*
|
||||
* @param s gzip stream
|
||||
* @return 1 if has an error, 0 otherwise
|
||||
*/
|
||||
static int gzip_error(IOStream *s){
|
||||
int err;
|
||||
gzFile *gz = get_gzfile(s);
|
||||
gzerror(gz, &err);
|
||||
return (err == Z_ERRNO ? 1 /* ferror(gzfile(gz)) */ : err);
|
||||
}
|
||||
|
||||
/** Close a gzip stream.
|
||||
*
|
||||
* @param s gzip stream to close
|
||||
* @return result of the close
|
||||
*/
|
||||
static int gzip_close(IOStream *s){
|
||||
return gzclose(get_gzfile(s));
|
||||
}
|
||||
|
||||
/** Free a gzip stream.
|
||||
*
|
||||
* @param s gzip stream
|
||||
*/
|
||||
static void gzip_free(IOStream *s){
|
||||
// Do nothing - fclose does it all?
|
||||
}
|
||||
|
||||
/** Create an IOStream for a gzip stream.
|
||||
*
|
||||
* @param f stream to wrap
|
||||
* @return new IOStream using f for i/o
|
||||
*/
|
||||
IOStream *gzip_stream_new(gzFile *f){
|
||||
IOStream *io = ALLOCATE(IOStream);
|
||||
if(io){
|
||||
io->methods = &gzip_methods;
|
||||
io->data = (void*)f;
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
/** IOStream version of fopen().
|
||||
*
|
||||
* @param file name of the file to open
|
||||
* @param flags giving the mode to open in (as for fopen())
|
||||
* @return new stream for the open file, or NULL if failed
|
||||
*/
|
||||
IOStream *gzip_stream_fopen(const char *file, const char *flags){
|
||||
IOStream *io = NULL;
|
||||
gzFile *fgz;
|
||||
fgz = gzopen(file, flags);
|
||||
if(fgz){
|
||||
io = gzip_stream_new(fgz);
|
||||
if(!io){
|
||||
gzclose(fgz);
|
||||
//free(fgz); // gzclose frees ?
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
/** IOStream version of fdopen().
|
||||
*
|
||||
* @param fd file descriptor
|
||||
* @param flags giving the mode to open in (as for fdopen())
|
||||
* @return new stream for the open file, or NULL if failed
|
||||
*/
|
||||
IOStream *gzip_stream_fdopen(int fd, const char *flags){
|
||||
IOStream *io = NULL;
|
||||
gzFile *fgz;
|
||||
fgz = gzdopen(fd, flags);
|
||||
if(fgz){
|
||||
io = gzip_stream_new(fgz);
|
||||
}
|
||||
return io;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
#/* $Id: gzip_stream.h,v 1.3 2003/09/30 15:22:53 mjw Exp $ */
|
||||
/*
|
||||
* Copyright (C) 2003 Hewlett-Packard Company.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _SP_GZIP_STREAM_H_
|
||||
#define _SP_GZIP_STREAM_H_
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#include "iostream.h"
|
||||
#include "zlib.h"
|
||||
|
||||
extern IOStream *gzip_stream_new(gzFile *f);
|
||||
extern IOStream *gzip_stream_fopen(const char *file, const char *flags);
|
||||
extern IOStream *gzip_stream_fdopen(int fd, const char *flags);
|
||||
#endif
|
||||
#endif /* !_SP_FILE_STREAM_H_ */
|
||||
@@ -0,0 +1,640 @@
|
||||
/*
|
||||
* Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef __KERNEL__
|
||||
# include <linux/config.h>
|
||||
# include <linux/module.h>
|
||||
# include <linux/kernel.h>
|
||||
# include <linux/errno.h>
|
||||
#else
|
||||
# include <errno.h>
|
||||
# include <stddef.h>
|
||||
#endif
|
||||
|
||||
//#include <limits.h>
|
||||
|
||||
#include "allocate.h"
|
||||
#include "hash_table.h"
|
||||
|
||||
/** @file
|
||||
* Base support for hashtables.
|
||||
*
|
||||
* Hash codes are reduced modulo the number of buckets to index tables,
|
||||
* so there is no need for hash functions to limit the range of hashcodes.
|
||||
* In fact it is assumed that hashcodes do not change when the number of
|
||||
* buckets in the table changes.
|
||||
*/
|
||||
|
||||
/*==========================================================================*/
|
||||
/** Number of bits in half a word. */
|
||||
//#if __WORDSIZE == 64
|
||||
//#define HALF_WORD_BITS 32
|
||||
//#else
|
||||
#define HALF_WORD_BITS 16
|
||||
//#endif
|
||||
|
||||
/** Mask for lo half of a word. On 32-bit this is
|
||||
* (1<<16) - 1 = 65535 = 0xffff
|
||||
* It's 4294967295 = 0xffffffff on 64-bit.
|
||||
*/
|
||||
#define LO_HALF_MASK ((1 << HALF_WORD_BITS) - 1)
|
||||
|
||||
/** Get the lo half of a word. */
|
||||
#define LO_HALF(x) ((x) & LO_HALF_MASK)
|
||||
|
||||
/** Get the hi half of a word. */
|
||||
#define HI_HALF(x) ((x) >> HALF_WORD_BITS)
|
||||
|
||||
/** Do a full hash on both inputs, using DES-style non-linear scrambling.
|
||||
* Both inputs are replaced with the results of the hash.
|
||||
*
|
||||
* @param pleft input/output word
|
||||
* @param pright input/output word
|
||||
*/
|
||||
void pseudo_des(unsigned long *pleft, unsigned long *pright){
|
||||
// Bit-rich mixing constant.
|
||||
static const unsigned long a_mixer[] = {
|
||||
0xbaa96887L, 0x1e17d32cL, 0x03bcdc3cL, 0x0f33d1b2L, };
|
||||
|
||||
// Bit-rich mixing constant.
|
||||
static const unsigned long b_mixer[] = {
|
||||
0x4b0f3b58L, 0xe874f0c3L, 0x6955c5a6L, 0x55a7ca46L, };
|
||||
|
||||
// Number of iterations - must be 2 or 4.
|
||||
static const int ncycle = 4;
|
||||
//static const int ncycle = 2;
|
||||
|
||||
unsigned long left = *pleft, right = *pright;
|
||||
unsigned long v, v_hi, v_lo;
|
||||
int i;
|
||||
|
||||
for(i=0; i<ncycle; i++){
|
||||
// Flip some bits in right to get v.
|
||||
v = right;
|
||||
v ^= a_mixer[i];
|
||||
// Get lo and hi halves of v.
|
||||
v_lo = LO_HALF(v);
|
||||
v_hi = HI_HALF(v);
|
||||
// Non-linear mix of the halves of v.
|
||||
v = ((v_lo * v_lo) + ~(v_hi * v_hi));
|
||||
// Swap the halves of v.
|
||||
v = (HI_HALF(v) | (LO_HALF(v) << HALF_WORD_BITS));
|
||||
// Flip some bits.
|
||||
v ^= b_mixer[i];
|
||||
// More non-linear mixing.
|
||||
v += (v_lo * v_hi);
|
||||
v ^= left;
|
||||
left = right;
|
||||
right = v;
|
||||
}
|
||||
*pleft = left;
|
||||
*pright = right;
|
||||
}
|
||||
|
||||
/** Hash a string.
|
||||
*
|
||||
* @param s input to hash
|
||||
* @return hashcode
|
||||
*/
|
||||
Hashcode hash_string(char *s){
|
||||
Hashcode h = 0;
|
||||
if(s){
|
||||
for( ; *s; s++){
|
||||
h = hash_2ul(h, *s);
|
||||
}
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
/** Get the bucket for a hashcode in a hash table.
|
||||
*
|
||||
* @param table to get bucket from
|
||||
* @param hashcode to get bucket for
|
||||
* @return bucket
|
||||
*/
|
||||
inline HTBucket * get_bucket(HashTable *table, Hashcode hashcode){
|
||||
return table->buckets + (hashcode % table->buckets_n);
|
||||
}
|
||||
|
||||
/** Initialize a hash table.
|
||||
* Can be safely called more than once.
|
||||
*
|
||||
* @param table to initialize
|
||||
*/
|
||||
void HashTable_init(HashTable *table){
|
||||
int i;
|
||||
|
||||
if(!table->init_done){
|
||||
table->init_done = 1;
|
||||
table->next_id = 0;
|
||||
for(i=0; i<table->buckets_n; i++){
|
||||
HTBucket *bucket = get_bucket(table, i);
|
||||
bucket->head = 0;
|
||||
bucket->count = 0;
|
||||
}
|
||||
table->entry_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Allocate a new hashtable.
|
||||
* If the number of buckets is not positive the default is used.
|
||||
* The number of buckets should usually be prime.
|
||||
*
|
||||
* @param buckets_n number of buckets
|
||||
* @return new hashtable or null
|
||||
*/
|
||||
HashTable *HashTable_new(int buckets_n){
|
||||
HashTable *z = ALLOCATE(HashTable);
|
||||
if(!z) goto exit;
|
||||
if(buckets_n <= 0){
|
||||
buckets_n = HT_BUCKETS_N;
|
||||
}
|
||||
z->buckets = (HTBucket*)allocate(buckets_n * sizeof(HTBucket));
|
||||
if(!z->buckets){
|
||||
deallocate(z);
|
||||
z = 0;
|
||||
goto exit;
|
||||
}
|
||||
z->buckets_n = buckets_n;
|
||||
HashTable_init(z);
|
||||
exit:
|
||||
return z;
|
||||
}
|
||||
|
||||
/** Free a hashtable.
|
||||
* Any entries are removed and freed.
|
||||
*
|
||||
* @param h hashtable (ignored if null)
|
||||
*/
|
||||
void HashTable_free(HashTable *h){
|
||||
if(h){
|
||||
HashTable_clear(h);
|
||||
deallocate(h->buckets);
|
||||
deallocate(h);
|
||||
}
|
||||
}
|
||||
|
||||
/** Push an entry on the list in the bucket for a given hashcode.
|
||||
*
|
||||
* @param table to add entry to
|
||||
* @param hashcode for the entry
|
||||
* @param entry to add
|
||||
*/
|
||||
static inline void push_on_bucket(HashTable *table, Hashcode hashcode,
|
||||
HTEntry *entry){
|
||||
HTBucket *bucket;
|
||||
HTEntry *old_head;
|
||||
|
||||
bucket = get_bucket(table, hashcode);
|
||||
old_head = bucket->head;
|
||||
bucket->count++;
|
||||
bucket->head = entry;
|
||||
entry->next = old_head;
|
||||
}
|
||||
|
||||
/** Change the number of buckets in a hashtable.
|
||||
* No-op if the number of buckets is not positive.
|
||||
* Existing entries are reallocated to buckets based on their hashcodes.
|
||||
* The table is unmodified if the number of buckets cannot be changed.
|
||||
*
|
||||
* @param table hashtable
|
||||
* @param buckets_n new number of buckets
|
||||
* @return 0 on success, error code otherwise
|
||||
*/
|
||||
int HashTable_set_buckets_n(HashTable *table, int buckets_n){
|
||||
int err = 0;
|
||||
HTBucket *old_buckets = table->buckets;
|
||||
int old_buckets_n = table->buckets_n;
|
||||
int i;
|
||||
|
||||
if(buckets_n <= 0){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
table->buckets = (HTBucket*)allocate(buckets_n * sizeof(HTBucket));
|
||||
if(!table->buckets){
|
||||
err = -ENOMEM;
|
||||
table->buckets = old_buckets;
|
||||
goto exit;
|
||||
}
|
||||
table->buckets_n = buckets_n;
|
||||
for(i=0; i<old_buckets_n; i++){
|
||||
HTBucket *bucket = old_buckets + i;
|
||||
HTEntry *entry, *next;
|
||||
for(entry = bucket->head; entry; entry = next){
|
||||
next = entry->next;
|
||||
push_on_bucket(table, entry->hashcode, entry);
|
||||
}
|
||||
}
|
||||
deallocate(old_buckets);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Adjust the number of buckets so the table is neither too full nor too empty.
|
||||
* The table is unmodified if adjusting fails.
|
||||
*
|
||||
* @param table hash table
|
||||
* @param buckets_min minimum number of buckets (use default if 0 or negative)
|
||||
* @return 0 on success, error code otherwise
|
||||
*/
|
||||
int HashTable_adjust(HashTable *table, int buckets_min){
|
||||
int buckets_n = 0;
|
||||
int err = 0;
|
||||
if(buckets_min <= 0) buckets_min = HT_BUCKETS_N;
|
||||
if(table->entry_count >= table->buckets_n){
|
||||
// The table is dense - expand it.
|
||||
buckets_n = 2 * table->buckets_n;
|
||||
} else if((table->buckets_n > buckets_min) &&
|
||||
(4 * table->entry_count < table->buckets_n)){
|
||||
// The table is more than minimum size and sparse - shrink it.
|
||||
buckets_n = 2 * table->entry_count;
|
||||
if(buckets_n < buckets_min) buckets_n = buckets_min;
|
||||
}
|
||||
if(buckets_n){
|
||||
err = HashTable_set_buckets_n(table, buckets_n);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Allocate a new entry for a given value.
|
||||
*
|
||||
* @param value to put in the entry
|
||||
* @return entry, or 0 on failure
|
||||
*/
|
||||
HTEntry * HTEntry_new(Hashcode hashcode, void *key, void *value){
|
||||
HTEntry *z = ALLOCATE(HTEntry);
|
||||
if(z){
|
||||
z->hashcode = hashcode;
|
||||
z->key = key;
|
||||
z->value = value;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
/** Free an entry.
|
||||
*
|
||||
* @param z entry to free
|
||||
*/
|
||||
inline void HTEntry_free(HTEntry *z){
|
||||
if(z){
|
||||
deallocate(z);
|
||||
}
|
||||
}
|
||||
|
||||
/** Free an entry in a hashtable.
|
||||
* The table's entry_free_fn is used is defined, otherwise
|
||||
* the HTEntry itself is freed.
|
||||
*
|
||||
* @param table hashtable
|
||||
* @param entry to free
|
||||
*/
|
||||
inline void HashTable_free_entry(HashTable *table, HTEntry *entry){
|
||||
if(!entry)return;
|
||||
if(table && table->entry_free_fn){
|
||||
table->entry_free_fn(table, entry);
|
||||
} else {
|
||||
HTEntry_free(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the first entry satisfying a test from the bucket for the
|
||||
* given hashcode.
|
||||
*
|
||||
* @param table to look in
|
||||
* @param hashcode indicates the bucket
|
||||
* @param test_fn test to apply to elements
|
||||
* @param arg first argument to calls to test_fn
|
||||
* @return entry found, or 0
|
||||
*/
|
||||
inline HTEntry * HashTable_find_entry(HashTable *table, Hashcode hashcode,
|
||||
TableTestFn *test_fn, TableArg arg){
|
||||
HTBucket *bucket;
|
||||
HTEntry *entry = 0;
|
||||
HTEntry *next;
|
||||
|
||||
bucket = get_bucket(table, hashcode);
|
||||
for(entry = bucket->head; entry; entry = next){
|
||||
next = entry->next;
|
||||
if(test_fn(arg, table, entry)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/** Test hashtable keys for equality.
|
||||
* Uses the table's key_equal_fn if defined, otherwise pointer equality.
|
||||
*
|
||||
* @param key1 key to compare
|
||||
* @param key2 key to compare
|
||||
* @return 1 if equal, 0 otherwise
|
||||
*/
|
||||
inline int HashTable_key_equal(HashTable *table, void *key1, void *key2){
|
||||
return (table->key_equal_fn ? table->key_equal_fn(key1, key2) : key1==key2);
|
||||
}
|
||||
|
||||
/** Compute the hashcode of a hashtable key.
|
||||
* The table's key_hash_fn is used if defined, otherwise the address of
|
||||
* the key is hashed.
|
||||
*
|
||||
* @param table hashtable
|
||||
* @param key to hash
|
||||
* @return hashcode
|
||||
*/
|
||||
inline Hashcode HashTable_key_hash(HashTable *table, void *key){
|
||||
return (table->key_hash_fn ? table->key_hash_fn(key) : hash_ul((unsigned long)key));
|
||||
}
|
||||
|
||||
/** Test if an entry has a given key.
|
||||
*
|
||||
* @param arg containing key to test for
|
||||
* @param table the entry is in
|
||||
* @param entry to test
|
||||
* @return 1 if the entry has the key, 0 otherwise
|
||||
*/
|
||||
static inline int has_key(TableArg arg, HashTable *table, HTEntry *entry){
|
||||
return HashTable_key_equal(table, arg.ptr, entry->key);
|
||||
}
|
||||
|
||||
/** Get an entry with a given key.
|
||||
*
|
||||
* @param table to search
|
||||
* @param key to look for
|
||||
* @return entry if found, null otherwise
|
||||
*/
|
||||
#if 0
|
||||
inline HTEntry * HashTable_get_entry(HashTable *table, void *key){
|
||||
TableArg arg = { ptr: key };
|
||||
return HashTable_find_entry(table, HashTable_key_hash(table, key), has_key, arg);
|
||||
}
|
||||
#else
|
||||
inline HTEntry * HashTable_get_entry(HashTable *table, void *key){
|
||||
Hashcode hashcode;
|
||||
HTBucket *bucket;
|
||||
HTEntry *entry = 0;
|
||||
HTEntry *next;
|
||||
|
||||
hashcode = HashTable_key_hash(table, key);
|
||||
bucket = get_bucket(table, hashcode);
|
||||
for(entry = bucket->head; entry; entry = next){
|
||||
next = entry->next;
|
||||
if(HashTable_key_equal(table, key, entry->key)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Get the value of an entry with a given key.
|
||||
*
|
||||
* @param table to search
|
||||
* @param key to look for
|
||||
* @return value if an entry was found, null otherwise
|
||||
*/
|
||||
inline void * HashTable_get(HashTable *table, void *key){
|
||||
HTEntry *entry = HashTable_get_entry(table, key);
|
||||
return (entry ? entry->value : 0);
|
||||
}
|
||||
|
||||
/** Print the buckets in a table.
|
||||
*
|
||||
* @param table to print
|
||||
*/
|
||||
void show_buckets(HashTable *table, IOStream *io){
|
||||
int i,j ;
|
||||
IOStream_print(io, "entry_count=%d buckets_n=%d\n", table->entry_count, table->buckets_n);
|
||||
for(i=0; i<table->buckets_n; i++){
|
||||
if(0 || table->buckets[i].count>0){
|
||||
IOStream_print(io, "bucket %3d %3d %10p ", i,
|
||||
table->buckets[i].count,
|
||||
table->buckets[i].head);
|
||||
for(j = table->buckets[i].count; j>0; j--){
|
||||
IOStream_print(io, "+");
|
||||
}
|
||||
IOStream_print(io, "\n");
|
||||
}
|
||||
}
|
||||
HashTable_print(table, io);
|
||||
}
|
||||
|
||||
/** Print an entry in a table.
|
||||
*
|
||||
* @param entry to print
|
||||
* @param arg a pointer to an IOStream to print to
|
||||
* @return 0
|
||||
*/
|
||||
static int print_entry(TableArg arg, HashTable *table, HTEntry *entry){
|
||||
IOStream *io = (IOStream*)arg.ptr;
|
||||
IOStream_print(io, " b=%4lx h=%08lx i=%08lx |-> e=%8p k=%8p v=%8p\n",
|
||||
entry->hashcode % table->buckets_n,
|
||||
entry->hashcode,
|
||||
entry->index,
|
||||
entry, entry->key, entry->value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Print a hash table.
|
||||
*
|
||||
* @param table to print
|
||||
*/
|
||||
void HashTable_print(HashTable *table, IOStream *io){
|
||||
IOStream_print(io, "{\n");
|
||||
HashTable_map(table, print_entry, (TableArg){ ptr: io });
|
||||
IOStream_print(io, "}\n");
|
||||
}
|
||||
/*==========================================================================*/
|
||||
|
||||
/** Get the next entry id to use for a table.
|
||||
*
|
||||
* @param table hash table
|
||||
* @return non-zero entry id
|
||||
*/
|
||||
static inline unsigned long get_next_id(HashTable *table){
|
||||
unsigned long id;
|
||||
|
||||
if(table->next_id == 0){
|
||||
table->next_id = 1;
|
||||
}
|
||||
id = table->next_id++;
|
||||
return id;
|
||||
}
|
||||
|
||||
/** Add an entry to the bucket for the
|
||||
* given hashcode.
|
||||
*
|
||||
* @param table to insert in
|
||||
* @param hashcode indicates the bucket
|
||||
* @param key to add an entry for
|
||||
* @param value to add an entry for
|
||||
* @return entry on success, 0 on failure
|
||||
*/
|
||||
inline HTEntry * HashTable_add_entry(HashTable *table, Hashcode hashcode, void *key, void *value){
|
||||
HTEntry *entry = HTEntry_new(hashcode, key, value);
|
||||
if(entry){
|
||||
entry->index = get_next_id(table);
|
||||
push_on_bucket(table, hashcode, entry);
|
||||
table->entry_count++;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/** Move the front entry for a bucket to the correct point in the bucket order as
|
||||
* defined by the order function. If this is called every time a new entry is added
|
||||
* the bucket will be maintained in sorted order.
|
||||
*
|
||||
* @param table to modify
|
||||
* @param hashcode indicates the bucket
|
||||
* @param order entry comparison function
|
||||
* @return 0 if an entry was moved, 1 if not
|
||||
*/
|
||||
int HashTable_order_bucket(HashTable *table, Hashcode hashcode, TableOrderFn *order){
|
||||
HTEntry *new_entry = NULL, *prev = NULL, *entry = NULL;
|
||||
HTBucket *bucket;
|
||||
int err = 1;
|
||||
|
||||
bucket = get_bucket(table, hashcode);
|
||||
new_entry = bucket->head;
|
||||
if(!new_entry || !new_entry->next) goto exit;
|
||||
for(entry = new_entry->next; entry; prev = entry, entry = entry->next){
|
||||
if(order(new_entry, entry) <= 0) break;
|
||||
}
|
||||
if(prev){
|
||||
err = 0;
|
||||
bucket->head = new_entry->next;
|
||||
new_entry->next = entry;
|
||||
prev->next = new_entry;
|
||||
}
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Add an entry to a hashtable.
|
||||
* The entry is added to the bucket for its key's hashcode.
|
||||
*
|
||||
* @param table to insert in
|
||||
* @param key to add an entry for
|
||||
* @param value to add an entry for
|
||||
* @return entry on success, 0 on failure
|
||||
*/
|
||||
inline HTEntry * HashTable_add(HashTable *table, void *key, void *value){
|
||||
return HashTable_add_entry(table, HashTable_key_hash(table, key), key, value);
|
||||
}
|
||||
|
||||
|
||||
/** Remove entries satisfying a test from the bucket for the
|
||||
* given hashcode.
|
||||
*
|
||||
* @param table to remove from
|
||||
* @param hashcode indicates the bucket
|
||||
* @param test_fn test to apply to elements
|
||||
* @param arg first argument to calls to test_fn
|
||||
* @return number of entries removed
|
||||
*/
|
||||
inline int HashTable_remove_entry(HashTable *table, Hashcode hashcode,
|
||||
TableTestFn *test_fn, TableArg arg){
|
||||
HTBucket *bucket;
|
||||
HTEntry *entry, *prev = 0, *next;
|
||||
int removed_count = 0;
|
||||
|
||||
bucket = get_bucket(table, hashcode);
|
||||
for(entry = bucket->head; entry; entry = next){
|
||||
next = entry->next;
|
||||
if(test_fn(arg, table, entry)){
|
||||
if(prev){
|
||||
prev->next = next;
|
||||
} else {
|
||||
bucket->head = next;
|
||||
}
|
||||
bucket->count--;
|
||||
table->entry_count--;
|
||||
removed_count++;
|
||||
HashTable_free_entry(table, entry);
|
||||
entry = 0;
|
||||
}
|
||||
prev = entry;
|
||||
}
|
||||
return removed_count;
|
||||
}
|
||||
|
||||
/** Remove entries with a given key.
|
||||
*
|
||||
* @param table to remove from
|
||||
* @param key of entries to remove
|
||||
* @return number of entries removed
|
||||
*/
|
||||
inline int HashTable_remove(HashTable *table, void *key){
|
||||
#if 1
|
||||
Hashcode hashcode;
|
||||
HTBucket *bucket;
|
||||
HTEntry *entry, *prev = 0, *next;
|
||||
int removed_count = 0;
|
||||
|
||||
hashcode = HashTable_key_hash(table, key);
|
||||
bucket = get_bucket(table, hashcode);
|
||||
for(entry = bucket->head; entry; entry = next){
|
||||
next = entry->next;
|
||||
if(HashTable_key_equal(table, key, entry->key)){
|
||||
if(prev){
|
||||
prev->next = next;
|
||||
} else {
|
||||
bucket->head = next;
|
||||
}
|
||||
bucket->count--;
|
||||
table->entry_count--;
|
||||
removed_count++;
|
||||
HashTable_free_entry(table, entry);
|
||||
entry = 0;
|
||||
}
|
||||
prev = entry;
|
||||
}
|
||||
return removed_count;
|
||||
#else
|
||||
return HashTable_remove_entry(table, HashTable_key_hash(table, key),
|
||||
has_key, (TableArg){ ptr: key});
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Remove (and free) all the entries in a bucket.
|
||||
*
|
||||
* @param bucket to clear
|
||||
*/
|
||||
static inline void bucket_clear(HashTable *table, HTBucket *bucket){
|
||||
HTEntry *entry, *next;
|
||||
|
||||
for(entry = bucket->head; entry; entry = next){
|
||||
next = entry->next;
|
||||
HashTable_free_entry(table, entry);
|
||||
}
|
||||
bucket->head = 0;
|
||||
table->entry_count -= bucket->count;
|
||||
bucket->count = 0;
|
||||
}
|
||||
|
||||
/** Remove (and free) all the entries in a table.
|
||||
*
|
||||
* @param table to clear
|
||||
*/
|
||||
void HashTable_clear(HashTable *table){
|
||||
int i, n = table->buckets_n;
|
||||
|
||||
for(i=0; i<n; i++){
|
||||
bucket_clear(table, table->buckets + i);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
/* $Id: hash_table.h,v 1.1 2004/03/30 16:21:26 mjw Exp $ */
|
||||
/*
|
||||
* Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _XEN_LIB_HASH_TABLE_H_
|
||||
#define _XEN_LIB_HASH_TABLE_H_
|
||||
|
||||
#include "iostream.h"
|
||||
|
||||
typedef unsigned long Hashcode;
|
||||
|
||||
/** Type used to pass parameters to table functions. */
|
||||
typedef union TableArg {
|
||||
unsigned long ul;
|
||||
void *ptr;
|
||||
} TableArg;
|
||||
|
||||
/** An entry in a bucket list. */
|
||||
typedef struct HTEntry {
|
||||
/** Hashcode of the entry's key. */
|
||||
Hashcode hashcode;
|
||||
/** Identifier for this entry in the table. */
|
||||
int index;
|
||||
/** The key for this entry. */
|
||||
void *key;
|
||||
/** The value in this entry. */
|
||||
void *value;
|
||||
/** The next entry in the list. */
|
||||
struct HTEntry *next;
|
||||
} HTEntry;
|
||||
|
||||
/** A bucket in a rule table. */
|
||||
typedef struct HTBucket {
|
||||
/** Number of entries in the bucket. */
|
||||
int count;
|
||||
/** First entry in the bucket (may be null). */
|
||||
HTEntry *head;
|
||||
} HTBucket;
|
||||
|
||||
/** Default number of buckets in a hash table.
|
||||
* You want enough buckets so the lists in the buckets will typically be short.
|
||||
* It's a good idea if this is prime, since that will help to spread hashcodes
|
||||
* around the table.
|
||||
*/
|
||||
//#define HT_BUCKETS_N 1
|
||||
//#define HT_BUCKETS_N 3
|
||||
//#define HT_BUCKETS_N 7
|
||||
//#define HT_BUCKETS_N 17
|
||||
//#define HT_BUCKETS_N 97
|
||||
//#define HT_BUCKETS_N 211
|
||||
//#define HT_BUCKETS_N 401
|
||||
#define HT_BUCKETS_N 1021
|
||||
|
||||
typedef struct HashTable HashTable;
|
||||
|
||||
/** Type for a function used to select table entries. */
|
||||
typedef int TableTestFn(TableArg arg, HashTable *table, HTEntry *entry);
|
||||
|
||||
/** Type for a function to map over table entries. */
|
||||
typedef int TableMapFn(TableArg arg, HashTable *table, HTEntry *entry);
|
||||
|
||||
/** Type for a function to free table entries. */
|
||||
typedef void TableFreeFn(HashTable *table, HTEntry *entry);
|
||||
|
||||
/** Type for a function to hash table keys. */
|
||||
typedef Hashcode TableHashFn(void *key);
|
||||
|
||||
/** Type for a function to test table keys for equality. */
|
||||
typedef int TableEqualFn(void *key1, void *key2);
|
||||
|
||||
/** Type for a function to order table entries. */
|
||||
typedef int TableOrderFn(HTEntry *e1, HTEntry *e2);
|
||||
|
||||
/** General hash table.
|
||||
* A hash table with a list in each bucket.
|
||||
* Functions can be supplied for freeing entries, hashing keys, and comparing keys.
|
||||
* These all default to 0, when default behaviour treating keys as integers is used.
|
||||
*/
|
||||
struct HashTable {
|
||||
/** Flag indicating whether the table has been initialised. */
|
||||
int init_done;
|
||||
/** Next value for the id field in inserted rules. */
|
||||
unsigned long next_id;
|
||||
/** Number of buckets in the bucket array. */
|
||||
int buckets_n;
|
||||
/** Array of buckets, each with its own list. */
|
||||
HTBucket *buckets;
|
||||
/** Number of entries in the table. */
|
||||
int entry_count;
|
||||
/** Function to free keys and values in entries. */
|
||||
TableFreeFn *entry_free_fn;
|
||||
/** Function to hash keys. */
|
||||
TableHashFn *key_hash_fn;
|
||||
/** Function to compare keys for equality. */
|
||||
TableEqualFn *key_equal_fn;
|
||||
/** Place for the user of the table to hang extra data. */
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
extern HashTable *HashTable_new(int bucket_n);
|
||||
extern void HashTable_free(HashTable *table);
|
||||
extern HTEntry * HTEntry_new(Hashcode hashcode, void *key, void *value);
|
||||
extern void HTEntry_free(HTEntry *entry);
|
||||
extern int HashTable_set_bucket_n(HashTable *table, int bucket_n);
|
||||
extern void HashTable_clear(HashTable *table);
|
||||
extern HTEntry * HashTable_add_entry(HashTable *table, Hashcode hashcode, void *key, void *value);
|
||||
extern HTEntry * HashTable_get_entry(HashTable *table, void *key);
|
||||
extern HTEntry * HashTable_add(HashTable *table, void *key, void *value);
|
||||
extern void * HashTable_get(HashTable *table, void *key);
|
||||
extern int HashTable_remove(HashTable *table, void *key);
|
||||
extern HTEntry * HashTable_find_entry(HashTable *table, Hashcode hashcode,
|
||||
TableTestFn *test_fn, TableArg arg);
|
||||
extern int HashTable_remove_entry(HashTable *table, Hashcode hashcode,
|
||||
TableTestFn *test_fn, TableArg arg);
|
||||
//extern int HashTable_map(HashTable *table, TableMapFn *map_fn, TableArg arg);
|
||||
extern void HashTable_print(HashTable *table, IOStream *out);
|
||||
extern int HashTable_set_buckets_n(HashTable *table, int buckets_n);
|
||||
extern int HashTable_adjust(HashTable *table, int buckets_min);
|
||||
extern void pseudo_des(unsigned long *pleft, unsigned long *pright);
|
||||
extern Hashcode hash_string(char *s);
|
||||
|
||||
extern int HashTable_order_bucket(HashTable *table, Hashcode hashcode, TableOrderFn *order);
|
||||
|
||||
/** Control whether to use hashing based on DES or simple
|
||||
* hashing. DES hashing is `more random' but much more expensive.
|
||||
*/
|
||||
#define HASH_PSEUDO_DES 0
|
||||
|
||||
/** Hash a long using a quick and dirty linear congruential random number generator.
|
||||
* See `Numerical Recipes in C', Chapter 7, "An Even Quicker Generator".
|
||||
*
|
||||
* @param a value to hash
|
||||
* @return hashed input
|
||||
*/
|
||||
static inline unsigned long lcrng_hash(unsigned long a){
|
||||
return (1664525L * a + 1013904223L);
|
||||
}
|
||||
|
||||
/** Hash an unsigned long.
|
||||
*
|
||||
* @param a input to hash
|
||||
* @return hashcode
|
||||
*/
|
||||
static inline Hashcode hash_ul(unsigned long a){
|
||||
#if HASH_PSEUDO_DES
|
||||
unsigned long left = a;
|
||||
unsigned long right = 0L;
|
||||
pseudo_des(&left, &right);
|
||||
return right;
|
||||
#else
|
||||
a = lcrng_hash(a);
|
||||
a = lcrng_hash(a);
|
||||
return a;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Hash two unsigned longs together.
|
||||
*
|
||||
* @param a input to hash
|
||||
* @param b input to hash
|
||||
* @return hashcode
|
||||
*/
|
||||
static inline Hashcode hash_2ul(unsigned long a, unsigned long b){
|
||||
#if HASH_PSEUDO_DES
|
||||
unsigned long left = a;
|
||||
unsigned long right = b;
|
||||
pseudo_des(&left, &right);
|
||||
return right;
|
||||
#else
|
||||
a = lcrng_hash(a);
|
||||
a ^= b;
|
||||
a = lcrng_hash(a);
|
||||
return a;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Hash a hashcode and an unsigned long together.
|
||||
*
|
||||
* @param a input hashcode
|
||||
* @param b input to hash
|
||||
* @return hashcode
|
||||
*/
|
||||
static inline Hashcode hash_hul(Hashcode a, unsigned long b){
|
||||
#if HASH_PSEUDO_DES
|
||||
unsigned long left = a;
|
||||
unsigned long right = b;
|
||||
pseudo_des(&left, &right);
|
||||
return right;
|
||||
#else
|
||||
a ^= b;
|
||||
a = lcrng_hash(a);
|
||||
return a;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Macro to declare variables for HashTable_for_each() to use.
|
||||
*
|
||||
* @param entry variable that is set to entries in the table
|
||||
*/
|
||||
#define HashTable_for_decl(entry) \
|
||||
HashTable *_var_table; \
|
||||
HTBucket *_var_bucket; \
|
||||
HTBucket *_var_end; \
|
||||
HTEntry *_var_next; \
|
||||
HTEntry *entry
|
||||
|
||||
/** Macro to iterate over the entries in a hashtable.
|
||||
* Must be in a scope where HashTable_for_decl() has been used to declare
|
||||
* variables for it to use.
|
||||
* The variable 'entry' is iterated over entries in the table.
|
||||
* The code produced is syntactically a loop, so it must be followed by
|
||||
* a loop body, typically some statements in braces:
|
||||
* HashTable_for_each(entry, table){ ...loop body... }
|
||||
*
|
||||
* HashTable_for_each() and HashTable_for_decl() cannot be used for nested
|
||||
* loops as variables will clash.
|
||||
*
|
||||
* @note The simplest way to code a direct loop over the entries in a hashtable
|
||||
* is to use a loop over the buckets, with a nested loop over the entries
|
||||
* in a bucket. Using this approach in a macro means the macro contains
|
||||
* an opening brace, and calls to it must be followed by 2 braces!
|
||||
* To avoid this the code has been restructured so that it is a for loop.
|
||||
* So that statements could be used in the test expression of the for loop,
|
||||
* we have used the gcc statement expression extension ({ ... }).
|
||||
*
|
||||
* @param entry variable to iterate over the entries
|
||||
* @param table to iterate over (non-null)
|
||||
*/
|
||||
#define HashTable_for_each(entry, table) \
|
||||
_var_table = table; \
|
||||
_var_bucket = _var_table->buckets; \
|
||||
_var_end = _var_bucket + _var_table->buckets_n; \
|
||||
for(entry=0, _var_next=0; \
|
||||
({ if(_var_next){ \
|
||||
entry = _var_next; \
|
||||
_var_next = entry->next; \
|
||||
} else { \
|
||||
while(_var_bucket < _var_end){ \
|
||||
entry = _var_bucket->head; \
|
||||
_var_bucket++; \
|
||||
if(entry){ \
|
||||
_var_next = entry->next; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
}; \
|
||||
entry; }); \
|
||||
entry = _var_next )
|
||||
|
||||
/** Map a function over the entries in a table.
|
||||
* Mapping stops when the function returns a non-zero value.
|
||||
* Uses the gcc statement expression extension ({ ... }).
|
||||
*
|
||||
* @param table to map over
|
||||
* @param fn function to apply to entries
|
||||
* @param arg first argument to call the function with
|
||||
* @return 0 if fn always returned 0, first non-zero value otherwise
|
||||
*/
|
||||
#define HashTable_map(table, fn, arg) \
|
||||
({ HashTable_for_decl(_var_entry); \
|
||||
TableArg _var_arg = arg; \
|
||||
int _var_value = 0; \
|
||||
HashTable_for_each(_var_entry, table){ \
|
||||
if((_var_value = fn(_var_arg, _var_table, _var_entry))) break; \
|
||||
} \
|
||||
_var_value; })
|
||||
|
||||
/** Cast x to the type for a key or value in a hash table.
|
||||
* This avoids compiler warnings when using short integers
|
||||
* as keys or values (especially on 64-bit platforms).
|
||||
*/
|
||||
#define HKEY(x) ((void*)(unsigned long)(x))
|
||||
|
||||
/** Cast x from the type for a key or value in a hash table.
|
||||
* to an unsigned long. This avoids compiler warnings when using
|
||||
* short integers as keys or values (especially on 64-bit platforms).
|
||||
*/
|
||||
#define HVAL(x) ((unsigned long)(x))
|
||||
|
||||
#endif /* !_XEN_LIB_HASH_TABLE_H_ */
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "iostream.h"
|
||||
#include "sys_string.h"
|
||||
|
||||
/** Print on a stream, like vfprintf().
|
||||
*
|
||||
* @param stream to print to
|
||||
* @param format for the print (as fprintf())
|
||||
* @param args arguments to print
|
||||
* @return result code from the print
|
||||
*/
|
||||
int IOStream_vprint(IOStream *stream, const char *format, va_list args){
|
||||
char buffer[1024];
|
||||
int k = sizeof(buffer), n;
|
||||
|
||||
n = vsnprintf(buffer, k, (char*)format, args);
|
||||
if(n < 0 || n > k ){
|
||||
n = k;
|
||||
}
|
||||
n = IOStream_write(stream, buffer, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
/** Print on a stream, like fprintf().
|
||||
*
|
||||
* @param stream to print to
|
||||
* @param format for the print (as fprintf())
|
||||
* @return result code from the print
|
||||
*/
|
||||
int IOStream_print(IOStream *stream, const char *format, ...){
|
||||
va_list args;
|
||||
int result = -1;
|
||||
|
||||
va_start(args, format);
|
||||
result = IOStream_vprint(stream, format, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
#ifndef _XC_LINUX_SAVE_H_
|
||||
#define _XC_LINUX_SAVE_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/errno.h>
|
||||
#else
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "allocate.h"
|
||||
|
||||
/** End of input return value. */
|
||||
#define IOSTREAM_EOF -1
|
||||
|
||||
/** An input/output abstraction.
|
||||
*/
|
||||
typedef struct IOStream IOStream;
|
||||
|
||||
/** Record of the functions to use for operations on an
|
||||
* IOStream implementation.
|
||||
*/
|
||||
typedef struct IOMethods {
|
||||
/** Read function. Called with the user data, buffer to read into
|
||||
* and number of bytes to read. Must return number of bytes read
|
||||
* on success, less than zero on error.
|
||||
*/
|
||||
int (*read)(IOStream *stream, void *buf, size_t n);
|
||||
|
||||
/** Write function. Called with user data, buffer to write and
|
||||
* number of bytes to write. Must return number of bytes written on
|
||||
* success, less than zero otherwise.
|
||||
*/
|
||||
int (*write)(IOStream *stream, const void *buf, size_t n);
|
||||
|
||||
int (*flush)(IOStream *s);
|
||||
|
||||
int (*error)(IOStream *s);
|
||||
|
||||
int (*close)(IOStream *s);
|
||||
|
||||
void (*free)(IOStream *s);
|
||||
|
||||
void (*lock)(IOStream *s);
|
||||
void (*unlock)(IOStream *s);
|
||||
|
||||
} IOMethods;
|
||||
|
||||
/** Abstract i/o object.
|
||||
*/
|
||||
struct IOStream {
|
||||
/** Methods to use to implement operations. */
|
||||
const IOMethods *methods;
|
||||
/** Private state for the implementation. */
|
||||
const void *data;
|
||||
/** Flag indicating whether the stream is closed. */
|
||||
int closed;
|
||||
/** Number of bytes written. */
|
||||
int written;
|
||||
/** Number of bytes read. */
|
||||
int read;
|
||||
};
|
||||
|
||||
|
||||
/** IOStream version of stdin. */
|
||||
extern IOStream *iostdin;
|
||||
|
||||
/** IOStream version of stdout, */
|
||||
extern IOStream *iostdout;
|
||||
|
||||
/** IOStream version of stderr. */
|
||||
extern IOStream *iostderr;
|
||||
|
||||
extern int IOStream_print(IOStream *io, const char *format, ...);
|
||||
extern int IOStream_vprint(IOStream *io, const char *format, va_list args);
|
||||
|
||||
/** Read from a stream.
|
||||
*
|
||||
* @param stream input
|
||||
* @param buf where to put input
|
||||
* @param n number of bytes to read
|
||||
* @return if ok, number of bytes read, otherwise negative error code
|
||||
*/
|
||||
static inline int IOStream_read(IOStream *stream, void *buf, size_t n){
|
||||
int result = 0;
|
||||
if(stream->closed) goto exit;
|
||||
if(!stream->methods || !stream->methods->read){
|
||||
result = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
result = stream->methods->read(stream, buf, n);
|
||||
if(result > 0){
|
||||
stream->read += result;
|
||||
}
|
||||
exit:
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Write to a stream.
|
||||
*
|
||||
* @param stream input
|
||||
* @param buf where to put input
|
||||
* @param n number of bytes to write
|
||||
* @return if ok, number of bytes read, otherwise negative error code
|
||||
*/
|
||||
static inline int IOStream_write(IOStream *stream, const void *buf, size_t n){
|
||||
int result = 0;
|
||||
if(stream->closed) goto exit;
|
||||
if(!stream->methods || !stream->methods->write){
|
||||
result = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
result = stream->methods->write(stream, buf, n);
|
||||
if(result > 0){
|
||||
stream->written += result;
|
||||
}
|
||||
exit:
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Flush the stream.
|
||||
*
|
||||
* @param stream stream
|
||||
* @return 0 on success, IOSTREAM_EOF otherwise
|
||||
*/
|
||||
static inline int IOStream_flush(IOStream *stream){
|
||||
int result = 0;
|
||||
if(stream->closed){
|
||||
result = IOSTREAM_EOF;
|
||||
} else if(stream->methods->flush){
|
||||
result = stream->methods->flush(stream);
|
||||
if(result < 0) result = IOSTREAM_EOF;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Check whether the stream has an error.
|
||||
*
|
||||
* @param stream to check
|
||||
* @return 1 for error, 0 otherwise
|
||||
*/
|
||||
static inline int IOStream_error(IOStream *stream){
|
||||
int err = 0;
|
||||
if(stream->methods && stream->methods->error){
|
||||
err = stream->methods->error(stream);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Close the stream.
|
||||
*
|
||||
* @param stream to close
|
||||
* @return 1 for error, 0 otherwise
|
||||
*/
|
||||
static inline int IOStream_close(IOStream *stream){
|
||||
int err = 1;
|
||||
if(stream->methods && stream->methods->close){
|
||||
err = stream->methods->close(stream);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Test if the stream has been closed.
|
||||
*
|
||||
* @param stream to check
|
||||
* @return 1 if closed, 0 otherwise
|
||||
*/
|
||||
static inline int IOStream_is_closed(IOStream *stream){
|
||||
return stream->closed;
|
||||
}
|
||||
|
||||
/** Free the memory used by the stream.
|
||||
*
|
||||
* @param stream to free
|
||||
*/
|
||||
static inline void IOStream_free(IOStream *stream){
|
||||
if(stream->methods && stream->methods->free){
|
||||
stream->methods->free(stream);
|
||||
}
|
||||
*stream = (IOStream){};
|
||||
deallocate(stream);
|
||||
}
|
||||
|
||||
|
||||
/** Print a character to a stream, like fputc().
|
||||
*
|
||||
* @param stream to print to
|
||||
* @param c character to print
|
||||
* @return result code from the print
|
||||
*/
|
||||
static inline int IOStream_putc(IOStream *stream, int c){
|
||||
int err;
|
||||
unsigned char b = (unsigned char)c;
|
||||
err = IOStream_write(stream, &b, 1);
|
||||
if(err < 1){
|
||||
err = IOSTREAM_EOF;
|
||||
} else {
|
||||
err = b;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Read from a stream, like fgetc().
|
||||
*
|
||||
* @param stream to read from
|
||||
* @return IOSTREAM_EOF on error, character read otherwise
|
||||
*/
|
||||
static inline int IOStream_getc(IOStream *stream){
|
||||
int err, rc;
|
||||
unsigned char b;
|
||||
|
||||
err = IOStream_read(stream, &b, 1);
|
||||
if(err < 1){
|
||||
rc = IOSTREAM_EOF;
|
||||
} else {
|
||||
rc = b;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** Get number of bytes read.
|
||||
*
|
||||
* @param stream to get from
|
||||
* @return number of bytes read
|
||||
*/
|
||||
static inline int IOStream_get_read(IOStream *stream){
|
||||
return stream->read;
|
||||
}
|
||||
|
||||
/** Get number of bytes written.
|
||||
*
|
||||
* @param stream to get from
|
||||
* @return number of bytes written
|
||||
*/
|
||||
static inline int IOStream_get_written(IOStream *stream){
|
||||
return stream->written;
|
||||
}
|
||||
|
||||
|
||||
#endif /* ! _XC_LINUX_SAVE_H_ */
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* An IOStream implementation using printk() for output.
|
||||
* Input is not implemented.
|
||||
*/
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "kernel_stream.h"
|
||||
#include "allocate.h"
|
||||
|
||||
/** Number of characters in the output buffer.
|
||||
* The kernel uses 1024 for printk, so that should suffice.
|
||||
*/
|
||||
#define BUF_N 1024
|
||||
|
||||
/** State for a kernel stream. */
|
||||
typedef struct KernelData {
|
||||
/** Stream lock. We need a lock to serialize access to the stream. */
|
||||
spinlock_t lock;
|
||||
/** Saved flags for locking. */
|
||||
unsigned long flags;
|
||||
/** Size of the output buffer. */
|
||||
int buf_n;
|
||||
/** Output buffer. */
|
||||
char buf[BUF_N];
|
||||
} KernelData;
|
||||
|
||||
static int kernel_write(IOStream *s, const char *msg, int n);
|
||||
static void kernel_free(IOStream *s);
|
||||
static void kernel_stream_lock(IOStream *s);
|
||||
static void kernel_stream_unlock(IOStream *s);
|
||||
|
||||
/** Methods for a kernel stream. Output only. */
|
||||
static const IOMethods kernel_methods = {
|
||||
write: kernel_write,
|
||||
free: kernel_free,
|
||||
lock: kernel_stream_lock,
|
||||
unlock: kernel_stream_unlock,
|
||||
};
|
||||
|
||||
/** Shared state for kernel streams.
|
||||
* All implementations write using printk, so we can use
|
||||
* shared state and avoid allocating it.
|
||||
*/
|
||||
static const KernelData kernel_data = {
|
||||
lock: SPIN_LOCK_UNLOCKED,
|
||||
flags: 0,
|
||||
buf_n: BUF_N,
|
||||
};
|
||||
|
||||
/** Stream for kernel printk. */
|
||||
static IOStream iokernel = {
|
||||
methods: &kernel_methods,
|
||||
data: &kernel_data,
|
||||
};
|
||||
|
||||
/** Stream for kernel printk. */
|
||||
IOStream *iostdout = &iokernel;
|
||||
|
||||
/** Stream for kernel printk. */
|
||||
IOStream *iostdin = &iokernel;
|
||||
|
||||
/** Stream for kernel printk. */
|
||||
IOStream *iostderr = &iokernel;
|
||||
|
||||
/** Get an output-only stream implementation using
|
||||
* printk(). The stream uses static storage, and must not be freed.
|
||||
*
|
||||
* @return kernel stream
|
||||
*/
|
||||
IOStream get_stream_kernel(void){
|
||||
return iokernel;
|
||||
}
|
||||
|
||||
/** Obtain the lock on the stream state.
|
||||
*
|
||||
* @param kdata stream state
|
||||
*/
|
||||
static inline void KernelData_lock(KernelData *kdata){
|
||||
spin_lock_irqsave(&kdata->lock, kdata->flags);
|
||||
}
|
||||
|
||||
/** Release the lock on the stream state.
|
||||
*
|
||||
* @param kdata stream state
|
||||
*/
|
||||
static inline void KernelData_unlock(KernelData *kdata){
|
||||
spin_unlock_irqrestore(&kdata->lock, kdata->flags);
|
||||
}
|
||||
|
||||
/** Get the stream state.
|
||||
*
|
||||
* @param s kernel stream
|
||||
* @return stream state
|
||||
*/
|
||||
static inline KernelData *get_kernel_data(IOStream *s){
|
||||
return (KernelData*)s->data;
|
||||
}
|
||||
|
||||
/** Obtain the lock on the stream state.
|
||||
*
|
||||
* @param s stream
|
||||
*/
|
||||
void kernel_stream_lock(IOStream *s){
|
||||
KernelData_lock(get_kernel_data(s));
|
||||
}
|
||||
|
||||
/** Release the lock on the stream state.
|
||||
*
|
||||
* @param s stream
|
||||
*/
|
||||
void kernel_stream_unlock(IOStream *s){
|
||||
KernelData_unlock(get_kernel_data(s));
|
||||
}
|
||||
|
||||
/** Write to a kernel stream.
|
||||
*
|
||||
* @param stream kernel stream
|
||||
* @param format print format
|
||||
* @param args print arguments
|
||||
* @return result of the print
|
||||
*/
|
||||
static int kernel_write(IOStream *stream, const char *buf, int n){
|
||||
KernelData *kdata = get_kernel_data(stream);
|
||||
int k;
|
||||
k = kdata->buf_n - 1;
|
||||
if(n < k) k = n;
|
||||
memcpy(kdata->buf, buf, k);
|
||||
kdata->buf[k] = '\0'
|
||||
printk(kdata->buf);
|
||||
return k;
|
||||
}
|
||||
|
||||
/** Free a kernel stream.
|
||||
* Frees the internal state of the stream.
|
||||
* Do not call this unless the stream was dynamically allocated.
|
||||
* Do not call this on a stream returned from get_stream_kernel().
|
||||
*
|
||||
* @param io stream to free
|
||||
*/
|
||||
static void kernel_free(IOStream *io){
|
||||
KernelData *kdata;
|
||||
if(io == &iokernel) return;
|
||||
kdata = get_kernel_data(io);
|
||||
zero(kdata, sizeof(*kdata));
|
||||
deallocate(kdata);
|
||||
}
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _XEN_LIB_KERNEL_STREAM_H_
|
||||
#define _XEN_LIB_KERNEL_STREAM_H_
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include "iostream.h"
|
||||
|
||||
extern IOStream get_stream_kernel(void);
|
||||
#define get_stream_stdout get_stream_kernel
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* !_XEN_LIB_KERNEL_STREAM_H_ */
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of the
|
||||
* License, or (at your option) any later version. This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Lexical analysis.
|
||||
*/
|
||||
|
||||
#include "sys_string.h"
|
||||
#include "lexis.h"
|
||||
#include <errno.h>
|
||||
|
||||
/** Check if a value lies in a (closed) range.
|
||||
*
|
||||
* @param x value to test
|
||||
* @param lo low end of the range
|
||||
* @param hi high end of the range
|
||||
* @return 1 if x is in the interval [lo, hi], 0 otherwise
|
||||
*/
|
||||
inline static int in_range(int x, int lo, int hi){
|
||||
return (lo <= x) && (x <= hi);
|
||||
}
|
||||
|
||||
/** Determine if a string is an (unsigned) decimal number.
|
||||
*
|
||||
* @param s pointer to characters to test
|
||||
* @param n length of string
|
||||
* @return 1 if s is a decimal number, 0 otherwise.
|
||||
*/
|
||||
int is_decimal_number(const char *s, int n){
|
||||
int i;
|
||||
if(n <= 0)return 0;
|
||||
for(i = 0; i < n; i++){
|
||||
if(!in_decimal_digit_class(s[i])) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Determine if a string is a hex number.
|
||||
* Hex numbers are 0, or start with 0x or 0X followed
|
||||
* by a non-zero number of hex digits (0-9,a-f,A-F).
|
||||
*
|
||||
* @param s pointer to characters to test
|
||||
* @param n length of string
|
||||
* @return 1 if s is a hex number, 0 otherwise.
|
||||
*/
|
||||
int is_hex_number(const char *s, int n){
|
||||
int i;
|
||||
if(n <= 0) return 0;
|
||||
if(n == 1){
|
||||
return s[0]=='0';
|
||||
}
|
||||
if(n <= 3) return 0;
|
||||
if(s[0] != '0' || (s[1] != 'x' && s[1] != 'X')) return 0;
|
||||
for(i = 2; i < n; i++){
|
||||
if(!in_hex_digit_class(s[i])) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Test if a string matches a keyword.
|
||||
* The comparison is case-insensitive.
|
||||
* The comparison fails if either argument is null.
|
||||
*
|
||||
* @param s string
|
||||
* @param k keyword
|
||||
* @return 1 if they match, 0 otherwise
|
||||
*/
|
||||
int is_keyword(const char *s, const char *k){
|
||||
return s && k && !strcasecmp(s, k);
|
||||
}
|
||||
|
||||
/** Test if a string matches a character.
|
||||
*
|
||||
* @param s string
|
||||
* @param c character (non-null)
|
||||
* @return 1 if s contains exactly c, 0 otherwise
|
||||
*/
|
||||
int is_keychar(const char *s, char c){
|
||||
return c && (s[0] == c) && !s[1];
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of the
|
||||
* License, or (at your option) any later version. This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _SP_LEXIS_H_
|
||||
#define _SP_LEXIS_H_
|
||||
|
||||
#include "sys_string.h"
|
||||
#include "sys_ctype.h"
|
||||
|
||||
/** @file
|
||||
* Lexical analysis.
|
||||
*/
|
||||
|
||||
/** Class of characters treated as space. */
|
||||
#define space_class ((char []){ '\n', '\r', '\t', ' ', '\f' , 0 })
|
||||
|
||||
/** Class of separator characters. */
|
||||
#define sep_class "{}()<>[]@!;"
|
||||
|
||||
#define comment_class "#"
|
||||
|
||||
/** Determine if a character is in a given class.
|
||||
*
|
||||
* @param c character to test
|
||||
* @param s null-terminated string of characters in the class
|
||||
* @return 1 if c is in the class, 0 otherwise.
|
||||
*/
|
||||
static inline int in_class(int c, const char *s){
|
||||
return s && (strchr(s, c) != 0);
|
||||
}
|
||||
|
||||
/** Determine if a character is in the space class.
|
||||
*
|
||||
* @param c character to test
|
||||
* @return 1 if c is in the class, 0 otherwise.
|
||||
*/
|
||||
static inline int in_space_class(int c){
|
||||
return in_class(c, space_class);
|
||||
}
|
||||
|
||||
static inline int in_comment_class(int c){
|
||||
return in_class(c, comment_class);
|
||||
}
|
||||
|
||||
/** Determine if a character is in the separator class.
|
||||
* Separator characters terminate tokens, and do not need space
|
||||
* to separate them.
|
||||
*
|
||||
* @param c character to test
|
||||
* @return 1 if c is in the class, 0 otherwise.
|
||||
*/
|
||||
static inline int in_sep_class(int c){
|
||||
return in_class(c, sep_class);
|
||||
}
|
||||
|
||||
/** Determine if a character is in the alpha class.
|
||||
*
|
||||
* @param c character to test
|
||||
* @return 1 if c is in the class, 0 otherwise.
|
||||
*/
|
||||
static inline int in_alpha_class(int c){
|
||||
return isalpha(c);
|
||||
}
|
||||
|
||||
/** Determine if a character is in the octal digit class.
|
||||
*
|
||||
* @param c character to test
|
||||
* @return 1 if c is in the class, 0 otherwise.
|
||||
*/
|
||||
static inline int in_octal_digit_class(int c){
|
||||
return '0' <= c && c <= '7';
|
||||
}
|
||||
|
||||
/** Determine if a character is in the decimal digit class.
|
||||
*
|
||||
* @param c character to test
|
||||
* @return 1 if c is in the class, 0 otherwise.
|
||||
*/
|
||||
static inline int in_decimal_digit_class(int c){
|
||||
return isdigit(c);
|
||||
}
|
||||
|
||||
/** Determine if a character is in the hex digit class.
|
||||
*
|
||||
* @param c character to test
|
||||
* @return 1 if c is in the class, 0 otherwise.
|
||||
*/
|
||||
static inline int in_hex_digit_class(int c){
|
||||
return isdigit(c) || in_class(c, "abcdefABCDEF");
|
||||
}
|
||||
|
||||
|
||||
static inline int in_string_quote_class(int c){
|
||||
return in_class(c, "'\"");
|
||||
}
|
||||
|
||||
static inline int in_printable_class(int c){
|
||||
return ('A' <= c && c <= 'Z')
|
||||
|| ('a' <= c && c <= 'z')
|
||||
|| ('0' <= c && c <= '9')
|
||||
|| in_class(c, "!$%&*+,-./:;<=>?@^_`{|}~");
|
||||
}
|
||||
|
||||
extern int is_decimal_number(const char *s, int n);
|
||||
extern int is_hex_number(const char *s, int n);
|
||||
extern int is_keyword(const char *s, const char *k);
|
||||
extern int is_keychar(const char *s, char c);
|
||||
|
||||
#endif /* !_SP_LEXIS_H_ */
|
||||
@@ -0,0 +1,590 @@
|
||||
/* $Id: lzi_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $ */
|
||||
#define __FILE_ID_INFO "$Id: lzi_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $"
|
||||
#include <what.h>
|
||||
static char __rcsid[] __attribute__((unused)) = WHAT_ID __FILE_ID_INFO;
|
||||
/*
|
||||
* Copyright (C) 2003 Hewlett-Packard Company.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* An IOStream implementation using LZI to provide compression and decompression.
|
||||
* This is designed to provide compression without output latency.
|
||||
* Flushing an LZI stream flushes all pending data to the underlying stream.
|
||||
* This is essential for stream-based (e.g. networked) applications.
|
||||
*
|
||||
* A compressed data stream is a sequence of blocks.
|
||||
* Each block is the block size followed by the compressed data.
|
||||
* The last block has size zero.
|
||||
* Sizes are 4-byte unsigned in network order.
|
||||
*
|
||||
* This format allows compressed data to be read from a stream without reading
|
||||
* past the logical end of compressed data.
|
||||
*
|
||||
* @author Mike Wray <mike.wray@hpl.hp.com>
|
||||
*/
|
||||
#ifndef __KERNEL__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
#include "allocate.h"
|
||||
#include "lzi_stream.h"
|
||||
#include "file_stream.h"
|
||||
#include "marshal.h"
|
||||
|
||||
#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] LZI>%s" fmt, __FUNCTION__, ##args)
|
||||
#define wprintf(fmt, args...) fprintf(stderr, "[WARN] LZI>%s" fmt, __FUNCTION__, ##args)
|
||||
#define iprintf(fmt, args...) fprintf(stdout, "[INFO] LZI>%s" fmt, __FUNCTION__, ##args)
|
||||
#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] LZI>%s" fmt, __FUNCTION__, ##args)
|
||||
|
||||
static int lzi_read(IOStream *s, void *buf, size_t size, size_t count);
|
||||
static int lzi_write(IOStream *s, const void *buf, size_t size, size_t count);
|
||||
static int lzi_print(IOStream *s, const char *msg, va_list args);
|
||||
static int lzi_getc(IOStream *s);
|
||||
static int lzi_error(IOStream *s);
|
||||
static int lzi_close(IOStream *s);
|
||||
static void lzi_free(IOStream *s);
|
||||
static int lzi_flush(IOStream *s);
|
||||
|
||||
enum {
|
||||
LZI_WRITE = 1,
|
||||
LZI_READ = 2,
|
||||
};
|
||||
|
||||
/** Methods used by a gzFile* IOStream. */
|
||||
static const IOMethods lzi_methods = {
|
||||
read: lzi_read,
|
||||
write: lzi_write,
|
||||
print: lzi_print,
|
||||
getc: lzi_getc,
|
||||
error: lzi_error,
|
||||
close: lzi_close,
|
||||
free: lzi_free,
|
||||
flush: lzi_flush,
|
||||
};
|
||||
|
||||
#define BUFFER_SIZE (512 * 1024)
|
||||
|
||||
typedef struct LZIState {
|
||||
z_stream zstream;
|
||||
void *inbuf;
|
||||
uint32_t inbuf_size;
|
||||
void *outbuf;
|
||||
uint32_t outbuf_size;
|
||||
/** Underlying stream for I/O. */
|
||||
IOStream *io;
|
||||
/** Flags. */
|
||||
int flags;
|
||||
/** Error indicator. */
|
||||
int error;
|
||||
int eof;
|
||||
int plain_bytes;
|
||||
int comp_bytes;
|
||||
int zstream_initialized;
|
||||
int flushed;
|
||||
} LZIState;
|
||||
|
||||
static inline int LZIState_writeable(LZIState *s){
|
||||
return (s->flags & LZI_WRITE) != 0;
|
||||
}
|
||||
|
||||
static inline int LZIState_readable(LZIState *s){
|
||||
return (s->flags & LZI_READ) != 0;
|
||||
}
|
||||
|
||||
void LZIState_free(LZIState *z){
|
||||
if(!z) return;
|
||||
if(z->zstream_initialized){
|
||||
if(LZIState_writeable(z)){
|
||||
deflateEnd(&z->zstream);
|
||||
} else if(LZIState_readable(z)){
|
||||
inflateEnd(&z->zstream);
|
||||
}
|
||||
}
|
||||
deallocate(z->inbuf);
|
||||
deallocate(z->outbuf);
|
||||
deallocate(z);
|
||||
}
|
||||
|
||||
static int mode_flags(const char *mode, int *flags){
|
||||
int err = 0;
|
||||
int r=0, w=0;
|
||||
if(!mode){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
for(; *mode; mode++){
|
||||
if(*mode == 'w') w = 1;
|
||||
if(*mode == 'r') r = 1;
|
||||
}
|
||||
if(r + w != 1){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
if(r) *flags |= LZI_READ;
|
||||
if(w) *flags |= LZI_WRITE;
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Get the stream state.
|
||||
*
|
||||
* @param s lzi stream
|
||||
* @return stream state.
|
||||
*/
|
||||
static inline LZIState * lzi_state(IOStream *io){
|
||||
return io->data;
|
||||
}
|
||||
|
||||
IOStream *lzi_stream_io(IOStream *io){
|
||||
LZIState *s = lzi_state(io);
|
||||
return s->io;
|
||||
}
|
||||
|
||||
static inline void set_error(LZIState *s, int err){
|
||||
if(err < 0 && !s->error){
|
||||
s->error = err;
|
||||
}
|
||||
}
|
||||
|
||||
static int zerror(LZIState *s, int err){
|
||||
if(err){
|
||||
//dprintf("> err=%d\n", err);
|
||||
if(err < 0) set_error(s, -EIO);
|
||||
}
|
||||
return s->error;
|
||||
}
|
||||
|
||||
int lzi_stream_plain_bytes(IOStream *io){
|
||||
LZIState *s = lzi_state(io);
|
||||
return s->plain_bytes;
|
||||
}
|
||||
|
||||
int lzi_stream_comp_bytes(IOStream *io){
|
||||
LZIState *s = lzi_state(io);
|
||||
return s->comp_bytes;
|
||||
}
|
||||
|
||||
float lzi_stream_ratio(IOStream *io){
|
||||
LZIState *s = lzi_state(io);
|
||||
float ratio = 0.0;
|
||||
if(s->comp_bytes){
|
||||
ratio = ((float) s->comp_bytes)/((float) s->plain_bytes);
|
||||
}
|
||||
return ratio;
|
||||
}
|
||||
|
||||
static int alloc(void **p, int n){
|
||||
*p = allocate(n);
|
||||
return (p ? 0 : -ENOMEM);
|
||||
}
|
||||
|
||||
LZIState * LZIState_new(IOStream *io, int flags){
|
||||
int err = -ENOMEM;
|
||||
int zlevel = Z_BEST_SPEED; // Level 1 compression - fastest.
|
||||
int zstrategy = Z_DEFAULT_STRATEGY;
|
||||
int zwindow = MAX_WBITS;
|
||||
int zmemory = 8;
|
||||
LZIState *z = ALLOCATE(LZIState);
|
||||
|
||||
//dprintf(">\n");
|
||||
if(!z) goto exit;
|
||||
z->io = io;
|
||||
z->flags = flags;
|
||||
|
||||
if(LZIState_writeable(z)){
|
||||
z->outbuf_size = BUFFER_SIZE;
|
||||
/* windowBits is passed < 0 to suppress zlib header */
|
||||
err = deflateInit2(&z->zstream, zlevel, Z_DEFLATED, -zwindow, zmemory, zstrategy);
|
||||
if (err != Z_OK) goto exit;
|
||||
z->zstream_initialized = 1;
|
||||
err = alloc(&z->outbuf, z->outbuf_size);
|
||||
if(err) goto exit;
|
||||
z->zstream.next_out = z->outbuf;
|
||||
z->zstream.avail_out = z->outbuf_size;
|
||||
} else {
|
||||
z->inbuf_size = BUFFER_SIZE;
|
||||
err = alloc(&z->inbuf, z->inbuf_size);
|
||||
if(err) goto exit;
|
||||
///z->zstream.next_in = z->inbuf;
|
||||
|
||||
/* windowBits is passed < 0 to tell that there is no zlib header.
|
||||
* Note that in this case inflate *requires* an extra "dummy" byte
|
||||
* after the compressed stream in order to complete decompression and
|
||||
* return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
|
||||
* present after the compressed stream.
|
||||
*/
|
||||
err = inflateInit2(&z->zstream, -zwindow);
|
||||
if(err != Z_OK) goto exit;
|
||||
z->zstream_initialized = 1;
|
||||
}
|
||||
|
||||
exit:
|
||||
if(err){
|
||||
LZIState_free(z);
|
||||
z = NULL;
|
||||
}
|
||||
//dprintf("< z=%p\n", z);
|
||||
return z;
|
||||
}
|
||||
|
||||
int read_block(LZIState *s){
|
||||
int err = 0, k = 0;
|
||||
//dprintf(">\n");
|
||||
if(s->eof) goto exit;
|
||||
err = unmarshal_uint32(s->io, &k);
|
||||
if(err) goto exit;
|
||||
if(k > s->inbuf_size){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
if(k){
|
||||
err = unmarshal_bytes(s->io, s->inbuf, k);
|
||||
if(err) goto exit;
|
||||
} else {
|
||||
s->eof = 1;
|
||||
}
|
||||
s->zstream.avail_in = k;
|
||||
s->zstream.next_in = s->inbuf;
|
||||
s->comp_bytes += 4;
|
||||
s->comp_bytes += k;
|
||||
exit:
|
||||
//dprintf("< err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int write_block(LZIState *s){
|
||||
int err = 0;
|
||||
int k = ((char*)s->zstream.next_out) - ((char*)s->outbuf);
|
||||
int k2 = s->outbuf_size - s->zstream.avail_out;
|
||||
//dprintf("> k=%d k2=%d\n", k, k2);
|
||||
if(!k) goto exit;
|
||||
err = marshal_uint32(s->io, k);
|
||||
if(err) goto exit;
|
||||
err = marshal_bytes(s->io, s->outbuf, k);
|
||||
if(err) goto exit;
|
||||
s->zstream.next_out = s->outbuf;
|
||||
s->zstream.avail_out = s->outbuf_size;
|
||||
s->comp_bytes += 4;
|
||||
s->comp_bytes += k;
|
||||
exit:
|
||||
//dprintf("< err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int write_terminator(LZIState *s){
|
||||
int err = 0;
|
||||
char c = 0;
|
||||
err = marshal_uint32(s->io, 1);
|
||||
if(err) goto exit;
|
||||
err = marshal_bytes(s->io, &c, 1);
|
||||
if(err) goto exit;
|
||||
err = marshal_uint32(s->io, 0);
|
||||
if(err) goto exit;
|
||||
s->comp_bytes += 9;
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Write to the underlying stream using fwrite();
|
||||
*
|
||||
* @param io destination
|
||||
* @param buf data
|
||||
* @param size size of data elements
|
||||
* @param count number of data elements to write
|
||||
* @return number of data elements written
|
||||
*/
|
||||
static int lzi_write(IOStream *io, const void *buf, size_t size, size_t count){
|
||||
int err = 0;
|
||||
int n = size * count;
|
||||
LZIState *s = lzi_state(io);
|
||||
|
||||
//dprintf("> buf=%p size=%d count=%d n=%d\n", buf, size, count, n);
|
||||
if(!LZIState_writeable(s)){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
s->flushed = 0;
|
||||
s->zstream.next_in = (void*)buf;
|
||||
s->zstream.avail_in = n;
|
||||
while(s->zstream.avail_in){
|
||||
if(s->zstream.avail_out == 0){
|
||||
err = write_block(s);
|
||||
if(err) goto exit;
|
||||
}
|
||||
//dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
|
||||
//dprintf("> 1 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out);
|
||||
err = zerror(s, deflate(&s->zstream, Z_NO_FLUSH));
|
||||
//dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
|
||||
//dprintf("> 2 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out);
|
||||
if(err) goto exit;
|
||||
}
|
||||
err = n;
|
||||
s->plain_bytes += n;
|
||||
if(size != 1) err /= size;
|
||||
exit:
|
||||
//dprintf("< err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/** Read from the underlying stream.
|
||||
*
|
||||
* @param io input
|
||||
* @param buf where to put input
|
||||
* @param size size of data elements
|
||||
* @param count number of data elements to read
|
||||
* @return number of data elements read
|
||||
*/
|
||||
static int lzi_read(IOStream *io, void *buf, size_t size, size_t count){
|
||||
int err, zerr;
|
||||
int n = size * count;
|
||||
LZIState *s = lzi_state(io);
|
||||
|
||||
//dprintf("> size=%d count=%d n=%d\n", size, count, n);
|
||||
if(!LZIState_readable(s)){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
s->zstream.next_out = buf;
|
||||
s->zstream.avail_out = n;
|
||||
while(s->zstream.avail_out){
|
||||
if(s->zstream.avail_in == 0){
|
||||
err = read_block(s);
|
||||
}
|
||||
//dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
|
||||
zerr = inflate(&s->zstream, Z_NO_FLUSH);
|
||||
//dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
|
||||
if(zerr == Z_STREAM_END) break;
|
||||
//dprintf("> zerr=%d\n", zerr);
|
||||
err = zerror(s, zerr);
|
||||
if(err) goto exit;
|
||||
}
|
||||
err = n - s->zstream.avail_out;
|
||||
s->plain_bytes += err;
|
||||
if(size != 1) err /= size;
|
||||
exit:
|
||||
set_error(s, err);
|
||||
//dprintf("< err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Print to the underlying stream.
|
||||
* Returns 0 if the formatted output is too big for the internal buffer.
|
||||
*
|
||||
* @param io lzi stream
|
||||
* @param msg format to use
|
||||
* @param args arguments
|
||||
* @return result of the print
|
||||
*/
|
||||
static int lzi_print(IOStream *io, const char *msg, va_list args){
|
||||
char buf[1024];
|
||||
int buf_n = sizeof(buf);
|
||||
int n;
|
||||
LZIState *s = lzi_state(io);
|
||||
if(!LZIState_writeable(s)){
|
||||
n = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
n = vsnprintf(buf, buf_n, (char*)msg, args);
|
||||
if(n < 0) goto exit;
|
||||
if(n > buf_n){
|
||||
n = 0;
|
||||
} else {
|
||||
n = lzi_write(io, buf, 1, n);
|
||||
}
|
||||
exit:
|
||||
return n;
|
||||
}
|
||||
|
||||
/** Read a character from the underlying stream
|
||||
*
|
||||
* @param io lzi stream
|
||||
* @return character read, IOSTREAM_EOF on end of file (or error)
|
||||
*/
|
||||
static int lzi_getc(IOStream *io){
|
||||
int err;
|
||||
char c;
|
||||
err = lzi_read(io, &c, 1, 1);
|
||||
if(err < 1) c = EOF;
|
||||
err = (c==EOF ? IOSTREAM_EOF : c);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int flush_output(LZIState *s, int mode){
|
||||
int err = 0, zerr;
|
||||
int done = 0;
|
||||
int avail_out_old;
|
||||
int count = 10;
|
||||
|
||||
//dprintf("> avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
|
||||
if(s->flushed == 1 + mode) goto exit;
|
||||
//s->zstream.avail_in = 0; /* should be zero already anyway */
|
||||
for(;;){
|
||||
// Write any available output.
|
||||
if(done || s->zstream.avail_out == 0){
|
||||
err = write_block(s);
|
||||
if(err) goto exit;
|
||||
if(done) break;
|
||||
}
|
||||
//dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
|
||||
avail_out_old = s->zstream.avail_out;
|
||||
zerr = deflate(&s->zstream, mode);
|
||||
err = zerror(s, zerr);
|
||||
//dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
|
||||
//dprintf("> deflate=%d\n", err);
|
||||
//done = (s->zstream.avail_out != 0);
|
||||
//done = (s->zstream.avail_in == 0) && (s->zstream.avail_out == avail_out_old);
|
||||
if(0 && mode == Z_FINISH){
|
||||
done = (zerr == Z_STREAM_END);
|
||||
} else {
|
||||
done = (s->zstream.avail_in == 0)
|
||||
//&& (s->zstream.avail_out == avail_out_old)
|
||||
&& (s->zstream.avail_out != 0);
|
||||
}
|
||||
}
|
||||
s->flushed = 1 + mode;
|
||||
exit:
|
||||
//dprintf("< err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Flush any pending input to the underlying stream.
|
||||
*
|
||||
* @param s lzi stream
|
||||
* @return 0 on success, error code otherwise
|
||||
*/
|
||||
static int lzi_flush(IOStream *io){
|
||||
int err = 0;
|
||||
LZIState *s = lzi_state(io);
|
||||
//dprintf(">\n");
|
||||
if(!LZIState_writeable(s)){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
err = flush_output(s, Z_SYNC_FLUSH);
|
||||
if(err) goto exit;
|
||||
err = IOStream_flush(s->io);
|
||||
exit:
|
||||
set_error(s, err);
|
||||
//dprintf("< err=%d\n", err);
|
||||
return (err < 0 ? err : 0);
|
||||
}
|
||||
|
||||
/** Check if a stream has an error.
|
||||
*
|
||||
* @param s lzi stream
|
||||
* @return code if has an error, 0 otherwise
|
||||
*/
|
||||
static int lzi_error(IOStream *s){
|
||||
int err = 0;
|
||||
LZIState *state = lzi_state(s);
|
||||
err = state->error;
|
||||
if(err) goto exit;
|
||||
err = IOStream_error(state->io);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Close an lzi stream.
|
||||
*
|
||||
* @param s lzi stream to close
|
||||
* @return result of the close
|
||||
*/
|
||||
static int lzi_close(IOStream *io){
|
||||
int err = 0;
|
||||
LZIState *s = lzi_state(io);
|
||||
if(LZIState_writeable(s)){
|
||||
err = flush_output(s, Z_FINISH);
|
||||
if(err) goto exit;
|
||||
err = write_terminator(s);
|
||||
if(err) goto exit;
|
||||
err = IOStream_flush(s->io);
|
||||
}
|
||||
exit:
|
||||
err = IOStream_close(s->io);
|
||||
set_error(s, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Free an lzi stream.
|
||||
*
|
||||
* @param s lzi stream
|
||||
*/
|
||||
static void lzi_free(IOStream *s){
|
||||
LZIState *state = lzi_state(s);
|
||||
IOStream_free(state->io);
|
||||
LZIState_free(state);
|
||||
s->data = NULL;
|
||||
}
|
||||
|
||||
/** Create an lzi stream for an IOStream.
|
||||
*
|
||||
* @param io stream to wrap
|
||||
* @return new IOStream using f for i/o
|
||||
*/
|
||||
IOStream *lzi_stream_new(IOStream *io, const char *mode){
|
||||
int err = -ENOMEM;
|
||||
int flags = 0;
|
||||
IOStream *zio = NULL;
|
||||
LZIState *state = NULL;
|
||||
|
||||
zio = ALLOCATE(IOStream);
|
||||
if(!zio) goto exit;
|
||||
err = mode_flags(mode, &flags);
|
||||
if(err) goto exit;
|
||||
state = LZIState_new(io, flags);
|
||||
if(!state) goto exit;
|
||||
err = 0;
|
||||
zio->data = state;
|
||||
zio->methods = &lzi_methods;
|
||||
exit:
|
||||
if(err){
|
||||
if(state) LZIState_free(state);
|
||||
if(zio) deallocate(zio);
|
||||
zio = NULL;
|
||||
}
|
||||
return zio;
|
||||
}
|
||||
|
||||
/** IOStream version of fdopen().
|
||||
*
|
||||
* @param fd file descriptor
|
||||
* @param flags giving the mode to open in (as for fdopen())
|
||||
* @return new stream for the open file, or NULL if failed
|
||||
*/
|
||||
IOStream *lzi_stream_fdopen(int fd, const char *mode){
|
||||
int err = -ENOMEM;
|
||||
IOStream *io = NULL, *zio = NULL;
|
||||
io = file_stream_fdopen(fd, mode);
|
||||
if(!io) goto exit;
|
||||
zio = lzi_stream_new(io, mode);
|
||||
if(!io) goto exit;
|
||||
err = 0;
|
||||
exit:
|
||||
if(err){
|
||||
IOStream_free(io);
|
||||
IOStream_free(zio);
|
||||
zio = NULL;
|
||||
}
|
||||
return zio;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
#/* $Id: lzi_stream.h,v 1.3 2003/09/30 15:22:53 mjw Exp $ */
|
||||
/*
|
||||
* Copyright (C) 2003 Hewlett-Packard Company.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _SP_LZI_STREAM_H_
|
||||
#define _SP_LZI_STREAM_H_
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#include "iostream.h"
|
||||
|
||||
extern IOStream *lzi_stream_new(IOStream *io, const char *mode);
|
||||
extern IOStream *lzi_stream_fopen(const char *file, const char *mode);
|
||||
extern IOStream *lzi_stream_fdopen(int fd, const char *mode);
|
||||
extern IOStream *lzi_stream_io(IOStream *zio);
|
||||
|
||||
extern int lzi_stream_plain_bytes(IOStream *io);
|
||||
extern int lzi_stream_comp_bytes(IOStream *io);
|
||||
extern float lzi_stream_ratio(IOStream *io);
|
||||
|
||||
#endif
|
||||
#endif /* !_SP_FILE_STREAM_H_ */
|
||||
@@ -0,0 +1,596 @@
|
||||
/* $Id: lzo_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $ */
|
||||
#define __FILE_ID_INFO "$Id: lzo_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $"
|
||||
#include <what.h>
|
||||
static char __rcsid[] __attribute__((unused)) = WHAT_ID __FILE_ID_INFO;
|
||||
/*
|
||||
* Copyright (C) 2003 Hewlett-Packard Company.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* An IOStream implementation using LZO to provide compression and decompression.
|
||||
* This is designed to provide reasonable compression without output latency.
|
||||
* Flushing an LZO stream flushes all pending data to the underlying stream.
|
||||
* This is essential for stream-based (e.g. networked) applications.
|
||||
*
|
||||
* A compressed data stream is a sequence of blocks.
|
||||
* Each block except the last is the plain data size followed by the compressed data size
|
||||
* and the compressed data. The last block has plain data size zero and omits the rest.
|
||||
* Sizes are 4-byte unsigned in network order. If the compressed size is smaller than
|
||||
* the plain size the block data is compressed, otherwise it is plain (uncompressed).
|
||||
*
|
||||
* This format allows compressed data to be read from a stream without reading
|
||||
* past the logical end of compressed data.
|
||||
*
|
||||
* @author Mike Wray <mike.wray@hpl.hp.com>
|
||||
*/
|
||||
#ifndef __KERNEL__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lzo1x.h"
|
||||
|
||||
#include "allocate.h"
|
||||
#include "lzo_stream.h"
|
||||
#include "file_stream.h"
|
||||
#include "marshal.h"
|
||||
|
||||
#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] LZO>%s" fmt, __FUNCTION__, ##args)
|
||||
#define wprintf(fmt, args...) fprintf(stderr, "[WARN] LZO>%s" fmt, __FUNCTION__, ##args)
|
||||
#define iprintf(fmt, args...) fprintf(stdout, "[INFO] LZO>%s" fmt, __FUNCTION__, ##args)
|
||||
#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] LZO>%s" fmt, __FUNCTION__, ##args)
|
||||
|
||||
static int lzo_read(IOStream *s, void *buf, size_t size, size_t count);
|
||||
static int lzo_write(IOStream *s, const void *buf, size_t size, size_t count);
|
||||
static int lzo_print(IOStream *s, const char *msg, va_list args);
|
||||
static int lzo_getc(IOStream *s);
|
||||
static int lzo_error(IOStream *s);
|
||||
static int lzo_close(IOStream *s);
|
||||
static void lzo_free(IOStream *s);
|
||||
static int lzo_flush(IOStream *s);
|
||||
|
||||
enum {
|
||||
LZO_WRITE = 1,
|
||||
LZO_READ = 2,
|
||||
};
|
||||
|
||||
/** Methods used by a gzFile* IOStream. */
|
||||
static const IOMethods lzo_methods = {
|
||||
read: lzo_read,
|
||||
write: lzo_write,
|
||||
print: lzo_print,
|
||||
getc: lzo_getc,
|
||||
error: lzo_error,
|
||||
close: lzo_close,
|
||||
free: lzo_free,
|
||||
flush: lzo_flush,
|
||||
};
|
||||
|
||||
//#define PLAIN_SIZE (64 * 1024)
|
||||
//#define PLAIN_SIZE (128 * 1024)
|
||||
#define PLAIN_SIZE (512 * 1024)
|
||||
|
||||
//#define NOCOMPRESS
|
||||
|
||||
typedef struct LZOState {
|
||||
/** Flags. */
|
||||
int flags;
|
||||
/** Error indicator. */
|
||||
int error;
|
||||
/** Underlying stream for I/O. */
|
||||
IOStream *io;
|
||||
/** Working memory (only needed for compression, not decompression). */
|
||||
lzo_byte *memory;
|
||||
/** Buffer for plain (uncompressed) data. */
|
||||
lzo_byte *plain;
|
||||
/** Size of the plain buffer. */
|
||||
lzo_uint plain_size;
|
||||
/** Pointer into the plain buffer. */
|
||||
lzo_byte *plain_ptr;
|
||||
/** Number of bytes of plain data available. */
|
||||
lzo_uint plain_n;
|
||||
/** Buffer for compressed data. */
|
||||
lzo_byte *comp;
|
||||
/** Size of the compressed buffer. */
|
||||
lzo_uint comp_size;
|
||||
|
||||
int plain_bytes;
|
||||
int comp_bytes;
|
||||
} LZOState;
|
||||
|
||||
void LZOState_free(LZOState *z){
|
||||
if(!z) return;
|
||||
deallocate(z->memory);
|
||||
deallocate(z->plain);
|
||||
deallocate(z->comp);
|
||||
deallocate(z);
|
||||
}
|
||||
|
||||
/** Maximum size of compressed data for the given plain data size.
|
||||
*
|
||||
* @param plain_size size of plain data
|
||||
* @return maximum size of compressed data
|
||||
*/
|
||||
static int comp_size(int plain_size){
|
||||
return plain_size + (plain_size / 64) + 16 + 3;
|
||||
}
|
||||
|
||||
static int mode_flags(const char *mode, int *flags){
|
||||
int err = 0;
|
||||
int r=0, w=0;
|
||||
if(!mode){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
for(; *mode; mode++){
|
||||
if(*mode == 'w') w = 1;
|
||||
if(*mode == 'r') r = 1;
|
||||
}
|
||||
if(r + w != 1){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
if(r) *flags |= LZO_READ;
|
||||
if(w) *flags |= LZO_WRITE;
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Get the stream state.
|
||||
*
|
||||
* @param s lzo stream
|
||||
* @return stream state.
|
||||
*/
|
||||
static inline LZOState * lzo_state(IOStream *s){
|
||||
return s->data;
|
||||
}
|
||||
|
||||
IOStream *lzo_stream_io(IOStream *s){
|
||||
LZOState *state = lzo_state(s);
|
||||
return state->io;
|
||||
}
|
||||
|
||||
static inline void set_error(LZOState *state, int err){
|
||||
if(err < 0 && !state->error){
|
||||
state->error = err;
|
||||
}
|
||||
}
|
||||
|
||||
int lzo_stream_plain_bytes(IOStream *s){
|
||||
LZOState *state = lzo_state(s);
|
||||
return state->plain_bytes;
|
||||
}
|
||||
|
||||
int lzo_stream_comp_bytes(IOStream *s){
|
||||
LZOState *state = lzo_state(s);
|
||||
return state->comp_bytes;
|
||||
}
|
||||
|
||||
float lzo_stream_ratio(IOStream *s){
|
||||
LZOState *state = lzo_state(s);
|
||||
float ratio = 0.0;
|
||||
if(state->comp_bytes){
|
||||
ratio = ((float) state->comp_bytes)/((float) state->plain_bytes);
|
||||
}
|
||||
return ratio;
|
||||
}
|
||||
|
||||
static inline int LZOState_writeable(LZOState *state){
|
||||
return (state->flags & LZO_WRITE) != 0;
|
||||
}
|
||||
|
||||
static inline int LZOState_readable(LZOState *state){
|
||||
return (state->flags & LZO_READ) != 0;
|
||||
}
|
||||
|
||||
LZOState * LZOState_new(IOStream *io, int flags){
|
||||
int err = -ENOMEM;
|
||||
LZOState *z = ALLOCATE(LZOState);
|
||||
//dprintf(">\n");
|
||||
if(!z) goto exit;
|
||||
z->io = io;
|
||||
z->flags = flags;
|
||||
if(LZOState_writeable(z)){
|
||||
z->memory = allocate(LZO1X_1_MEM_COMPRESS);
|
||||
if(!z->memory) goto exit;
|
||||
}
|
||||
z->plain_size = PLAIN_SIZE;
|
||||
z->plain = allocate(z->plain_size);
|
||||
if(!z->plain) goto exit;
|
||||
z->plain_ptr = z->plain;
|
||||
z->comp_size = comp_size(z->plain_size);
|
||||
z->comp = allocate(z->comp_size);
|
||||
if(!z->comp) goto exit;
|
||||
err = 0;
|
||||
exit:
|
||||
if(err){
|
||||
LZOState_free(z);
|
||||
z = NULL;
|
||||
}
|
||||
//dprintf("< z=%p\n", z);
|
||||
return z;
|
||||
}
|
||||
|
||||
static int lzo_compress(LZOState *state){
|
||||
int err = 0;
|
||||
int k, comp_n;
|
||||
//dprintf(">\n");
|
||||
//dprintf(">plain=%p plain_n=%d comp=%p memory=%p\n", state->plain, state->plain_n, state->comp, state->memory);
|
||||
// Compress the plain buffer.
|
||||
err = lzo1x_1_compress(state->plain, state->plain_n,
|
||||
state->comp, &comp_n,
|
||||
state->memory);
|
||||
//dprintf("> err=%d plain_n=%d comp_n=%d\n", err, state->plain_n, comp_n);
|
||||
// Write plain size, compressed size.
|
||||
err = marshal_uint32(state->io, state->plain_n);
|
||||
if(err) goto exit;
|
||||
err = marshal_uint32(state->io, comp_n);
|
||||
if(err) goto exit;
|
||||
//dprintf("> write data...\n");
|
||||
// Write the smaller of the compressed and plain data.
|
||||
if(state->plain_n < comp_n){
|
||||
k = state->plain_n;
|
||||
err = marshal_bytes(state->io, state->plain, state->plain_n);
|
||||
} else {
|
||||
k = comp_n;
|
||||
err = marshal_bytes(state->io, state->comp, comp_n);
|
||||
}
|
||||
if(err) goto exit;
|
||||
// Total output bytes.
|
||||
k+= 8;
|
||||
//dprintf("> wrote %d bytes\n", k);
|
||||
state->plain_bytes += state->plain_n;
|
||||
state->comp_bytes += k;
|
||||
//dprintf("> plain=%d, comp=%d, ratio=%3.2f\n",
|
||||
// state->plain_bytes, state->comp_bytes,
|
||||
// ((float)state->comp_bytes)/((float)state->plain_bytes));
|
||||
// Reset the plain buffer.
|
||||
state->plain_ptr = state->plain;
|
||||
state->plain_n = 0;
|
||||
err = k;
|
||||
exit:
|
||||
//dprintf("< err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int lzo_decompress(LZOState *state){
|
||||
int plain_n, comp_n;
|
||||
int err, k;
|
||||
//dprintf(">\n");
|
||||
err = unmarshal_uint32(state->io, &plain_n);
|
||||
//dprintf("> err=%d plain_n=%d\n", err, plain_n);
|
||||
if(err) goto exit;
|
||||
state->comp_bytes += 4;
|
||||
if(plain_n == 0) goto exit;
|
||||
err = unmarshal_uint32(state->io, &comp_n);
|
||||
//dprintf("> err=%d comp_n=%d\n", err, comp_n);
|
||||
if(err) goto exit;
|
||||
state->comp_bytes += 4;
|
||||
if(plain_n > state->plain_size){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
if(comp_n > plain_n){
|
||||
//dprintf("> reading plain data %d...\n", plain_n);
|
||||
k = plain_n;
|
||||
err = unmarshal_bytes(state->io, state->plain, plain_n);
|
||||
state->plain_n = plain_n;
|
||||
} else {
|
||||
//dprintf("> reading comp data %d...\n", comp_n);
|
||||
k = comp_n;
|
||||
err = unmarshal_bytes(state->io, state->comp, comp_n);
|
||||
//dprintf("> decompress comp_n=%d\n", comp_n);
|
||||
err = lzo1x_decompress(state->comp, comp_n,
|
||||
state->plain, &state->plain_n,
|
||||
state->memory);
|
||||
//dprintf("> err=%d plain=%d state->plain_n=%d\n", err, plain_n, state->plain_n);
|
||||
if(err != LZO_E_OK || state->plain_n != plain_n){
|
||||
// Bad. Corrupted input.
|
||||
err = -EINVAL;
|
||||
eprintf("> Corrupted!\n");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
state->comp_bytes += k;
|
||||
state->plain_bytes += state->plain_n;
|
||||
state->plain_ptr = state->plain;
|
||||
err = k;
|
||||
exit:
|
||||
//dprintf("< err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Write to the underlying stream using fwrite();
|
||||
*
|
||||
* @param stream destination
|
||||
* @param buf data
|
||||
* @param size size of data elements
|
||||
* @param count number of data elements to write
|
||||
* @return number of data elements written
|
||||
*/
|
||||
static int lzo_write(IOStream *s, const void *buf, size_t size, size_t count){
|
||||
int err = 0;
|
||||
int n = size * count; // Total number of bytes to write.
|
||||
int chunk; // Size of chunk to write.
|
||||
int remaining; // Number of bytes remaining to write.
|
||||
int space; // Amount of space left in plain buffer.
|
||||
LZOState *state = lzo_state(s);
|
||||
#ifdef NOCOMPRESS
|
||||
//dprintf("> buf=%p size=%d count=%d\n", buf, size, count);
|
||||
err = IOStream_write(state->io, buf, size, count);
|
||||
//dprintf("< err=%d\n", err);
|
||||
#else
|
||||
//dprintf("> buf=%p size=%d count=%d n=%d\n", buf, size, count, n);
|
||||
remaining = n;
|
||||
space = state->plain_size - state->plain_n;
|
||||
//dprintf("> plain=%p plain_ptr=%p plain_n=%d space=%d\n",
|
||||
// state->plain, state->plain_ptr, state->plain_n, space);
|
||||
while(remaining){
|
||||
chunk = remaining;
|
||||
if(chunk > space) chunk = space;
|
||||
//dprintf("> memcpy %p %p %d\n", state->plain_ptr, buf, chunk);
|
||||
memcpy(state->plain_ptr, buf, chunk);
|
||||
remaining -= chunk;
|
||||
space -= chunk;
|
||||
state->plain_ptr += chunk;
|
||||
state->plain_n += chunk;
|
||||
if(space == 0){
|
||||
// Input buffer is full. Compress and write it.
|
||||
err = lzo_compress(state);
|
||||
if(err < 0) goto exit;
|
||||
space = state->plain_size - state->plain_n;
|
||||
}
|
||||
}
|
||||
err = (size > 1 ? n / size : n);
|
||||
exit:
|
||||
set_error(state, err);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/** Read from the underlying stream.
|
||||
*
|
||||
* @param stream input
|
||||
* @param buf where to put input
|
||||
* @param size size of data elements
|
||||
* @param count number of data elements to read
|
||||
* @return number of data elements read
|
||||
*/
|
||||
static int lzo_read(IOStream *s, void *buf, size_t size, size_t count){
|
||||
int err = 0;
|
||||
int k = 0; // Number of (plain) bytes read.
|
||||
int remaining = size * count; // Number of bytes remaining to read.
|
||||
int chunk; // Size of chunk to read.
|
||||
LZOState *state = lzo_state(s);
|
||||
#ifdef NOCOMPRESS
|
||||
//dprintf("> buf=%p size=%d count=%d\n", buf, size, count);
|
||||
err = IOStream_read(state->io, buf, size, count);
|
||||
//dprintf("< err=%d\n", err);
|
||||
#else
|
||||
if(!(state->flags & LZO_READ)){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
while(remaining){
|
||||
if(state->plain_n == 0){
|
||||
// No more plain input, decompress some more.
|
||||
err = lzo_decompress(state);
|
||||
if(err < 0) goto exit;
|
||||
// Stop reading if there is no more input.
|
||||
if(err == 0 || state->plain_n == 0) break;
|
||||
}
|
||||
chunk = remaining;
|
||||
if(chunk > state->plain_n) chunk = state->plain_n;
|
||||
memcpy(buf, state->plain_ptr, chunk);
|
||||
k += chunk;
|
||||
buf += chunk;
|
||||
state->plain_ptr += chunk;
|
||||
state->plain_n -= chunk;
|
||||
remaining -= chunk;
|
||||
}
|
||||
err = k;
|
||||
exit:
|
||||
set_error(state, err);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Print to the underlying stream.
|
||||
* Returns 0 if the formatted output is too big for the internal buffer.
|
||||
*
|
||||
* @param s lzo stream
|
||||
* @param msg format to use
|
||||
* @param args arguments
|
||||
* @return result of the print
|
||||
*/
|
||||
static int lzo_print(IOStream *s, const char *msg, va_list args){
|
||||
char buf[1024];
|
||||
int buf_n = sizeof(buf);
|
||||
int n;
|
||||
LZOState *state = lzo_state(s);
|
||||
if(!LZOState_writeable(state)){
|
||||
n = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
n = vsnprintf(buf, buf_n, (char*)msg, args);
|
||||
if(n < 0) goto exit;
|
||||
if(n > buf_n){
|
||||
n = 0;
|
||||
} else {
|
||||
n = lzo_write(s, buf, 1, n);
|
||||
}
|
||||
exit:
|
||||
return n;
|
||||
}
|
||||
|
||||
/** Read a character from the underlying stream
|
||||
*
|
||||
* @param s lzo stream
|
||||
* @return character read, IOSTREAM_EOF on end of file (or error)
|
||||
*/
|
||||
static int lzo_getc(IOStream *s){
|
||||
int err;
|
||||
char c;
|
||||
err = lzo_read(s, &c, 1, 1);
|
||||
if(err < 1) c = EOF;
|
||||
err = (c==EOF ? IOSTREAM_EOF : c);
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Flush any pending input to the underlying stream.
|
||||
*
|
||||
* @param s lzo stream
|
||||
* @return 0 on success, error code otherwise
|
||||
*/
|
||||
static int lzo_flush(IOStream *s){
|
||||
int err = 0;
|
||||
LZOState *state = lzo_state(s);
|
||||
//dprintf(">\n");
|
||||
#ifdef NOCOMPRESS
|
||||
err = IOStream_flush(state->io);
|
||||
#else
|
||||
if(!LZOState_writeable(state)){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
if(state->plain_n){
|
||||
err = lzo_compress(state);
|
||||
if(err < 0) goto exit;
|
||||
}
|
||||
err = IOStream_flush(state->io);
|
||||
exit:
|
||||
set_error(state, err);
|
||||
#endif
|
||||
//dprintf("< err=%d\n", err);
|
||||
return (err < 0 ? err : 0);
|
||||
}
|
||||
|
||||
/** Check if a stream has an error.
|
||||
*
|
||||
* @param s lzo stream
|
||||
* @return code if has an error, 0 otherwise
|
||||
*/
|
||||
static int lzo_error(IOStream *s){
|
||||
int err = 0;
|
||||
LZOState *state = lzo_state(s);
|
||||
err = state->error;
|
||||
if(err) goto exit;
|
||||
err = IOStream_error(state->io);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int lzo_stream_finish(IOStream *s){
|
||||
int err = 0;
|
||||
LZOState *state = lzo_state(s);
|
||||
if(!LZOState_writeable(state)){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
err = lzo_flush(s);
|
||||
if(err < 0) goto exit;
|
||||
err = marshal_int32(state->io, 0);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Close an lzo stream.
|
||||
*
|
||||
* @param s lzo stream to close
|
||||
* @return result of the close
|
||||
*/
|
||||
static int lzo_close(IOStream *s){
|
||||
int err = 0;
|
||||
LZOState *state = lzo_state(s);
|
||||
#ifdef NOCOMPRESS
|
||||
err = IOStream_close(state->io);
|
||||
#else
|
||||
if(LZOState_writeable(state)){
|
||||
err = lzo_stream_finish(s);
|
||||
}
|
||||
err = IOStream_close(state->io);
|
||||
set_error(state, err);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Free an lzo stream.
|
||||
*
|
||||
* @param s lzo stream
|
||||
*/
|
||||
static void lzo_free(IOStream *s){
|
||||
LZOState *state = lzo_state(s);
|
||||
IOStream_free(state->io);
|
||||
LZOState_free(state);
|
||||
s->data = NULL;
|
||||
}
|
||||
|
||||
/** Create an lzo stream for an IOStream.
|
||||
*
|
||||
* @param io stream to wrap
|
||||
* @return new IOStream using f for i/o
|
||||
*/
|
||||
IOStream *lzo_stream_new(IOStream *io, const char *mode){
|
||||
int err = -ENOMEM;
|
||||
int flags = 0;
|
||||
IOStream *zio = NULL;
|
||||
LZOState *state = NULL;
|
||||
|
||||
zio = ALLOCATE(IOStream);
|
||||
if(!zio) goto exit;
|
||||
err = mode_flags(mode, &flags);
|
||||
if(err) goto exit;
|
||||
state = LZOState_new(io, flags);
|
||||
if(!state) goto exit;
|
||||
err = 0;
|
||||
zio->data = state;
|
||||
zio->methods = &lzo_methods;
|
||||
exit:
|
||||
if(err){
|
||||
if(state) LZOState_free(state);
|
||||
if(zio) deallocate(zio);
|
||||
zio = NULL;
|
||||
}
|
||||
return zio;
|
||||
}
|
||||
|
||||
/** IOStream version of fdopen().
|
||||
*
|
||||
* @param fd file descriptor
|
||||
* @param flags giving the mode to open in (as for fdopen())
|
||||
* @return new stream for the open file, or NULL if failed
|
||||
*/
|
||||
IOStream *lzo_stream_fdopen(int fd, const char *mode){
|
||||
int err = -ENOMEM;
|
||||
IOStream *io = NULL, *zio = NULL;
|
||||
io = file_stream_fdopen(fd, mode);
|
||||
if(!io) goto exit;
|
||||
zio = lzo_stream_new(io, mode);
|
||||
if(!io) goto exit;
|
||||
err = 0;
|
||||
exit:
|
||||
if(err){
|
||||
IOStream_free(io);
|
||||
IOStream_free(zio);
|
||||
zio = NULL;
|
||||
}
|
||||
return zio;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
#/* $Id: lzo_stream.h,v 1.3 2003/09/30 15:22:53 mjw Exp $ */
|
||||
/*
|
||||
* Copyright (C) 2003 Hewlett-Packard Company.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _SP_LZO_STREAM_H_
|
||||
#define _SP_LZO_STREAM_H_
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#include "iostream.h"
|
||||
|
||||
extern IOStream *lzo_stream_new(IOStream *io, const char *mode);
|
||||
extern IOStream *lzo_stream_fopen(const char *file, const char *mode);
|
||||
extern IOStream *lzo_stream_fdopen(int fd, const char *mode);
|
||||
extern IOStream *lzo_stream_io(IOStream *zio);
|
||||
|
||||
extern int lzo_stream_plain_bytes(IOStream *io);
|
||||
extern int lzo_stream_comp_bytes(IOStream *io);
|
||||
extern float lzo_stream_ratio(IOStream *io);
|
||||
|
||||
#endif
|
||||
#endif /* !_SP_FILE_STREAM_H_ */
|
||||
@@ -0,0 +1,207 @@
|
||||
#include <errno.h>
|
||||
#include "sys_net.h"
|
||||
#include "allocate.h"
|
||||
#include "marshal.h"
|
||||
|
||||
#define dprintf(fmt, args...) IOStream_print(iostdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args)
|
||||
#define wprintf(fmt, args...) IOStream_print(iostderr, "[WARN] %s" fmt, __FUNCTION__, ##args)
|
||||
#define iprintf(fmt, args...) IOStream_print(iostdout, "[INFO] %s" fmt, __FUNCTION__, ##args)
|
||||
#define eprintf(fmt, args...) IOStream_print(iostderr, "[ERROR] %s" fmt, __FUNCTION__, ##args)
|
||||
|
||||
|
||||
#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof((ary)[0]))
|
||||
|
||||
/* Messages are coded as msgid followed by message fields.
|
||||
* Initial message on any channel is hello - so can check version
|
||||
* compatibility.
|
||||
*
|
||||
* char* -> uint16_t:n <n bytes>
|
||||
* ints/uints go as suitable number of bytes (e.g. uint16_t is 2 bytes).
|
||||
* optional fields go as '1' <val> or '0' (the 0/1 is 1 byte).
|
||||
* lists go as ('1' <elt>)* '0'
|
||||
*/
|
||||
|
||||
int marshal_flush(IOStream *io){
|
||||
int err = 0;
|
||||
err = IOStream_flush(io);
|
||||
return err;
|
||||
}
|
||||
|
||||
int marshal_bytes(IOStream *io, void *s, uint32_t s_n){
|
||||
int err = 0;
|
||||
int n;
|
||||
n = IOStream_write(io, s, s_n);
|
||||
if(n < 0){
|
||||
err = n;
|
||||
} else if (n < s_n){
|
||||
wprintf("> Wanted %d, got %d\n", s_n, n);
|
||||
err = -EIO;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n){
|
||||
int err = 0;
|
||||
int n;
|
||||
//dprintf("> s_n=%d\n", s_n);
|
||||
n = IOStream_read(io, s, s_n);
|
||||
//dprintf("> n=%d\n", n);
|
||||
if(n < 0){
|
||||
err = n;
|
||||
} else if(n < s_n){
|
||||
wprintf("> Wanted %d, got %d\n", s_n, n);
|
||||
err = -EIO;
|
||||
}
|
||||
//dprintf("< err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int marshal_uint8(IOStream *io, uint8_t x){
|
||||
return marshal_bytes(io, &x, sizeof(x));
|
||||
}
|
||||
|
||||
int unmarshal_uint8(IOStream *io, uint8_t *x){
|
||||
return unmarshal_bytes(io, x, sizeof(*x));
|
||||
}
|
||||
|
||||
int marshal_uint16(IOStream *io, uint16_t x){
|
||||
x = htons(x);
|
||||
return marshal_bytes(io, &x, sizeof(x));
|
||||
}
|
||||
|
||||
int unmarshal_uint16(IOStream *io, uint16_t *x){
|
||||
int err = 0;
|
||||
err = unmarshal_bytes(io, x, sizeof(*x));
|
||||
*x = ntohs(*x);
|
||||
return err;
|
||||
}
|
||||
|
||||
int marshal_int32(IOStream *io, int32_t x){
|
||||
int err = 0;
|
||||
//dprintf("> x=%d\n", x);
|
||||
x = htonl(x);
|
||||
err = marshal_bytes(io, &x, sizeof(x));
|
||||
//dprintf("< err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int unmarshal_int32(IOStream *io, int32_t *x){
|
||||
int err = 0;
|
||||
//dprintf(">\n");
|
||||
err = unmarshal_bytes(io, x, sizeof(*x));
|
||||
*x = ntohl(*x);
|
||||
//dprintf("< err=%d x=%d\n", err, *x);
|
||||
return err;
|
||||
}
|
||||
|
||||
int marshal_uint32(IOStream *io, uint32_t x){
|
||||
int err = 0;
|
||||
//dprintf("> x=%u\n", x);
|
||||
x = htonl(x);
|
||||
err = marshal_bytes(io, &x, sizeof(x));
|
||||
//dprintf("< err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int unmarshal_uint32(IOStream *io, uint32_t *x){
|
||||
int err = 0;
|
||||
//dprintf(">\n");
|
||||
err = unmarshal_bytes(io, x, sizeof(*x));
|
||||
*x = ntohl(*x);
|
||||
//dprintf("< err=%d x=%u\n", err, *x);
|
||||
return err;
|
||||
}
|
||||
|
||||
int marshal_uint64(IOStream *io, uint64_t x){
|
||||
int err;
|
||||
err = marshal_uint32(io, (uint32_t) ((x >> 32) & 0xffffffff));
|
||||
if(err) goto exit;
|
||||
err = marshal_uint32(io, (uint32_t) ( x & 0xffffffff));
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int unmarshal_uint64(IOStream *io, uint64_t *x){
|
||||
int err = 0;
|
||||
uint32_t hi, lo;
|
||||
err = unmarshal_uint32(io, &hi);
|
||||
if(err) goto exit;
|
||||
err = unmarshal_uint32(io, &lo);
|
||||
*x = (((uint64_t) hi) << 32) | lo;
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int marshal_net16(IOStream *io, net16_t x){
|
||||
return marshal_bytes(io, &x, sizeof(x));
|
||||
}
|
||||
|
||||
int unmarshal_net16(IOStream *io, net16_t *x){
|
||||
int err = 0;
|
||||
err = unmarshal_bytes(io, x, sizeof(*x));
|
||||
return err;
|
||||
}
|
||||
|
||||
int marshal_net32(IOStream *io, net32_t x){
|
||||
return marshal_bytes(io, &x, sizeof(x));
|
||||
}
|
||||
|
||||
int unmarshal_net32(IOStream *io, net32_t *x){
|
||||
int err = 0;
|
||||
err = unmarshal_bytes(io, x, sizeof(*x));
|
||||
return err;
|
||||
}
|
||||
|
||||
int marshal_string(IOStream *io, char *s, uint32_t s_n){
|
||||
int err;
|
||||
//dprintf("> s=%s\n", s);
|
||||
err = marshal_uint32(io, s_n);
|
||||
if(err) goto exit;
|
||||
err = marshal_bytes(io, s, s_n);
|
||||
exit:
|
||||
//dprintf("< err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int unmarshal_string(IOStream *io, char *s, uint32_t s_n){
|
||||
int err = 0, val_n = 0;
|
||||
//dprintf(">\n");
|
||||
err = unmarshal_uint32(io, &val_n);
|
||||
if(err) goto exit;
|
||||
if(val_n >= s_n){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
err = unmarshal_bytes(io, s, val_n);
|
||||
if(err) goto exit;
|
||||
s[val_n] = '\0';
|
||||
exit:
|
||||
//dprintf("< err=%d s=%s\n", err, s);
|
||||
return err;
|
||||
}
|
||||
|
||||
int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n){
|
||||
int err = 0, val_n = 0;
|
||||
char *val = NULL;
|
||||
//dprintf(">\n");
|
||||
err = unmarshal_uint32(io, &val_n);
|
||||
if(err) goto exit;
|
||||
val = allocate(val_n + 1);
|
||||
if(!val){
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
err = unmarshal_bytes(io, val, val_n);
|
||||
if(err) goto exit;
|
||||
val[val_n] = '\0';
|
||||
exit:
|
||||
if(err){
|
||||
if(val) deallocate(val);
|
||||
val = NULL;
|
||||
val_n = 0;
|
||||
}
|
||||
*s = val;
|
||||
if(s_n) *s_n = val_n;
|
||||
//dprintf("< err=%d s=%s\n", err, *s);
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* $Id: marshal.h,v 1.1 2003/10/17 15:48:43 mjw Exp $ */
|
||||
#ifndef _SP_MARSHAL_H_
|
||||
#define _SP_MARSHAL_H_
|
||||
|
||||
#include "iostream.h"
|
||||
|
||||
/** A 16-bit uint in network order, e.g. a port number. */
|
||||
typedef uint16_t net16_t;
|
||||
|
||||
/** A 32-bit uint in network order, e.g. an IP address. */
|
||||
typedef uint32_t net32_t;
|
||||
|
||||
extern int marshal_flush(IOStream *io);
|
||||
|
||||
extern int marshal_bytes(IOStream *io, void *s, uint32_t s_n);
|
||||
extern int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n);
|
||||
|
||||
extern int marshal_uint8(IOStream *io, uint8_t x);
|
||||
extern int unmarshal_uint8(IOStream *io, uint8_t *x);
|
||||
|
||||
extern int marshal_uint16(IOStream *io, uint16_t x);
|
||||
extern int unmarshal_uint16(IOStream *io, uint16_t *x);
|
||||
|
||||
extern int marshal_uint32(IOStream *io, uint32_t x);
|
||||
extern int unmarshal_uint32(IOStream *io, uint32_t *x);
|
||||
|
||||
extern int marshal_int32(IOStream *io, int32_t x);
|
||||
extern int unmarshal_int32(IOStream *io, int32_t *x);
|
||||
|
||||
extern int marshal_uint64(IOStream *io, uint64_t x);
|
||||
extern int unmarshal_uint64(IOStream *io, uint64_t *x);
|
||||
|
||||
extern int marshal_net16(IOStream *io, net16_t x);
|
||||
extern int unmarshal_net16(IOStream *io, net16_t *x);
|
||||
|
||||
extern int marshal_net32(IOStream *io, net32_t x);
|
||||
extern int unmarshal_net32(IOStream *io, net32_t *x);
|
||||
|
||||
extern int marshal_string(IOStream *io, char *s, uint32_t s_n);
|
||||
extern int unmarshal_string(IOStream *io, char *s, uint32_t s_n);
|
||||
extern int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n);
|
||||
|
||||
#endif /* ! _SP_MARSHAL_H_ */
|
||||
@@ -0,0 +1,259 @@
|
||||
/* $Id: socket_stream.c,v 1.9 2004/03/05 14:45:34 mjw Exp $ */
|
||||
/*
|
||||
* Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* An IOStream implementation using sockets.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "allocate.h"
|
||||
#include "socket_stream.h"
|
||||
|
||||
#define MODULE_NAME "sock"
|
||||
#define DEBUG 0
|
||||
//#undef DEBUG
|
||||
#include "debug.h"
|
||||
|
||||
static int socket_read(IOStream *s, void *buf, size_t n);
|
||||
static int socket_write(IOStream *s, const void *buf, size_t n);
|
||||
static int socket_error(IOStream *s);
|
||||
static int socket_close(IOStream *s);
|
||||
static void socket_free(IOStream *s);
|
||||
static int socket_flush(IOStream *s);
|
||||
|
||||
/** Methods used by a socket IOStream. */
|
||||
static const IOMethods socket_methods = {
|
||||
read: socket_read,
|
||||
write: socket_write,
|
||||
error: socket_error,
|
||||
close: socket_close,
|
||||
free: socket_free,
|
||||
flush: socket_flush,
|
||||
};
|
||||
|
||||
/** Get the socket data.
|
||||
*
|
||||
* @param io socket stream
|
||||
* @return data
|
||||
*/
|
||||
static inline SocketData * socket_data(IOStream *io){
|
||||
return (SocketData *)io->data;
|
||||
}
|
||||
|
||||
/** Test if a stream is a socket stream.
|
||||
*
|
||||
* @param io stream
|
||||
* @return 0 if a socket stream, -EINVAL if not
|
||||
*/
|
||||
int socket_stream_check(IOStream *io){
|
||||
return (io && io->methods == &socket_methods ? 0 : -EINVAL);
|
||||
}
|
||||
|
||||
/** Get the data for a socket stream.
|
||||
*
|
||||
* @param io stream
|
||||
* @param data return value for the data
|
||||
* @return 0 if a socket stream, -EINVAL if not
|
||||
*/
|
||||
int socket_stream_data(IOStream *io, SocketData **data){
|
||||
int err = socket_stream_check(io);
|
||||
if(err){
|
||||
*data = NULL;
|
||||
} else {
|
||||
*data = socket_data(io);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Set the destination address for a socket stream.
|
||||
*
|
||||
* @param io stream
|
||||
* @param addr address
|
||||
* @return 0 if a socket stream, -EINVAL if not
|
||||
*/
|
||||
int socket_stream_set_addr(IOStream *io, struct sockaddr_in *addr){
|
||||
int err = 0;
|
||||
SocketData *data = NULL;
|
||||
err = socket_stream_data(io, &data);
|
||||
if(!err){
|
||||
data->daddr = *addr;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Set the send flags for a socket stream.
|
||||
*
|
||||
* @param io stream
|
||||
* @param flags flags
|
||||
* @return 0 if a socket stream, -EINVAL if not
|
||||
*/
|
||||
int socket_stream_set_flags(IOStream *io, int flags){
|
||||
int err = 0;
|
||||
SocketData *data = NULL;
|
||||
err = socket_stream_data(io, &data);
|
||||
if(!err){
|
||||
data->flags = flags;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Write to the underlying socket using sendto.
|
||||
*
|
||||
* @param stream input
|
||||
* @param buf where to put input
|
||||
* @param n number of bytes to write
|
||||
* @return number of bytes written
|
||||
*/
|
||||
static int socket_write(IOStream *s, const void *buf, size_t n){
|
||||
SocketData *data = socket_data(s);
|
||||
struct sockaddr *daddr = (struct sockaddr *)&data->daddr;
|
||||
socklen_t daddr_n = sizeof(data->daddr);
|
||||
int k;
|
||||
dprintf("> sock=%d addr=%s:%d n=%d\n",
|
||||
data->fd, inet_ntoa(data->daddr.sin_addr), ntohs(data->daddr.sin_port), n);
|
||||
if(0){
|
||||
struct sockaddr_in self = {};
|
||||
socklen_t self_n;
|
||||
getsockname(data->fd, (struct sockaddr *)&self, &self_n);
|
||||
dprintf("> sockname sock=%d %s:%d\n",
|
||||
data->fd, inet_ntoa(self.sin_addr), ntohs(self.sin_port));
|
||||
}
|
||||
k = sendto(data->fd, buf, n, data->flags, daddr, daddr_n);
|
||||
dprintf("> sendto=%d\n", k);
|
||||
return k;
|
||||
}
|
||||
|
||||
/** Read from the underlying stream using recv();
|
||||
*
|
||||
* @param stream input
|
||||
* @param buf where to put input
|
||||
* @param n number of bytes to read
|
||||
* @return number of bytes read
|
||||
*/
|
||||
static int socket_read(IOStream *s, void *buf, size_t n){
|
||||
SocketData *data = socket_data(s);
|
||||
int k;
|
||||
struct sockaddr *saddr = (struct sockaddr *)&data->saddr;
|
||||
socklen_t saddr_n = sizeof(data->saddr);
|
||||
k = recvfrom(data->fd, buf, n, data->flags, saddr, &saddr_n);
|
||||
return k;
|
||||
}
|
||||
|
||||
/** Print to the underlying socket.
|
||||
*
|
||||
* @param s socket stream
|
||||
* @param msg format to use
|
||||
* @param args arguments
|
||||
* @return result of the print
|
||||
*/
|
||||
static int socket_print(IOStream *s, const char *msg, va_list args){
|
||||
SocketData *data = socket_data(s);
|
||||
int n;
|
||||
n = vsnprintf(data->buf, data->buf_n - 1, msg, args);
|
||||
if(0 < n && n < data->buf_n){
|
||||
socket_write(s, data->buf, n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/** Read a character from the underlying socket
|
||||
*
|
||||
* @param s socket stream
|
||||
* @return character read, IOSTREAM_EOF on end of socket (or error)
|
||||
*/
|
||||
static int socket_getc(IOStream *s){
|
||||
char b;
|
||||
int n, c;
|
||||
n = socket_read(s, &b, 1);
|
||||
c = (n <= 0 ? IOSTREAM_EOF : b);
|
||||
return c;
|
||||
}
|
||||
|
||||
/** Flush the socket (no-op).
|
||||
*
|
||||
* @param s socket stream
|
||||
* @return 0 on success, error code otherwise
|
||||
*/
|
||||
static int socket_flush(IOStream *s){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Check if a socket stream has an error (no-op).
|
||||
*
|
||||
* @param s socket stream
|
||||
* @return 1 if has an error, 0 otherwise
|
||||
*/
|
||||
static int socket_error(IOStream *s){
|
||||
// Read SOL_SOCKET/SO_ERROR ?
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Close a socket stream.
|
||||
*
|
||||
* @param s socket stream to close
|
||||
* @return result of the close
|
||||
*/
|
||||
static int socket_close(IOStream *s){
|
||||
SocketData *data = socket_data(s);
|
||||
return close(data->fd);
|
||||
}
|
||||
|
||||
/** Free a socket stream.
|
||||
*
|
||||
* @param s socket stream
|
||||
*/
|
||||
static void socket_free(IOStream *s){
|
||||
SocketData *data = socket_data(s);
|
||||
deallocate(data);
|
||||
}
|
||||
|
||||
/** Create an IOStream for a socket.
|
||||
*
|
||||
* @param fd socket to wtap
|
||||
* @return new IOStream using fd for i/o
|
||||
*/
|
||||
IOStream *socket_stream_new(int fd){
|
||||
int err = -ENOMEM;
|
||||
IOStream *io = NULL;
|
||||
SocketData *data = NULL;
|
||||
|
||||
io = ALLOCATE(IOStream);
|
||||
if(!io) goto exit;
|
||||
io->methods = &socket_methods;
|
||||
data = ALLOCATE(SocketData);
|
||||
if(!data) goto exit;
|
||||
io->data = data;
|
||||
data->fd = fd;
|
||||
data->buf_n = sizeof(data->buf);
|
||||
err = 0;
|
||||
exit:
|
||||
if(err){
|
||||
if(io){
|
||||
if(data) deallocate(data);
|
||||
deallocate(io);
|
||||
io = NULL;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/* $Id: socket_stream.h,v 1.2 2004/03/04 17:38:13 mjw Exp $ */
|
||||
/*
|
||||
* Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _XEN_LIB_SOCKET_STREAM_H_
|
||||
#define _XEN_LIB_SOCKET_STREAM_H_
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#include "iostream.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
/** Data associated with a socket stream. */
|
||||
typedef struct SocketData {
|
||||
/** The socket file descriptor. */
|
||||
int fd;
|
||||
/** Source address from last read (recvfrom). */
|
||||
struct sockaddr_in saddr;
|
||||
/** Destination address for writes (sendto). */
|
||||
struct sockaddr_in daddr;
|
||||
/** Write flags (sendto). */
|
||||
int flags;
|
||||
/** Buffer size. */
|
||||
int buf_n;
|
||||
/** Buffer for formatted printing. */
|
||||
char buf[1024];
|
||||
} SocketData;
|
||||
|
||||
extern IOStream *socket_stream_new(int fd);
|
||||
extern int socket_stream_data(IOStream *io, SocketData **data);
|
||||
extern int socket_stream_check(IOStream *io);
|
||||
extern int socket_stream_set_addr(IOStream *io, struct sockaddr_in *addr);
|
||||
extern int socket_stream_set_flags(IOStream *io, int flags);
|
||||
|
||||
#endif
|
||||
#endif /* !_XEN_LIB_SOCKET_STREAM_H_ */
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2001, 2002 Hewlett-Packard Company.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* IOStream subtype for input and output to strings.
|
||||
* Usable from user or kernel code (with __KERNEL__ defined).
|
||||
*/
|
||||
|
||||
#include "sys_string.h"
|
||||
#include "string_stream.h"
|
||||
#include "allocate.h"
|
||||
|
||||
static int string_print(IOStream *io, const char *msg, va_list args);
|
||||
static int string_getc(IOStream *io);
|
||||
static int string_error(IOStream *io);
|
||||
static int string_close(IOStream *io);
|
||||
static void string_free(IOStream *io);
|
||||
|
||||
/** Methods for a string stream. */
|
||||
static IOMethods string_methods = {
|
||||
//print: string_print,
|
||||
//getc: string_getc,
|
||||
error: string_error,
|
||||
close: string_close,
|
||||
free: string_free,
|
||||
};
|
||||
|
||||
/** Get the string stream state.
|
||||
*
|
||||
* @param io string stream
|
||||
* @return state
|
||||
*/
|
||||
static inline StringData *get_string_data(IOStream *io){
|
||||
return (StringData*)io->data;
|
||||
}
|
||||
|
||||
/** Read a character from a string stream.
|
||||
*
|
||||
* @param io string stream
|
||||
* @return character read, IOSTREAM_EOF if no more input
|
||||
*/
|
||||
static int string_getc(IOStream *io){
|
||||
StringData *data = get_string_data(io);
|
||||
int c = IOSTREAM_EOF;
|
||||
char *s = data->in;
|
||||
|
||||
if(s && s < data->end){
|
||||
c = (unsigned)*s;
|
||||
data->in = s+1;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/** Print to a string stream.
|
||||
* Formats the data to an internal buffer and prints it.
|
||||
* The formatted data must fit into the internal buffer.
|
||||
*
|
||||
* @param io string stream
|
||||
* @param format print format
|
||||
* @param args print arguments
|
||||
* @return result of the print
|
||||
*/
|
||||
static int string_print(IOStream *io, const char *msg, va_list args){
|
||||
StringData *data = get_string_data(io);
|
||||
int k = data->end - data->out;
|
||||
int n = vsnprintf(data->out, k, (char*)msg, args);
|
||||
if(n < 0 || n > k ){
|
||||
n = k;
|
||||
IOStream_close(io);
|
||||
} else {
|
||||
data->out += n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/** Test if a string stream has an error.
|
||||
*
|
||||
* @param io string stream
|
||||
* @return 0 if ok, error code otherwise
|
||||
*/
|
||||
static int string_error(IOStream *io){
|
||||
StringData *data = get_string_data(io);
|
||||
return data->out == NULL;
|
||||
}
|
||||
|
||||
/** Close a string stream.
|
||||
*
|
||||
* @param io string stream
|
||||
* @return 0
|
||||
*/
|
||||
static int string_close(IOStream *io){
|
||||
StringData *data = get_string_data(io);
|
||||
data->in = NULL;
|
||||
data->out = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Free a string stream.
|
||||
* The stream must have been allocated, not statically created.
|
||||
* The stream state is freed, but the underlying string is not.
|
||||
*
|
||||
* @param io string stream
|
||||
*/
|
||||
static void string_free(IOStream *io){
|
||||
StringData *data = get_string_data(io);
|
||||
zero(data, sizeof(*data));
|
||||
deallocate(data);
|
||||
}
|
||||
|
||||
/** Get the methods to use for a string stream.
|
||||
*
|
||||
* @return methods
|
||||
*/
|
||||
IOMethods *string_stream_get_methods(void){
|
||||
return &string_methods;
|
||||
}
|
||||
|
||||
/** Initialise a string stream, usually from static data.
|
||||
*
|
||||
* @param io address of IOStream to fill in
|
||||
* @param data address of StringData to fill in
|
||||
* @param s string to use
|
||||
* @param n length of the string
|
||||
*/
|
||||
void string_stream_init(IOStream *io, StringData *data, char *s, int n){
|
||||
if(data && io){
|
||||
zero(data, sizeof(*data));
|
||||
data->string = (char*)s;
|
||||
data->in = data->string;
|
||||
data->out = data->string;
|
||||
data->size = n;
|
||||
data->end = data->string + n;
|
||||
zero(io, sizeof(*io));
|
||||
io->methods = &string_methods;
|
||||
io->data = data;
|
||||
}
|
||||
}
|
||||
|
||||
/** Allocate and initialise a string stream.
|
||||
*
|
||||
* @param s string to use
|
||||
* @param n length of the string
|
||||
* @return new stream (free using IOStream_free)
|
||||
*/
|
||||
IOStream *string_stream_new(char *s, int n){
|
||||
int ok = 0;
|
||||
StringData *data = ALLOCATE(StringData);
|
||||
IOStream *io = ALLOCATE(IOStream);
|
||||
if(data && io){
|
||||
ok = 1;
|
||||
string_stream_init(io, data, s, n);
|
||||
}
|
||||
if(!ok){
|
||||
deallocate(data);
|
||||
deallocate(io);
|
||||
io = NULL;
|
||||
}
|
||||
return io;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* $Id: string_stream.h,v 1.1 2003/08/22 14:25:48 mjw Exp $ */
|
||||
/*
|
||||
* Copyright (C) 2001, 2002 Hewlett-Packard Company.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _SP_STRING_STREAM_H_
|
||||
#define _SP_STRING_STREAM_H_
|
||||
|
||||
#include "iostream.h"
|
||||
|
||||
/** Internal state for a string stream.
|
||||
* Exposed here so that string streams can be statically created, using
|
||||
* string_stream_init().
|
||||
*/
|
||||
typedef struct {
|
||||
/** The string used for input and ouput. */
|
||||
char *string;
|
||||
/** Output pointer. */
|
||||
char *out;
|
||||
/** Input pointer. */
|
||||
char *in;
|
||||
/** Length of string. */
|
||||
int size;
|
||||
/** End marker. */
|
||||
char *end;
|
||||
} StringData;
|
||||
|
||||
extern IOMethods *string_stream_get_methods(void);
|
||||
extern IOStream *string_stream_new(char *s, int n);
|
||||
extern void string_stream_init(IOStream *stream, StringData *data, char *s, int n);
|
||||
|
||||
#endif /* !_SP_STRING_STREAM_H_ */
|
||||
@@ -0,0 +1,935 @@
|
||||
/*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of the
|
||||
* License, or (at your option) any later version. This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "sys_string.h"
|
||||
#include "lexis.h"
|
||||
#include "sys_net.h"
|
||||
#include "hash_table.h"
|
||||
#include "sxpr.h"
|
||||
|
||||
#include <errno.h>
|
||||
#undef free
|
||||
|
||||
/** @file
|
||||
* General representation of sxprs.
|
||||
* Includes print, equal, and free functions for the sxpr types.
|
||||
*
|
||||
* Zero memory containing an Sxpr will have the value ONONE - this is intentional.
|
||||
* When a function returning an sxpr cannot allocate memory we return ONOMEM.
|
||||
*
|
||||
*/
|
||||
|
||||
static int atom_print(IOStream *io, Sxpr obj, unsigned flags);
|
||||
static int atom_equal(Sxpr x, Sxpr y);
|
||||
static void atom_free(Sxpr obj);
|
||||
|
||||
static int string_print(IOStream *io, Sxpr obj, unsigned flags);
|
||||
static int string_equal(Sxpr x, Sxpr y);
|
||||
static void string_free(Sxpr obj);
|
||||
|
||||
static int cons_print(IOStream *io, Sxpr obj, unsigned flags);
|
||||
static int cons_equal(Sxpr x, Sxpr y);
|
||||
static void cons_free(Sxpr obj);
|
||||
|
||||
static int null_print(IOStream *io, Sxpr obj, unsigned flags);
|
||||
static int none_print(IOStream *io, Sxpr obj, unsigned flags);
|
||||
static int int_print(IOStream *io, Sxpr obj, unsigned flags);
|
||||
static int bool_print(IOStream *io, Sxpr obj, unsigned flags);
|
||||
|
||||
/** Type definitions. */
|
||||
static SxprType types[1024] = {
|
||||
[T_NONE] { type: T_NONE, name: "none", print: none_print },
|
||||
[T_NULL] { type: T_NULL, name: "null", print: null_print },
|
||||
[T_UINT] { type: T_UINT, name: "int", print: int_print, },
|
||||
[T_BOOL] { type: T_BOOL, name: "bool", print: bool_print, },
|
||||
[T_ATOM] { type: T_ATOM, name: "atom", print: atom_print,
|
||||
pointer: TRUE,
|
||||
free: atom_free,
|
||||
equal: atom_equal,
|
||||
},
|
||||
[T_STRING] { type: T_STRING, name: "string", print: string_print,
|
||||
pointer: TRUE,
|
||||
free: string_free,
|
||||
equal: string_equal,
|
||||
},
|
||||
[T_CONS] { type: T_CONS, name: "cons", print: cons_print,
|
||||
pointer: TRUE,
|
||||
free: cons_free,
|
||||
equal: cons_equal,
|
||||
},
|
||||
};
|
||||
|
||||
/** Number of entries in the types array. */
|
||||
static int type_sup = sizeof(types)/sizeof(types[0]);
|
||||
|
||||
/** Get the type definition for a given type code.
|
||||
*
|
||||
* @param ty type code
|
||||
* @return type definition or null
|
||||
*/
|
||||
SxprType *get_sxpr_type(int ty){
|
||||
if(0 <= ty && ty < type_sup){
|
||||
return types+ty;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** The default print function.
|
||||
*
|
||||
* @param io stream to print to
|
||||
* @param x sxpr to print
|
||||
* @param flags print flags
|
||||
* @return number of bytes written on success
|
||||
*/
|
||||
int default_print(IOStream *io, Sxpr x, unsigned flags){
|
||||
return IOStream_print(io, "#<%u %lu>\n", get_type(x), get_ul(x));
|
||||
}
|
||||
|
||||
/** The default equal function.
|
||||
* Uses eq().
|
||||
*
|
||||
* @param x sxpr to compare
|
||||
* @param y sxpr to compare
|
||||
* @return 1 if equal, 0 otherwise
|
||||
*/
|
||||
int default_equal(Sxpr x, Sxpr y){
|
||||
return eq(x, y);
|
||||
}
|
||||
|
||||
/** General sxpr print function.
|
||||
* Prints an sxpr on a stream using the print function for the sxpr type.
|
||||
* Printing is controlled by flags from the PrintFlags enum.
|
||||
* If PRINT_TYPE is in the flags the sxpr type is printed before the sxpr
|
||||
* (for debugging).
|
||||
*
|
||||
* @param io stream to print to
|
||||
* @param x sxpr to print
|
||||
* @param flags print flags
|
||||
* @return number of bytes written
|
||||
*/
|
||||
int objprint(IOStream *io, Sxpr x, unsigned flags){
|
||||
SxprType *def = get_sxpr_type(get_type(x));
|
||||
ObjPrintFn *print_fn = (def && def->print ? def->print : default_print);
|
||||
int k = 0;
|
||||
if(!io) return k;
|
||||
if(flags & PRINT_TYPE){
|
||||
k += IOStream_print(io, "%s:", def->name);
|
||||
}
|
||||
k += print_fn(io, x, flags);
|
||||
return k;
|
||||
}
|
||||
|
||||
/** General sxpr free function.
|
||||
* Frees an sxpr using the free function for its type.
|
||||
* Free functions must recursively free any subsxprs.
|
||||
* If no function is defined then the default is to
|
||||
* free sxprs whose type has pointer true.
|
||||
* Sxprs must not be used after freeing.
|
||||
*
|
||||
* @param x sxpr to free
|
||||
*/
|
||||
void objfree(Sxpr x){
|
||||
SxprType *def = get_sxpr_type(get_type(x));
|
||||
|
||||
if(def){
|
||||
if(def->free){
|
||||
def->free(x);
|
||||
} else if (def->pointer){
|
||||
hfree(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** General sxpr equality function.
|
||||
* Compares x and y using the equal function for x.
|
||||
* Uses default_equal() if x has no equal function.
|
||||
*
|
||||
* @param x sxpr to compare
|
||||
* @param y sxpr to compare
|
||||
* @return 1 if equal, 0 otherwise
|
||||
*/
|
||||
int objequal(Sxpr x, Sxpr y){
|
||||
SxprType *def = get_sxpr_type(get_type(x));
|
||||
ObjEqualFn *equal_fn = (def && def->equal ? def->equal : default_equal);
|
||||
return equal_fn(x, y);
|
||||
}
|
||||
|
||||
/** Search for a key in an alist.
|
||||
* An alist is a list of conses, where the cars
|
||||
* of the conses are the keys. Compares keys using equality.
|
||||
*
|
||||
* @param k key
|
||||
* @param l alist to search
|
||||
* @return first element of l with car k, or ONULL
|
||||
*/
|
||||
Sxpr assoc(Sxpr k, Sxpr l){
|
||||
for( ; CONSP(l) ; l = CDR(l)){
|
||||
Sxpr x = CAR(l);
|
||||
if(CONSP(x) && objequal(k, CAR(x))){
|
||||
return x;
|
||||
}
|
||||
}
|
||||
return ONULL;
|
||||
}
|
||||
|
||||
/** Search for a key in an alist.
|
||||
* An alist is a list of conses, where the cars
|
||||
* of the conses are the keys. Compares keys using eq.
|
||||
*
|
||||
* @param k key
|
||||
* @param l alist to search
|
||||
* @return first element of l with car k, or ONULL
|
||||
*/
|
||||
Sxpr assocq(Sxpr k, Sxpr l){
|
||||
for( ; CONSP(l); l = CDR(l)){
|
||||
Sxpr x = CAR(l);
|
||||
if(CONSP(x) && eq(k, CAR(x))){
|
||||
return x;
|
||||
}
|
||||
}
|
||||
return ONULL;
|
||||
}
|
||||
|
||||
/** Add a new key and value to an alist.
|
||||
*
|
||||
* @param k key
|
||||
* @param l value
|
||||
* @param l alist
|
||||
* @return l with the new cell added to the front
|
||||
*/
|
||||
Sxpr acons(Sxpr k, Sxpr v, Sxpr l){
|
||||
Sxpr x, y;
|
||||
x = cons_new(k, v);
|
||||
if(NOMEMP(x)) return x;
|
||||
y = cons_new(x, l);
|
||||
if(NOMEMP(y)) cons_free_cells(x);
|
||||
return y;
|
||||
}
|
||||
|
||||
/** Test if a list contains an element.
|
||||
* Uses sxpr equality.
|
||||
*
|
||||
* @param l list
|
||||
* @param x element to look for
|
||||
* @return a tail of l with x as car, or ONULL
|
||||
*/
|
||||
Sxpr cons_member(Sxpr l, Sxpr x){
|
||||
for( ; CONSP(l) && !eq(x, CAR(l)); l = CDR(l)){}
|
||||
return l;
|
||||
}
|
||||
|
||||
/** Test if a list contains an element satisfying a test.
|
||||
* The test function is called with v and an element of the list.
|
||||
*
|
||||
* @param l list
|
||||
* @param test_fn test function to use
|
||||
* @param v value for first argument to the test
|
||||
* @return a tail of l with car satisfying the test, or 0
|
||||
*/
|
||||
Sxpr cons_member_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v){
|
||||
for( ; CONSP(l) && !test_fn(v, CAR(l)); l = CDR(l)){ }
|
||||
return l;
|
||||
}
|
||||
|
||||
/** Test if the elements of list 't' are a subset of the elements
|
||||
* of list 's'. Element order is not significant.
|
||||
*
|
||||
* @param s element list to check subset of
|
||||
* @param t element list to check if is a subset
|
||||
* @return 1 if is a subset, 0 otherwise
|
||||
*/
|
||||
int cons_subset(Sxpr s, Sxpr t){
|
||||
for( ; CONSP(t); t = CDR(t)){
|
||||
if(!CONSP(cons_member(s, CAR(t)))){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Test if two lists have equal sets of elements.
|
||||
* Element order is not significant.
|
||||
*
|
||||
* @param s list to check
|
||||
* @param t list to check
|
||||
* @return 1 if equal, 0 otherwise
|
||||
*/
|
||||
int cons_set_equal(Sxpr s, Sxpr t){
|
||||
return cons_subset(s, t) && cons_subset(t, s);
|
||||
}
|
||||
|
||||
#ifdef USE_GC
|
||||
/*============================================================================*/
|
||||
/* The functions inside this ifdef are only safe if GC is used.
|
||||
* Otherwise they may leak memory.
|
||||
*/
|
||||
|
||||
/** Remove an element from a list (GC only).
|
||||
* Uses sxpr equality and removes all instances, even
|
||||
* if there are more than one.
|
||||
*
|
||||
* @param l list to remove elements from
|
||||
* @param x element to remove
|
||||
* @return modified input list
|
||||
*/
|
||||
Sxpr cons_remove(Sxpr l, Sxpr x){
|
||||
return cons_remove_if(l, eq, x);
|
||||
}
|
||||
|
||||
/** Remove elements satisfying a test (GC only).
|
||||
* The test function is called with v and an element of the set.
|
||||
*
|
||||
* @param l list to remove elements from
|
||||
* @param test_fn function to use to decide if an element should be removed
|
||||
* @return modified input list
|
||||
*/
|
||||
Sxpr cons_remove_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v){
|
||||
Sxpr prev = ONULL, elt, next;
|
||||
|
||||
for(elt = l; CONSP(elt); elt = next){
|
||||
next = CDR(elt);
|
||||
if(test_fn(v, CAR(elt))){
|
||||
if(NULLP(prev)){
|
||||
l = next;
|
||||
} else {
|
||||
CDR(prev) = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
/** Set the value for a key in an alist (GC only).
|
||||
* If the key is present, changes the value, otherwise
|
||||
* adds a new cell.
|
||||
*
|
||||
* @param k key
|
||||
* @param v value
|
||||
* @param l alist
|
||||
* @return modified or extended list
|
||||
*/
|
||||
Sxpr setf(Sxpr k, Sxpr v, Sxpr l){
|
||||
Sxpr e = assoc(k, l);
|
||||
if(NULLP(e)){
|
||||
l = acons(k, v, l);
|
||||
} else {
|
||||
CAR(CDR(e)) = v;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
/*============================================================================*/
|
||||
#endif /* USE_GC */
|
||||
|
||||
/** Create a new atom with the given name.
|
||||
*
|
||||
* @param name the name
|
||||
* @return new atom
|
||||
*/
|
||||
Sxpr atom_new(char *name){
|
||||
Sxpr n, obj = ONOMEM;
|
||||
|
||||
n = string_new(name);
|
||||
if(NOMEMP(n)) goto exit;
|
||||
obj = HALLOC(ObjAtom, T_ATOM);
|
||||
if(NOMEMP(obj)) goto exit;
|
||||
OBJ_ATOM(obj)->name = n;
|
||||
exit:
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** Free an atom.
|
||||
*
|
||||
* @param obj to free
|
||||
*/
|
||||
void atom_free(Sxpr obj){
|
||||
// Interned atoms are shared, so do not free.
|
||||
if(OBJ_ATOM(obj)->interned) return;
|
||||
objfree(OBJ_ATOM(obj)->name);
|
||||
hfree(obj);
|
||||
}
|
||||
|
||||
/** Print an atom. Prints the atom name.
|
||||
*
|
||||
* @param io stream to print to
|
||||
* @param obj to print
|
||||
* @param flags print flags
|
||||
* @return number of bytes printed
|
||||
*/
|
||||
int atom_print(IOStream *io, Sxpr obj, unsigned flags){
|
||||
//return string_print(io, OBJ_ATOM(obj)->name, (flags | PRINT_RAW));
|
||||
return string_print(io, OBJ_ATOM(obj)->name, flags);
|
||||
}
|
||||
|
||||
/** Atom equality.
|
||||
*
|
||||
* @param x to compare
|
||||
* @param y to compare
|
||||
* @return 1 if equal, 0 otherwise
|
||||
*/
|
||||
int atom_equal(Sxpr x, Sxpr y){
|
||||
int ok;
|
||||
ok = eq(x, y);
|
||||
if(ok) goto exit;
|
||||
ok = ATOMP(y) && string_equal(OBJ_ATOM(x)->name, OBJ_ATOM(y)->name);
|
||||
if(ok) goto exit;
|
||||
ok = STRINGP(y) && string_equal(OBJ_ATOM(x)->name, y);
|
||||
exit:
|
||||
return ok;
|
||||
}
|
||||
|
||||
/** Get the name of an atom.
|
||||
*
|
||||
* @param obj atom
|
||||
* @return name
|
||||
*/
|
||||
char * atom_name(Sxpr obj){
|
||||
return string_string(OBJ_ATOM(obj)->name);
|
||||
}
|
||||
|
||||
/** Get the C string from a string sxpr.
|
||||
*
|
||||
* @param obj string sxpr
|
||||
* @return string
|
||||
*/
|
||||
char * string_string(Sxpr obj){
|
||||
return OBJ_STRING(obj);
|
||||
}
|
||||
|
||||
/** Get the length of a string.
|
||||
*
|
||||
* @param obj string
|
||||
* @return length
|
||||
*/
|
||||
int string_length(Sxpr obj){
|
||||
return strlen(OBJ_STRING(obj));
|
||||
}
|
||||
|
||||
/** Create a new string. The input string is copied,
|
||||
* and must be null-terminated.
|
||||
*
|
||||
* @param s characters to put in the string
|
||||
* @return new sxpr
|
||||
*/
|
||||
Sxpr string_new(char *s){
|
||||
int n = (s ? strlen(s) : 0);
|
||||
Sxpr obj;
|
||||
obj = halloc(n+1, T_STRING);
|
||||
if(!NOMEMP(obj)){
|
||||
char *str = OBJ_STRING(obj);
|
||||
strncpy(str, s, n);
|
||||
str[n] = '\0';
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** Free a string.
|
||||
*
|
||||
* @param obj to free
|
||||
*/
|
||||
void string_free(Sxpr obj){
|
||||
hfree(obj);
|
||||
}
|
||||
|
||||
/** Determine if a string needs escapes when printed
|
||||
* using the given flags.
|
||||
*
|
||||
* @param str string to check
|
||||
* @param flags print flags
|
||||
* @return 1 if needs escapes, 0 otherwise
|
||||
*/
|
||||
int needs_escapes(char *str, unsigned flags){
|
||||
char *c;
|
||||
int val = 0;
|
||||
|
||||
if(str){
|
||||
for(c=str; *c; c++){
|
||||
if(in_alpha_class(*c)) continue;
|
||||
if(in_decimal_digit_class(*c)) continue;
|
||||
if(in_class(*c, "/._+:@~-")) continue;
|
||||
val = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//printf("\n> val=%d str=|%s|\n", val, str);
|
||||
return val;
|
||||
}
|
||||
|
||||
/** Print a string to a stream, with escapes if necessary.
|
||||
*
|
||||
* @param io stream to print to
|
||||
* @param str string
|
||||
* @param flags print flags
|
||||
* @return number of bytes written
|
||||
*/
|
||||
int _string_print(IOStream *io, char *str, unsigned flags){
|
||||
int k = 0;
|
||||
if((flags & PRINT_RAW) || !needs_escapes(str, flags)){
|
||||
k += IOStream_print(io, str);
|
||||
} else {
|
||||
k += IOStream_print(io, "\"");
|
||||
if(str){
|
||||
char *s;
|
||||
for(s = str; *s; s++){
|
||||
if(*s < ' ' || *s >= 127 ){
|
||||
switch(*s){
|
||||
case '\a': k += IOStream_print(io, "\\a"); break;
|
||||
case '\b': k += IOStream_print(io, "\\b"); break;
|
||||
case '\f': k += IOStream_print(io, "\\f"); break;
|
||||
case '\n': k += IOStream_print(io, "\\n"); break;
|
||||
case '\r': k += IOStream_print(io, "\\r"); break;
|
||||
case '\t': k += IOStream_print(io, "\\t"); break;
|
||||
case '\v': k += IOStream_print(io, "\\v"); break;
|
||||
default:
|
||||
// Octal escape;
|
||||
k += IOStream_print(io, "\\%o", *s);
|
||||
break;
|
||||
}
|
||||
} else if(*s == c_double_quote ||
|
||||
*s == c_single_quote ||
|
||||
*s == c_escape){
|
||||
k += IOStream_print(io, "\\%c", *s);
|
||||
} else {
|
||||
k+= IOStream_print(io, "%c", *s);
|
||||
}
|
||||
}
|
||||
}
|
||||
k += IOStream_print(io, "\"");
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
/** Print a string to a stream, with escapes if necessary.
|
||||
*
|
||||
* @param io stream to print to
|
||||
* @param obj string
|
||||
* @param flags print flags
|
||||
* @return number of bytes written
|
||||
*/
|
||||
int string_print(IOStream *io, Sxpr obj, unsigned flags){
|
||||
return _string_print(io, OBJ_STRING(obj), flags);
|
||||
}
|
||||
|
||||
/** Compare an sxpr with a string for equality.
|
||||
*
|
||||
* @param x string to compare with
|
||||
* @param y sxpr to compare
|
||||
* @return 1 if equal, 0 otherwise
|
||||
*/
|
||||
int string_equal(Sxpr x, Sxpr y){
|
||||
int ok = 0;
|
||||
ok = eq(x,y);
|
||||
if(ok) goto exit;
|
||||
ok = has_type(y, T_STRING) && !strcmp(OBJ_STRING(x), OBJ_STRING(y));
|
||||
if(ok) goto exit;
|
||||
ok = has_type(y, T_ATOM) && !strcmp(OBJ_STRING(x), atom_name(y));
|
||||
exit:
|
||||
return ok;
|
||||
}
|
||||
|
||||
/** Create a new cons cell.
|
||||
* The cell is ONOMEM if either argument is.
|
||||
*
|
||||
* @param car sxpr for the car
|
||||
* @param cdr sxpr for the cdr
|
||||
* @return new cons
|
||||
*/
|
||||
Sxpr cons_new(Sxpr car, Sxpr cdr){
|
||||
Sxpr obj;
|
||||
if(NOMEMP(car) || NOMEMP(cdr)){
|
||||
obj = ONOMEM;
|
||||
} else {
|
||||
obj = HALLOC(ObjCons, T_CONS);
|
||||
if(!NOMEMP(obj)){
|
||||
ObjCons *z = OBJ_CONS(obj);
|
||||
z->car = car;
|
||||
z->cdr = cdr;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** Push a new element onto a list.
|
||||
*
|
||||
* @param list list to add to
|
||||
* @param elt element to add
|
||||
* @return 0 if successful, error code otherwise
|
||||
*/
|
||||
int cons_push(Sxpr *list, Sxpr elt){
|
||||
Sxpr l;
|
||||
l = cons_new(elt, *list);
|
||||
if(NOMEMP(l)) return -ENOMEM;
|
||||
*list = l;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Free a cons. Recursively frees the car and cdr.
|
||||
*
|
||||
* @param obj to free
|
||||
*/
|
||||
void cons_free(Sxpr obj){
|
||||
Sxpr next;
|
||||
for(; CONSP(obj); obj = next){
|
||||
next = CDR(obj);
|
||||
objfree(CAR(obj));
|
||||
hfree(obj);
|
||||
}
|
||||
if(!NULLP(obj)){
|
||||
objfree(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/** Free a cons and its cdr cells, but not the car sxprs.
|
||||
* Does nothing if called on something that is not a cons.
|
||||
*
|
||||
* @param obj to free
|
||||
*/
|
||||
void cons_free_cells(Sxpr obj){
|
||||
Sxpr next;
|
||||
for(; CONSP(obj); obj = next){
|
||||
next = CDR(obj);
|
||||
hfree(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/** Print a cons.
|
||||
* Prints the cons in list format if the cdrs are conses.
|
||||
* uses pair (dot) format if the last cdr is not a cons (or null).
|
||||
*
|
||||
* @param io stream to print to
|
||||
* @param obj to print
|
||||
* @param flags print flags
|
||||
* @return number of bytes written
|
||||
*/
|
||||
int cons_print(IOStream *io, Sxpr obj, unsigned flags){
|
||||
int first = 1;
|
||||
int k = 0;
|
||||
k += IOStream_print(io, "(");
|
||||
for( ; CONSP(obj) ; obj = CDR(obj)){
|
||||
if(first){
|
||||
first = 0;
|
||||
} else {
|
||||
k += IOStream_print(io, " ");
|
||||
}
|
||||
k += objprint(io, CAR(obj), flags);
|
||||
}
|
||||
if(!NULLP(obj)){
|
||||
k += IOStream_print(io, " . ");
|
||||
k += objprint(io, obj, flags);
|
||||
}
|
||||
k += IOStream_print(io, ")");
|
||||
return (IOStream_error(io) ? -1 : k);
|
||||
}
|
||||
|
||||
/** Compare a cons with another sxpr for equality.
|
||||
* If y is a cons, compares the cars and cdrs recursively.
|
||||
*
|
||||
* @param x cons to compare
|
||||
* @param y sxpr to compare
|
||||
* @return 1 if equal, 0 otherwise
|
||||
*/
|
||||
int cons_equal(Sxpr x, Sxpr y){
|
||||
return CONSP(y) &&
|
||||
objequal(CAR(x), CAR(y)) &&
|
||||
objequal(CDR(x), CDR(y));
|
||||
}
|
||||
|
||||
/** Return the length of a cons list.
|
||||
*
|
||||
* @param obj list
|
||||
* @return length
|
||||
*/
|
||||
int cons_length(Sxpr obj){
|
||||
int count = 0;
|
||||
for( ; CONSP(obj); obj = CDR(obj)){
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/** Destructively reverse a cons list in-place.
|
||||
* If the argument is not a cons it is returned unchanged.
|
||||
*
|
||||
* @param l to reverse
|
||||
* @return reversed list
|
||||
*/
|
||||
Sxpr nrev(Sxpr l){
|
||||
if(CONSP(l)){
|
||||
// Iterate down the cells in the list making the cdr of
|
||||
// each cell point to the previous cell. The last cell
|
||||
// is the head of the reversed list.
|
||||
Sxpr prev = ONULL;
|
||||
Sxpr cell = l;
|
||||
Sxpr next;
|
||||
|
||||
while(1){
|
||||
next = CDR(cell);
|
||||
CDR(cell) = prev;
|
||||
if(!CONSP(next)) break;
|
||||
prev = cell;
|
||||
cell = next;
|
||||
}
|
||||
l = cell;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
/** Print the null sxpr.
|
||||
*
|
||||
* @param io stream to print to
|
||||
* @param obj to print
|
||||
* @param flags print flags
|
||||
* @return number of bytes written
|
||||
*/
|
||||
static int null_print(IOStream *io, Sxpr obj, unsigned flags){
|
||||
return IOStream_print(io, "()");
|
||||
}
|
||||
|
||||
/** Print the `unspecified' sxpr none.
|
||||
*
|
||||
* @param io stream to print to
|
||||
* @param obj to print
|
||||
* @param flags print flags
|
||||
* @return number of bytes written
|
||||
*/
|
||||
static int none_print(IOStream *io, Sxpr obj, unsigned flags){
|
||||
return IOStream_print(io, "<none>");
|
||||
}
|
||||
|
||||
/** Print an integer.
|
||||
*
|
||||
* @param io stream to print to
|
||||
* @param obj to print
|
||||
* @param flags print flags
|
||||
* @return number of bytes written
|
||||
*/
|
||||
static int int_print(IOStream *io, Sxpr obj, unsigned flags){
|
||||
return IOStream_print(io, "%d", OBJ_INT(obj));
|
||||
}
|
||||
|
||||
/** Print a boolean.
|
||||
*
|
||||
* @param io stream to print to
|
||||
* @param obj to print
|
||||
* @param flags print flags
|
||||
* @return number of bytes written
|
||||
*/
|
||||
static int bool_print(IOStream *io, Sxpr obj, unsigned flags){
|
||||
return IOStream_print(io, (OBJ_UINT(obj) ? k_true : k_false));
|
||||
}
|
||||
|
||||
int sxprp(Sxpr obj, Sxpr name){
|
||||
return CONSP(obj) && objequal(CAR(obj), name);
|
||||
}
|
||||
|
||||
/** Get the name of an element.
|
||||
*
|
||||
* @param obj element
|
||||
* @return name
|
||||
*/
|
||||
Sxpr sxpr_name(Sxpr obj){
|
||||
Sxpr val = ONONE;
|
||||
if(CONSP(obj)){
|
||||
val = CAR(obj);
|
||||
} else if(STRINGP(obj) || ATOMP(obj)){
|
||||
val = obj;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
int sxpr_is(Sxpr obj, char *s){
|
||||
if(ATOMP(obj)) return !strcmp(atom_name(obj), s);
|
||||
if(STRINGP(obj)) return !strcmp(string_string(obj), s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sxpr_elementp(Sxpr obj, Sxpr name){
|
||||
return CONSP(obj) && objequal(CAR(obj), name);
|
||||
}
|
||||
|
||||
/** Get the attributes of an sxpr.
|
||||
*
|
||||
* @param obj sxpr
|
||||
* @return attributes
|
||||
*/
|
||||
Sxpr sxpr_attributes(Sxpr obj){
|
||||
Sxpr val = ONULL;
|
||||
if(CONSP(obj)){
|
||||
obj = CDR(obj);
|
||||
if(CONSP(obj)){
|
||||
obj = CAR(obj);
|
||||
if(sxprp(obj, intern("@"))){
|
||||
val = CDR(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
Sxpr sxpr_attribute(Sxpr obj, Sxpr key, Sxpr def){
|
||||
Sxpr val = ONONE;
|
||||
val = assoc(sxpr_attributes(obj), key);
|
||||
if(CONSP(val) && CONSP(CDR(val))){
|
||||
val = CADR(def);
|
||||
} else {
|
||||
val = def;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/** Get the children of an sxpr.
|
||||
*
|
||||
* @param obj sxpr
|
||||
* @return children
|
||||
*/
|
||||
Sxpr sxpr_children(Sxpr obj){
|
||||
Sxpr val = ONULL;
|
||||
if(CONSP(obj)){
|
||||
val = CDR(obj);
|
||||
if(CONSP(val) && sxprp(CAR(val), intern("@"))){
|
||||
val = CDR(val);
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
Sxpr sxpr_child(Sxpr obj, Sxpr name, Sxpr def){
|
||||
Sxpr val = ONONE;
|
||||
Sxpr l;
|
||||
for(l = sxpr_children(obj); CONSP(l); l = CDR(l)){
|
||||
if(sxprp(CAR(l), name)){
|
||||
val = CAR(l);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(NONEP(val)) val = def;
|
||||
return val;
|
||||
}
|
||||
|
||||
Sxpr sxpr_child0(Sxpr obj, Sxpr def){
|
||||
Sxpr val = ONONE;
|
||||
Sxpr l = sxpr_children(obj);
|
||||
if(CONSP(l)){
|
||||
val = CAR(l);
|
||||
} else {
|
||||
val = def;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
Sxpr sxpr_child_value(Sxpr obj, Sxpr name, Sxpr def){
|
||||
Sxpr val = ONONE;
|
||||
val = sxpr_child(obj, name, ONONE);
|
||||
if(NONEP(val)){
|
||||
val = def;
|
||||
} else {
|
||||
val = sxpr_child0(val, def);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/** Table of interned symbols. Indexed by symbol name. */
|
||||
static HashTable *symbols = NULL;
|
||||
|
||||
/** Hash function for entries in the symbol table.
|
||||
*
|
||||
* @param key to hash
|
||||
* @return hashcode
|
||||
*/
|
||||
static Hashcode sym_hash_fn(void *key){
|
||||
return hash_string((char*)key);
|
||||
}
|
||||
|
||||
/** Key equality function for the symbol table.
|
||||
*
|
||||
* @param x to compare
|
||||
* @param y to compare
|
||||
* @return 1 if equal, 0 otherwise
|
||||
*/
|
||||
static int sym_equal_fn(void *x, void *y){
|
||||
return !strcmp((char*)x, (char*)y);
|
||||
}
|
||||
|
||||
/** Entry free function for the symbol table.
|
||||
*
|
||||
* @param table the entry is in
|
||||
* @param entry being freed
|
||||
*/
|
||||
static void sym_free_fn(HashTable *table, HTEntry *entry){
|
||||
if(entry){
|
||||
objfree(((ObjAtom*)entry->value)->name);
|
||||
HTEntry_free(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/** Initialize the symbol table.
|
||||
*
|
||||
* @return 0 on sucess, error code otherwise
|
||||
*/
|
||||
static int init_symbols(void){
|
||||
symbols = HashTable_new(100);
|
||||
if(symbols){
|
||||
symbols->key_hash_fn = sym_hash_fn;
|
||||
symbols->key_equal_fn = sym_equal_fn;
|
||||
symbols->entry_free_fn = sym_free_fn;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Cleanup the symbol table. Frees the table and all its symbols.
|
||||
*/
|
||||
void cleanup_symbols(void){
|
||||
HashTable_free(symbols);
|
||||
symbols = NULL;
|
||||
}
|
||||
|
||||
/** Get the interned symbol with the given name.
|
||||
* No new symbol is created.
|
||||
*
|
||||
* @return symbol or null
|
||||
*/
|
||||
Sxpr get_symbol(char *sym){
|
||||
HTEntry *entry;
|
||||
if(!symbols){
|
||||
if(init_symbols()) return ONOMEM;
|
||||
return ONULL;
|
||||
}
|
||||
entry = HashTable_get_entry(symbols, sym);
|
||||
if(entry){
|
||||
return OBJP(T_ATOM, entry->value);
|
||||
} else {
|
||||
return ONULL;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the interned symbol with the given name.
|
||||
* Creates a new symbol if necessary.
|
||||
*
|
||||
* @return symbol
|
||||
*/
|
||||
Sxpr intern(char *sym){
|
||||
Sxpr symbol = get_symbol(sym);
|
||||
if(NULLP(symbol)){
|
||||
if(!symbols) return ONOMEM;
|
||||
symbol = atom_new(sym);
|
||||
if(!NOMEMP(symbol)){
|
||||
OBJ_ATOM(symbol)->interned = TRUE;
|
||||
HashTable_add(symbols, atom_name(symbol), get_ptr(symbol));
|
||||
}
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of the
|
||||
* License, or (at your option) any later version. This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _XEN_LIB_SXPR_H_
|
||||
#define _XEN_LIB_SXPR_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hash_table.h"
|
||||
#include "iostream.h"
|
||||
#include "allocate.h"
|
||||
|
||||
/** @file
|
||||
* Definitions for rules and sxprs.
|
||||
*/
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
/** Sxpr type. */
|
||||
typedef int16_t TypeCode;
|
||||
|
||||
/** A typed sxpr handle.*/
|
||||
typedef struct Sxpr {
|
||||
/** Sxpr type. */
|
||||
TypeCode type;
|
||||
union {
|
||||
/** Sxpr value. */
|
||||
unsigned long ul;
|
||||
/** Pointer. */
|
||||
void *ptr;
|
||||
} v;
|
||||
} Sxpr;
|
||||
|
||||
/** Sxpr type to indicate out of memory. */
|
||||
#define T_NOMEM ((TypeCode)-1)
|
||||
/** The 'unspecified' sxpr. */
|
||||
#define T_NONE ((TypeCode)0)
|
||||
/** The empty list. */
|
||||
#define T_NULL ((TypeCode)1)
|
||||
/** Unsigned integer. */
|
||||
#define T_UINT ((TypeCode)2)
|
||||
/** A string. */
|
||||
#define T_STRING ((TypeCode)3)
|
||||
/** An atom. */
|
||||
#define T_ATOM ((TypeCode)4)
|
||||
/** A boolean. */
|
||||
#define T_BOOL ((TypeCode)5)
|
||||
|
||||
/** A cons (pair or list). */
|
||||
#define T_CONS ((TypeCode)10)
|
||||
|
||||
/** An error. */
|
||||
#define T_ERR ((TypeCode)40)
|
||||
|
||||
/** An atom. */
|
||||
typedef struct ObjAtom {
|
||||
Sxpr name;
|
||||
Hashcode hashcode;
|
||||
int interned;
|
||||
} ObjAtom;
|
||||
|
||||
/** A cons (pair). */
|
||||
typedef struct ObjCons {
|
||||
Sxpr car;
|
||||
Sxpr cdr;
|
||||
} ObjCons;
|
||||
|
||||
/** A vector. */
|
||||
typedef struct ObjVector {
|
||||
int n;
|
||||
Sxpr data[0];
|
||||
} ObjVector;
|
||||
|
||||
/** Flags for sxpr printing. */
|
||||
enum PrintFlags {
|
||||
PRINT_RAW = 0x001,
|
||||
PRINT_TYPE = 0x002,
|
||||
PRINT_PRETTY = 0x004,
|
||||
PRINT_NUM = 0x008,
|
||||
};
|
||||
|
||||
/** An integer sxpr.
|
||||
*
|
||||
* @param ty type
|
||||
* @param val integer value
|
||||
*/
|
||||
#define OBJI(ty, val) (Sxpr){ type: (ty), v: { ul: (val) }}
|
||||
|
||||
/** A pointer sxpr.
|
||||
* If the pointer is non-null, returns an sxpr containing it.
|
||||
* If the pointer is null, returns ONOMEM.
|
||||
*
|
||||
* @param ty type
|
||||
* @param val pointer
|
||||
*/
|
||||
#define OBJP(ty, val) ((val) ? (Sxpr){ type: (ty), v: { ptr: (val) }} : ONOMEM)
|
||||
|
||||
/** Make an integer sxpr containing a pointer.
|
||||
*
|
||||
* @param val pointer
|
||||
*/
|
||||
#define PTR(val) OBJP(T_UINT, (void*)(val))
|
||||
|
||||
/** Make an integer sxpr.
|
||||
* @param x value
|
||||
*/
|
||||
#define OINT(x) OBJI(T_UINT, x)
|
||||
|
||||
/** Make an error sxpr.
|
||||
*
|
||||
* @param x value
|
||||
*/
|
||||
#define OERR(x) OBJI(T_ERR, x)
|
||||
|
||||
/** Out of memory constant. */
|
||||
#define ONOMEM OBJI(T_NOMEM, 0)
|
||||
|
||||
/** The `unspecified' constant. */
|
||||
#define ONONE OBJI(T_NONE, 0)
|
||||
|
||||
/** Empty list constant. */
|
||||
#define ONULL OBJI(T_NULL, 0)
|
||||
|
||||
/** False constant. */
|
||||
#define OFALSE OBJI(T_BOOL, 0)
|
||||
|
||||
/** True constant. */
|
||||
#define OTRUE OBJI(T_BOOL, 1)
|
||||
|
||||
/* Recognizers for the various sxpr types. */
|
||||
#define ATOMP(obj) has_type(obj, T_ATOM)
|
||||
#define BOOLP(obj) has_type(obj, T_BOOL)
|
||||
#define CONSP(obj) has_type(obj, T_CONS)
|
||||
#define ERRP(obj) has_type(obj, T_ERR)
|
||||
#define INTP(obj) has_type(obj, T_UINT)
|
||||
#define NOMEMP(obj) has_type(obj, T_NOMEM)
|
||||
#define NONEP(obj) has_type(obj, T_NONE)
|
||||
#define NULLP(obj) has_type(obj, T_NULL)
|
||||
#define STRINGP(obj) has_type(obj, T_STRING)
|
||||
|
||||
#define TRUEP(obj) get_ul(obj)
|
||||
|
||||
/** Convert an sxpr to an unsigned integer. */
|
||||
#define OBJ_UINT(x) get_ul(x)
|
||||
/** Convert an sxpr to an integer. */
|
||||
#define OBJ_INT(x) (int)get_ul(x)
|
||||
|
||||
/* Conversions of sxprs to their values.
|
||||
* No checking is done.
|
||||
*/
|
||||
#define OBJ_STRING(x) ((char*)get_ptr(x))
|
||||
#define OBJ_CONS(x) ((ObjCons*)get_ptr(x))
|
||||
#define OBJ_ATOM(x) ((ObjAtom*)get_ptr(x))
|
||||
#define OBJ_SET(x) ((ObjSet*)get_ptr(x))
|
||||
#define CAR(x) (OBJ_CONS(x)->car)
|
||||
#define CDR(x) (OBJ_CONS(x)->cdr)
|
||||
|
||||
#define CAAR(x) (CAR(CAR(x)))
|
||||
#define CADR(x) (CAR(CDR(x)))
|
||||
#define CDAR(x) (CDR(CAR(x)))
|
||||
#define CDDR(x) (CDR(CDR(x)))
|
||||
|
||||
/** Get the integer value from an sxpr.
|
||||
*
|
||||
* @param obj sxpr
|
||||
* @return value
|
||||
*/
|
||||
static inline unsigned long get_ul(Sxpr obj){
|
||||
return obj.v.ul;
|
||||
}
|
||||
|
||||
/** Get the pointer value from an sxpr.
|
||||
*
|
||||
* @param obj sxpr
|
||||
* @return value
|
||||
*/
|
||||
static inline void * get_ptr(Sxpr obj){
|
||||
return obj.v.ptr;
|
||||
}
|
||||
|
||||
/** Create an sxpr containing a pointer.
|
||||
*
|
||||
* @param type typecode
|
||||
* @param val pointer
|
||||
* @return sxpr
|
||||
*/
|
||||
static inline Sxpr obj_ptr(TypeCode type, void *val){
|
||||
return (Sxpr){ type: type, v: { ptr: val } };
|
||||
}
|
||||
|
||||
/** Create an sxpr containing an integer.
|
||||
*
|
||||
* @param type typecode
|
||||
* @param val integer
|
||||
* @return sxpr
|
||||
*/
|
||||
static inline Sxpr obj_ul(TypeCode type, unsigned long val){
|
||||
return (Sxpr){ type: type, v: { ul: val } };
|
||||
}
|
||||
|
||||
/** Get the type of an sxpr.
|
||||
*
|
||||
* @param obj sxpr
|
||||
* @return type
|
||||
*/
|
||||
static inline TypeCode get_type(Sxpr obj){
|
||||
return obj.type;
|
||||
}
|
||||
|
||||
/** Check the type of an sxpr.
|
||||
*
|
||||
* @param obj sxpr
|
||||
* @param type to check
|
||||
* @return 1 if has the type, 0 otherwise
|
||||
*/
|
||||
static inline int has_type(Sxpr obj, TypeCode type){
|
||||
return get_type(obj) == type;
|
||||
}
|
||||
|
||||
/** Compare sxprs for literal equality of type and value.
|
||||
*
|
||||
* @param x sxpr to compare
|
||||
* @param y sxpr to compare
|
||||
* @return 1 if equal, 0 otherwise
|
||||
*/
|
||||
static inline int eq(Sxpr x, Sxpr y){
|
||||
return ((get_type(x) == get_type(y)) && (get_ul(x) == get_ul(y)));
|
||||
}
|
||||
|
||||
/** Checked version of CAR
|
||||
*
|
||||
* @param x sxpr
|
||||
* @return CAR if a cons, x otherwise
|
||||
*/
|
||||
static inline Sxpr car(Sxpr x){
|
||||
return (CONSP(x) ? CAR(x) : x);
|
||||
}
|
||||
|
||||
/** Checked version of CDR.
|
||||
*
|
||||
* @param x sxpr
|
||||
* @return CDR if a cons, null otherwise
|
||||
*/
|
||||
static inline Sxpr cdr(Sxpr x){
|
||||
return (CONSP(x) ? CDR(x) : ONULL);
|
||||
}
|
||||
|
||||
/** Allocate some memory and return an sxpr containing it.
|
||||
* Returns ONOMEM if allocation failed.
|
||||
*
|
||||
* @param n number of bytes to allocate
|
||||
* @param ty typecode
|
||||
* @return sxpr
|
||||
*/
|
||||
static inline Sxpr halloc(size_t n, TypeCode ty){
|
||||
return OBJP(ty, allocate(n));
|
||||
}
|
||||
|
||||
/** Allocate an sxpr containing a pointer to the given type.
|
||||
*
|
||||
* @param ty type (uses sizeof to determine how many bytes to allocate)
|
||||
* @param code typecode
|
||||
* @return sxpr, ONOMEM if allocation failed
|
||||
*/
|
||||
#define HALLOC(ty, code) halloc(sizeof(ty), code)
|
||||
|
||||
typedef int ObjPrintFn(IOStream *io, Sxpr obj, unsigned flags);
|
||||
typedef int ObjEqualFn(Sxpr obj, Sxpr other);
|
||||
typedef void ObjFreeFn(Sxpr obj);
|
||||
|
||||
/** An sxpr type definition. */
|
||||
typedef struct SxprType {
|
||||
TypeCode type;
|
||||
char *name;
|
||||
int pointer;
|
||||
ObjPrintFn *print;
|
||||
ObjEqualFn *equal;
|
||||
ObjFreeFn *free;
|
||||
} SxprType;
|
||||
|
||||
|
||||
extern SxprType *get_sxpr_type(int ty);
|
||||
|
||||
/** Free the pointer in an sxpr.
|
||||
*
|
||||
* @param x sxpr containing a pointer
|
||||
*/
|
||||
static inline void hfree(Sxpr x){
|
||||
deallocate(get_ptr(x));
|
||||
}
|
||||
|
||||
extern int objprint(IOStream *io, Sxpr x, unsigned flags);
|
||||
extern int objequal(Sxpr x, Sxpr y);
|
||||
extern void objfree(Sxpr x);
|
||||
|
||||
extern void cons_free_cells(Sxpr obj);
|
||||
extern Sxpr intern(char *s);
|
||||
|
||||
extern Sxpr assoc(Sxpr k, Sxpr l);
|
||||
extern Sxpr assocq(Sxpr k, Sxpr l);
|
||||
extern Sxpr acons(Sxpr k, Sxpr v, Sxpr l);
|
||||
extern Sxpr nrev(Sxpr l);
|
||||
extern Sxpr cons_member(Sxpr l, Sxpr x);
|
||||
extern Sxpr cons_member_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v);
|
||||
extern int cons_subset(Sxpr s, Sxpr t);
|
||||
extern int cons_set_equal(Sxpr s, Sxpr t);
|
||||
|
||||
#ifdef USE_GC
|
||||
extern Sxpr cons_remove(Sxpr l, Sxpr x);
|
||||
extern Sxpr cons_remove_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v);
|
||||
#endif
|
||||
|
||||
extern Sxpr atom_new(char *name);
|
||||
extern char * atom_name(Sxpr obj);
|
||||
|
||||
extern Sxpr string_new(char *s);
|
||||
extern char * string_string(Sxpr obj);
|
||||
extern int string_length(Sxpr obj);
|
||||
|
||||
extern Sxpr cons_new(Sxpr car, Sxpr cdr);
|
||||
extern int cons_push(Sxpr *list, Sxpr elt);
|
||||
extern int cons_length(Sxpr obj);
|
||||
|
||||
Sxpr sxpr_name(Sxpr obj);
|
||||
int sxpr_is(Sxpr obj, char *s);
|
||||
int sxpr_elementp(Sxpr obj, Sxpr name);
|
||||
Sxpr sxpr_attributes(Sxpr obj);
|
||||
Sxpr sxpr_attribute(Sxpr obj, Sxpr key, Sxpr def);
|
||||
Sxpr sxpr_children(Sxpr obj);
|
||||
Sxpr sxpr_child(Sxpr obj, Sxpr name, Sxpr def);
|
||||
Sxpr sxpr_child0(Sxpr obj, Sxpr def);
|
||||
Sxpr sxpr_child_value(Sxpr obj, Sxpr name, Sxpr def);
|
||||
|
||||
/** Create a new atom.
|
||||
*
|
||||
* @param s atom name
|
||||
* @return new atom
|
||||
*/
|
||||
static inline Sxpr mkatom(char *s){
|
||||
return atom_new(s);
|
||||
}
|
||||
|
||||
/** Create a new string sxpr.
|
||||
*
|
||||
* @param s string bytes (copied)
|
||||
* @return new string
|
||||
*/
|
||||
static inline Sxpr mkstring(char *s){
|
||||
return string_new(s);
|
||||
}
|
||||
|
||||
/** Create an integer sxpr.
|
||||
*
|
||||
* @param i value
|
||||
* @return sxpr
|
||||
*/
|
||||
static inline Sxpr mkint(int i){
|
||||
return OBJI(T_UINT, i);
|
||||
}
|
||||
|
||||
/** Create a boolean sxpr.
|
||||
*
|
||||
* @param b value
|
||||
* @return sxpr
|
||||
*/
|
||||
static inline Sxpr mkbool(int b){
|
||||
return OBJI(T_BOOL, (b ? 1 : 0));
|
||||
}
|
||||
|
||||
/* Constants used in parsing and printing. */
|
||||
#define k_list_open "("
|
||||
#define c_list_open '('
|
||||
#define k_list_close ")"
|
||||
#define c_list_close ')'
|
||||
#define k_true "true"
|
||||
#define k_false "false"
|
||||
|
||||
#define c_var '$'
|
||||
#define c_escape '\\'
|
||||
#define c_single_quote '\''
|
||||
#define c_double_quote '"'
|
||||
#define c_string_open c_double_quote
|
||||
#define c_string_close c_double_quote
|
||||
#define c_data_open '['
|
||||
#define c_data_close ']'
|
||||
#define c_binary '*'
|
||||
#define c_eval '!'
|
||||
#define c_concat_open '{'
|
||||
#define c_concat_close '}'
|
||||
|
||||
#endif /* ! _XEN_LIB_SXPR_H_ */
|
||||
@@ -0,0 +1,897 @@
|
||||
|
||||
#ifdef __KERNEL__
|
||||
# include <linux/config.h>
|
||||
# include <linux/module.h>
|
||||
# include <linux/kernel.h>
|
||||
# include <linux/string.h>
|
||||
# include <linux/errno.h>
|
||||
#else
|
||||
# include <stdlib.h>
|
||||
# include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "iostream.h"
|
||||
#include "lexis.h"
|
||||
#include "sxpr_parser.h"
|
||||
#include "sys_string.h"
|
||||
|
||||
/** @file
|
||||
* Sxpr parsing.
|
||||
*
|
||||
* So that the parser does not leak memory, all sxprs constructed by
|
||||
* the parser must be freed on error. On successful parse the sxpr
|
||||
* returned becomes the responsibility of the caller.
|
||||
*
|
||||
* @author Mike Wray <mike.wray@hpl.hp.com>
|
||||
*/
|
||||
|
||||
#define dprintf(fmt, args...) IOStream_print(iostdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args)
|
||||
#define printf(fmt, args...) IOStream_print(iostdout, fmt, ##args)
|
||||
|
||||
static void reset(Parser *z);
|
||||
static int inputchar(Parser *p, char c);
|
||||
static int savechar(Parser *p, char c);
|
||||
extern void parse_error(Parser *in);
|
||||
extern void parse_error_id(Parser *in, ParseErrorId id);
|
||||
|
||||
static int begin_start(Parser *p, char c);
|
||||
static int state_start(Parser *p, char c);
|
||||
static int end_start(Parser *p);
|
||||
|
||||
static int begin_comment(Parser *p, char c);
|
||||
static int state_comment(Parser *p, char c);
|
||||
static int end_comment(Parser *p);
|
||||
|
||||
static int begin_string(Parser *p, char c);
|
||||
static int state_string(Parser *p, char c);
|
||||
static int end_string(Parser *p);
|
||||
static int state_escape(Parser *p, char c);
|
||||
static int state_octal(Parser *p, char c);
|
||||
static int state_hex(Parser *p, char c);
|
||||
|
||||
static int begin_atom(Parser *p, char c);
|
||||
static int state_atom(Parser *p, char c);
|
||||
static int end_atom(Parser *p);
|
||||
|
||||
static int state_list(Parser *p, char c);
|
||||
static int begin_list(Parser *p, char c);
|
||||
static int end_list(Parser *p);
|
||||
|
||||
/** Print a parse error.
|
||||
*
|
||||
* @param in parser
|
||||
* @param msg format followed by printf arguments
|
||||
*/
|
||||
void eprintf(Parser *in, char *msg, ...){
|
||||
va_list args;
|
||||
if(in->error_out){
|
||||
va_start(args, msg);
|
||||
IOStream_vprint(in->error_out, msg, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
/** Print a parse warning.
|
||||
*
|
||||
* @param in parser
|
||||
* @param msg format followed by printf arguments
|
||||
*/
|
||||
void wprintf(Parser *in, char *msg, ...){
|
||||
va_list args;
|
||||
if(in->error_out){
|
||||
va_start(args, msg);
|
||||
IOStream_vprint(in->error_out, msg, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
|
||||
/** Record defining the message for a parse error. */
|
||||
typedef struct {
|
||||
ParseErrorId id;
|
||||
char *message;
|
||||
} ParseError;
|
||||
|
||||
/** Format for printing parse error messages. */
|
||||
#define PARSE_ERR_FMT "parse error> line %3d, column %2d: %s"
|
||||
|
||||
/** Message catalog for the parse error codes. */
|
||||
static ParseError catalog[] = {
|
||||
{ PARSE_ERR_UNSPECIFIED, "unspecified error" },
|
||||
{ PARSE_ERR_NOMEM, "out of memory" },
|
||||
{ PARSE_ERR_UNEXPECTED_EOF, "unexpected end of input" },
|
||||
{ PARSE_ERR_TOKEN_TOO_LONG, "token too long" },
|
||||
{ PARSE_ERR_INVALID_SYNTAX, "syntax error" },
|
||||
{ PARSE_ERR_INVALID_ESCAPE, "invalid escape" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/** Number of entries in the message catalog. */
|
||||
const static int catalog_n = sizeof(catalog)/sizeof(ParseError);
|
||||
|
||||
void ParserState_free(ParserState *z){
|
||||
if(!z) return;
|
||||
objfree(z->val);
|
||||
deallocate(z);
|
||||
}
|
||||
|
||||
int ParserState_new(ParserStateFn *fn, ParserState *parent, ParserState **val){
|
||||
int err = 0;
|
||||
ParserState *z;
|
||||
z = ALLOCATE(ParserState);
|
||||
if(z){
|
||||
z->fn = fn;
|
||||
z->parent = parent;
|
||||
z->val = ONULL;
|
||||
} else {
|
||||
err = -ENOMEM;
|
||||
}
|
||||
if(!err) *val = z;
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Free a parser.
|
||||
* No-op if the parser is null.
|
||||
*
|
||||
* @param z parser
|
||||
*/
|
||||
void Parser_free(Parser *z){
|
||||
if(!z) return;
|
||||
objfree(z->val);
|
||||
z->val = ONONE;
|
||||
deallocate(z);
|
||||
}
|
||||
|
||||
/** Create a new parser. The error stream defaults to null.
|
||||
*/
|
||||
Parser * Parser_new(void){
|
||||
Parser *z = ALLOCATE(Parser);
|
||||
int err = -ENOMEM;
|
||||
|
||||
if(!z) goto exit;
|
||||
err = 0;
|
||||
reset(z);
|
||||
exit:
|
||||
if(err){
|
||||
Parser_free(z);
|
||||
z = NULL;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
/** Get the next character.
|
||||
* Records the character read in the parser,
|
||||
* and sets the line and character counts.
|
||||
*
|
||||
* @param p parser
|
||||
* @return error flag: 0 on success, non-zero on error
|
||||
*/
|
||||
static int inputchar(Parser *p, char c){
|
||||
int err = 0;
|
||||
if(c=='\n'){
|
||||
p->line_no++;
|
||||
p->char_no = 0;
|
||||
} else {
|
||||
p->char_no++;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int savechar(Parser *p, char c){
|
||||
int err = 0;
|
||||
if(p->buf_i >= p->buf_n){
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
p->buf[p->buf_i] = c;
|
||||
p->buf_i++;
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int Parser_input_char(Parser *p, char c){
|
||||
int err = 0;
|
||||
if(at_eof(p)){
|
||||
//skip;
|
||||
} else {
|
||||
inputchar(p, c);
|
||||
}
|
||||
if(!p->state){
|
||||
err = begin_start(p, c);
|
||||
if(err) goto exit;
|
||||
}
|
||||
err = p->state->fn(p, c);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int Parser_input_eof(Parser *p){
|
||||
int err = 0;
|
||||
p->eof = 1;
|
||||
err = Parser_input_char(p, IOSTREAM_EOF);
|
||||
return err;
|
||||
}
|
||||
|
||||
int Parser_input(Parser *p, char *buf, int buf_n){
|
||||
int err = 0;
|
||||
int i = 0;
|
||||
if(buf_n <= 0){
|
||||
err = Parser_input_eof(p);
|
||||
goto exit;
|
||||
}
|
||||
for(i = 0; i<buf_n; i++){
|
||||
err = Parser_input_char(p, buf[i]);
|
||||
if(err) goto exit;
|
||||
}
|
||||
exit:
|
||||
err = (err < 0 ? err : buf_n);
|
||||
return err;
|
||||
}
|
||||
|
||||
int Parser_push(Parser *p, ParserStateFn *fn){
|
||||
int err = 0;
|
||||
err = ParserState_new(fn, p->state, &p->state);
|
||||
return err;
|
||||
}
|
||||
|
||||
int Parser_pop(Parser *p){
|
||||
int err = 0;
|
||||
ParserState *s = p->state;
|
||||
p->state = s->parent;
|
||||
ParserState_free(s);
|
||||
return err;
|
||||
}
|
||||
|
||||
int Parser_return(Parser *p){
|
||||
int err = 0;
|
||||
Sxpr val = ONONE;
|
||||
if(!p->state){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
val = p->state->val;
|
||||
p->state->val = ONONE;
|
||||
err = Parser_pop(p);
|
||||
if(err) goto exit;
|
||||
if(p->state){
|
||||
err = cons_push(&p->state->val, val);
|
||||
} else {
|
||||
val = nrev(val);
|
||||
p->val = val;
|
||||
}
|
||||
exit:
|
||||
if(err){
|
||||
objfree(val);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Determine if a character is a separator.
|
||||
*
|
||||
* @param p parser
|
||||
* @param c character to test
|
||||
* @return 1 if a separator, 0 otherwise
|
||||
*/
|
||||
static int is_separator(Parser *p, char c){
|
||||
return in_sep_class(c);
|
||||
}
|
||||
|
||||
/** Return the current token.
|
||||
* The return value points at the internal buffer, so
|
||||
* it must not be modified (or freed). Use copy_token() if you need a copy.
|
||||
*
|
||||
* @param p parser
|
||||
* @return token
|
||||
*/
|
||||
char *peek_token(Parser *p){
|
||||
return p->buf;
|
||||
}
|
||||
|
||||
/** Return a copy of the current token.
|
||||
* The returned value should be freed when finished with.
|
||||
*
|
||||
* @param p parser
|
||||
* @return copy of token
|
||||
*/
|
||||
char *copy_token(Parser *p){
|
||||
return strdup(peek_token(p));
|
||||
}
|
||||
|
||||
static int do_intern(Parser *p){
|
||||
int err = 0;
|
||||
Sxpr obj = intern(peek_token(p));
|
||||
if(NOMEMP(obj)){
|
||||
err = -ENOMEM;
|
||||
} else {
|
||||
p->state->val = obj;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_string(Parser *p){
|
||||
int err = 0;
|
||||
Sxpr obj;
|
||||
obj = string_new(peek_token(p));
|
||||
if(NOMEMP(obj)){
|
||||
err = -ENOMEM;
|
||||
} else {
|
||||
p->state->val = obj;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void newtoken(Parser *p){
|
||||
memset(p->buf, 0, p->buf_n);
|
||||
p->buf_i = 0;
|
||||
p->tok_begin_line = p->line_no;
|
||||
p->tok_begin_char = p->char_no;
|
||||
}
|
||||
|
||||
int get_escape(char c, char *d){
|
||||
int err = 0;
|
||||
switch(c){
|
||||
case 'a': *d = '\a'; break;
|
||||
case 'b': *d = '\b'; break;
|
||||
case 'f': *d = '\f'; break;
|
||||
case 'n': *d = '\n'; break;
|
||||
case 'r': *d = '\r'; break;
|
||||
case 't': *d = '\t'; break;
|
||||
case 'v': *d = '\v'; break;
|
||||
case c_escape: *d = c_escape; break;
|
||||
case c_single_quote: *d = c_single_quote; break;
|
||||
case c_double_quote: *d = c_double_quote; break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int begin_start(Parser *p, char c){
|
||||
return Parser_push(p, state_start);
|
||||
}
|
||||
|
||||
int state_start(Parser *p, char c){
|
||||
int err = 0;
|
||||
if(at_eof(p)){
|
||||
err = end_start(p);
|
||||
} else if(in_space_class(c)){
|
||||
//skip
|
||||
} else if(in_comment_class(c)){
|
||||
begin_comment(p, c);
|
||||
} else if(c == c_list_open){
|
||||
begin_list(p, c);
|
||||
} else if(c == c_list_close){
|
||||
parse_error(p);
|
||||
err = -EINVAL;
|
||||
} else if(in_string_quote_class(c)){
|
||||
begin_string(p, c);
|
||||
} else if(in_printable_class(c)){
|
||||
begin_atom(p, c);
|
||||
} else if(c == 0x04){
|
||||
//ctrl-D, EOT: end-of-text.
|
||||
Parser_input_eof(p);
|
||||
} else {
|
||||
parse_error(p);
|
||||
err = -EINVAL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int end_start(Parser *p){
|
||||
int err = 0;
|
||||
err = Parser_return(p);
|
||||
return err;
|
||||
}
|
||||
|
||||
int begin_comment(Parser *p, char c){
|
||||
int err = 0;
|
||||
err = Parser_push(p, state_comment);
|
||||
if(err) goto exit;
|
||||
err = inputchar(p, c);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int state_comment(Parser *p, char c){
|
||||
int err = 0;
|
||||
if(c == '\n' || at_eof(p)){
|
||||
err = end_comment(p);
|
||||
} else {
|
||||
err = inputchar(p, c);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int end_comment(Parser *p){
|
||||
return Parser_pop(p);
|
||||
}
|
||||
|
||||
int begin_string(Parser *p, char c){
|
||||
int err = 0;
|
||||
err = Parser_push(p, state_string);
|
||||
if(err) goto exit;
|
||||
newtoken(p);
|
||||
p->state->delim = c;
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int state_string(Parser *p, char c){
|
||||
int err = 0;
|
||||
if(at_eof(p)){
|
||||
parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
|
||||
err = -EINVAL;
|
||||
} else if(c == p->state->delim){
|
||||
err = end_string(p);
|
||||
} else if(c == '\\'){
|
||||
err = Parser_push(p, state_escape);
|
||||
} else {
|
||||
err = savechar(p, c);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int end_string(Parser *p){
|
||||
int err = 0;
|
||||
err = do_string(p);
|
||||
if(err) goto exit;
|
||||
err = Parser_return(p);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int state_escape(Parser *p, char c){
|
||||
int err = 0;
|
||||
char d;
|
||||
if(at_eof(p)){
|
||||
parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
if(get_escape(c, &d) == 0){
|
||||
err = savechar(p, d);
|
||||
if(err) goto exit;
|
||||
err = Parser_pop(p);
|
||||
} else if(c == 'x'){
|
||||
p->state->fn = state_hex;
|
||||
p->state->ival = 0;
|
||||
p->state->count = 0;
|
||||
} else {
|
||||
p->state->fn = state_octal;
|
||||
p->state->ival = 0;
|
||||
p->state->count = 0;
|
||||
err = Parser_input_char(p, c);
|
||||
}
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int octaldone(Parser *p){
|
||||
int err = 0;
|
||||
char d = (char)(p->state->ival & 0xff);
|
||||
err = Parser_pop(p);
|
||||
if(err) goto exit;
|
||||
err = Parser_input_char(p, d);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int octaldigit(Parser *p, char c){
|
||||
int err = 0;
|
||||
p->state->ival *= 8;
|
||||
p->state->ival += c - '0';
|
||||
p->state->count++;
|
||||
if(err) goto exit;
|
||||
if(p->state->ival < 0 || p->state->ival > 0xff){
|
||||
parse_error(p);
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
if(p->state->count == 3){
|
||||
err = octaldone(p);
|
||||
}
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int state_octal(Parser *p, char c){
|
||||
int err = 0;
|
||||
if(at_eof(p)){
|
||||
parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
} else if('0' <= c && c <= '7'){
|
||||
err = octaldigit(p, c);
|
||||
} else {
|
||||
err = octaldone(p);
|
||||
if(err) goto exit;
|
||||
Parser_input_char(p, c);
|
||||
}
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int hexdone(Parser *p){
|
||||
int err = 0;
|
||||
char d = (char)(p->state->ival & 0xff);
|
||||
err = Parser_pop(p);
|
||||
if(err) goto exit;
|
||||
err = Parser_input_char(p, d);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int hexdigit(Parser *p, char c, char d){
|
||||
int err = 0;
|
||||
p->state->ival *= 16;
|
||||
p->state->ival += c - d;
|
||||
p->state->count++;
|
||||
if(err) goto exit;
|
||||
if(p->state->ival < 0 || p->state->ival > 0xff){
|
||||
parse_error(p);
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
if(p->state->count == 2){
|
||||
err = hexdone(p);
|
||||
}
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int state_hex(Parser *p, char c){
|
||||
int err = 0;
|
||||
if(at_eof(p)){
|
||||
parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
} else if('0' <= c && c <= '9'){
|
||||
err = hexdigit(p, c, '0');
|
||||
} else if('A' <= c && c <= 'F'){
|
||||
err = hexdigit(p, c, 'A');
|
||||
} else if('a' <= c && c <= 'f'){
|
||||
err = hexdigit(p, c, 'a');
|
||||
} else if(p->state->count){
|
||||
err =hexdone(p);
|
||||
if(err) goto exit;
|
||||
Parser_input_char(p, c);
|
||||
}
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int begin_atom(Parser *p, char c){
|
||||
int err = 0;
|
||||
err = Parser_push(p, state_atom);
|
||||
if(err) goto exit;
|
||||
newtoken(p);
|
||||
err = savechar(p, c);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int state_atom(Parser *p, char c){
|
||||
int err = 0;
|
||||
if(at_eof(p)){
|
||||
err = end_atom(p);
|
||||
} else if(is_separator(p, c) ||
|
||||
in_space_class(c) ||
|
||||
in_comment_class(c)){
|
||||
err = end_atom(p);
|
||||
if(err) goto exit;
|
||||
err = Parser_input_char(p, c);
|
||||
} else {
|
||||
err = savechar(p, c);
|
||||
}
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int end_atom(Parser *p){
|
||||
int err = 0;
|
||||
err = do_intern(p);
|
||||
if(err) goto exit;
|
||||
err = Parser_return(p);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int state_list(Parser *p, char c){
|
||||
int err = 0;
|
||||
if(at_eof(p)){
|
||||
parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
|
||||
err = -EINVAL;
|
||||
} else if(c == c_list_close){
|
||||
p->state->val = nrev(p->state->val);
|
||||
err = end_list(p);
|
||||
} else {
|
||||
err = state_start(p, c);
|
||||
}
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
int begin_list(Parser *p, char c){
|
||||
return Parser_push(p, state_list);
|
||||
}
|
||||
|
||||
int end_list(Parser *p){
|
||||
return Parser_return(p);
|
||||
}
|
||||
|
||||
/** Reset the fields of a parser to initial values.
|
||||
*
|
||||
* @param z parser
|
||||
*/
|
||||
static void reset(Parser *z){
|
||||
IOStream *error_out = z->error_out;
|
||||
int flags = z->flags;
|
||||
zero(z, sizeof(Parser));
|
||||
z->buf_n = sizeof(z->buf) - 1;
|
||||
z->buf_i = 0;
|
||||
z->line_no = 1;
|
||||
z->char_no = 0;
|
||||
z->error_out = error_out;
|
||||
z->flags = flags;
|
||||
}
|
||||
|
||||
/** Set the parser error stream.
|
||||
* Parse errors are reported on the the error stream if it is non-null.
|
||||
*
|
||||
* @param z parser
|
||||
* @param error_out error stream
|
||||
*/
|
||||
void set_error_stream(Parser *z, IOStream *error_out){
|
||||
if(z){
|
||||
z->error_out = error_out;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the parser error message for an error code.
|
||||
*
|
||||
* @param id error code
|
||||
* @return error message (empty string if the code is unknown)
|
||||
*/
|
||||
static char *get_message(ParseErrorId id){
|
||||
int i;
|
||||
for(i=0; i<catalog_n; i++){
|
||||
if(id == catalog[i].id){
|
||||
return catalog[i].message;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/** Get the line number.
|
||||
*
|
||||
* @param in parser
|
||||
*/
|
||||
int get_line(Parser *in){
|
||||
return in->line_no;
|
||||
}
|
||||
|
||||
/** Get the column number.
|
||||
*
|
||||
* @param in parser
|
||||
*/
|
||||
int get_column(Parser *in){
|
||||
return in->char_no;
|
||||
}
|
||||
|
||||
/** Get the line number the current token started on.
|
||||
*
|
||||
* @param in parser
|
||||
*/
|
||||
int get_tok_line(Parser *in){
|
||||
return in->tok_begin_line;
|
||||
}
|
||||
|
||||
/** Get the column number the current token started on.
|
||||
*
|
||||
* @param in parser
|
||||
*/
|
||||
int get_tok_column(Parser *in){
|
||||
return in->tok_begin_char;
|
||||
}
|
||||
|
||||
/** Report a parse error.
|
||||
* Does nothing if the error stream is null or there is no error.
|
||||
*
|
||||
* @param in parser
|
||||
*/
|
||||
static void report_error(Parser *in){
|
||||
if(in->error_out && in->err){
|
||||
char *msg = get_message(in->err);
|
||||
char *tok = peek_token(in);
|
||||
IOStream_print(in->error_out, PARSE_ERR_FMT,
|
||||
get_tok_line(in), get_tok_column(in), msg);
|
||||
if(tok && tok[0]){
|
||||
IOStream_print(in->error_out, " '%s'", tok);
|
||||
}
|
||||
IOStream_print(in->error_out, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the error message for the current parse error code.
|
||||
* Does nothing if there is no error.
|
||||
*
|
||||
* @param in parser
|
||||
* @param buf where to place the message
|
||||
* @param n maximum number of characters to place in buf
|
||||
* @return current error code (zero for no error)
|
||||
*/
|
||||
int parse_error_message(Parser *in, char *buf, int n){
|
||||
if(in->err){
|
||||
char *msg = get_message(in->err);
|
||||
snprintf(buf, n, PARSE_ERR_FMT, get_tok_line(in), get_tok_column(in), msg);
|
||||
}
|
||||
return in->err;
|
||||
}
|
||||
|
||||
/** Flag an unspecified parse error. All subsequent reads will fail.
|
||||
*
|
||||
* @param in parser
|
||||
*/
|
||||
void parse_error(Parser *in){
|
||||
parse_error_id(in, PARSE_ERR_INVALID_SYNTAX);
|
||||
}
|
||||
|
||||
/** Flag a parse error. All subsequent reads will fail.
|
||||
* Does not change the parser error code if it is already set.
|
||||
*
|
||||
* @param in parser
|
||||
* @param id error code
|
||||
*/
|
||||
void parse_error_id(Parser *in, ParseErrorId id){
|
||||
if(!in->err){
|
||||
in->err = id;
|
||||
report_error(in);
|
||||
}
|
||||
}
|
||||
|
||||
/** Test if the parser's error flag is set.
|
||||
*
|
||||
* @param in parser
|
||||
* @return 1 if set, 0 otherwise
|
||||
*/
|
||||
int has_error(Parser *in){
|
||||
return (in->err > 0);
|
||||
}
|
||||
|
||||
/** Test if the parser is at end of input.
|
||||
*
|
||||
* @param in parser
|
||||
* @return 1 if at EOF, 0 otherwise
|
||||
*/
|
||||
int at_eof(Parser *p){
|
||||
return p->eof;
|
||||
}
|
||||
|
||||
#ifdef SXPR_PARSER_MAIN
|
||||
/* Stuff for standalone testing. */
|
||||
|
||||
#include "file_stream.h"
|
||||
#include "string_stream.h"
|
||||
|
||||
int stringof(Sxpr exp, char **s){
|
||||
int err = 0;
|
||||
if(ATOMP(exp)){
|
||||
*s = atom_name(exp);
|
||||
} else if(STRINGP(exp)){
|
||||
*s = string_string(exp);
|
||||
} else {
|
||||
err = -EINVAL;
|
||||
*s = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int child_string(Sxpr exp, Sxpr key, char **s){
|
||||
int err = 0;
|
||||
Sxpr val = sxpr_child_value(exp, key, ONONE);
|
||||
err = stringof(val, s);
|
||||
return err;
|
||||
}
|
||||
|
||||
int intof(Sxpr exp, int *v){
|
||||
int err = 0;
|
||||
char *s;
|
||||
unsigned long l;
|
||||
if(INTP(exp)){
|
||||
*v = OBJ_INT(exp);
|
||||
} else {
|
||||
err = stringof(exp, &s);
|
||||
if(err) goto exit;
|
||||
err = convert_atoul(s, &l);
|
||||
*v = (int)l;
|
||||
}
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int child_int(Sxpr exp, Sxpr key, int *v){
|
||||
int err = 0;
|
||||
Sxpr val = sxpr_child_value(exp, key, ONONE);
|
||||
err = intof(val, v);
|
||||
return err;
|
||||
}
|
||||
|
||||
int eval_vnet(Sxpr exp){
|
||||
int err = 0;
|
||||
Sxpr oid = intern("id");
|
||||
int id;
|
||||
err = child_int(exp, oid, &id);
|
||||
if(err) goto exit;
|
||||
dprintf("> vnet id=%d\n", id);
|
||||
exit:
|
||||
dprintf("< err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int eval_connect(Sxpr exp){
|
||||
int err = 0;
|
||||
Sxpr ovif = intern("vif");
|
||||
Sxpr ovnet = intern("vnet");
|
||||
char *vif;
|
||||
int vnet;
|
||||
|
||||
err = child_string(exp, ovif, &vif);
|
||||
if(err) goto exit;
|
||||
err = child_int(exp, ovnet, &vnet);
|
||||
if(err) goto exit;
|
||||
dprintf("> connect vif=%s vnet=%d\n", vif, vnet);
|
||||
exit:
|
||||
dprintf("< err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int eval(Sxpr exp){
|
||||
int err = 0;
|
||||
Sxpr oconnect = intern("connect");
|
||||
Sxpr ovnet = intern("vnet");
|
||||
|
||||
if(sxpr_elementp(exp, ovnet)){
|
||||
err = eval_vnet(exp);
|
||||
} else if(sxpr_elementp(exp, oconnect)){
|
||||
err = eval_connect(exp);
|
||||
} else {
|
||||
err = -EINVAL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Main program for testing.
|
||||
* Parses input and prints it.
|
||||
*
|
||||
* @param argc number of arguments
|
||||
* @param argv arguments
|
||||
* @return error code
|
||||
*/
|
||||
int main(int argc, char *argv[]){
|
||||
Parser *pin;
|
||||
int err = 0;
|
||||
char buf[1024];
|
||||
int k;
|
||||
Sxpr obj, l, x;
|
||||
|
||||
pin = Parser_new();
|
||||
set_error_stream(pin, iostdout);
|
||||
dprintf("> parse...\n");
|
||||
while(1){
|
||||
k = fread(buf, 1, 1024, stdin);
|
||||
err = Parser_input(pin, buf, k);
|
||||
dprintf("> Parser_input=%d\n", err);
|
||||
if(k <= 0) break;
|
||||
}
|
||||
obj = pin->val;
|
||||
for(l = obj ; CONSP(l); l = CDR(l)){
|
||||
x = CAR(l);
|
||||
objprint(iostdout, x, 0); printf("\n");
|
||||
eval(x);
|
||||
}
|
||||
dprintf("> err=%d\n", err);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of the
|
||||
* License, or (at your option) any later version. This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _XEN_LIB_SXPR_PARSER_H_
|
||||
#define _XEN_LIB_SXPR_PARSER_H_
|
||||
|
||||
#include "sxpr.h"
|
||||
#include "iostream.h"
|
||||
|
||||
/** @file
|
||||
* Sxpr parsing definitions.
|
||||
*/
|
||||
|
||||
/** Size of a parser input buffer.
|
||||
* Tokens read must fit into this size (including trailing null).
|
||||
*/
|
||||
#define PARSER_BUF_SIZE 1024
|
||||
|
||||
struct Parser;
|
||||
typedef int ParserStateFn(struct Parser *, char c);
|
||||
|
||||
typedef struct ParserState {
|
||||
struct ParserState *parent;
|
||||
Sxpr val;
|
||||
int ival;
|
||||
int count;
|
||||
char delim;
|
||||
ParserStateFn *fn;
|
||||
} ParserState;
|
||||
|
||||
/** Structure representing an input source for the parser.
|
||||
* Can read from any IOStream implementation.
|
||||
*/
|
||||
typedef struct Parser {
|
||||
Sxpr val;
|
||||
/** Error reporting stream (null for no reports). */
|
||||
IOStream *error_out;
|
||||
int eof;
|
||||
/** Error flag. Non-zero if there has been a read error. */
|
||||
int err;
|
||||
/** Line number on input (from 1). */
|
||||
int line_no;
|
||||
/** Column number of input (reset on new line). */
|
||||
int char_no;
|
||||
/** Lookahead character. */
|
||||
char c;
|
||||
/** Buffer for reading tokens. */
|
||||
char buf[PARSER_BUF_SIZE];
|
||||
/** Size of token buffer. */
|
||||
int buf_n;
|
||||
int buf_i;
|
||||
/** Line the last token started on. */
|
||||
int tok_begin_line;
|
||||
/** Character number the last token started on. */
|
||||
int tok_begin_char;
|
||||
/** Parsing flags. */
|
||||
int flags;
|
||||
ParserState *state;
|
||||
} Parser;
|
||||
|
||||
/** Parser error codes. */
|
||||
typedef enum {
|
||||
PARSE_ERR_NONE=0,
|
||||
PARSE_ERR_UNSPECIFIED,
|
||||
PARSE_ERR_NOMEM,
|
||||
PARSE_ERR_UNEXPECTED_EOF,
|
||||
PARSE_ERR_TOKEN_TOO_LONG,
|
||||
PARSE_ERR_INVALID_SYNTAX,
|
||||
PARSE_ERR_INVALID_ESCAPE,
|
||||
} ParseErrorId;
|
||||
|
||||
|
||||
/** Parser flags. */
|
||||
//enum {
|
||||
//};
|
||||
|
||||
/** Raise some parser flags.
|
||||
*
|
||||
* @param in parser
|
||||
* @param flags flags mask
|
||||
*/
|
||||
inline static void parser_flags_raise(Parser *in, int flags){
|
||||
in->flags |= flags;
|
||||
}
|
||||
|
||||
/** Lower some parser flags.
|
||||
*
|
||||
* @param in parser
|
||||
* @param flags flags mask
|
||||
*/
|
||||
inline static void parser_flags_lower(Parser *in, int flags){
|
||||
in->flags &= ~flags;
|
||||
}
|
||||
|
||||
/** Clear all parser flags.
|
||||
*
|
||||
* @param in parser
|
||||
*/
|
||||
inline static void parser_flags_clear(Parser *in){
|
||||
in->flags = 0;
|
||||
}
|
||||
|
||||
extern void Parser_free(Parser *z);
|
||||
extern Parser * Parser_new(void);
|
||||
extern int Parser_input(Parser *p, char *buf, int buf_n);
|
||||
extern int Parser_input_eof(Parser *p);
|
||||
|
||||
extern int parse_error_message(Parser *in, char *buf, int n);
|
||||
extern int has_error(Parser *in);
|
||||
extern int at_eof(Parser *in);
|
||||
|
||||
#endif /* ! _XEN_LIB_SXPR_PARSER_H_ */
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef _XENO_SYS_CTYPE_H_
|
||||
#define _XENO_SYS_CTYPE_H_
|
||||
/** @file
|
||||
** Replacement for ctype include that can be used
|
||||
* from user or kernel code.
|
||||
*/
|
||||
#ifdef __KERNEL__
|
||||
# include <linux/ctype.h>
|
||||
#else
|
||||
# include <ctype.h>
|
||||
#endif
|
||||
#endif /* ! _XENO_SYS_CTYPE_H_ */
|
||||
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of the
|
||||
* License, or (at your option) any later version. This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "sys_net.h"
|
||||
#include "sys_string.h"
|
||||
|
||||
/** @file
|
||||
* All network data are kept in network order and only converted to
|
||||
* host order for display. Network data includes IP addresses, port numbers and
|
||||
* network masks.
|
||||
*/
|
||||
|
||||
/** Maximum value for a port. */
|
||||
#define PORT_MAX 0xffff
|
||||
|
||||
/** Convert a number of bits to a network mask
|
||||
* for IP addresses. The number of bits must
|
||||
* be in the range 1-31.
|
||||
*
|
||||
* @param n number of bits to set in the mask
|
||||
* @return value with n high bits set (in network order)
|
||||
*/
|
||||
unsigned long bits_to_mask(int n){
|
||||
unsigned long mask = (n ? (1 << 31) : 0);
|
||||
int i;
|
||||
for(i=1; i<n; i++){
|
||||
mask |= (mask >> 1);
|
||||
}
|
||||
return htonl(mask);
|
||||
}
|
||||
|
||||
/** Convert a network mask to a number of bits.
|
||||
*
|
||||
* @param mask network mask in network order
|
||||
* @return number of bits in mask
|
||||
*/
|
||||
int mask_to_bits(unsigned long mask){
|
||||
// Start with n set to the number of bits in the mask. Then reduce n by
|
||||
// the number of low zero bits in the mask.
|
||||
int n = 32;
|
||||
for(mask = ntohl(mask);
|
||||
(mask & 1)==0 && n>0;
|
||||
mask >>= 1){
|
||||
n--;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/** Get the index of the first occurrence of a character in a string.
|
||||
* Stops at end of string or after n characters.
|
||||
*
|
||||
* @param s input string
|
||||
* @param n maximum number of charactes to search
|
||||
* @param c character to look for
|
||||
* @return index of first occurrence, -1 if not found
|
||||
*/
|
||||
inline static int indexof(const char *s, int n, char c){
|
||||
int i;
|
||||
for(i=0; i<n && *s; i++, s++){
|
||||
if(*s == c) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Convert an IPv4 address in dot notation into an unsigned long (in network order).
|
||||
*
|
||||
* @param s input string
|
||||
* @param address where to put the address
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int get_inet_addr(const char *s, unsigned long *address){
|
||||
// Number of bits in a byte.
|
||||
const int BYTE_BITS = 8;
|
||||
// Number of bytes in a word.
|
||||
const int WORD_BYTES = 4;
|
||||
// Max value for a component of an address.
|
||||
const int ADDR_MAX = 255;
|
||||
// Separator for components of an address.
|
||||
const char dot = '.';
|
||||
|
||||
int n;
|
||||
unsigned long addr = 0;
|
||||
unsigned long v;
|
||||
int i;
|
||||
int err = -1;
|
||||
// Bit shift for the current byte.
|
||||
int shift = BYTE_BITS * (WORD_BYTES - 1);
|
||||
char buf[64];
|
||||
|
||||
n = strlen(s);
|
||||
if(n >= sizeof(buf)){
|
||||
goto exit;
|
||||
}
|
||||
for(i=0; i < WORD_BYTES; i++){
|
||||
int idx = indexof(s, n, dot);
|
||||
idx = (idx < 0 ? strlen(s) : idx);
|
||||
strncpy(buf, s, idx); buf[idx]='\0';
|
||||
if(convert_atoul(buf, &v)){
|
||||
goto exit;
|
||||
}
|
||||
if(v < 0 || v > ADDR_MAX){
|
||||
goto exit;
|
||||
}
|
||||
addr |= (v << shift);
|
||||
if(idx == n) break;
|
||||
shift -= BYTE_BITS;
|
||||
s += idx+1;
|
||||
}
|
||||
err = 0;
|
||||
exit:
|
||||
addr = htonl(addr);
|
||||
*address = (err ? 0 : addr);
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/** Convert an address in network order to IPv4 dot notation.
|
||||
* The return value is a static buffer which is overwritten on each call.
|
||||
*
|
||||
* @param inaddr address (in network order)
|
||||
* @return address in dot notation
|
||||
*/
|
||||
char *inet_ntoa(struct in_addr inaddr){
|
||||
static char address[16] = {};
|
||||
uint32_t addr = ntohl(inaddr.s_addr);
|
||||
snprintf(address, sizeof(address), "%d.%d.%d.%d",
|
||||
(unsigned)((addr >> 24) & 0xff),
|
||||
(unsigned)((addr >> 16) & 0xff),
|
||||
(unsigned)((addr >> 8) & 0xff),
|
||||
(unsigned)((addr ) & 0xff));
|
||||
return address;
|
||||
}
|
||||
|
||||
|
||||
/** Convert a string in IPv4 dot notation to an int in network order.
|
||||
*
|
||||
* @param address address in dot notation
|
||||
* @param inp result of conversion (in network order)
|
||||
* @return 0 on success, error code on error
|
||||
*/
|
||||
int inet_aton(const char *address, struct in_addr *inp){
|
||||
int err = 0;
|
||||
unsigned long addr;
|
||||
|
||||
err = get_inet_addr(address, &addr);
|
||||
if(err) goto exit;
|
||||
inp->s_addr = addr;
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Convert a hostname or IPv4 address string to an address in network order.
|
||||
*
|
||||
* @param name input hostname or address string
|
||||
* @param address where to put the address
|
||||
* @return 1 if address found OK, 0 otherwise
|
||||
*/
|
||||
int get_host_address(const char *name, unsigned long *address){
|
||||
#ifdef __KERNEL__
|
||||
return get_inet_addr(name, address) == 0;
|
||||
#else
|
||||
struct hostent *host = gethostbyname(name);
|
||||
if(!host){
|
||||
return 0;
|
||||
}
|
||||
*address = ((struct in_addr *)(host->h_addr))->s_addr;
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Convert a service name to a port (in network order).
|
||||
*
|
||||
* @param name service name
|
||||
* @param port where to put the port
|
||||
* @return 1 if service port found OK, 0 otherwise
|
||||
*/
|
||||
int get_service_port(const char *name, unsigned long *port){
|
||||
#ifdef __KERNEL__
|
||||
return 0;
|
||||
#else
|
||||
struct servent *service;
|
||||
service = getservbyname(name, 0);
|
||||
if(!service){
|
||||
return 0;
|
||||
}
|
||||
*port = service->s_port;
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Convert a port number (in network order) to a service name.
|
||||
*
|
||||
* @param port the port number
|
||||
* @return service name if found OK, 0 otherwise
|
||||
*/
|
||||
char *get_port_service(unsigned long port){
|
||||
#ifdef __KERNEL__
|
||||
return 0;
|
||||
#else
|
||||
struct servent *service = getservbyport(port, 0);
|
||||
return (service ? service->s_name : 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Convert a decimal integer or service name to a port (in network order).
|
||||
*
|
||||
* @param s input to convert
|
||||
* @param port where to put the port
|
||||
* @return 1 if port found OK, 0 otherwise
|
||||
*/
|
||||
int convert_service_to_port(const char *s, unsigned long *port){
|
||||
int ok = 0;
|
||||
unsigned long value;
|
||||
if(convert_atoul(s, &value)){
|
||||
ok = get_service_port(s, &value);
|
||||
} else {
|
||||
ok = (0 <= value) && (value <= PORT_MAX);
|
||||
value = htons((unsigned short)value);
|
||||
}
|
||||
*port = (ok ? value : 0);
|
||||
return ok;
|
||||
}
|
||||
|
||||
#define MAC_ELEMENT_N 6 // Number of elements in a MAC address.
|
||||
#define MAC_DIGIT_N 2 // Number of digits in an element in a MAC address.
|
||||
#define MAC_LENGTH 17 //((MAC_ELEMENT_N * MAC_DIGIT_N) + MAC_ELEMENT_N - 1)
|
||||
|
||||
/** Convert a mac address from a string of the form
|
||||
* XX:XX:XX:XX:XX:XX to numerical form (an array of 6 unsigned chars).
|
||||
* Each X denotes a hex digit: 0..9, a..f, A..F.
|
||||
* Also supports using '-' as the separator instead of ':'.
|
||||
*
|
||||
* @param mac_in string to convert
|
||||
* @param mac destination for the value
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int mac_aton(const char *mac_in, unsigned char *mac){
|
||||
int err = 0;
|
||||
int i, j;
|
||||
const char *p;
|
||||
char sep = 0;
|
||||
unsigned char d;
|
||||
if(!mac_in || strlen(mac_in) != MAC_LENGTH){
|
||||
err = -1;
|
||||
goto exit;
|
||||
}
|
||||
for(i = 0, p = mac_in; i < MAC_ELEMENT_N; i++){
|
||||
d = 0;
|
||||
if(i){
|
||||
if(!sep){
|
||||
if(*p == ':' || *p == '-') sep = *p;
|
||||
}
|
||||
if(sep && *p == sep){
|
||||
p++;
|
||||
} else {
|
||||
err = -1;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
for(j = 0; j < MAC_DIGIT_N; j++, p++){
|
||||
if(j) d <<= 4;
|
||||
if(*p >= '0' && *p <= '9'){
|
||||
d += (*p - '0');
|
||||
} else if(*p >= 'A' && *p <= 'F'){
|
||||
d += (*p - 'A') + 10;
|
||||
} else if(*p >= 'a' && *p <= 'f'){
|
||||
d += (*p - 'a') + 10;
|
||||
} else {
|
||||
err = -1;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
mac[i] = d;
|
||||
}
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Convert a MAC address from numerical form to a string.
|
||||
*
|
||||
* @param mac address to convert
|
||||
* @return static string value
|
||||
*/
|
||||
char *mac_ntoa(const unsigned char *mac){
|
||||
static char buf[MAC_LENGTH + 1];
|
||||
int buf_n = sizeof(buf);
|
||||
|
||||
memset(buf, buf_n, 0);
|
||||
snprintf(buf, buf_n, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
mac[0], mac[1], mac[2],
|
||||
mac[3], mac[4], mac[5]);
|
||||
buf[buf_n - 1] = '\0';
|
||||
return buf;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _XEN_LIB_SYS_NET_H_
|
||||
#define _XEN_LIB_SYS_NET_H_
|
||||
/** @file
|
||||
*
|
||||
* Replacement for standard network includes.
|
||||
* Works in user or kernel code.
|
||||
*/
|
||||
|
||||
extern int get_inet_addr(const char *s, unsigned long *address);
|
||||
extern unsigned long bits_to_mask(int n);
|
||||
extern int mask_to_bits(unsigned long mask);
|
||||
extern int get_host_address(const char *name, unsigned long *address);
|
||||
extern int get_service_port(const char *name, unsigned long *port);
|
||||
extern char *get_port_service(unsigned long port);
|
||||
extern int convert_service_to_port(const char *s, unsigned long *port);
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#ifndef htonl
|
||||
#define htonl(x) __constant_htonl(x)
|
||||
#endif
|
||||
|
||||
#ifndef ntohl
|
||||
#define ntohl(x) __constant_ntohl(x)
|
||||
#endif
|
||||
|
||||
#ifndef htons
|
||||
#define htons(x) __constant_htons(x)
|
||||
#endif
|
||||
|
||||
#ifndef ntohs
|
||||
#define ntohs(x) __constant_ntohs(x)
|
||||
#endif
|
||||
|
||||
#include <linux/in.h>
|
||||
extern char *inet_ntoa(struct in_addr inaddr);
|
||||
extern int inet_aton(const char *address, struct in_addr *inp);
|
||||
|
||||
#else
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#endif
|
||||
|
||||
extern char *mac_ntoa(const unsigned char *macaddr);
|
||||
extern int mac_aton(const char *addr, unsigned char *macaddr);
|
||||
|
||||
#endif /* !_SP_SYS_NET_H_ */
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef __KERNEL__
|
||||
# include <linux/config.h>
|
||||
# include <linux/module.h>
|
||||
# include <linux/kernel.h>
|
||||
# include <linux/errno.h>
|
||||
#else
|
||||
# include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "allocate.h"
|
||||
#include "sys_string.h"
|
||||
|
||||
/** Set the base to use for converting a string to a number. Base is
|
||||
* hex if starts with 0x, otherwise decimal.
|
||||
*
|
||||
* @param s input string
|
||||
* @param base where to put the base
|
||||
* @return rest of s to parse as a number
|
||||
*/
|
||||
inline static const char * convert_set_base(const char *s, int *base){
|
||||
*base = 10;
|
||||
if(s){
|
||||
if(*s=='0'){
|
||||
s++;
|
||||
if(*s=='x' || *s=='X'){
|
||||
*base = 16;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/** Get the numerical value of a digit in the given base.
|
||||
*
|
||||
* @param c digit character
|
||||
* @param base to use
|
||||
* @return numerical value of digit in range 0..base-1 or
|
||||
* -1 if not in range for the base
|
||||
*/
|
||||
inline static int convert_get_digit(char c, int base){
|
||||
int d;
|
||||
|
||||
if('0'<=c && c<='9'){
|
||||
d = c - '0';
|
||||
} else if('a'<=c && c<='f'){
|
||||
d = c - 'a' + 10;
|
||||
} else if('A'<=c && c<='F'){
|
||||
d = c - 'A' + 10;
|
||||
} else {
|
||||
d = -1;
|
||||
}
|
||||
return (d < base ? d : -1);
|
||||
}
|
||||
|
||||
/** Convert a string to an unsigned long by parsing it as a number.
|
||||
* Will accept hex or decimal in usual C syntax.
|
||||
*
|
||||
* @param str input string
|
||||
* @param val where to put the result
|
||||
* @return 0 if converted OK, negative otherwise
|
||||
*/
|
||||
int convert_atoul(const char *str, unsigned long *val){
|
||||
int err = 0;
|
||||
unsigned long v = 0;
|
||||
int base;
|
||||
const char *s = str;
|
||||
|
||||
if(!s) {
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
s = convert_set_base(s, &base);
|
||||
for( ; !err && *s; s++){
|
||||
int digit = convert_get_digit(*s, base);
|
||||
if(digit<0){
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
v *= base;
|
||||
v += digit;
|
||||
}
|
||||
exit:
|
||||
*val = (err ? 0 : v);
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Combine a directory path with a relative path to produce
|
||||
* a new path.
|
||||
*
|
||||
* @param s directory path
|
||||
* @param t relative path
|
||||
* @return new combined path s/t
|
||||
*/
|
||||
int path_concat(char *s, char *t, char **val){
|
||||
int err = 0;
|
||||
int sn, tn, vn;
|
||||
char *v;
|
||||
sn = strlen(s);
|
||||
if(sn > 0 && s[sn-1] == '/'){
|
||||
sn--;
|
||||
}
|
||||
tn = strlen(t);
|
||||
if(tn > 0 && t[0] == '/'){
|
||||
tn--;
|
||||
}
|
||||
vn = sn+tn+1;
|
||||
v = (char*)allocate(vn+1);
|
||||
if(!v){
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
strncpy(v, s, sn);
|
||||
v[sn] = '/';
|
||||
strncpy(v+sn+1, t, tn);
|
||||
v[vn] = '\0';
|
||||
exit:
|
||||
*val = (err ? NULL : v);
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _XEN_LIB_SYS_STRING_H_
|
||||
#define _XEN_LIB_SYS_STRING_H_
|
||||
/** @file
|
||||
* Replacement for standard string includes.
|
||||
* Works in user or kernel code.
|
||||
*/
|
||||
/*============================================================================*/
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <stdarg.h>
|
||||
#include "allocate.h"
|
||||
|
||||
#if 0
|
||||
static inline int tolower(int c){
|
||||
return (c>='A' && c<='Z' ? (c-'A')+'a' : c);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int isalpha(int c){
|
||||
return (c>='A' && c<='Z') || (c>='a' && c<='z');
|
||||
}
|
||||
|
||||
static inline int isdigit(int c){
|
||||
return (c>='0' && c<='9');
|
||||
}
|
||||
|
||||
#if 0
|
||||
static inline int strcasecmp(const char *s1, const char *s2){
|
||||
int c1, c2;
|
||||
|
||||
do {
|
||||
c1 = tolower(*s1++);
|
||||
c2 = tolower(*s2++);
|
||||
} while (c1 && c1 == c2);
|
||||
return c1 - c2;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline char * strdup(const char *s){
|
||||
int n = (s ? 1+strlen(s) : 0);
|
||||
char *copy = (n ? allocate(n) : NULL);
|
||||
if(copy){
|
||||
strcpy(copy, s);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
#else
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
static inline size_t strnlen(const char *s, size_t n){
|
||||
int k = 0;
|
||||
if(s){
|
||||
for(k=0; *s && k<n; s++, k++){}
|
||||
}
|
||||
return k;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/*============================================================================*/
|
||||
|
||||
extern int convert_atoul(const char *s, unsigned long *v);
|
||||
extern int path_concat(char *s, char *t, char **val);
|
||||
|
||||
#endif /* !_XEN_LIB_SYS_STRING_H_ */
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
/* $Id: xdr.c,v 1.3 2003/09/29 13:40:00 mjw Exp $ */
|
||||
#include "xdr.h"
|
||||
#include <errno.h>
|
||||
/** @file
|
||||
* XDR packer/unpacker for elements.
|
||||
*
|
||||
* string -> [T_STRING] [len:u32] <len bytes>
|
||||
* atom -> [T_ATOM] [len:u32] <len bytes>
|
||||
* uint -> [T_UINT] [value]
|
||||
* cons -> [T_CONS] <car> <cdr>
|
||||
* null -> [T_NULL]
|
||||
* none -> [T_NONE]
|
||||
* bool -> [T_BOOL] { 0:u8 | 1:u8 }
|
||||
*
|
||||
* types packed as u16.
|
||||
*
|
||||
* So (a b c) -> [T_CONS] a [T_CONS] b [T_CONS] c [T_NULL]
|
||||
* () -> [T_NULL]
|
||||
*/
|
||||
|
||||
int pack_bool(IOStream *io, int x){
|
||||
int err=0;
|
||||
err = IOStream_print(io, "%c", 0xff & x);
|
||||
if(err > 0) err = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
int unpack_bool(IOStream *io, int *x){
|
||||
int err = 0;
|
||||
int c;
|
||||
c = IOStream_getc(io);
|
||||
*x = (c < 0 ? 0 : c);
|
||||
err = IOStream_error(io);
|
||||
if(c < 0 && !err) err = -EIO;
|
||||
return err;
|
||||
}
|
||||
|
||||
int pack_ushort(IOStream *io, unsigned short x){
|
||||
int err=0;
|
||||
err = IOStream_print(io, "%c%c",
|
||||
0xff & (x >> 8),
|
||||
0xff & (x ));
|
||||
if(err > 0) err = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
int unpack_ushort(IOStream *io, unsigned short *x){
|
||||
int err = 0;
|
||||
int i, c = 0;
|
||||
*x = 0;
|
||||
for(i = 0; i< 2; i++){
|
||||
c = IOStream_getc(io);
|
||||
if(c < 0) break;
|
||||
*x <<= 8;
|
||||
*x |= (0xff & c);
|
||||
}
|
||||
err = IOStream_error(io);
|
||||
if(c < 0 && !err) err = -EIO;
|
||||
return err;
|
||||
}
|
||||
|
||||
int pack_uint(IOStream *io, unsigned int x){
|
||||
int err=0;
|
||||
err = IOStream_print(io, "%c%c%c%c",
|
||||
0xff & (x >> 24),
|
||||
0xff & (x >> 16),
|
||||
0xff & (x >> 8),
|
||||
0xff & (x ));
|
||||
if(err > 0) err = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
int unpack_uint(IOStream *io, unsigned int *x){
|
||||
int err = 0;
|
||||
int i, c = 0;
|
||||
*x = 0;
|
||||
for(i = 0; i< 4; i++){
|
||||
c = IOStream_getc(io);
|
||||
if(c < 0) break;
|
||||
*x <<= 8;
|
||||
*x |= (0xff & c);
|
||||
}
|
||||
err = IOStream_error(io);
|
||||
if(c < 0 && !err) err = -EIO;
|
||||
return err;
|
||||
}
|
||||
|
||||
int pack_string(IOStream *io, Sxpr x){
|
||||
int err = 0;
|
||||
int n = string_length(x);
|
||||
char *s = string_string(x);
|
||||
int i;
|
||||
err = pack_uint(io, n);
|
||||
if(err) goto exit;
|
||||
for(i = 0; i < n; i++){
|
||||
err = IOStream_print(io, "%c", s[i]);
|
||||
if(err < 0) break;
|
||||
}
|
||||
if(err > 0) err = 0;
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int unpack_string(IOStream *io, Sxpr *x){
|
||||
int err;
|
||||
unsigned int n;
|
||||
int i, c = 0;
|
||||
char *s;
|
||||
Sxpr val = ONONE;
|
||||
|
||||
err = unpack_uint(io, &n);
|
||||
if(err) goto exit;
|
||||
val = halloc(n+1, T_STRING);
|
||||
if(NOMEMP(val)){
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
s = string_string(val);
|
||||
for(i=0; i<n; i++){
|
||||
c = IOStream_getc(io);
|
||||
if(c < 0) break;
|
||||
s[i] = (char)c;
|
||||
}
|
||||
s[n] = '\0';
|
||||
exit:
|
||||
err = IOStream_error(io);
|
||||
if(c < 0 && !err) err = -EIO;
|
||||
if(err){
|
||||
objfree(val);
|
||||
val = ONONE;
|
||||
}
|
||||
*x = val;
|
||||
return err;
|
||||
}
|
||||
|
||||
int pack_cons(IOStream *io, Sxpr x){
|
||||
int err = 0;
|
||||
err = pack_sxpr(io, CAR(x));
|
||||
if(err) goto exit;
|
||||
err = pack_sxpr(io, CDR(x));
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int unpack_cons(IOStream *io, Sxpr *x){
|
||||
int err = 0;
|
||||
Sxpr u = ONONE, v = ONONE, val = ONONE;
|
||||
err = unpack_sxpr(io, &u);
|
||||
if(err) goto exit;
|
||||
err = unpack_sxpr(io, &v);
|
||||
if(err) goto exit;
|
||||
val = cons_new(u, v);
|
||||
if(NOMEMP(val)){
|
||||
err = -ENOMEM;
|
||||
}
|
||||
exit:
|
||||
if(err){
|
||||
objfree(u);
|
||||
objfree(v);
|
||||
val = ONONE;
|
||||
}
|
||||
*x = val;
|
||||
return err;
|
||||
}
|
||||
|
||||
int pack_sxpr(IOStream *io, Sxpr x){
|
||||
int err = 0;
|
||||
unsigned short type = get_type(x);
|
||||
err = pack_ushort(io, type);
|
||||
if(err) goto exit;
|
||||
switch(type){
|
||||
case T_NULL:
|
||||
break;
|
||||
case T_NONE:
|
||||
break;
|
||||
break;
|
||||
case T_BOOL:
|
||||
err = pack_bool(io, get_ul(x));
|
||||
break;
|
||||
case T_CONS:
|
||||
err = pack_cons(io, x);
|
||||
break;
|
||||
case T_ATOM:
|
||||
err = pack_string(io, OBJ_ATOM(x)->name);
|
||||
break;
|
||||
case T_STRING:
|
||||
err = pack_string(io, x);
|
||||
break;
|
||||
case T_UINT:
|
||||
err = pack_uint(io, get_ul(x));
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
IOStream_print(iostderr, "%s> invalid type %d\n", __FUNCTION__, type);
|
||||
break;
|
||||
}
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int unpack_sxpr(IOStream *io, Sxpr *x){
|
||||
int err = 0;
|
||||
unsigned short type;
|
||||
unsigned int u;
|
||||
Sxpr val = ONONE, y;
|
||||
|
||||
err = unpack_ushort(io, &type);
|
||||
if(err) goto exit;
|
||||
switch(type){
|
||||
case T_NULL:
|
||||
val = ONULL;
|
||||
break;
|
||||
case T_NONE:
|
||||
val = ONONE;
|
||||
break;
|
||||
case T_CONS:
|
||||
err = unpack_cons(io, &val);
|
||||
break;
|
||||
case T_BOOL:
|
||||
err = unpack_bool(io, &u);
|
||||
if(err) goto exit;
|
||||
val = (u ? OTRUE : OFALSE);
|
||||
break;
|
||||
case T_ATOM:
|
||||
err = unpack_string(io, &y);
|
||||
if(err) goto exit;
|
||||
val = intern(string_string(y));
|
||||
objfree(y);
|
||||
break;
|
||||
case T_STRING:
|
||||
err = unpack_string(io, &val);
|
||||
break;
|
||||
case T_UINT:
|
||||
err = unpack_uint(io, &u);
|
||||
if(err) goto exit;
|
||||
val = OBJI(type, u);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
IOStream_print(iostderr, "%s> invalid type %d\n", __FUNCTION__, type);
|
||||
break;
|
||||
}
|
||||
exit:
|
||||
*x = (err ? ONONE : val);
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* $Id: xdr.h,v 1.2 2003/09/29 13:40:00 mjw Exp $ */
|
||||
#ifndef _SP_XDR_H_
|
||||
#define _SP_XDR_H_
|
||||
#include "iostream.h"
|
||||
#include "sxpr.h"
|
||||
int pack_uint(IOStream *out, unsigned int x);
|
||||
int unpack_uint(IOStream *in, unsigned int *x);
|
||||
int pack_string(IOStream *out, Sxpr x);
|
||||
int unpack_string(IOStream *in, Sxpr *x);
|
||||
int pack_cons(IOStream *out, Sxpr x);
|
||||
int unpack_cons(IOStream *in, Sxpr *x);
|
||||
int pack_sxpr(IOStream *out, Sxpr x);
|
||||
int unpack_sxpr(IOStream *in, Sxpr *x);
|
||||
#endif /* _SP_XDR_H_ */
|
||||
@@ -4,6 +4,7 @@ CFLAGS = -Wall -O3
|
||||
EXTRA_INC = -I../../xen/include/hypervisor-ifs
|
||||
EXTRA_INC += -I../../linux-xen-sparse/include -I../xc/lib
|
||||
EXTRA_INC += -I../xu/lib
|
||||
EXTRA_INC += -I../lib
|
||||
|
||||
HDRS = $(wildcard *.h)
|
||||
SRCS = $(wildcard *.c)
|
||||
|
||||
+73
-8
@@ -4,13 +4,73 @@ MINOR = 0
|
||||
SONAME = libxc.so.$(MAJOR)
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -c -Werror -O3 -fno-strict-aliasing
|
||||
CFLAGS += -I../../../xen/include/hypervisor-ifs
|
||||
CFLAGS += -I../../xu/lib
|
||||
CFLAGS += -I../../../linux-xen-sparse/include
|
||||
|
||||
HDRS = $(wildcard *.h)
|
||||
OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
|
||||
XEN_ROOT = ../../..
|
||||
|
||||
vpath %.h $(XEN_ROOT)/xen/include/hypervisor-ifs
|
||||
INCLUDES += -I $(XEN_ROOT)/xen/include/hypervisor-ifs
|
||||
|
||||
vpath %.h $(XEN_ROOT)/tools/xu/lib
|
||||
INCLUDES += -I $(XEN_ROOT)/tools/xu/lib
|
||||
|
||||
vpath %h $(XEN_ROOT)/linux-xen-sparse/include
|
||||
INCLUDES += -I $(XEN_ROOT)/linux-xen-sparse/include
|
||||
|
||||
vpath %c $(XEN_ROOT)/tools/lib
|
||||
INCLUDES += -I $(XEN_ROOT)/tools/lib
|
||||
|
||||
LIB_SRCS :=
|
||||
LIB_SRCS += allocate.c
|
||||
#LIB_SRCS += enum.c
|
||||
LIB_SRCS += file_stream.c
|
||||
LIB_SRCS += gzip_stream.c
|
||||
#LIB_SRCS += hash_table.c
|
||||
LIB_SRCS += iostream.c
|
||||
#LIB_SRCS += kernel_stream.c
|
||||
#LIB_SRCS += lexis.c
|
||||
#LIB_SRCS += lzi_stream.c
|
||||
#LIB_SRCS += lzo_stream.c
|
||||
#LIB_SRCS += marshal.c
|
||||
#LIB_SRCS += socket_stream.c
|
||||
#LIB_SRCS += string_stream.c
|
||||
#LIB_SRCS += sxpr.c
|
||||
#LIB_SRCS += sxpr_parser.c
|
||||
LIB_SRCS += sys_net.c
|
||||
LIB_SRCS += sys_string.c
|
||||
#LIB_SRCS += xdr.c
|
||||
|
||||
SRCS :=
|
||||
SRCS += xc_atropos.c
|
||||
SRCS += xc_bvtsched.c
|
||||
SRCS += xc_domain.c
|
||||
SRCS += xc_evtchn.c
|
||||
SRCS += xc_io.c
|
||||
SRCS += xc_linux_build.c
|
||||
SRCS += xc_linux_restore.c
|
||||
SRCS += xc_linux_save.c
|
||||
SRCS += xc_misc.c
|
||||
SRCS += xc_netbsd_build.c
|
||||
SRCS += xc_physdev.c
|
||||
SRCS += xc_private.c
|
||||
SRCS += xc_rrobin.c
|
||||
|
||||
SRCS += $(LIB_SRCS)
|
||||
|
||||
#CFLAGS += -I../../../xen/include/hypervisor-ifs
|
||||
#CFLAGS += -I../../xu/lib
|
||||
#CFLAGS += -I../../../linux-xen-sparse/include
|
||||
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -Werror
|
||||
CFLAGS += -g
|
||||
CFLAGS += -O3
|
||||
CFLAGS += -fno-strict-aliasing
|
||||
CFLAGS += $(INCLUDES)
|
||||
# Get gcc to generate the dependencies for us.
|
||||
CFLAGS += -Wp,-MD,.$(@F).d
|
||||
DEPS = .*.d
|
||||
|
||||
OBJS = $(patsubst %.c,%.o,$(SRCS))
|
||||
|
||||
LIB = libxc.so libxc.so.$(MAJOR) libxc.so.$(MAJOR).$(MINOR)
|
||||
|
||||
@@ -32,6 +92,8 @@ install: all
|
||||
|
||||
clean:
|
||||
$(RM) *.a *.so *.o *.rpm $(LIB)
|
||||
$(RM) *~
|
||||
$(RM) $(DEPS)
|
||||
|
||||
rpm: all
|
||||
rm -rf staging
|
||||
@@ -49,5 +111,8 @@ libxc.so.$(MAJOR):
|
||||
libxc.so.$(MAJOR).$(MINOR): $(OBJS)
|
||||
$(CC) -Wl,-soname -Wl,$(SONAME) -shared -o $@ $^ -lz
|
||||
|
||||
%.o: %.c $(HDRS) Makefile
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
%.o: %.c Makefile
|
||||
|
||||
# $(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
-include $(DEPS)
|
||||
+3
-12
@@ -75,18 +75,9 @@ int xc_shadow_control(int xc_handle,
|
||||
#define XCFLAGS_LIVE 2
|
||||
#define XCFLAGS_DEBUG 4
|
||||
|
||||
int xc_linux_save(int xc_handle,
|
||||
u32 domid,
|
||||
unsigned int flags,
|
||||
int (*writerfn)(void *, const void *, size_t),
|
||||
void *writerst);
|
||||
|
||||
int xc_linux_restore(int xc_handle,
|
||||
u32 domid,
|
||||
unsigned int flags,
|
||||
int (*readerfn)(void *, void *, size_t),
|
||||
void *readerst,
|
||||
u32 *pdomid);
|
||||
struct XcIOContext;
|
||||
int xc_linux_save(int xc_handle, struct XcIOContext *ioctxt);
|
||||
int xc_linux_restore(int xc_handle, struct XcIOContext *ioctxt);
|
||||
|
||||
int xc_linux_build(int xc_handle,
|
||||
u32 domid,
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "xc_io.h"
|
||||
|
||||
void xcio_error(XcIOContext *ctxt, const char *msg, ...){
|
||||
va_list args;
|
||||
|
||||
va_start(args, msg);
|
||||
IOStream_vprint(ctxt->info, msg, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void xcio_info(XcIOContext *ctxt, const char *msg, ...){
|
||||
va_list args;
|
||||
|
||||
if(!(ctxt->flags & XCFLAGS_VERBOSE)) return;
|
||||
va_start(args, msg);
|
||||
IOStream_vprint(ctxt->info, msg, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void xcio_debug(XcIOContext *ctxt, const char *msg, ...){
|
||||
va_list args;
|
||||
|
||||
if(!(ctxt->flags & XCFLAGS_DEBUG)) return;
|
||||
va_start(args, msg);
|
||||
IOStream_vprint(ctxt->info, msg, args);
|
||||
va_end(args);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef __XC_XC_IO_H__
|
||||
#define __XC_XC_IO_H__
|
||||
|
||||
#include "xc_private.h"
|
||||
#include "iostream.h"
|
||||
|
||||
typedef struct XcIOContext {
|
||||
u32 domain;
|
||||
unsigned flags;
|
||||
IOStream *io;
|
||||
IOStream *info;
|
||||
IOStream *err;
|
||||
char *vmconfig;
|
||||
int vmconfig_n;
|
||||
} XcIOContext;
|
||||
|
||||
static inline int xcio_read(XcIOContext *ctxt, void *buf, int n){
|
||||
int rc;
|
||||
|
||||
rc = IOStream_read(ctxt->io, buf, n);
|
||||
return (rc == n ? 0 : rc);
|
||||
}
|
||||
|
||||
static inline int xcio_write(XcIOContext *ctxt, void *buf, int n){
|
||||
int rc;
|
||||
|
||||
rc = IOStream_write(ctxt->io, buf, n);
|
||||
return (rc == n ? 0 : rc);
|
||||
}
|
||||
|
||||
static inline int xcio_flush(XcIOContext *ctxt){
|
||||
return IOStream_flush(ctxt->io);
|
||||
}
|
||||
|
||||
extern void xcio_error(XcIOContext *ctxt, const char *msg, ...);
|
||||
extern void xcio_info(XcIOContext *ctxt, const char *msg, ...);
|
||||
|
||||
#define xcio_perror(_ctxt, _msg...) \
|
||||
xcio_error(_ctxt, "(errno %d %s)" _msg, errno, strerror(errno), ## _msg)
|
||||
|
||||
#endif /* ! __XC_XC_IO_H__ */
|
||||
|
||||
|
||||
|
||||
+158
-188
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "xc_private.h"
|
||||
#include <asm-xen/suspend.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#define MAX_BATCH_SIZE 1024
|
||||
|
||||
@@ -21,14 +20,6 @@
|
||||
#endif
|
||||
|
||||
|
||||
/* This may allow us to create a 'quiet' command-line option, if necessary. */
|
||||
#define verbose_printf(_f, _a...) \
|
||||
do { \
|
||||
if ( !verbose ) break; \
|
||||
printf( _f , ## _a ); \
|
||||
fflush(stdout); \
|
||||
} while ( 0 )
|
||||
|
||||
static int get_pfn_list(int xc_handle,
|
||||
u32 domain_id,
|
||||
unsigned long *pfn_buf,
|
||||
@@ -54,19 +45,44 @@ static int get_pfn_list(int xc_handle,
|
||||
return (ret < 0) ? -1 : op.u.getmemlist.num_pfns;
|
||||
}
|
||||
|
||||
/** Read the vmconfig string from the state input.
|
||||
* It is stored as a 4-byte count 'n' followed by n bytes.
|
||||
* The config data is stored in a new string in 'ioctxt->vmconfig',
|
||||
* and is null-terminated. The count is stored in 'ioctxt->vmconfig_n'.
|
||||
*
|
||||
* @param ioctxt i/o context
|
||||
* @return 0 on success, non-zero on error.
|
||||
*/
|
||||
static int read_vmconfig(XcIOContext *ioctxt){
|
||||
int err = -1;
|
||||
if(xcio_read(ioctxt, &ioctxt->vmconfig_n, sizeof(ioctxt->vmconfig_n))){
|
||||
goto exit;
|
||||
}
|
||||
ioctxt->vmconfig = malloc(ioctxt->vmconfig_n + 1);
|
||||
if(!ioctxt->vmconfig) goto exit;
|
||||
if(xcio_read(ioctxt, ioctxt->vmconfig, ioctxt->vmconfig_n)){
|
||||
goto exit;
|
||||
}
|
||||
ioctxt->vmconfig[ioctxt->vmconfig_n] = '\0';
|
||||
err = 0;
|
||||
exit:
|
||||
if(err){
|
||||
if(ioctxt->vmconfig){
|
||||
free(ioctxt->vmconfig);
|
||||
}
|
||||
ioctxt->vmconfig = NULL;
|
||||
ioctxt->vmconfig_n = 0;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int xc_linux_restore(int xc_handle,
|
||||
u32 dom,
|
||||
unsigned int flags,
|
||||
int (*readerfn)(void *, void *, size_t),
|
||||
void *readerst,
|
||||
u32 *pdomid)
|
||||
int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
|
||||
{
|
||||
dom0_op_t op;
|
||||
int rc = 1, i, j, n, k;
|
||||
int rc = 1, i, n, k;
|
||||
unsigned long mfn, pfn, xpfn;
|
||||
unsigned int prev_pc, this_pc;
|
||||
int verbose = flags & XCFLAGS_VERBOSE;
|
||||
u32 dom = ioctxt->domain;
|
||||
int verify = 0;
|
||||
|
||||
/* Number of page frames in use by this Linux session. */
|
||||
@@ -115,8 +131,7 @@ int xc_linux_restore(int xc_handle,
|
||||
/* used by debug verify code */
|
||||
unsigned long buf[PAGE_SIZE/sizeof(unsigned long)];
|
||||
|
||||
if ( mlock(&ctxt, sizeof(ctxt) ) )
|
||||
{
|
||||
if ( mlock(&ctxt, sizeof(ctxt) ) ) {
|
||||
/* needed for when we do the build dom0 op,
|
||||
but might as well do early */
|
||||
PERROR("Unable to mlock ctxt");
|
||||
@@ -124,35 +139,36 @@ int xc_linux_restore(int xc_handle,
|
||||
}
|
||||
|
||||
/* Start writing out the saved-domain record. */
|
||||
if ( (*readerfn)(readerst, signature, 16) ||
|
||||
(memcmp(signature, "LinuxGuestRecord", 16) != 0) )
|
||||
{
|
||||
ERROR("Unrecognised state format -- no signature found");
|
||||
if ( xcio_read(ioctxt, signature, 16) ||
|
||||
(memcmp(signature, "LinuxGuestRecord", 16) != 0) ) {
|
||||
xcio_error(ioctxt, "Unrecognised state format -- no signature found");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( (*readerfn)(readerst, name, sizeof(name)) ||
|
||||
(*readerfn)(readerst, &nr_pfns, sizeof(unsigned long)) ||
|
||||
(*readerfn)(readerst, pfn_to_mfn_frame_list, PAGE_SIZE) )
|
||||
{
|
||||
ERROR("Error when reading from state file");
|
||||
if ( xcio_read(ioctxt, name, sizeof(name)) ||
|
||||
xcio_read(ioctxt, &nr_pfns, sizeof(unsigned long)) ||
|
||||
xcio_read(ioctxt, pfn_to_mfn_frame_list, PAGE_SIZE) ) {
|
||||
xcio_error(ioctxt, "Error reading header");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for ( i = 0; i < MAX_DOMAIN_NAME; i++ )
|
||||
{
|
||||
if(read_vmconfig(ioctxt)){
|
||||
xcio_error(ioctxt, "Error writing vmconfig");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for ( i = 0; i < MAX_DOMAIN_NAME; i++ ) {
|
||||
if ( name[i] == '\0' ) break;
|
||||
if ( name[i] & 0x80 )
|
||||
{
|
||||
ERROR("Random characters in domain name");
|
||||
xcio_error(ioctxt, "Random characters in domain name");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
name[MAX_DOMAIN_NAME-1] = '\0';
|
||||
|
||||
if ( nr_pfns > 1024*1024 )
|
||||
{
|
||||
ERROR("Invalid state file -- pfn count out of range");
|
||||
if ( nr_pfns > 1024*1024 ) {
|
||||
xcio_error(ioctxt, "Invalid state file -- pfn count out of range");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -162,22 +178,19 @@ int xc_linux_restore(int xc_handle,
|
||||
region_mfn = calloc(1, 4 * MAX_BATCH_SIZE);
|
||||
|
||||
if ( (pfn_to_mfn_table == NULL) || (pfn_type == NULL) ||
|
||||
(region_mfn == NULL) )
|
||||
{
|
||||
(region_mfn == NULL) ) {
|
||||
errno = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( mlock(region_mfn, 4 * MAX_BATCH_SIZE ) )
|
||||
{
|
||||
ERROR("Could not mlock region_mfn");
|
||||
if ( mlock(region_mfn, 4 * MAX_BATCH_SIZE ) ) {
|
||||
xcio_error(ioctxt, "Could not mlock region_mfn");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Set the domain's name to that from the restore file */
|
||||
if ( xc_domain_setname( xc_handle, dom, name ) )
|
||||
{
|
||||
ERROR("Could not set domain name");
|
||||
if ( xc_domain_setname( xc_handle, dom, name ) ) {
|
||||
xcio_error(ioctxt, "Could not set domain name");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -187,7 +200,7 @@ int xc_linux_restore(int xc_handle,
|
||||
if ( xc_domain_setinitialmem(xc_handle, dom,
|
||||
nr_pfns * (PAGE_SIZE / 1024)) )
|
||||
{
|
||||
ERROR("Could not set domain initial memory");
|
||||
xcio_error(ioctxt, "Could not set domain initial memory");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -195,9 +208,8 @@ int xc_linux_restore(int xc_handle,
|
||||
op.cmd = DOM0_GETDOMAININFO;
|
||||
op.u.getdomaininfo.domain = (domid_t)dom;
|
||||
op.u.getdomaininfo.ctxt = NULL;
|
||||
if ( do_dom0_op(xc_handle, &op) < 0 )
|
||||
{
|
||||
ERROR("Could not get information on new domain");
|
||||
if ( do_dom0_op(xc_handle, &op) < 0 ) {
|
||||
xcio_error(ioctxt, "Could not get information on new domain");
|
||||
goto out;
|
||||
}
|
||||
shared_info_frame = op.u.getdomaininfo.shared_info_frame;
|
||||
@@ -208,19 +220,17 @@ int xc_linux_restore(int xc_handle,
|
||||
|
||||
|
||||
/* Build the pfn-to-mfn table. We choose MFN ordering returned by Xen. */
|
||||
if ( get_pfn_list(xc_handle, dom, pfn_to_mfn_table, nr_pfns) != nr_pfns )
|
||||
{
|
||||
ERROR("Did not read correct number of frame numbers for new dom");
|
||||
if ( get_pfn_list(xc_handle, dom, pfn_to_mfn_table, nr_pfns) != nr_pfns ) {
|
||||
xcio_error(ioctxt, "Did not read correct number of frame numbers for new dom");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( (mmu = init_mmu_updates(xc_handle, dom)) == NULL )
|
||||
{
|
||||
ERROR("Could not initialise for MMU updates");
|
||||
if ( (mmu = init_mmu_updates(xc_handle, dom)) == NULL ) {
|
||||
xcio_error(ioctxt, "Could not initialise for MMU updates");
|
||||
goto out;
|
||||
}
|
||||
|
||||
verbose_printf("Reloading memory pages: 0%%");
|
||||
xcio_info(ioctxt, "Reloading memory pages: 0%%");
|
||||
|
||||
/*
|
||||
* Now simply read each saved frame into its new machine frame.
|
||||
@@ -229,56 +239,45 @@ int xc_linux_restore(int xc_handle,
|
||||
prev_pc = 0;
|
||||
|
||||
n=0;
|
||||
while(1)
|
||||
{
|
||||
while(1) {
|
||||
int j;
|
||||
unsigned long region_pfn_type[MAX_BATCH_SIZE];
|
||||
|
||||
this_pc = (n * 100) / nr_pfns;
|
||||
if ( (this_pc - prev_pc) >= 5 )
|
||||
{
|
||||
verbose_printf("\b\b\b\b%3d%%", this_pc);
|
||||
if ( (this_pc - prev_pc) >= 5 ) {
|
||||
xcio_info(ioctxt, "\b\b\b\b%3d%%", this_pc);
|
||||
prev_pc = this_pc;
|
||||
}
|
||||
|
||||
if ( (*readerfn)(readerst, &j, sizeof(int)) )
|
||||
{
|
||||
ERROR("Error when reading from state file");
|
||||
if ( xcio_read(ioctxt, &j, sizeof(int)) ) {
|
||||
xcio_error(ioctxt, "Error when reading from state file");
|
||||
goto out;
|
||||
}
|
||||
|
||||
DPRINTF("batch %d\n",j);
|
||||
|
||||
if ( j == -1 )
|
||||
{
|
||||
if ( j == -1 ) {
|
||||
verify = 1;
|
||||
printf("Entering page verify mode\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( j == 0 )
|
||||
break; /* our work here is done */
|
||||
if ( j == 0 ) break; /* our work here is done */
|
||||
|
||||
if( j > MAX_BATCH_SIZE )
|
||||
{
|
||||
ERROR("Max batch size exceeded. Giving up.");
|
||||
if( j > MAX_BATCH_SIZE ) {
|
||||
xcio_error(ioctxt, "Max batch size exceeded. Giving up.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( (*readerfn)(readerst, region_pfn_type, j*sizeof(unsigned long)) )
|
||||
{
|
||||
ERROR("Error when reading from state file");
|
||||
if ( xcio_read(ioctxt, region_pfn_type, j*sizeof(unsigned long)) ) {
|
||||
xcio_error(ioctxt, "Error when reading from state file");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for(i=0;i<j;i++)
|
||||
{
|
||||
if ( (region_pfn_type[i] & LTAB_MASK) == XTAB)
|
||||
{
|
||||
for(i=0; i<j; i++) {
|
||||
if ( (region_pfn_type[i] & LTAB_MASK) == XTAB) {
|
||||
region_mfn[i] = 0; /* we know map will fail, but don't care */
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
pfn = region_pfn_type[i] & ~LTAB_MASK;
|
||||
region_mfn[i] = pfn_to_mfn_table[pfn];
|
||||
}
|
||||
@@ -287,24 +286,20 @@ int xc_linux_restore(int xc_handle,
|
||||
if ( (region_base = mfn_mapper_map_batch( xc_handle, dom,
|
||||
PROT_WRITE,
|
||||
region_mfn,
|
||||
j )) == 0)
|
||||
{
|
||||
PERROR("map batch failed");
|
||||
j )) == 0) {
|
||||
xcio_error(ioctxt, "map batch failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for(i=0;i<j;i++)
|
||||
{
|
||||
for(i=0;i<j;i++) {
|
||||
unsigned long *ppage;
|
||||
|
||||
pfn = region_pfn_type[i] & ~LTAB_MASK;
|
||||
|
||||
if ( (region_pfn_type[i] & LTAB_MASK) == XTAB)
|
||||
continue;
|
||||
if ( (region_pfn_type[i] & LTAB_MASK) == XTAB) continue;
|
||||
|
||||
if (pfn>nr_pfns)
|
||||
{
|
||||
ERROR("pfn out of range");
|
||||
if (pfn>nr_pfns) {
|
||||
xcio_error(ioctxt, "pfn out of range");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -314,36 +309,32 @@ int xc_linux_restore(int xc_handle,
|
||||
|
||||
mfn = pfn_to_mfn_table[pfn];
|
||||
|
||||
if ( verify )
|
||||
if ( verify ) {
|
||||
ppage = (unsigned long*) buf; /* debug case */
|
||||
else
|
||||
} else {
|
||||
ppage = (unsigned long*) (region_base + i*PAGE_SIZE);
|
||||
}
|
||||
|
||||
if ( (*readerfn)(readerst, ppage, PAGE_SIZE) )
|
||||
{
|
||||
ERROR("Error when reading from state file");
|
||||
if ( xcio_read(ioctxt, ppage, PAGE_SIZE) ) {
|
||||
xcio_error(ioctxt, "Error when reading from state file");
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch( region_pfn_type[i] )
|
||||
{
|
||||
switch( region_pfn_type[i] ) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case L1TAB:
|
||||
{
|
||||
for ( k = 0; k < 1024; k++ )
|
||||
{
|
||||
if ( ppage[k] & _PAGE_PRESENT )
|
||||
{
|
||||
for ( k = 0; k < 1024; k++ ) {
|
||||
if ( ppage[k] & _PAGE_PRESENT ) {
|
||||
xpfn = ppage[k] >> PAGE_SHIFT;
|
||||
|
||||
if ( xpfn >= nr_pfns )
|
||||
{
|
||||
ERROR("Frame number in type %d page table is "
|
||||
"out of range. i=%d k=%d pfn=0x%x "
|
||||
"nr_pfns=%d", region_pfn_type[i]>>28, i,
|
||||
k, xpfn,nr_pfns);
|
||||
if ( xpfn >= nr_pfns ) {
|
||||
xcio_error(ioctxt, "Frame number in type %lu page table is "
|
||||
"out of range. i=%d k=%d pfn=0x%lx "
|
||||
"nr_pfns=%lu", region_pfn_type[i]>>28, i,
|
||||
k, xpfn, nr_pfns);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -359,16 +350,13 @@ int xc_linux_restore(int xc_handle,
|
||||
{
|
||||
for ( k = 0;
|
||||
k < (HYPERVISOR_VIRT_START>>L2_PAGETABLE_SHIFT);
|
||||
k++ )
|
||||
{
|
||||
if ( ppage[k] & _PAGE_PRESENT )
|
||||
{
|
||||
k++ ) {
|
||||
if ( ppage[k] & _PAGE_PRESENT ) {
|
||||
xpfn = ppage[k] >> PAGE_SHIFT;
|
||||
|
||||
if ( xpfn >= nr_pfns )
|
||||
{
|
||||
ERROR("Frame number in type %d page table is "
|
||||
"out of range. i=%d k=%d pfn=%d nr_pfns=%d",
|
||||
if ( xpfn >= nr_pfns ) {
|
||||
xcio_error(ioctxt, "Frame number in type %lu page table is "
|
||||
"out of range. i=%d k=%d pfn=%lu nr_pfns=%lu",
|
||||
region_pfn_type[i]>>28, i, k, xpfn, nr_pfns);
|
||||
|
||||
goto out;
|
||||
@@ -383,24 +371,21 @@ int xc_linux_restore(int xc_handle,
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR("Bogus page type %x page table is out of range."
|
||||
" i=%d nr_pfns=%d", region_pfn_type[i], i, nr_pfns);
|
||||
xcio_error(ioctxt, "Bogus page type %lx page table is out of range."
|
||||
" i=%d nr_pfns=%lu", region_pfn_type[i], i, nr_pfns);
|
||||
goto out;
|
||||
|
||||
} /* end of page type switch statement */
|
||||
|
||||
if ( verify )
|
||||
{
|
||||
if ( verify ) {
|
||||
int res = memcmp(buf, (region_base + i*PAGE_SIZE), PAGE_SIZE );
|
||||
if (res)
|
||||
{
|
||||
if (res) {
|
||||
int v;
|
||||
printf("************** pfn=%x type=%x gotcs=%08lx "
|
||||
printf("************** pfn=%lx type=%lx gotcs=%08lx "
|
||||
"actualcs=%08lx\n", pfn, pfn_type[pfn],
|
||||
csum_page(region_base + i*PAGE_SIZE),
|
||||
csum_page(buf));
|
||||
for ( v = 0; v < 4; v++ )
|
||||
{
|
||||
for ( v = 0; v < 4; v++ ) {
|
||||
unsigned long *p = (unsigned long *)
|
||||
(region_base + i*PAGE_SIZE);
|
||||
if ( buf[v] != p[v] )
|
||||
@@ -411,8 +396,7 @@ int xc_linux_restore(int xc_handle,
|
||||
}
|
||||
|
||||
if ( add_mmu_update(xc_handle, mmu,
|
||||
(mfn<<PAGE_SHIFT) | MMU_MACHPHYS_UPDATE, pfn) )
|
||||
{
|
||||
(mfn<<PAGE_SHIFT) | MMU_MACHPHYS_UPDATE, pfn) ) {
|
||||
printf("machpys mfn=%ld pfn=%ld\n",mfn,pfn);
|
||||
goto out;
|
||||
}
|
||||
@@ -431,44 +415,36 @@ int xc_linux_restore(int xc_handle,
|
||||
* Pin page tables. Do this after writing to them as otherwise Xen
|
||||
* will barf when doing the type-checking.
|
||||
*/
|
||||
for ( i = 0; i < nr_pfns; i++ )
|
||||
{
|
||||
if ( pfn_type[i] == L1TAB )
|
||||
{
|
||||
for ( i = 0; i < nr_pfns; i++ ) {
|
||||
if ( pfn_type[i] == L1TAB ) {
|
||||
if ( add_mmu_update(xc_handle, mmu,
|
||||
(pfn_to_mfn_table[i]<<PAGE_SHIFT) |
|
||||
MMU_EXTENDED_COMMAND,
|
||||
MMUEXT_PIN_L1_TABLE) )
|
||||
{
|
||||
MMUEXT_PIN_L1_TABLE) ) {
|
||||
printf("ERR pin L1 pfn=%lx mfn=%lx\n",
|
||||
i, pfn_to_mfn_table[i]);
|
||||
(unsigned long)i, pfn_to_mfn_table[i]);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if ( pfn_type[i] == L2TAB )
|
||||
{
|
||||
} else if ( pfn_type[i] == L2TAB ) {
|
||||
if ( add_mmu_update(xc_handle, mmu,
|
||||
(pfn_to_mfn_table[i]<<PAGE_SHIFT) |
|
||||
MMU_EXTENDED_COMMAND,
|
||||
MMUEXT_PIN_L2_TABLE) )
|
||||
{
|
||||
MMUEXT_PIN_L2_TABLE) ) {
|
||||
printf("ERR pin L2 pfn=%lx mfn=%lx\n",
|
||||
i, pfn_to_mfn_table[i]);
|
||||
(unsigned long)i, pfn_to_mfn_table[i]);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( finish_mmu_updates(xc_handle, mmu) )
|
||||
goto out;
|
||||
if ( finish_mmu_updates(xc_handle, mmu) ) goto out;
|
||||
|
||||
verbose_printf("\b\b\b\b100%%\nMemory reloaded.\n");
|
||||
xcio_info(ioctxt, "\b\b\b\b100%%\nMemory reloaded.\n");
|
||||
|
||||
|
||||
if ( (*readerfn)(readerst, &ctxt, sizeof(ctxt)) ||
|
||||
(*readerfn)(readerst, shared_info, PAGE_SIZE) )
|
||||
{
|
||||
ERROR("Error when reading from state file");
|
||||
if ( xcio_read(ioctxt, &ctxt, sizeof(ctxt)) ||
|
||||
xcio_read(ioctxt, shared_info, PAGE_SIZE) ) {
|
||||
xcio_error(ioctxt, "Error when reading from state file");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -476,7 +452,7 @@ int xc_linux_restore(int xc_handle,
|
||||
pfn = ctxt.cpu_ctxt.esi;
|
||||
if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NOTAB) )
|
||||
{
|
||||
ERROR("Suspend record frame number is bad");
|
||||
xcio_error(ioctxt, "Suspend record frame number is bad");
|
||||
goto out;
|
||||
}
|
||||
ctxt.cpu_ctxt.esi = mfn = pfn_to_mfn_table[pfn];
|
||||
@@ -487,17 +463,14 @@ int xc_linux_restore(int xc_handle,
|
||||
unmap_pfn(pm_handle, p_srec);
|
||||
|
||||
/* Uncanonicalise each GDT frame number. */
|
||||
if ( ctxt.gdt_ents > 8192 )
|
||||
{
|
||||
ERROR("GDT entry count out of range");
|
||||
if ( ctxt.gdt_ents > 8192 ) {
|
||||
xcio_error(ioctxt, "GDT entry count out of range");
|
||||
goto out;
|
||||
}
|
||||
for ( i = 0; i < ctxt.gdt_ents; i += 512 )
|
||||
{
|
||||
for ( i = 0; i < ctxt.gdt_ents; i += 512 ) {
|
||||
pfn = ctxt.gdt_frames[i];
|
||||
if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NOTAB) )
|
||||
{
|
||||
ERROR("GDT frame number is bad");
|
||||
if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NOTAB) ) {
|
||||
xcio_error(ioctxt, "GDT frame number is bad");
|
||||
goto out;
|
||||
}
|
||||
ctxt.gdt_frames[i] = pfn_to_mfn_table[pfn];
|
||||
@@ -505,11 +478,10 @@ int xc_linux_restore(int xc_handle,
|
||||
|
||||
/* Uncanonicalise the page table base pointer. */
|
||||
pfn = ctxt.pt_base >> PAGE_SHIFT;
|
||||
if ( (pfn >= nr_pfns) || (pfn_type[pfn] != L2TAB) )
|
||||
{
|
||||
printf("PT base is bad. pfn=%d nr=%d type=%08lx %08lx\n",
|
||||
pfn, nr_pfns, pfn_type[pfn], L2TAB);
|
||||
ERROR("PT base is bad.");
|
||||
if ( (pfn >= nr_pfns) || (pfn_type[pfn] != L2TAB) ) {
|
||||
printf("PT base is bad. pfn=%lu nr=%lu type=%08lx %08lx\n",
|
||||
pfn, nr_pfns, pfn_type[pfn], (unsigned long)L2TAB);
|
||||
xcio_error(ioctxt, "PT base is bad.");
|
||||
goto out;
|
||||
}
|
||||
ctxt.pt_base = pfn_to_mfn_table[pfn] << PAGE_SHIFT;
|
||||
@@ -527,14 +499,12 @@ int xc_linux_restore(int xc_handle,
|
||||
|
||||
|
||||
/* Uncanonicalise the pfn-to-mfn table frame-number list. */
|
||||
for ( i = 0; i < (nr_pfns+1023)/1024; i++ )
|
||||
{
|
||||
for ( i = 0; i < (nr_pfns+1023)/1024; i++ ) {
|
||||
unsigned long pfn, mfn;
|
||||
|
||||
pfn = pfn_to_mfn_frame_list[i];
|
||||
if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NOTAB) )
|
||||
{
|
||||
ERROR("PFN-to-MFN frame number is bad");
|
||||
if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NOTAB) ) {
|
||||
xcio_error(ioctxt, "PFN-to-MFN frame number is bad");
|
||||
goto out;
|
||||
}
|
||||
mfn = pfn_to_mfn_table[pfn];
|
||||
@@ -545,9 +515,8 @@ int xc_linux_restore(int xc_handle,
|
||||
mfn_mapper_map_batch(xc_handle, dom,
|
||||
PROT_WRITE,
|
||||
pfn_to_mfn_frame_list,
|
||||
(nr_pfns+1023)/1024 )) == 0 )
|
||||
{
|
||||
ERROR("Couldn't map pfn_to_mfn table");
|
||||
(nr_pfns+1023)/1024 )) == 0 ) {
|
||||
xcio_error(ioctxt, "Couldn't map pfn_to_mfn table");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -569,24 +538,26 @@ int xc_linux_restore(int xc_handle,
|
||||
* 9. debugregs are checked by Xen.
|
||||
* 10. callback code selectors need checking.
|
||||
*/
|
||||
for ( i = 0; i < 256; i++ )
|
||||
{
|
||||
for ( i = 0; i < 256; i++ ) {
|
||||
ctxt.trap_ctxt[i].vector = i;
|
||||
if ( (ctxt.trap_ctxt[i].cs & 3) == 0 )
|
||||
ctxt.trap_ctxt[i].cs = FLAT_GUESTOS_CS;
|
||||
}
|
||||
if ( (ctxt.guestos_ss & 3) == 0 )
|
||||
if ( (ctxt.guestos_ss & 3) == 0 ){
|
||||
ctxt.guestos_ss = FLAT_GUESTOS_DS;
|
||||
if ( (ctxt.event_callback_cs & 3) == 0 )
|
||||
}
|
||||
if ( (ctxt.event_callback_cs & 3) == 0 ){
|
||||
ctxt.event_callback_cs = FLAT_GUESTOS_CS;
|
||||
if ( (ctxt.failsafe_callback_cs & 3) == 0 )
|
||||
}
|
||||
if ( (ctxt.failsafe_callback_cs & 3) == 0 ){
|
||||
ctxt.failsafe_callback_cs = FLAT_GUESTOS_CS;
|
||||
}
|
||||
if ( ((ctxt.ldt_base & (PAGE_SIZE - 1)) != 0) ||
|
||||
(ctxt.ldt_ents > 8192) ||
|
||||
(ctxt.ldt_base > HYPERVISOR_VIRT_START) ||
|
||||
((ctxt.ldt_base + ctxt.ldt_ents*8) > HYPERVISOR_VIRT_START) )
|
||||
{
|
||||
ERROR("Bad LDT base or size");
|
||||
xcio_error(ioctxt, "Bad LDT base or size");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -597,34 +568,33 @@ int xc_linux_restore(int xc_handle,
|
||||
|
||||
/* don't start the domain as we have console etc to set up */
|
||||
|
||||
if( rc == 0 )
|
||||
{
|
||||
if( rc == 0 ) {
|
||||
/* Success: print the domain id. */
|
||||
verbose_printf("DOM=%u\n", dom);
|
||||
xcio_info(ioctxt, "DOM=%lu\n", dom);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
out:
|
||||
if ( (rc != 0) && (dom != 0) )
|
||||
if ( (rc != 0) && (dom != 0) ){
|
||||
xc_domain_destroy(xc_handle, dom);
|
||||
|
||||
if ( mmu != NULL )
|
||||
}
|
||||
if ( mmu != NULL ){
|
||||
free(mmu);
|
||||
|
||||
if ( pm_handle >= 0 )
|
||||
}
|
||||
if ( pm_handle >= 0 ){
|
||||
(void)close_pfn_mapper(pm_handle);
|
||||
|
||||
if ( pfn_to_mfn_table != NULL )
|
||||
}
|
||||
if ( pfn_to_mfn_table != NULL ){
|
||||
free(pfn_to_mfn_table);
|
||||
if ( pfn_type != NULL )
|
||||
}
|
||||
if ( pfn_type != NULL ){
|
||||
free(pfn_type);
|
||||
}
|
||||
|
||||
|
||||
if ( rc == 0 )
|
||||
*pdomid = dom;
|
||||
|
||||
if ( rc == 0 ){
|
||||
ioctxt->domain = dom;
|
||||
}
|
||||
DPRINTF("Restore exit with rc=%d\n",rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
+129
-172
@@ -6,6 +6,7 @@
|
||||
* Copyright (c) 2003, K A Fraser.
|
||||
*/
|
||||
|
||||
#include <sys/time.h>
|
||||
#include "xc_private.h"
|
||||
#include <asm-xen/suspend.h>
|
||||
|
||||
@@ -26,14 +27,6 @@
|
||||
#define DDPRINTF(_f, _a...) ((void)0)
|
||||
#endif
|
||||
|
||||
/* This may allow us to create a 'quiet' command-line option, if necessary. */
|
||||
#define verbose_printf(_f, _a...) \
|
||||
do { \
|
||||
if ( !verbose ) break; \
|
||||
printf( _f , ## _a ); \
|
||||
fflush(stdout); \
|
||||
} while ( 0 )
|
||||
|
||||
/*
|
||||
* Returns TRUE if the given machine frame number has a unique mapping
|
||||
* in the guest's pseudophysical map.
|
||||
@@ -189,6 +182,20 @@ static int print_stats( int xc_handle, u32 domid,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Write the vmconfig string.
|
||||
* It is stored as a 4-byte count 'n' followed by n bytes.
|
||||
*
|
||||
* @param ioctxt i/o context
|
||||
* @return 0 on success, non-zero on error.
|
||||
*/
|
||||
static int write_vmconfig(XcIOContext *ioctxt){
|
||||
int err = -1;
|
||||
if(xcio_write(ioctxt, &ioctxt->vmconfig_n, sizeof(ioctxt->vmconfig_n))) goto exit;
|
||||
if(xcio_write(ioctxt, ioctxt->vmconfig, ioctxt->vmconfig_n)) goto exit;
|
||||
err = 0;
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int analysis_phase( int xc_handle, u32 domid,
|
||||
int nr_pfns, unsigned long *arr )
|
||||
@@ -225,19 +232,15 @@ static int analysis_phase( int xc_handle, u32 domid,
|
||||
return -1;
|
||||
}
|
||||
|
||||
int xc_linux_save(int xc_handle,
|
||||
u32 domid,
|
||||
unsigned int flags,
|
||||
int (*writerfn)(void *, const void *, size_t),
|
||||
void *writerst )
|
||||
int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
|
||||
{
|
||||
dom0_op_t op;
|
||||
int rc = 1, i, j, k, last_iter, iter = 0;
|
||||
unsigned long mfn;
|
||||
int verbose = flags & XCFLAGS_VERBOSE;
|
||||
int live = flags & XCFLAGS_LIVE;
|
||||
int debug = flags & XCFLAGS_DEBUG;
|
||||
int sent_last_iter, sent_this_iter, skip_this_iter;
|
||||
u32 domid = ioctxt->domain;
|
||||
int live = (ioctxt->flags & XCFLAGS_LIVE);
|
||||
int debug = (ioctxt->flags & XCFLAGS_DEBUG);
|
||||
int sent_last_iter, skip_this_iter;
|
||||
|
||||
/* Important tuning parameters */
|
||||
int max_iters = 29; /* limit us to 30 times round loop */
|
||||
@@ -272,7 +275,7 @@ int xc_linux_save(int xc_handle,
|
||||
unsigned long *live_shinfo;
|
||||
|
||||
/* base of the region in which domain memory is mapped */
|
||||
unsigned char *region_base;
|
||||
unsigned char *region_base = NULL;
|
||||
|
||||
/* A temporary mapping, and a copy, of the guest's suspend record. */
|
||||
suspend_record_t *p_srec;
|
||||
@@ -294,17 +297,14 @@ int xc_linux_save(int xc_handle,
|
||||
int needed_to_fix = 0;
|
||||
int total_sent = 0;
|
||||
|
||||
if ( mlock(&ctxt, sizeof(ctxt) ) )
|
||||
{
|
||||
PERROR("Unable to mlock ctxt");
|
||||
if (mlock(&ctxt, sizeof(ctxt))) {
|
||||
xcio_perror(ioctxt, "Unable to mlock ctxt");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Ensure that the domain exists, and that it is stopped. */
|
||||
|
||||
if ( xc_domain_pause( xc_handle, domid ) )
|
||||
{
|
||||
PERROR("Could not pause domain");
|
||||
if ( xc_domain_pause(xc_handle, domid) ){
|
||||
xcio_perror(ioctxt, "Could not pause domain");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -317,9 +317,8 @@ int xc_linux_save(int xc_handle,
|
||||
shared_info_frame = op.u.getdomaininfo.shared_info_frame;
|
||||
|
||||
/* A cheesy test to see whether the domain contains valid state. */
|
||||
if ( ctxt.pt_base == 0 )
|
||||
{
|
||||
ERROR("Domain is not in a valid Linux guest OS state");
|
||||
if ( ctxt.pt_base == 0 ){
|
||||
xcio_error(ioctxt, "Domain is not in a valid Linux guest OS state");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -327,20 +326,17 @@ int xc_linux_save(int xc_handle,
|
||||
domid for this to succeed. */
|
||||
p_srec = mfn_mapper_map_single(xc_handle, domid,
|
||||
sizeof(*p_srec), PROT_READ,
|
||||
ctxt.cpu_ctxt.esi );
|
||||
|
||||
if (!p_srec)
|
||||
{
|
||||
ERROR("Couldn't map state record");
|
||||
ctxt.cpu_ctxt.esi);
|
||||
if (!p_srec){
|
||||
xcio_error(ioctxt, "Couldn't map state record");
|
||||
goto out;
|
||||
}
|
||||
|
||||
nr_pfns = p_srec->nr_pfns;
|
||||
|
||||
/* cheesy sanity check */
|
||||
if ( nr_pfns > 1024*1024 )
|
||||
{
|
||||
ERROR("Invalid state record -- pfn count out of range");
|
||||
if ( nr_pfns > 1024*1024 ){
|
||||
xcio_error(ioctxt, "Invalid state record -- pfn count out of range");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -350,9 +346,8 @@ int xc_linux_save(int xc_handle,
|
||||
PAGE_SIZE, PROT_READ,
|
||||
p_srec->pfn_to_mfn_frame_list );
|
||||
|
||||
if (!live_pfn_to_mfn_frame_list)
|
||||
{
|
||||
ERROR("Couldn't map pfn_to_mfn_frame_list");
|
||||
if (!live_pfn_to_mfn_frame_list){
|
||||
xcio_error(ioctxt, "Couldn't map pfn_to_mfn_frame_list");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -379,24 +374,21 @@ int xc_linux_save(int xc_handle,
|
||||
(its not clear why it would want to change them, and we'll be OK
|
||||
from a safety POV anyhow. */
|
||||
|
||||
live_pfn_to_mfn_table = mfn_mapper_map_batch( xc_handle, domid,
|
||||
PROT_READ,
|
||||
live_pfn_to_mfn_frame_list,
|
||||
(nr_pfns+1023)/1024 );
|
||||
if( !live_pfn_to_mfn_table )
|
||||
{
|
||||
PERROR("Couldn't map pfn_to_mfn table");
|
||||
live_pfn_to_mfn_table = mfn_mapper_map_batch(xc_handle, domid,
|
||||
PROT_READ,
|
||||
live_pfn_to_mfn_frame_list,
|
||||
(nr_pfns+1023)/1024 );
|
||||
if( !live_pfn_to_mfn_table ){
|
||||
xcio_perror(ioctxt, "Couldn't map pfn_to_mfn table");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
/* Canonicalise the pfn-to-mfn table frame-number list. */
|
||||
memcpy( pfn_to_mfn_frame_list, live_pfn_to_mfn_frame_list, PAGE_SIZE );
|
||||
for ( i = 0; i < nr_pfns; i += 1024 )
|
||||
{
|
||||
if ( !translate_mfn_to_pfn(&pfn_to_mfn_frame_list[i/1024]) )
|
||||
{
|
||||
ERROR("Frame # in pfn-to-mfn frame list is not in pseudophys");
|
||||
for ( i = 0; i < nr_pfns; i += 1024 ){
|
||||
if ( !translate_mfn_to_pfn(&pfn_to_mfn_frame_list[i/1024]) ){
|
||||
xcio_error(ioctxt, "Frame # in pfn-to-mfn frame list is not in pseudophys");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -404,27 +396,24 @@ int xc_linux_save(int xc_handle,
|
||||
/* At this point, we can start the domain again if we're doing a
|
||||
live suspend */
|
||||
|
||||
if( live )
|
||||
{
|
||||
if( live ){
|
||||
if ( xc_shadow_control( xc_handle, domid,
|
||||
DOM0_SHADOW_CONTROL_OP_ENABLE_LOGDIRTY,
|
||||
NULL, 0, NULL ) < 0 )
|
||||
{
|
||||
ERROR("Couldn't enable shadow mode");
|
||||
NULL, 0, NULL ) < 0 ) {
|
||||
xcio_error(ioctxt, "Couldn't enable shadow mode");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( xc_domain_unpause( xc_handle, domid ) < 0 )
|
||||
{
|
||||
ERROR("Couldn't unpause domain");
|
||||
if ( xc_domain_unpause(xc_handle, domid) < 0 ){
|
||||
xcio_error(ioctxt, "Couldn't unpause domain");
|
||||
goto out;
|
||||
}
|
||||
|
||||
last_iter = 0;
|
||||
sent_last_iter = 1<<20; /* 4GB of pages */
|
||||
}
|
||||
else
|
||||
} else{
|
||||
last_iter = 1;
|
||||
}
|
||||
|
||||
/* calculate the power of 2 order of nr_pfns, e.g.
|
||||
15->4 16->4 17->5 */
|
||||
@@ -438,25 +427,22 @@ int xc_linux_save(int xc_handle,
|
||||
to_fix = calloc( 1, sz );
|
||||
to_skip = malloc( sz );
|
||||
|
||||
if (!to_send || !to_fix || !to_skip)
|
||||
{
|
||||
ERROR("Couldn't allocate to_send array");
|
||||
if (!to_send || !to_fix || !to_skip){
|
||||
xcio_error(ioctxt, "Couldn't allocate to_send array");
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset( to_send, 0xff, sz );
|
||||
|
||||
if ( mlock( to_send, sz ) )
|
||||
{
|
||||
PERROR("Unable to mlock to_send");
|
||||
if ( mlock( to_send, sz ) ){
|
||||
xcio_perror(ioctxt, "Unable to mlock to_send");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* (to fix is local only) */
|
||||
|
||||
if ( mlock( to_skip, sz ) )
|
||||
{
|
||||
PERROR("Unable to mlock to_skip");
|
||||
if ( mlock( to_skip, sz ) ){
|
||||
xcio_perror(ioctxt, "Unable to mlock to_skip");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -468,15 +454,13 @@ int xc_linux_save(int xc_handle,
|
||||
pfn_type = calloc(BATCH_SIZE, sizeof(unsigned long));
|
||||
pfn_batch = calloc(BATCH_SIZE, sizeof(unsigned long));
|
||||
|
||||
if ( (pfn_type == NULL) || (pfn_batch == NULL) )
|
||||
{
|
||||
if ( (pfn_type == NULL) || (pfn_batch == NULL) ){
|
||||
errno = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( mlock( pfn_type, BATCH_SIZE * sizeof(unsigned long) ) )
|
||||
{
|
||||
ERROR("Unable to mlock");
|
||||
if ( mlock( pfn_type, BATCH_SIZE * sizeof(unsigned long) ) ){
|
||||
xcio_error(ioctxt, "Unable to mlock");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -485,8 +469,7 @@ int xc_linux_save(int xc_handle,
|
||||
* Quick belt and braces sanity check.
|
||||
*/
|
||||
#if DEBUG
|
||||
for ( i = 0; i < nr_pfns; i++ )
|
||||
{
|
||||
for ( i = 0; i < nr_pfns; i++ ){
|
||||
mfn = live_pfn_to_mfn_table[i];
|
||||
|
||||
if( (live_mfn_to_pfn_table[mfn] != i) && (mfn != 0x80000004) )
|
||||
@@ -500,20 +483,22 @@ int xc_linux_save(int xc_handle,
|
||||
PAGE_SIZE, PROT_READ,
|
||||
shared_info_frame);
|
||||
|
||||
if (!live_shinfo)
|
||||
{
|
||||
ERROR("Couldn't map live_shinfo");
|
||||
if (!live_shinfo){
|
||||
xcio_error(ioctxt, "Couldn't map live_shinfo");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Start writing out the saved-domain record. */
|
||||
|
||||
if ( (*writerfn)(writerst, "LinuxGuestRecord", 16) ||
|
||||
(*writerfn)(writerst, name, sizeof(name)) ||
|
||||
(*writerfn)(writerst, &nr_pfns, sizeof(unsigned long)) ||
|
||||
(*writerfn)(writerst, pfn_to_mfn_frame_list, PAGE_SIZE) )
|
||||
{
|
||||
ERROR("Error when writing to state file (1)");
|
||||
if ( xcio_write(ioctxt, "LinuxGuestRecord", 16) ||
|
||||
xcio_write(ioctxt, name, sizeof(name)) ||
|
||||
xcio_write(ioctxt, &nr_pfns, sizeof(unsigned long)) ||
|
||||
xcio_write(ioctxt, pfn_to_mfn_frame_list, PAGE_SIZE) ){
|
||||
xcio_error(ioctxt, "Error writing header");
|
||||
goto out;
|
||||
}
|
||||
if(write_vmconfig(ioctxt)){
|
||||
xcio_error(ioctxt, "Error writing vmconfig");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -521,8 +506,7 @@ int xc_linux_save(int xc_handle,
|
||||
|
||||
/* Now write out each data page, canonicalising page tables as we go... */
|
||||
|
||||
while(1)
|
||||
{
|
||||
while(1){
|
||||
unsigned int prev_pc, sent_this_iter, N, batch;
|
||||
|
||||
iter++;
|
||||
@@ -531,15 +515,13 @@ int xc_linux_save(int xc_handle,
|
||||
prev_pc = 0;
|
||||
N=0;
|
||||
|
||||
verbose_printf("Saving memory pages: iter %d 0%%", iter);
|
||||
xcio_info(ioctxt, "Saving memory pages: iter %d 0%%", iter);
|
||||
|
||||
while( N < nr_pfns )
|
||||
{
|
||||
while( N < nr_pfns ){
|
||||
unsigned int this_pc = (N * 100) / nr_pfns;
|
||||
|
||||
if ( (this_pc - prev_pc) >= 5 )
|
||||
{
|
||||
verbose_printf("\b\b\b\b%3d%%", this_pc);
|
||||
if ( (this_pc - prev_pc) >= 5 ){
|
||||
xcio_info(ioctxt, "\b\b\b\b%3d%%", this_pc);
|
||||
prev_pc = this_pc;
|
||||
}
|
||||
|
||||
@@ -549,9 +531,8 @@ int xc_linux_save(int xc_handle,
|
||||
if ( !last_iter &&
|
||||
xc_shadow_control(xc_handle, domid,
|
||||
DOM0_SHADOW_CONTROL_OP_PEEK,
|
||||
to_skip, nr_pfns, NULL) != nr_pfns )
|
||||
{
|
||||
ERROR("Error peeking shadow bitmap");
|
||||
to_skip, nr_pfns, NULL) != nr_pfns ) {
|
||||
xcio_error(ioctxt, "Error peeking shadow bitmap");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -563,23 +544,26 @@ int xc_linux_save(int xc_handle,
|
||||
{
|
||||
int n = permute(N, nr_pfns, order_nr );
|
||||
|
||||
if ( 0 && debug )
|
||||
if ( 0 && debug ) {
|
||||
fprintf(stderr,"%d pfn= %08lx mfn= %08lx %d "
|
||||
" [mfn]= %08lx\n",
|
||||
iter, n, live_pfn_to_mfn_table[n],
|
||||
iter, (unsigned long)n, live_pfn_to_mfn_table[n],
|
||||
test_bit(n,to_send),
|
||||
live_mfn_to_pfn_table[live_pfn_to_mfn_table[n]&
|
||||
0xFFFFF]);
|
||||
}
|
||||
|
||||
if ( !last_iter &&
|
||||
test_bit(n, to_send) &&
|
||||
test_bit(n, to_skip) )
|
||||
test_bit(n, to_skip) ) {
|
||||
skip_this_iter++; /* stats keeping */
|
||||
}
|
||||
|
||||
if ( !((test_bit(n, to_send) && !test_bit(n, to_skip)) ||
|
||||
(test_bit(n, to_send) && last_iter) ||
|
||||
(test_bit(n, to_fix) && last_iter)) )
|
||||
(test_bit(n, to_fix) && last_iter)) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* we get here if:
|
||||
1. page is marked to_send & hasn't already been re-dirtied
|
||||
@@ -590,8 +574,7 @@ int xc_linux_save(int xc_handle,
|
||||
pfn_batch[batch] = n;
|
||||
pfn_type[batch] = live_pfn_to_mfn_table[n];
|
||||
|
||||
if( pfn_type[batch] == 0x80000004 )
|
||||
{
|
||||
if( pfn_type[batch] == 0x80000004 ){
|
||||
/* not currently in pusedo-physical map -- set bit
|
||||
in to_fix that we must send this page in last_iter
|
||||
unless its sent sooner anyhow */
|
||||
@@ -625,22 +608,18 @@ int xc_linux_save(int xc_handle,
|
||||
if ( (region_base = mfn_mapper_map_batch(xc_handle, domid,
|
||||
PROT_READ,
|
||||
pfn_type,
|
||||
batch)) == 0 )
|
||||
{
|
||||
PERROR("map batch failed");
|
||||
batch)) == 0 ){
|
||||
xcio_perror(ioctxt, "map batch failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( get_pfn_type_batch(xc_handle, domid, batch, pfn_type) )
|
||||
{
|
||||
ERROR("get_pfn_type_batch failed");
|
||||
if ( get_pfn_type_batch(xc_handle, domid, batch, pfn_type) ){
|
||||
xcio_error(ioctxt, "get_pfn_type_batch failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for ( j = 0; j < batch; j++ )
|
||||
{
|
||||
if ( (pfn_type[j] & LTAB_MASK) == XTAB )
|
||||
{
|
||||
for ( j = 0; j < batch; j++ ){
|
||||
if ( (pfn_type[j] & LTAB_MASK) == XTAB ){
|
||||
DDPRINTF("type fail: page %i mfn %08lx\n",j,pfn_type[j]);
|
||||
continue;
|
||||
}
|
||||
@@ -658,39 +637,33 @@ int xc_linux_save(int xc_handle,
|
||||
pfn_type[j] = (pfn_type[j] & LTAB_MASK) | pfn_batch[j];
|
||||
}
|
||||
|
||||
if ( (*writerfn)(writerst, &batch, sizeof(int) ) )
|
||||
{
|
||||
ERROR("Error when writing to state file (2)");
|
||||
if ( xcio_write(ioctxt, &batch, sizeof(int) ) ){
|
||||
xcio_error(ioctxt, "Error when writing to state file (2)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( (*writerfn)(writerst, pfn_type, sizeof(unsigned long)*j ) )
|
||||
{
|
||||
ERROR("Error when writing to state file (3)");
|
||||
if ( xcio_write(ioctxt, pfn_type, sizeof(unsigned long)*j ) ){
|
||||
xcio_error(ioctxt, "Error when writing to state file (3)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* entering this loop, pfn_type is now in pfns (Not mfns) */
|
||||
for( j = 0; j < batch; j++ )
|
||||
{
|
||||
for( j = 0; j < batch; j++ ){
|
||||
/* write out pages in batch */
|
||||
if( (pfn_type[j] & LTAB_MASK) == XTAB)
|
||||
{
|
||||
if( (pfn_type[j] & LTAB_MASK) == XTAB){
|
||||
DDPRINTF("SKIP BOGUS page %i mfn %08lx\n",j,pfn_type[j]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ((pfn_type[j] & LTAB_MASK) == L1TAB) ||
|
||||
((pfn_type[j] & LTAB_MASK) == L2TAB) )
|
||||
{
|
||||
((pfn_type[j] & LTAB_MASK) == L2TAB) ){
|
||||
memcpy(page, region_base + (PAGE_SIZE*j), PAGE_SIZE);
|
||||
|
||||
for ( k = 0;
|
||||
k < (((pfn_type[j] & LTAB_MASK) == L2TAB) ?
|
||||
(HYPERVISOR_VIRT_START >> L2_PAGETABLE_SHIFT) :
|
||||
1024);
|
||||
k++ )
|
||||
{
|
||||
k++ ){
|
||||
unsigned long pfn;
|
||||
|
||||
if ( !(page[k] & _PAGE_PRESENT) )
|
||||
@@ -726,20 +699,16 @@ int xc_linux_save(int xc_handle,
|
||||
|
||||
} /* end of page table rewrite for loop */
|
||||
|
||||
if ( (*writerfn)(writerst, page, PAGE_SIZE) )
|
||||
{
|
||||
ERROR("Error when writing to state file (4)");
|
||||
if ( xcio_write(ioctxt, page, PAGE_SIZE) ){
|
||||
xcio_error(ioctxt, "Error when writing to state file (4)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
} /* end of it's a PT page */
|
||||
else
|
||||
{ /* normal page */
|
||||
} /* end of it's a PT page */ else { /* normal page */
|
||||
|
||||
if ( (*writerfn)(writerst, region_base + (PAGE_SIZE*j),
|
||||
PAGE_SIZE) )
|
||||
{
|
||||
ERROR("Error when writing to state file (5)");
|
||||
if ( xcio_write(ioctxt, region_base + (PAGE_SIZE*j),
|
||||
PAGE_SIZE) ){
|
||||
xcio_error(ioctxt, "Error when writing to state file (5)");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -755,37 +724,34 @@ int xc_linux_save(int xc_handle,
|
||||
|
||||
total_sent += sent_this_iter;
|
||||
|
||||
verbose_printf("\r %d: sent %d, skipped %d, ",
|
||||
xcio_info(ioctxt, "\r %d: sent %d, skipped %d, ",
|
||||
iter, sent_this_iter, skip_this_iter );
|
||||
|
||||
if ( last_iter )
|
||||
{
|
||||
if ( last_iter ) {
|
||||
print_stats( xc_handle, domid, sent_this_iter, &stats, 1);
|
||||
|
||||
verbose_printf("Total pages sent= %d (%.2fx)\n",
|
||||
xcio_info(ioctxt, "Total pages sent= %d (%.2fx)\n",
|
||||
total_sent, ((float)total_sent)/nr_pfns );
|
||||
verbose_printf("(of which %d were fixups)\n", needed_to_fix );
|
||||
xcio_info(ioctxt, "(of which %d were fixups)\n", needed_to_fix );
|
||||
}
|
||||
|
||||
if ( debug && last_iter )
|
||||
{
|
||||
if (last_iter && debug){
|
||||
int minusone = -1;
|
||||
memset( to_send, 0xff, (nr_pfns+8)/8 );
|
||||
debug = 0;
|
||||
printf("Entering debug resend-all mode\n");
|
||||
|
||||
/* send "-1" to put receiver into debug mode */
|
||||
if ( (*writerfn)(writerst, &minusone, sizeof(int)) )
|
||||
if ( xcio_write(ioctxt, &minusone, sizeof(int)) )
|
||||
{
|
||||
ERROR("Error when writing to state file (6)");
|
||||
xcio_error(ioctxt, "Error when writing to state file (6)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( last_iter )
|
||||
break;
|
||||
if ( last_iter ) break;
|
||||
|
||||
if ( live )
|
||||
{
|
||||
@@ -805,7 +771,7 @@ int xc_linux_save(int xc_handle,
|
||||
DOM0_SHADOW_CONTROL_OP_CLEAN2,
|
||||
to_send, nr_pfns, &stats ) != nr_pfns )
|
||||
{
|
||||
ERROR("Error flushing shadow PT");
|
||||
xcio_error(ioctxt, "Error flushing shadow PT");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -824,60 +790,51 @@ int xc_linux_save(int xc_handle,
|
||||
rc = 0;
|
||||
|
||||
/* Zero terminate */
|
||||
if ( (*writerfn)(writerst, &rc, sizeof(int)) )
|
||||
if ( xcio_write(ioctxt, &rc, sizeof(int)) )
|
||||
{
|
||||
ERROR("Error when writing to state file (6)");
|
||||
xcio_error(ioctxt, "Error when writing to state file (6)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get the final execution context */
|
||||
if ( xc_domain_getfullinfo( xc_handle, domid, &op, &ctxt) )
|
||||
{
|
||||
PERROR("Could not get full domain info");
|
||||
xcio_perror(ioctxt, "Could not get full domain info");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Canonicalise the suspend-record frame number. */
|
||||
if ( !translate_mfn_to_pfn(&ctxt.cpu_ctxt.esi) )
|
||||
{
|
||||
ERROR("State record is not in range of pseudophys map");
|
||||
if ( !translate_mfn_to_pfn(&ctxt.cpu_ctxt.esi) ){
|
||||
xcio_error(ioctxt, "State record is not in range of pseudophys map");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Canonicalise each GDT frame number. */
|
||||
for ( i = 0; i < ctxt.gdt_ents; i += 512 )
|
||||
{
|
||||
if ( !translate_mfn_to_pfn(&ctxt.gdt_frames[i]) )
|
||||
{
|
||||
ERROR("GDT frame is not in range of pseudophys map");
|
||||
for ( i = 0; i < ctxt.gdt_ents; i += 512 ) {
|
||||
if ( !translate_mfn_to_pfn(&ctxt.gdt_frames[i]) ) {
|
||||
xcio_error(ioctxt, "GDT frame is not in range of pseudophys map");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Canonicalise the page table base pointer. */
|
||||
if ( !MFN_IS_IN_PSEUDOPHYS_MAP(ctxt.pt_base >> PAGE_SHIFT) )
|
||||
{
|
||||
ERROR("PT base is not in range of pseudophys map");
|
||||
if ( !MFN_IS_IN_PSEUDOPHYS_MAP(ctxt.pt_base >> PAGE_SHIFT) ) {
|
||||
xcio_error(ioctxt, "PT base is not in range of pseudophys map");
|
||||
goto out;
|
||||
}
|
||||
ctxt.pt_base = live_mfn_to_pfn_table[ctxt.pt_base >> PAGE_SHIFT] <<
|
||||
PAGE_SHIFT;
|
||||
|
||||
if ( (*writerfn)(writerst, &ctxt, sizeof(ctxt)) ||
|
||||
(*writerfn)(writerst, live_shinfo, PAGE_SIZE) )
|
||||
{
|
||||
ERROR("Error when writing to state file (1)");
|
||||
if ( xcio_write(ioctxt, &ctxt, sizeof(ctxt)) ||
|
||||
xcio_write(ioctxt, live_shinfo, PAGE_SIZE) ) {
|
||||
xcio_error(ioctxt, "Error when writing to state file (1)");
|
||||
goto out;
|
||||
}
|
||||
munmap(live_shinfo, PAGE_SIZE);
|
||||
|
||||
out:
|
||||
|
||||
if ( pfn_type != NULL )
|
||||
free(pfn_type);
|
||||
|
||||
if ( pfn_type != NULL ) free(pfn_type);
|
||||
DPRINTF("Save exit rc=%d\n",rc);
|
||||
|
||||
return !!rc;
|
||||
|
||||
}
|
||||
@@ -136,6 +136,8 @@ int close_pfn_mapper(int pm_handle);
|
||||
void *map_pfn_writeable(int pm_handle, unsigned long pfn);
|
||||
void *map_pfn_readonly(int pm_handle, unsigned long pfn);
|
||||
void unmap_pfn(int pm_handle, void *vaddr);
|
||||
int get_pfn_type_batch(int xc_handle, u32 dom, int num, unsigned long *arr);
|
||||
unsigned long csum_page (void * page);
|
||||
|
||||
/*
|
||||
* MMU updates.
|
||||
@@ -202,4 +204,10 @@ void * mfn_mapper_queue_entry(mfn_mapper_t *t, int offset,
|
||||
|
||||
long long xc_domain_get_cpu_usage( int xc_handle, domid_t domid );
|
||||
|
||||
#include "xc_io.h"
|
||||
|
||||
int xc_domain_getfullinfo(int xc_handle,
|
||||
u32 domid,
|
||||
dom0_op_t *op,
|
||||
full_execution_context_t *ctxt );
|
||||
#endif /* __XC_PRIVATE_H__ */
|
||||
+94
-275
@@ -14,6 +14,8 @@
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "xc_private.h"
|
||||
#include "gzip_stream.h"
|
||||
|
||||
/* Needed for Python versions earlier than 2.3. */
|
||||
#ifndef PyMODINIT_FUNC
|
||||
@@ -189,115 +191,35 @@ static PyObject *pyxc_domain_getinfo(PyObject *self,
|
||||
return list;
|
||||
}
|
||||
|
||||
static PyObject *tcp_save(XcObject *xc, u32 dom, char *url, unsigned flags){
|
||||
#define max_namelen 64
|
||||
char server[max_namelen];
|
||||
char *port_s;
|
||||
int port=777;
|
||||
int sd = -1;
|
||||
struct hostent *h;
|
||||
struct sockaddr_in s;
|
||||
int sockbufsize;
|
||||
static int file_save(XcObject *xc, XcIOContext *ctxt, char *state_file){
|
||||
int rc = -1;
|
||||
|
||||
int writerfn(void *fd, const void *buf, size_t count) {
|
||||
int tot = 0, rc;
|
||||
do {
|
||||
rc = write( (int) fd, ((char*)buf)+tot, count-tot );
|
||||
if ( rc < 0 ) { perror("WRITE"); return rc; };
|
||||
tot += rc;
|
||||
}
|
||||
while ( tot < count );
|
||||
return 0;
|
||||
int fd = -1;
|
||||
int open_flags = (O_CREAT | O_EXCL | O_WRONLY);
|
||||
int open_mode = 0644;
|
||||
|
||||
printf("%s>\n", __FUNCTION__);
|
||||
fd = open(state_file, open_flags, open_mode);
|
||||
if(fd < 0){
|
||||
xcio_perror(ctxt, "Could not open file for writing");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
strncpy( server, url+strlen("tcp://"), max_namelen);
|
||||
server[max_namelen-1]='\0';
|
||||
if ( (port_s = strchr(server,':')) != NULL ) {
|
||||
*port_s = '\0';
|
||||
port = atoi(port_s+1);
|
||||
/* Compression rate 1: we want speed over compression.
|
||||
* We're mainly going for those zero pages, after all.
|
||||
*/
|
||||
printf("%s>gzip_stream_fdopen... \n", __FUNCTION__);
|
||||
ctxt->io = gzip_stream_fdopen(fd, "wb1");
|
||||
if(!ctxt->io){
|
||||
xcio_perror(ctxt, "Could not allocate compression state");
|
||||
goto exit;
|
||||
}
|
||||
printf("X server=%s port=%d\n",server,port);
|
||||
h = gethostbyname(server);
|
||||
sd = socket(AF_INET, SOCK_STREAM,0);
|
||||
if (sd < 0) goto serr;
|
||||
s.sin_family = AF_INET;
|
||||
bcopy ( h->h_addr, &(s.sin_addr.s_addr), h->h_length);
|
||||
s.sin_port = htons(port);
|
||||
if ( connect(sd, (struct sockaddr *) &s, sizeof(s)) ) goto serr;
|
||||
sockbufsize=128*1024;
|
||||
if ( setsockopt(sd, SOL_SOCKET, SO_SNDBUF, &sockbufsize, sizeof sockbufsize) < 0 ) goto serr;
|
||||
|
||||
if ( xc_linux_save(xc->xc_handle, dom, flags, writerfn, (void*)sd) == 0 ) {
|
||||
if ( read( sd, &rc, sizeof(int) ) != sizeof(int) ) goto serr;
|
||||
|
||||
if ( rc == 0 ) {
|
||||
printf("Migration succesful -- destroy local copy\n");
|
||||
xc_domain_destroy(xc->xc_handle, dom);
|
||||
close(sd);
|
||||
Py_INCREF(zero);
|
||||
return zero;
|
||||
} else {
|
||||
errno = rc;
|
||||
}
|
||||
}
|
||||
|
||||
serr:
|
||||
printf("Migration failed -- restart local copy\n");
|
||||
xc_domain_unpause(xc->xc_handle, dom);
|
||||
PyErr_SetFromErrno(xc_error);
|
||||
if ( sd >= 0 ) close(sd);
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
static PyObject *file_save(XcObject *xc, u32 dom, char *state_file, unsigned flags){
|
||||
int fd = -1;
|
||||
gzFile gfd = NULL;
|
||||
|
||||
int writerfn(void *fd, const void *buf, size_t count) {
|
||||
int rc;
|
||||
while ( ((rc = gzwrite( (gzFile*)fd, (void*)buf, count)) == -1) &&
|
||||
(errno = EINTR) )
|
||||
continue;
|
||||
return ! (rc == count);
|
||||
}
|
||||
|
||||
if (strncmp(state_file,"file:",strlen("file:")) == 0){
|
||||
state_file += strlen("file:");
|
||||
}
|
||||
|
||||
if ( (fd = open(state_file, O_CREAT|O_EXCL|O_WRONLY, 0644)) == -1 ) {
|
||||
perror("Could not open file for writing");
|
||||
goto err;
|
||||
}
|
||||
/*
|
||||
* Compression rate 1: we want speed over compression.
|
||||
* We're mainly going for those zero pages, after all.
|
||||
*/
|
||||
if ( (gfd = gzdopen(fd, "wb1")) == NULL ) {
|
||||
perror("Could not allocate compression state for state file");
|
||||
close(fd);
|
||||
goto err;
|
||||
}
|
||||
if ( xc_linux_save(xc->xc_handle, dom, flags, writerfn, gfd) == 0 ) {
|
||||
/* kill domain. We don't want to do this for checkpointing, but
|
||||
if we don't do it here I think people will hurt themselves
|
||||
by accident... */
|
||||
xc_domain_destroy(xc->xc_handle, dom);
|
||||
gzclose(gfd);
|
||||
close(fd);
|
||||
|
||||
Py_INCREF(zero);
|
||||
return zero;
|
||||
}
|
||||
|
||||
err:
|
||||
PyErr_SetFromErrno(xc_error);
|
||||
if ( gfd != NULL ) gzclose(gfd);
|
||||
if ( fd >= 0 ) close(fd);
|
||||
unlink(state_file);
|
||||
return NULL;
|
||||
printf("%s> xc_linux_save...\n", __FUNCTION__);
|
||||
rc = xc_linux_save(xc->xc_handle, ctxt);
|
||||
exit:
|
||||
if(ctxt->io) IOStream_close(ctxt->io);
|
||||
if(fd >= 0) close(fd);
|
||||
unlink(state_file);
|
||||
printf("%s> rc=%d\n", __FUNCTION__, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static PyObject *pyxc_linux_save(PyObject *self,
|
||||
@@ -306,195 +228,92 @@ static PyObject *pyxc_linux_save(PyObject *self,
|
||||
{
|
||||
XcObject *xc = (XcObject *)self;
|
||||
|
||||
u32 dom;
|
||||
u32 dom;
|
||||
char *state_file;
|
||||
int progress = 1, live = -1, debug = 0;
|
||||
int progress = 1, debug = 0;
|
||||
unsigned int flags = 0;
|
||||
PyObject *val = NULL;
|
||||
int rc = -1;
|
||||
XcIOContext ioctxt = { .info = iostdout, .err = iostderr };
|
||||
|
||||
static char *kwd_list[] = { "dom", "state_file", "progress",
|
||||
"live", "debug", NULL };
|
||||
static char *kwd_list[] = { "dom", "state_file", "vmconfig", "progress", "debug", NULL };
|
||||
|
||||
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is|iii", kwd_list,
|
||||
&dom, &state_file, &progress,
|
||||
&live, &debug) )
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "is|sii", kwd_list,
|
||||
&ioctxt.domain,
|
||||
&state_file,
|
||||
&ioctxt.vmconfig,
|
||||
&progress,
|
||||
&debug)){
|
||||
goto exit;
|
||||
|
||||
if (progress) flags |= XCFLAGS_VERBOSE;
|
||||
if (live == 1) flags |= XCFLAGS_LIVE;
|
||||
if (debug) flags |= XCFLAGS_DEBUG;
|
||||
|
||||
if ( strncmp(state_file,"tcp:", strlen("tcp:")) == 0 ) {
|
||||
/* default to live for tcp */
|
||||
if (live == -1) flags |= XCFLAGS_LIVE;
|
||||
val = tcp_save(xc, dom, state_file, flags);
|
||||
} else {
|
||||
val = file_save(xc, dom, state_file, flags);
|
||||
}
|
||||
ioctxt.vmconfig_n = (ioctxt.vmconfig ? strlen(ioctxt.vmconfig) : 0);
|
||||
if (progress) ioctxt.flags |= XCFLAGS_VERBOSE;
|
||||
if (debug) ioctxt.flags |= XCFLAGS_DEBUG;
|
||||
if(!state_file || state_file[0] == '\0') goto exit;
|
||||
rc = file_save(xc, &ioctxt, state_file);
|
||||
if(rc){
|
||||
PyErr_SetFromErrno(xc_error);
|
||||
goto exit;
|
||||
}
|
||||
//xc_domain_destroy(xc->xc_handle, dom);
|
||||
Py_INCREF(zero);
|
||||
val = zero;
|
||||
exit:
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
static int file_restore(XcObject *xc, XcIOContext *ioctxt, char *state_file){
|
||||
int rc = -1;
|
||||
|
||||
ioctxt->io = gzip_stream_fopen(state_file, "rb");
|
||||
if (!ioctxt->io) {
|
||||
xcio_perror(ioctxt, "Could not open file for reading");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = xc_linux_restore(xc->xc_handle, ioctxt);
|
||||
exit:
|
||||
if(ioctxt->io) IOStream_close(ioctxt->io);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static PyObject *pyxc_linux_restore(PyObject *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
XcObject *xc = (XcObject *)self;
|
||||
char *state_file;
|
||||
int progress = 1, debug = 0;
|
||||
u32 dom;
|
||||
PyObject *val = NULL;
|
||||
XcIOContext ioctxt = { .info = iostdout, .err = iostderr };
|
||||
int rc =-1;
|
||||
|
||||
char *state_file;
|
||||
int progress = 1;
|
||||
u32 dom;
|
||||
unsigned int flags = 0;
|
||||
static char *kwd_list[] = { "state_file", "progress", "debug", NULL };
|
||||
|
||||
static char *kwd_list[] = { "dom", "state_file", "progress", NULL };
|
||||
|
||||
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is|i", kwd_list,
|
||||
&dom, &state_file, &progress) )
|
||||
return NULL;
|
||||
|
||||
if ( progress )
|
||||
flags |= XCFLAGS_VERBOSE;
|
||||
|
||||
if ( strncmp(state_file,"tcp:", strlen("tcp:")) == 0 )
|
||||
{
|
||||
#define max_namelen 64
|
||||
char server[max_namelen];
|
||||
char *port_s;
|
||||
int port=777;
|
||||
int ld = -1, sd = -1;
|
||||
struct hostent *h;
|
||||
struct sockaddr_in s, d, p;
|
||||
socklen_t dlen, plen;
|
||||
int sockbufsize;
|
||||
int on = 1, rc = -1;
|
||||
|
||||
int readerfn(void *fd, void *buf, size_t count)
|
||||
{
|
||||
int rc, tot = 0;
|
||||
do {
|
||||
rc = read( (int) fd, ((char*)buf)+tot, count-tot );
|
||||
if ( rc < 0 ) { perror("READ"); return rc; }
|
||||
if ( rc == 0 ) { printf("read: need %d, tot=%d got zero\n",
|
||||
count-tot, tot); return -1; }
|
||||
tot += rc;
|
||||
}
|
||||
while ( tot < count );
|
||||
return 0;
|
||||
}
|
||||
|
||||
strncpy( server, state_file+strlen("tcp://"), max_namelen);
|
||||
server[max_namelen-1]='\0';
|
||||
if ( (port_s = strchr(server,':')) != NULL )
|
||||
{
|
||||
*port_s = '\0';
|
||||
port = atoi(port_s+1);
|
||||
}
|
||||
|
||||
printf("X server=%s port=%d\n",server,port);
|
||||
|
||||
h = gethostbyname(server);
|
||||
ld = socket (AF_INET,SOCK_STREAM,0);
|
||||
if ( ld < 0 ) goto serr;
|
||||
s.sin_family = AF_INET;
|
||||
s.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
s.sin_port = htons(port);
|
||||
|
||||
if ( setsockopt(ld, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0 )
|
||||
goto serr;
|
||||
|
||||
if ( bind(ld, (struct sockaddr *) &s, sizeof(s)) )
|
||||
goto serr;
|
||||
|
||||
if ( listen(ld, 1) )
|
||||
goto serr;
|
||||
|
||||
dlen=sizeof(struct sockaddr);
|
||||
if ( (sd = accept(ld, (struct sockaddr *) &d, &dlen )) < 0 )
|
||||
goto serr;
|
||||
|
||||
plen = sizeof(p);
|
||||
if ( getpeername(sd, (struct sockaddr_in *) &p,
|
||||
&plen) < 0 )
|
||||
goto serr;
|
||||
|
||||
printf("Accepted connection from %s\n", inet_ntoa(p.sin_addr));
|
||||
|
||||
sockbufsize=128*1024;
|
||||
if ( setsockopt(sd, SOL_SOCKET, SO_SNDBUF, &sockbufsize,
|
||||
sizeof sockbufsize) < 0 )
|
||||
goto serr;
|
||||
|
||||
rc = xc_linux_restore(xc->xc_handle, dom, flags,
|
||||
readerfn, (void*)sd, &dom);
|
||||
|
||||
write( sd, &rc, sizeof(int) );
|
||||
|
||||
if (rc == 0)
|
||||
{
|
||||
close(sd);
|
||||
Py_INCREF(zero);
|
||||
return zero;
|
||||
}
|
||||
errno = rc;
|
||||
|
||||
serr:
|
||||
PyErr_SetFromErrno(xc_error);
|
||||
if ( ld >= 0 ) close(ld);
|
||||
if ( sd >= 0 ) close(sd);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
int fd = -1;
|
||||
gzFile gfd = NULL;
|
||||
|
||||
int readerfn(void *fd, void *buf, size_t count)
|
||||
{
|
||||
int rc;
|
||||
while ( ((rc = gzread( (gzFile*)fd, (void*)buf, count)) == -1) &&
|
||||
(errno = EINTR) )
|
||||
continue;
|
||||
return ! (rc == count);
|
||||
}
|
||||
|
||||
if ( strncmp(state_file,"file:",strlen("file:")) == 0 )
|
||||
state_file += strlen("file:");
|
||||
|
||||
if ( (fd = open(state_file, O_RDONLY)) == -1 )
|
||||
{
|
||||
perror("Could not open file for writing");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compression rate 1: we want speed over compression.
|
||||
* We're mainly going for those zero pages, after all.
|
||||
*/
|
||||
if ( (gfd = gzdopen(fd, "rb")) == NULL )
|
||||
{
|
||||
perror("Could not allocate compression state for state file");
|
||||
close(fd);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
if ( xc_linux_restore(xc->xc_handle, dom, flags,
|
||||
readerfn, gfd, &dom) == 0 )
|
||||
{
|
||||
gzclose(gfd);
|
||||
close(fd);
|
||||
|
||||
Py_INCREF(zero);
|
||||
return zero;
|
||||
}
|
||||
|
||||
err:
|
||||
PyErr_SetFromErrno(xc_error);
|
||||
if ( gfd != NULL ) gzclose(gfd);
|
||||
if ( fd >= 0 ) close(fd);
|
||||
return NULL;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "is|ii", kwd_list,
|
||||
&ioctxt.domain,
|
||||
&state_file,
|
||||
&progress,
|
||||
&debug)){
|
||||
goto exit;
|
||||
}
|
||||
if (progress) ioctxt.flags |= XCFLAGS_VERBOSE;
|
||||
if (debug) ioctxt.flags |= XCFLAGS_DEBUG;
|
||||
|
||||
if(!state_file || state_file[0] == '\0') goto exit;
|
||||
rc = file_restore(xc, &ioctxt, state_file);
|
||||
if(rc){
|
||||
PyErr_SetFromErrno(xc_error);
|
||||
goto exit;
|
||||
}
|
||||
val = Py_BuildValue("{s:i,s:s}",
|
||||
"dom", ioctxt.domain,
|
||||
"vmconfig", ioctxt.vmconfig);
|
||||
//? free(ioctxt.vmconfig);
|
||||
exit:
|
||||
return val;
|
||||
}
|
||||
|
||||
static PyObject *pyxc_linux_build(PyObject *self,
|
||||
|
||||
@@ -3,12 +3,17 @@ from distutils.core import setup, Extension
|
||||
|
||||
module = Extension("xc",
|
||||
extra_compile_args = ["-fno-strict-aliasing"],
|
||||
include_dirs = ["../lib"],
|
||||
library_dirs = ["../lib"],
|
||||
include_dirs = ["../lib",
|
||||
"../../../xen/include/hypervisor-ifs",
|
||||
"../../../linux-xen-sparse/include",
|
||||
"../../xu/lib",
|
||||
"../../lib" ],
|
||||
library_dirs = ["../lib",
|
||||
"../../lib" ],
|
||||
libraries = ["xc"],
|
||||
sources = ["Xc.c"])
|
||||
|
||||
setup(name = "xc",
|
||||
version = "1.0",
|
||||
version = "2.0",
|
||||
ext_package = "xen.ext",
|
||||
ext_modules = [module])
|
||||
@@ -9,7 +9,6 @@ import sys
|
||||
from twisted.internet import defer
|
||||
|
||||
import xen.ext.xc; xc = xen.ext.xc.new()
|
||||
import xenctl.ip
|
||||
|
||||
import sxp
|
||||
import XendRoot
|
||||
@@ -235,7 +234,7 @@ class XendDomain:
|
||||
def domain_get(self, id):
|
||||
id = str(id)
|
||||
self.refresh_domain(id)
|
||||
return self.domain[id]
|
||||
return self.domain.get(id)
|
||||
|
||||
def domain_unpause(self, id):
|
||||
"""(Re)start domain running.
|
||||
@@ -278,22 +277,26 @@ class XendDomain:
|
||||
"""
|
||||
# Need a cancel too?
|
||||
pass
|
||||
|
||||
|
||||
def domain_save(self, id, dst, progress=0):
|
||||
"""Save domain state to file, destroy domain.
|
||||
"""
|
||||
dom = int(id)
|
||||
dominfo = self.domain_get(id)
|
||||
if not dominfo:
|
||||
return -1
|
||||
vmconfig = sxp.to_string(dominfo.sxpr())
|
||||
self.domain_pause(id)
|
||||
eserver.inject('xend.domain.save', id)
|
||||
rc = xc.linux_save(dom=dom, state_file=dst, progress=progress)
|
||||
rc = xc.linux_save(dom=dom, state_file=dst, vmconfig=vmconfig, progress=progress)
|
||||
if rc == 0:
|
||||
self.domain_destroy(id)
|
||||
return rc
|
||||
|
||||
def domain_restore(self, src, config, progress=0):
|
||||
def domain_restore(self, src, progress=0):
|
||||
"""Restore domain from file.
|
||||
"""
|
||||
dominfo = XendDomainInfo.dom_restore(dom, config)
|
||||
dominfo = XendDomainInfo.vm_restore(src, progress=progress)
|
||||
self._add_domain(dominfo.id, dominfo)
|
||||
return dominfo
|
||||
|
||||
@@ -332,9 +335,9 @@ class XendDomain:
|
||||
if not dominfo: return None
|
||||
return dominfo.get_device_by_index(vif)
|
||||
|
||||
def domain_vif_ip_add(self, dom, vif, ip):
|
||||
dom = int(dom)
|
||||
return xenctl.ip.setup_vfr_rules_for_vif(dom, vif, ip)
|
||||
## def domain_vif_ip_add(self, dom, vif, ip):
|
||||
## dom = int(dom)
|
||||
## return xenctl.ip.setup_vfr_rules_for_vif(dom, vif, ip)
|
||||
|
||||
def domain_vbd_ls(self, dom):
|
||||
dominfo = self.domain_get(dom)
|
||||
|
||||
@@ -17,7 +17,7 @@ import os
|
||||
from twisted.internet import defer
|
||||
|
||||
import xen.ext.xc; xc = xen.ext.xc.new()
|
||||
import xenctl.ip
|
||||
import xen.util.ip
|
||||
|
||||
import sxp
|
||||
|
||||
@@ -167,14 +167,14 @@ def vif_up(iplist):
|
||||
print >> open(IP_NONLOCAL_BIND, 'w'), str(v)
|
||||
|
||||
def link_local(ip):
|
||||
return xenctl.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0')
|
||||
return xen.util.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0')
|
||||
|
||||
def arping(ip, gw):
|
||||
cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw)
|
||||
print cmd
|
||||
os.system(cmd)
|
||||
|
||||
gateway = xenctl.ip.get_current_ipgw() or '255.255.255.255'
|
||||
gateway = xen.util.ip.get_current_ipgw() or '255.255.255.255'
|
||||
nlb = get_ip_nonlocal_bind()
|
||||
if not nlb: set_ip_nonlocal_bind(1)
|
||||
try:
|
||||
@@ -272,22 +272,23 @@ def vm_recreate(config, info):
|
||||
d.callback(vm)
|
||||
return d
|
||||
|
||||
def vm_restore(src, config, progress=0):
|
||||
def vm_restore(src, progress=0):
|
||||
"""Restore a VM from a disk image.
|
||||
|
||||
src saved state to restore
|
||||
config configuration
|
||||
progress progress reporting flag
|
||||
returns deferred
|
||||
raises VmError for invalid configuration
|
||||
"""
|
||||
vm = XendDomainInfo()
|
||||
vm.config = config
|
||||
ostype = "linux" #todo set from config
|
||||
ostype = "linux" #todo Set from somewhere (store in the src?).
|
||||
restorefn = getattr(xc, "%s_restore" % ostype)
|
||||
dom = restorefn(state_file=src, progress=progress)
|
||||
d = restorefn(state_file=src, progress=progress)
|
||||
dom = int(d['dom'])
|
||||
if dom < 0:
|
||||
raise VMError('restore failed')
|
||||
vmconfig = sxp.from_string(d['vmconfig'])
|
||||
vm.config = sxp.child_value(vmconfig, 'config')
|
||||
deferred = vm.dom_configure(dom)
|
||||
def vifs_cb(val, vm):
|
||||
vif_up(vm.ipaddrs)
|
||||
@@ -855,9 +856,7 @@ def vm_field_vfr(vm, config, val, index):
|
||||
if not ip:
|
||||
raise VmError('vfr: missing ip address')
|
||||
ipaddrs.append(ip);
|
||||
#Don't do this in new i/o model.
|
||||
#print 'vm_field_vfr> add rule', 'dom=', vm.dom, 'vif=', vif, 'ip=', ip
|
||||
#xenctl.ip.setup_vfr_rules_for_vif(vm.dom, vif, ip)
|
||||
# todo: Configure the ipaddrs.
|
||||
vm.ipaddrs = ipaddrs
|
||||
|
||||
def vnet_bridge(vnet, vmac, dom, idx):
|
||||
|
||||
@@ -44,13 +44,6 @@ class SrvDomain(SrvDir):
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_restore(self, op, req):
|
||||
fn = FormFn(self.xd.domain_restore,
|
||||
[['dom', 'int'],
|
||||
['file', 'str']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_migrate(self, op, req):
|
||||
fn = FormFn(self.xd.domain_migrate,
|
||||
[['dom', 'int'],
|
||||
|
||||
@@ -7,6 +7,7 @@ from twisted.web import error
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import XendDomain
|
||||
from xen.xend.Args import FormFn
|
||||
|
||||
from SrvDir import SrvDir
|
||||
from SrvDomain import SrvDomain
|
||||
@@ -88,6 +89,12 @@ class SrvDomainDir(SrvDir):
|
||||
out.close()
|
||||
return val
|
||||
|
||||
def op_restore(self, op, req):
|
||||
fn = FormFn(self.xd.domain_restore,
|
||||
[['file', 'str']])
|
||||
val = fn(req.args)
|
||||
return val
|
||||
|
||||
def render_POST(self, req):
|
||||
return self.perform(req)
|
||||
|
||||
@@ -129,3 +136,9 @@ class SrvDomainDir(SrvDir):
|
||||
req.write('<button type="submit" name="op" value="create">Create Domain</button>')
|
||||
req.write('Config <input type="file" name="config"><br>')
|
||||
req.write('</form>')
|
||||
req.write('<form method="post" action="%s" enctype="multipart/form-data">'
|
||||
% req.prePathURL())
|
||||
req.write('<button type="submit" name="op" value="create">Restore Domain</button>')
|
||||
req.write('State <input type="string" name="state"><br>')
|
||||
req.write('</form>')
|
||||
|
||||
@@ -16,6 +16,7 @@ import sys
|
||||
import types
|
||||
import errno
|
||||
import string
|
||||
from StringIO import StringIO
|
||||
|
||||
__all__ = [
|
||||
"mime_type",
|
||||
@@ -521,6 +522,28 @@ def elements(sxpr, ctxt=None):
|
||||
yield v
|
||||
i += 1
|
||||
|
||||
def to_string(sxpr):
|
||||
"""Convert an sxpr to a string.
|
||||
|
||||
sxpr sxpr
|
||||
returns string
|
||||
"""
|
||||
io = StringIO()
|
||||
show(sxpr, io)
|
||||
io.seek(0)
|
||||
val = io.getvalue()
|
||||
io.close()
|
||||
return val
|
||||
|
||||
def from_string(str):
|
||||
"""Create an sxpr by parsing a string.
|
||||
|
||||
str string
|
||||
returns sxpr
|
||||
"""
|
||||
io = StringIO(str)
|
||||
return parse(io)
|
||||
|
||||
def parse(io):
|
||||
"""Completely parse all input from 'io'.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from xen.xend import sxp
|
||||
from xen.xend import PrettyPrint
|
||||
from xen.xend.XendClient import server
|
||||
|
||||
from xen.xend.xm.opts import *
|
||||
from xen.xm.opts import *
|
||||
|
||||
gopts = Opts(use="""[options]
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from getopt import getopt
|
||||
from xen.xend import PrettyPrint
|
||||
from xen.xend import sxp
|
||||
from xen.xend.XendClient import server
|
||||
from xen.xend.xm import create, shutdown
|
||||
from xen.xm import create, shutdown
|
||||
|
||||
class Prog:
|
||||
"""Base class for sub-programs.
|
||||
|
||||
@@ -290,6 +290,7 @@ class Opts:
|
||||
cmd = '\n'.join(["import sys",
|
||||
"import os",
|
||||
"import os.path",
|
||||
"import xen.util.ip",
|
||||
"xm_file = '%s'" % defaults,
|
||||
"xm_help = %d" % help ])
|
||||
exec cmd in globals, locals
|
||||
|
||||
@@ -6,7 +6,7 @@ import sys
|
||||
import time
|
||||
|
||||
from xen.xend.XendClient import server
|
||||
from xen.xend.xm.opts import *
|
||||
from xen.xm.opts import *
|
||||
|
||||
gopts = Opts(use="""[options] [DOM]
|
||||
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
|
||||
xend stop
|
||||
|
||||
Unfortunately restarting it upsets the channel to dom0 and
|
||||
domain management stops working - needs a reboot to fix.
|
||||
The daemon should reconnect to device control interfaces
|
||||
and recover its state when restarted.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -4,6 +4,7 @@ CFLAGS = -Wall -O3 -Werror
|
||||
CFLAGS += -I../../xen/include/hypervisor-ifs
|
||||
CFLAGS += -I../../linux-xen-sparse/include
|
||||
CFLAGS += -I../xu/lib
|
||||
CFLAGS += -I../lib
|
||||
|
||||
HDRS = $(wildcard *.h)
|
||||
OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
|
||||
|
||||