libxml-ruby 0.3.8.2 → 0.3.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile CHANGED
@@ -120,13 +120,14 @@ end
120
120
  task :update_version do
121
121
  unless PKG_VERSION == CURRENT_VERSION
122
122
  File.open('ext/xml/libxml.h.new','w+') do |f|
123
- maj, min, mic = /(\d+)\.(\d+)(?:\.(\d+))?/.match(PKG_VERSION).captures
123
+ maj, min, mic, patch = /(\d+)\.(\d+)(?:\.(\d+))?(?:\.(\d+))?/.match(PKG_VERSION).captures
124
124
  f << File.read('ext/xml/libxml.h').
125
125
  gsub(/RUBY_LIBXML_VERSION\s+"(\d.+)"/) { "RUBY_LIBXML_VERSION \"#{PKG_VERSION}\"" }.
126
126
  gsub(/RUBY_LIBXML_VERNUM\s+\d+/) { "RUBY_LIBXML_VERNUM #{PKG_VERSION.tr('.','').sub(/^0*/,'')}" }.
127
- gsub(/RUBY_LIBXML_VER_MAJ\s+\d+/) { "RUBY_LIBXML_VER_MAJ #{maj}" }.
128
- gsub(/RUBY_LIBXML_VER_MIN\s+\d+/) { "RUBY_LIBXML_VER_MIN #{min}" }.
129
- gsub(/RUBY_LIBXML_VER_MIC\s+\d+/) { "RUBY_LIBXML_VER_MIC #{mic || 0}" }
127
+ gsub(/RUBY_LIBXML_VER_MAJ\s+\d+/) { "RUBY_LIBXML_VER_MAJ #{maj}" }.
128
+ gsub(/RUBY_LIBXML_VER_MIN\s+\d+/) { "RUBY_LIBXML_VER_MIN #{min}" }.
129
+ gsub(/RUBY_LIBXML_VER_MIC\s+\d+/) { "RUBY_LIBXML_VER_MIC #{mic || 0}" }.
130
+ gsub(/RUBY_LIBXML_VER_PATCH\s+\d+/) { "RUBY_LIBXML_VER_PATCH #{patch || 0}" }
130
131
  end
131
132
  mv('ext/xml/libxml.h.new', 'ext/xml/libxml.h')
132
133
  end
@@ -1,4 +1,4 @@
1
- /* $Id: libxml.c,v 1.3 2006/11/20 01:22:07 roscopeco Exp $ */
1
+ /* $Id: libxml.c,v 1.3.2.1 2006/11/26 12:37:41 roscopeco Exp $ */
2
2
 
3
3
  /* Please see the LICENSE file for copyright and distribution information */
4
4
 
@@ -39,6 +39,7 @@
39
39
  #define RubyMemStrdup ruby_strdup
40
40
 
41
41
  VALUE mXML;
42
+ VALUE eXMLError;
42
43
 
43
44
  static xmlFreeFunc freeFunc = NULL;
44
45
  static xmlMallocFunc mallocFunc = NULL;
@@ -59,6 +60,7 @@ Init_libxml_so(void) {
59
60
  xmlInitParser();
60
61
 
61
62
  mXML = rb_define_module("XML");
63
+ eXMLError = rb_define_class_under(mXML, "Error", rb_eRuntimeError);
62
64
 
63
65
  rb_define_const(mXML, "XML_NAMESPACE", rb_str_new2((const char*)XML_XML_NAMESPACE));
64
66
 
@@ -77,9 +79,9 @@ Init_libxml_so(void) {
77
79
  ruby_init_xml_xpath_context();
78
80
  ruby_init_xml_xpointer();
79
81
  ruby_init_xml_xpointer_context();
80
- ruby_init_input_callbacks(); /* MUFF */
81
- ruby_init_xml_dtd(); /* MUFF */
82
- ruby_init_xml_schema(); /* MUFF */
82
+ ruby_init_input_callbacks();
83
+ ruby_init_xml_dtd();
84
+ ruby_init_xml_schema();
83
85
 
84
86
  ruby_xml_parser_default_substitute_entities_set(cXMLParser, Qtrue);
85
87
  ruby_xml_parser_default_load_external_dtd_set(cXMLParser, Qtrue);
@@ -6,11 +6,12 @@
6
6
  /* Don't nuke this block! It is used for automatically updating the
7
7
  * versions below. VERSION = string formatting, VERNUM = numbered
8
8
  * version for inline testing: increment both or none at all. */
9
- #define RUBY_LIBXML_VERSION "0.3.8.2"
10
- #define RUBY_LIBXML_VERNUM 382
11
- #define RUBY_LIBXML_VER_MAJ 0
12
- #define RUBY_LIBXML_VER_MIN 3
13
- #define RUBY_LIBXML_VER_MIC 8
9
+ #define RUBY_LIBXML_VERSION "0.3.8.4"
10
+ #define RUBY_LIBXML_VERNUM 384
11
+ #define RUBY_LIBXML_VER_MAJ 0
12
+ #define RUBY_LIBXML_VER_MIN 3
13
+ #define RUBY_LIBXML_VER_MIC 8
14
+ #define RUBY_LIBXML_VER_PATCH 4
14
15
 
15
16
  #include <ruby.h>
16
17
  #include <rubyio.h>
@@ -75,6 +76,7 @@ typedef struct rx_xpath_data {
75
76
  #include "ruby_xml_schema.h"
76
77
 
77
78
  extern VALUE mXML;
79
+ extern VALUE eXMLError;
78
80
 
79
81
  void ruby_init_parser(void);
80
82
  void ruby_xml_parser_free(ruby_xml_parser *rxp);
@@ -1,17 +1,7 @@
1
- # $Id: libxml.rb,v 1.5 2006/10/29 19:42:45 roscopeco Exp $
1
+ # $Id: libxml.rb,v 1.5.2.1 2006/11/26 12:37:42 roscopeco Exp $
2
2
  # Please see the LICENSE file for copyright and distribution information
3
3
  require 'xml/libxml_so'
4
4
 
5
- class XML::Node::Set
6
- def empty?
7
- self.length <= 0
8
- end
9
-
10
- def first
11
- self.each { |n| return n }
12
- end
13
- end
14
-
15
5
  class XML::Document
16
6
  include Enumerable
17
7
 
@@ -22,6 +12,8 @@ class XML::Document
22
12
  end
23
13
 
24
14
  class XML::Node::Set
15
+ include Enumerable
16
+
25
17
  # inefficient, but maybe can find a way to cache the
26
18
  # ary and dump on change?
27
19
  def [](i, count = nil)
@@ -1,4 +1,4 @@
1
- /* $Id: ruby_xml_attr.c,v 1.2 2006/11/20 01:22:07 roscopeco Exp $ */
1
+ /* $Id: ruby_xml_attr.c,v 1.2.2.1 2006/11/26 12:29:57 roscopeco Exp $ */
2
2
 
3
3
  /* Please see the LICENSE file for copyright and distribution information */
4
4
 
@@ -12,7 +12,7 @@ ruby_xml_attr_free(ruby_xml_attr *rxa) {
12
12
  if (rxa->attr != NULL && // got an attr?
13
13
  rxa->attr->parent == NULL && // unparented (otherwise, it gets freed with parent)
14
14
  rxa->attr->doc == NULL) { // No document? (otherwise, freed with doc)
15
- if ((int)rxa->attr->_private <= 1) {
15
+ if (rxa->attr->_private <= (void*)1) {
16
16
  // is null or last reference,
17
17
  xmlFreeNode((xmlNodePtr)rxa->attr);
18
18
  } else {
@@ -169,7 +169,7 @@ ruby_xml_attr_new2(VALUE class, VALUE xd, xmlAttrPtr attr) {
169
169
 
170
170
  rxa->attr = attr;
171
171
  if (attr->_private) {
172
- attr->_private++;
172
+ attr->_private++;
173
173
  } else {
174
174
  attr->_private = (void*)1;
175
175
  }
@@ -1,4 +1,4 @@
1
- /* $Id: ruby_xml_document.c,v 1.3 2006/11/20 01:22:07 roscopeco Exp $ */
1
+ /* $Id: ruby_xml_document.c,v 1.3.2.1 2006/11/26 12:37:41 roscopeco Exp $ */
2
2
 
3
3
  /* Please see the LICENSE file for copyright and distribution information */
4
4
 
@@ -1118,8 +1118,8 @@ ruby_init_xml_document(void) {
1118
1118
  rb_define_singleton_method(cXMLDocument, "new", ruby_xml_document_initialize, -1);
1119
1119
 
1120
1120
  //rb_raise(eXMLNodeFailedModify, "unable to add a child to the document");
1121
- //eDTDValidityWarning = rb_define_class_under(cXMLNode, "ValidityWarning", rb_eException);
1122
- //eDTDValidityError = rb_define_class_under(cXMLNode, "ValidityWarning", rb_eException);
1121
+ //eDTDValidityWarning = rb_define_class_under(cXMLNode, "ValidityWarning", eXMLError);
1122
+ //eDTDValidityError = rb_define_class_under(cXMLNode, "ValidityWarning", eXMLError);
1123
1123
  rb_define_method(cXMLDocument, "[]", ruby_xml_document_property_get, 1);
1124
1124
  rb_define_method(cXMLDocument, "[]=", ruby_xml_document_property_set, 2);
1125
1125
  rb_define_method(cXMLDocument, "child", ruby_xml_document_child_get, 0);
@@ -32,11 +32,6 @@ void* ic_open (char const *filename) {
32
32
  if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST scheme->scheme_name, scheme->name_len)) {
33
33
  ic_doc = (ic_doc_context*)malloc( sizeof(ic_doc_context) );
34
34
 
35
- // MUFF res = rb_funcall(
36
- // rb_funcall( rb_mKernel,
37
- // rb_intern("const_get"), 1,
38
- // rb_str_new2(scheme->class) ),
39
- // rb_intern("document_query"), 1, rb_str_new2(filename) );
40
35
  res = rb_funcall( scheme->class,
41
36
  rb_intern("document_query"),
42
37
  1,
@@ -86,13 +81,11 @@ input_callbacks_add_scheme (VALUE self, VALUE scheme_name, VALUE class) {
86
81
  ic_scheme *scheme;
87
82
 
88
83
  Check_Type(scheme_name, T_STRING);
89
- //MUFF Check_Type(class, T_STRING);
90
84
 
91
85
  scheme = (ic_scheme*)malloc(sizeof(ic_scheme));
92
86
  scheme->next_scheme = 0;
93
87
  scheme->scheme_name = strdup(StringValuePtr(scheme_name)); /* TODO alloc, dealloc */
94
88
  scheme->name_len = strlen(scheme->scheme_name);
95
- //MUFF scheme->class = strdup(StringValuePtr(class)); /* TODO alloc, dealloc */
96
89
  scheme->class = class; /* TODO alloc, dealloc */
97
90
 
98
91
  //fprintf( stderr, "registered: %s, %d, %s\n", scheme->scheme_name, scheme->name_len, scheme->class );
@@ -126,7 +119,6 @@ input_callbacks_remove_scheme (VALUE self, VALUE scheme_name) {
126
119
  save_scheme = first_scheme->next_scheme;
127
120
 
128
121
  free(first_scheme->scheme_name);
129
- //MUFF free(first_scheme->class);
130
122
  free(first_scheme);
131
123
 
132
124
  first_scheme = save_scheme;
@@ -139,7 +131,6 @@ input_callbacks_remove_scheme (VALUE self, VALUE scheme_name) {
139
131
  save_scheme = scheme->next_scheme->next_scheme;
140
132
 
141
133
  free(scheme->next_scheme->scheme_name);
142
- //MUFF free(scheme->next_scheme->class);
143
134
  free(scheme->next_scheme);
144
135
 
145
136
  scheme->next_scheme = save_scheme;
@@ -11,7 +11,6 @@ typedef struct ic_doc_context {
11
11
 
12
12
  typedef struct ic_scheme {
13
13
  char *scheme_name;
14
- //MUFF char *class;
15
14
  VALUE class;
16
15
  int name_len;
17
16
 
@@ -1,4 +1,4 @@
1
- /* $Id: ruby_xml_node.c,v 1.9 2006/11/20 01:22:07 roscopeco Exp $ */
1
+ /* $Id: ruby_xml_node.c,v 1.9.2.3 2006/11/27 10:32:01 roscopeco Exp $ */
2
2
 
3
3
  /* Please see the LICENSE file for copyright and distribution information */
4
4
 
@@ -707,12 +707,12 @@ void ruby_xml_node_free(ruby_xml_node *rxn) {
707
707
  if (rxn->node != NULL && // got a node?
708
708
  rxn->node->parent == NULL && // unparented (otherwise, it gets freed with parent)
709
709
  rxn->node->doc == NULL) { // No document? (otherwise, freed with doc)
710
- if ((int)rxn->node->_private <= 1) {
710
+ if (rxn->node->_private <= (void*)1) {
711
711
  // is null or last reference,
712
712
  xmlFreeNode(rxn->node);
713
713
  } else {
714
714
  // other pointers remain
715
- rxn->node->_private--;
715
+ rxn->node->_private--;
716
716
  }
717
717
  }
718
718
 
@@ -1907,9 +1907,12 @@ ruby_xml_node_property_set(VALUE self, VALUE key, VALUE val) {
1907
1907
 
1908
1908
  /*
1909
1909
  * call-seq:
1910
- * node.properties => attributes
1910
+ * node.properties => attributes or nil
1911
1911
  *
1912
- * Returns the +XML::Attr+ for this node.
1912
+ * Returns the first +XML::Attr+ for this node. Use the
1913
+ * +name+ and +value+ methods to obtain the attribute's
1914
+ * data, and the +prev+ and +next+ methods to
1915
+ * navigate the property list.
1913
1916
  */
1914
1917
  VALUE
1915
1918
  ruby_xml_node_properties_get(VALUE self) {
@@ -1920,7 +1923,12 @@ ruby_xml_node_properties_get(VALUE self) {
1920
1923
 
1921
1924
  if (node->node->type == XML_ELEMENT_NODE) {
1922
1925
  attr = node->node->properties;
1923
- return(ruby_xml_attr_new2(cXMLAttr, node->xd, attr));
1926
+
1927
+ if (attr == NULL) {
1928
+ return(Qnil);
1929
+ } else {
1930
+ return(ruby_xml_attr_new2(cXMLAttr, node->xd, attr));
1931
+ }
1924
1932
  } else {
1925
1933
  return(Qnil);
1926
1934
  }
@@ -2230,7 +2238,7 @@ ruby_xml_node_xinclude_start_q(VALUE self) {
2230
2238
  * Create a copy of this node.
2231
2239
  */
2232
2240
  VALUE
2233
- ruby_xml_node_copy(VALUE self, VALUE deep) { /* MUFF */
2241
+ ruby_xml_node_copy(VALUE self, VALUE deep) {
2234
2242
  ruby_xml_node *rxn;
2235
2243
  xmlNode *copy;
2236
2244
 
@@ -2254,9 +2262,9 @@ ruby_xml_node_copy(VALUE self, VALUE deep) { /* MUFF */
2254
2262
  void
2255
2263
  ruby_init_xml_node(void) {
2256
2264
  cXMLNode = rb_define_class_under(mXML, "Node", rb_cObject);
2257
- eXMLNodeSetNamespace = rb_define_class_under(cXMLNode, "SetNamespace", rb_eException);
2258
- eXMLNodeFailedModify = rb_define_class_under(cXMLNode, "FailedModify", rb_eException);
2259
- eXMLNodeUnknownType = rb_define_class_under(cXMLNode, "UnknownType", rb_eException);
2265
+ eXMLNodeSetNamespace = rb_define_class_under(cXMLNode, "SetNamespace", eXMLError);
2266
+ eXMLNodeFailedModify = rb_define_class_under(cXMLNode, "FailedModify", eXMLError);
2267
+ eXMLNodeUnknownType = rb_define_class_under(cXMLNode, "UnknownType", eXMLError);
2260
2268
 
2261
2269
  rb_define_const(cXMLNode, "SPACE_DEFAULT", INT2NUM(0));
2262
2270
  rb_define_const(cXMLNode, "SPACE_PRESERVE", INT2NUM(1));
@@ -1,4 +1,4 @@
1
- /* $Id: ruby_xml_node_set.c,v 1.4 2006/11/20 01:22:07 roscopeco Exp $ */
1
+ /* $Id: ruby_xml_node_set.c,v 1.4.2.1 2006/11/26 12:37:42 roscopeco Exp $ */
2
2
 
3
3
  /* Please see the LICENSE file for copyright and distribution information */
4
4
 
@@ -150,7 +150,7 @@ ruby_xml_node_set_length(VALUE self) {
150
150
  ruby_xml_node_set *rxnset;
151
151
  Data_Get_Struct(self, ruby_xml_node_set, rxnset);
152
152
  if (rxnset->node_set == NULL)
153
- return(Qnil);
153
+ return(INT2FIX(0));
154
154
  else
155
155
  return(INT2NUM(rxnset->node_set->nodeNr));
156
156
  }
@@ -240,6 +240,7 @@ ruby_init_xml_node_set(void) {
240
240
  rb_define_method(cXMLNodeSet, "empty?", ruby_xml_node_set_empty_q, 0);
241
241
  rb_define_method(cXMLNodeSet, "first", ruby_xml_node_set_first, 0);
242
242
  rb_define_method(cXMLNodeSet, "length", ruby_xml_node_set_length, 0);
243
+ rb_define_method(cXMLNodeSet, "size", ruby_xml_node_set_length, 0);
243
244
  rb_define_method(cXMLNodeSet, "to_a", ruby_xml_node_set_to_a, 0);
244
245
  rb_define_method(cXMLNodeSet, "xpath", ruby_xml_node_set_xpath_get, 0);
245
246
  rb_define_method(cXMLNodeSet, "xpath_ctxt",
@@ -1,4 +1,4 @@
1
- /* $Id: ruby_xml_xpath.c,v 1.2 2006/04/14 14:45:25 roscopeco Exp $ */
1
+ /* $Id: ruby_xml_xpath.c,v 1.2.4.1 2006/11/26 12:37:41 roscopeco Exp $ */
2
2
 
3
3
  /* Please see the LICENSE file for copyright and distribution information */
4
4
 
@@ -337,8 +337,7 @@ ruby_init_xml_xpath(void) {
337
337
  cXMLXPath = rb_define_class_under(mXML, "XPath", rb_cObject);
338
338
  rb_include_module(cXMLNode, rb_const_get(rb_cObject, rb_intern("Enumerable")));
339
339
 
340
- eXMLXPathInvalidPath = rb_define_class_under(cXMLXPath,
341
- "InvalidPath", rb_eException);
340
+ eXMLXPathInvalidPath = rb_define_class_under(cXMLXPath, "InvalidPath", eXMLError);
342
341
 
343
342
  rb_define_const(cXMLXPath, "UNDEFINED", INT2NUM(XPATH_UNDEFINED));
344
343
  rb_define_const(cXMLXPath, "NODESET", INT2NUM(XPATH_NODESET));
@@ -1,4 +1,4 @@
1
- /* $Id: ruby_xml_xpointer.c,v 1.1 2006/02/21 20:40:16 roscopeco Exp $ */
1
+ /* $Id: ruby_xml_xpointer.c,v 1.1.4.1 2006/11/26 12:37:42 roscopeco Exp $ */
2
2
 
3
3
  /* Please see the LICENSE file for copyright and distribution information */
4
4
 
@@ -94,7 +94,7 @@ ruby_xml_xpointer_range(VALUE class, VALUE rstart, VALUE rend) {
94
94
  void
95
95
  ruby_init_xml_xpointer(void) {
96
96
  cXMLXPointer = rb_define_class_under(mXML, "XPointer", rb_cObject);
97
- eXMLXPointerInvalidExpression = rb_define_class_under(cXMLXPointer, "InvalidExpression", rb_eException);
97
+ eXMLXPointerInvalidExpression = rb_define_class_under(cXMLXPointer, "InvalidExpression", eXMLError);
98
98
 
99
99
  rb_define_singleton_method(cXMLXPointer, "range", ruby_xml_xpointer_range, 2);
100
100
  }
@@ -1,4 +1,4 @@
1
- /* $Id: ruby_xml_xpointer_context.c,v 1.2 2006/02/27 12:55:32 roscopeco Exp $ */
1
+ /* $Id: ruby_xml_xpointer_context.c,v 1.2.4.1 2006/11/26 12:37:42 roscopeco Exp $ */
2
2
 
3
3
  /* Please see the LICENSE file for copyright and distribution information */
4
4
 
@@ -17,5 +17,5 @@ VALUE eXMLXPointerContextInvalidPath;
17
17
  void
18
18
  ruby_init_xml_xpointer_context(void) {
19
19
  cXMLXPointerContext = rb_define_class_under(cXMLXPointer, "Context", cXMLXPathContext);
20
- eXMLXPointerContextInvalidPath = rb_define_class_under(cXMLXPointerContext, "InvalidPath", rb_eException);
20
+ eXMLXPointerContextInvalidPath = rb_define_class_under(cXMLXPointerContext, "InvalidPath", eXMLError);
21
21
  }
@@ -1,4 +1,4 @@
1
- /* $Id: sax_parser_callbacks.inc,v 1.1 2006/04/14 14:50:58 roscopeco Exp $ */
1
+ /* $Id: sax_parser_callbacks.inc,v 1.1.4.1 2006/11/26 12:45:14 roscopeco Exp $ */
2
2
 
3
3
  /* Please see the LICENSE file for copyright and distribution information */
4
4
 
@@ -121,13 +121,23 @@ static void comment_func(ruby_xml_sax_parser_callbacks *cbp,
121
121
  }
122
122
  }
123
123
 
124
- // TODO these next three should actually be formatting messages.
124
+ #define FORMAT_AND_CALL() \
125
+ do { \
126
+ va_list args; \
127
+ char buf[1024]; \
128
+ va_start(args, msg); \
129
+ vsnprintf(buf, sizeof buf, msg, args); \
130
+ rb_funcall(handler,callsym,1,rb_str_new2(buf)); \
131
+ va_end(args); \
132
+ } \
133
+ while (0)
134
+
125
135
  static void warning_func(ruby_xml_sax_parser_callbacks *cbp,
126
136
  const char *msg, ...) {
127
137
  VALUE handler = cbp->xmlParserWarning;
128
138
 
129
139
  if (handler && handler != Qnil) {
130
- rb_funcall(handler,callsym,1,rb_str_new2(msg));
140
+ FORMAT_AND_CALL();
131
141
  }
132
142
  }
133
143
 
@@ -136,7 +146,7 @@ static void error_func(ruby_xml_sax_parser_callbacks *cbp,
136
146
  VALUE handler = cbp->xmlParserError;
137
147
 
138
148
  if (handler && handler != Qnil) {
139
- rb_funcall(handler,callsym,1,rb_str_new2(msg));
149
+ FORMAT_AND_CALL();
140
150
  }
141
151
  }
142
152
 
@@ -145,7 +155,7 @@ static void fatal_error_func(ruby_xml_sax_parser_callbacks *cbp,
145
155
  VALUE handler = cbp->xmlParserFatalError;
146
156
 
147
157
  if (handler && handler != Qnil) {
148
- rb_funcall(handler,callsym,1,rb_str_new2(msg));
158
+ FORMAT_AND_CALL();
149
159
  }
150
160
  }
151
161
 
@@ -1,4 +1,4 @@
1
- # $Id: tc_xml_node7.rb,v 1.2 2006/11/20 01:22:08 roscopeco Exp $
1
+ # $Id: tc_xml_node7.rb,v 1.2.2.1 2006/11/27 10:32:01 roscopeco Exp $
2
2
  require "libxml_test"
3
3
  require 'test/unit'
4
4
 
@@ -25,4 +25,12 @@ class TC_XML_Node7 < Test::Unit::TestCase
25
25
  assert_equal 'foo', prop.name
26
26
  assert_equal 'bar', prop.value
27
27
  end
28
+
29
+ def test_xml_node_properties_when_no_attributes
30
+ xp = XML::Parser.string("<root></root>")
31
+ doc = xp.parse
32
+ root = doc.root
33
+
34
+ assert_nil root.properties
35
+ end
28
36
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: libxml-ruby
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.3.8.2
7
- date: 2006-11-22 00:00:00 +00:00
6
+ version: 0.3.8.4
7
+ date: 2006-12-02 00:00:00 +00:00
8
8
  summary: LibXML2 bindings for Ruby
9
9
  require_paths:
10
10
  - lib