gettextpo 0.1.0 → 0.1.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3ac361edc71b888a2d229cde89bb5502d51a46b3461a8d335879a5a4234141c
4
- data.tar.gz: fc2308b078480e83ba35408a8b37eb2044bde4066b73390946507fba6da7d1bd
3
+ metadata.gz: 939f88fac034fa8ae14d06943e12163df3a34ca1797c625c7d5c6c591c18756b
4
+ data.tar.gz: 972a70b2e9f8c5c623e839a7e8c5d54e2b95cd62d1fe0d3f6c26fe4f3d53b429
5
5
  SHA512:
6
- metadata.gz: c987dabcd215ecaa0faaf18958c62337443c838432eb45b7f154132aa1c7ebe11cb5771cc7b0e17fd9317de73e2c3c298aea28baca61c5305f5ffe4f1251d934
7
- data.tar.gz: 616e8b974e467b8bfbb11f8b316d937769bcead95bdf2785c0bd84767e8589a8e13e8ed41fd6bdc8fa8e511d15491130995f739506f96bf7a0d9c0116b001566
6
+ metadata.gz: 9d1f6fbf4c89921e78629b4f23b2f120671fb04db1dfcb09d73db5f28c6396d37f4bf792a9f69e034ce5ab077ac5c79b3d310beb0deb7707966be306065e8a2f
7
+ data.tar.gz: b9bdbc78813ad3e20c783e5a2745297624c7c629fc2b97a55081a5b7f904fa3be99df556dc0b6664199e4d71e2e4e02c0d59340129f6f273ff3ae2e8607de3ad
data/.dir-locals.el CHANGED
@@ -1,5 +1,6 @@
1
1
  ;;; Directory Local Variables -*- no-byte-compile: t -*-
2
2
  ;;; For more information see (info "(emacs) Directory Variables")
3
3
 
4
- ((nil . ((eval . (progn (add-to-list 'grep-find-ignored-directories "tmp")
4
+ ((nil . ((eval . (progn (require 'grep)
5
+ (add-to-list 'grep-find-ignored-directories "tmp")
5
6
  (add-to-list 'grep-find-ignored-directories "doc"))))))
data/.env.example ADDED
@@ -0,0 +1,2 @@
1
+ MRUBY_SRC=/path/to/mruby
2
+ DEVELOP_MRUBY=no
data/.envrc CHANGED
@@ -1,3 +1,18 @@
1
+ # Copyright (C) 2026 gemmaro
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
1
16
  use guix \
2
17
  clang \
3
18
  gcc-toolchain \
@@ -7,5 +22,17 @@ use guix \
7
22
  gettext \
8
23
  gdb
9
24
 
10
- path_add C_INCLUDE_PATH "$(ruby -e "puts RbConfig::CONFIG['rubyhdrdir']")"
11
- path_add C_INCLUDE_PATH "$(ruby -e "puts RbConfig::CONFIG['rubyarchhdrdir']")"
25
+ dotenv
26
+
27
+ if [ "$DEVELOP_MRUBY" != 'yes' ]
28
+ then
29
+ # CRuby
30
+ path_add C_INCLUDE_PATH "$(ruby -e "puts RbConfig::CONFIG['rubyhdrdir']")"
31
+ path_add C_INCLUDE_PATH "$(ruby -e "puts RbConfig::CONFIG['rubyarchhdrdir']")"
32
+ else
33
+ # mruby
34
+ env_vars_required MRUBY_SRC
35
+ path_add C_INCLUDE_PATH include
36
+ path_add C_INCLUDE_PATH "$MRUBY_SRC/include"
37
+ path_add C_INCLUDE_PATH "$MRUBY_SRC/build/host/include"
38
+ fi
data/.rdoc_options CHANGED
@@ -4,3 +4,4 @@ exclude:
4
4
  - bin
5
5
  - Rakefile
6
6
  - Gemfile
7
+ - vendor
data/CHANGELOG.md CHANGED
@@ -1,7 +1,19 @@
1
1
  # Change log of Ruby GettextPO gem
2
2
 
3
- ## [Unreleased]
3
+ ## Unreleased
4
4
 
5
- ## [0.1.0] - 2026-03-07
5
+ ## CRuby version 0.1.2 and mruby version 0.1.0 - 2026-03-11
6
+
7
+ - Add mruby version.
8
+ - CRuby: Fix exception error 2 handling.
9
+
10
+ ## 0.1.1 - 2026-03-09
11
+
12
+ - Fix dangling pointer issue.
13
+ - Add libgettextpo version constant.
14
+ - Add severities constants.
15
+ - Improve API document.
16
+
17
+ ## 0.1.0 - 2026-03-07
6
18
 
7
19
  - Initial release
data/README.md CHANGED
@@ -2,8 +2,9 @@
2
2
 
3
3
  This is a Ruby library for the GNU gettext PO files. This is a C
4
4
  binding of the libgettextpo library, which is provided by the GNU
5
- gettext package. It is designed to promote Ruby's idiomatic coding
6
- style while avoiding memory leaks.
5
+ gettext package. The API of this gem is designed to promote Ruby's
6
+ idiomatic coding style while avoiding memory safety. Currently this
7
+ supports both CRuby and mruby.
7
8
 
8
9
  ## Installation
9
10
 
@@ -23,17 +24,22 @@ apt search libgettextpo
23
24
  # process PO files - shared library
24
25
  ```
25
26
 
26
- Install the gem and add to the application's `Gemfile` by executing:
27
+ For CRuby:
27
28
 
28
- ```bash
29
- bundle add gettextpo
30
- ```
29
+ * Install the gem and add to the application's `Gemfile` by executing:
31
30
 
32
- If Bundler is not being used to manage dependencies, install the gem by executing:
31
+ ```bash
32
+ bundle add gettextpo
33
+ ```
33
34
 
34
- ```bash
35
- gem install gettextpo
36
- ```
35
+ * If Bundler is not being used to manage dependencies, install the gem by executing:
36
+
37
+ ```bash
38
+ gem install gettextpo
39
+ ```
40
+
41
+ For mruby, modify `build_config.rb` as you like, set `MRUBY_SRC` to
42
+ mruby source repository, and run `./bin/compile` or `./bin/test`.
37
43
 
38
44
  ## Usage
39
45
 
@@ -55,15 +61,12 @@ GettextPO::File.read(po_path).message_iterator.each do |message|
55
61
  end
56
62
  ```
57
63
 
58
- Please refer to the API documentation and test cases for details.
59
-
60
- ## Development
64
+ Please refer to the [API documentation][api] and test cases for
65
+ details. The mruby version has same API.
61
66
 
62
- `./bin/debug` to debug when segmentation fault.
67
+ [api]: https://gemmaro.github.io/ruby-gettextpo/
63
68
 
64
- References: [RubyのC APIの手引き 決定版][def], [Developers'
65
- documentation for Ruby][dev], [Rubyの拡張ライブラリの作り方][ext],
66
- [library rdoc/parser/c][parser], and [library rdoc][rdoc].
69
+ ## Development
67
70
 
68
71
  After checking out the repo, run `bin/setup` to install
69
72
  dependencies. Then, run `rake test` to run the tests. You can also run
@@ -76,16 +79,52 @@ install`. To release a new version, update the version number in
76
79
  create a git tag for the version, push git commits and the created
77
80
  tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
78
81
 
79
- [def]: https://gemmaro.github.io/emberb/
80
- [dev]: https://docs.ruby-lang.org/capi/en/master/index.html
81
- [ext]: https://docs.ruby-lang.org/en/master/extension_ja_rdoc.html
82
- [parser]: https://docs.ruby-lang.org/ja/latest/library/rdoc=2fparser=2fc.html
83
- [rdoc]: https://docs.ruby-lang.org/ja/latest/library/rdoc.html
82
+ File structure:
83
+
84
+ ```text
85
+ bin : script ifles
86
+ build_config.rb : for mruby
87
+ * build_config.rb.lock
88
+ CHANGELOG.md
89
+ COPYING
90
+ * doc : generated by RDoc from CRuby sources
91
+ ext : for CRuby
92
+ Gemfile : for CRuby
93
+ gettextpo.gemspec : for CRuby
94
+ include : for mruby
95
+ lib : for CRuby
96
+ mrbgem.rake : for mruby
97
+ mrblib : for mruby
98
+ * pkg : generated by RubyGem
99
+ Rakefile : for CRuby
100
+ README.md
101
+ - sample.rb : for debug purpose especially with GDB
102
+ sig : for CRuby
103
+ src : for mruby
104
+ test : common test cases for both CRuby and mruby
105
+ test.cruby : CRuby specific tests
106
+ * tmp
107
+ - .env
108
+ .env.example
109
+ .envrc : Direnv config
110
+ .clang-format
111
+ .dir-locals.el : Emacs config
112
+ .gitignore
113
+
114
+ * generated files
115
+ - ignored by version control
116
+ ```
84
117
 
85
118
  ## Contributing
86
119
 
87
- Bug reports and pull requests are welcome on Disroot at
88
- <https://git.disroot.org/gemmaro/ruby-gettextpo>.
120
+ Bug reports and pull requests are welcome on [Disroot][disroot].
121
+
122
+ Other links:
123
+
124
+ * [RubyGems](https://rubygems.org/gems/gettextpo)
125
+ * [GitHub repository](https://github.com/gemmaro/ruby-gettextpo)
126
+
127
+ [disroot]: https://git.disroot.org/gemmaro/ruby-gettextpo
89
128
 
90
129
  ## License
91
130
 
data/Rakefile CHANGED
@@ -19,9 +19,9 @@ require "bundler/gem_tasks"
19
19
  require "rake/testtask"
20
20
 
21
21
  Rake::TestTask.new(:test) do |t|
22
- t.libs << "test"
22
+ t.libs << "test.cruby"
23
23
  t.libs << "lib"
24
- t.test_files = FileList["test/**/*_test.rb"]
24
+ t.test_files = FileList["test.cruby/**/*_test.rb"]
25
25
  end
26
26
 
27
27
  require "rake/extensiontask"
data/build_config.rb ADDED
@@ -0,0 +1,27 @@
1
+ # Copyright (C) 2026 gemmaro
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ MRuby::Build.new do |conf|
17
+ toolchain :gcc
18
+ conf.gem File.expand_path(File.dirname(__FILE__))
19
+ conf.linker.libraries << 'gettextpo'
20
+ conf.enable_test
21
+ conf.gem core: 'hal-posix-io'
22
+ conf.gem core: 'mruby-bin-mruby'
23
+ conf.gem core: 'mruby-bin-mirb'
24
+ conf.cc do |cc|
25
+ cc.flags << '-O0'
26
+ end
27
+ end
@@ -1,5 +1,4 @@
1
- /**
2
- * Copyright (C) 2026 gemmaro
1
+ /* Copyright (C) 2026 gemmaro
3
2
  *
4
3
  * This program is free software: you can redistribute it and/or modify
5
4
  * it under the terms of the GNU General Public License as published by
@@ -15,36 +14,26 @@
15
14
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
15
  */
17
16
 
17
+ /* FilePos, Message, and MessageIterator each hold a @file instance
18
+ * variable (ivar) pointing to the GettextPO::File object that owns
19
+ * the underlying po_file_t. This prevents the File object from being
20
+ * garbage collected while any of these objects are still alive, which
21
+ * would otherwise cause their po_file_t pointers to become dangling.
22
+ * When allocating one of these objects, @file must be propagated from
23
+ * the parent object: File -> MessageIterator -> Message -> FilePos.
24
+ */
25
+
18
26
  #include "gettextpo.h"
19
- #include <gettext-po.h>
20
- #include <ruby/internal/anyargs.h>
21
- #include <ruby/internal/arithmetic/int.h>
22
27
  #include <ruby/internal/core/rdata.h>
23
- #include <ruby/internal/core/rstring.h>
24
- #include <ruby/internal/core/rtypeddata.h>
25
- #include <ruby/internal/eval.h>
26
- #include <ruby/internal/globals.h>
27
- #include <ruby/internal/intern/array.h>
28
- #include <ruby/internal/intern/hash.h>
29
- #include <ruby/internal/intern/object.h>
30
- #include <ruby/internal/intern/proc.h>
31
- #include <ruby/internal/intern/range.h>
32
- #include <ruby/internal/intern/string.h>
33
- #include <ruby/internal/intern/vm.h>
34
- #include <ruby/internal/module.h>
35
- #include <ruby/internal/scan_args.h>
36
- #include <ruby/internal/special_consts.h>
37
- #include <ruby/internal/symbol.h>
38
- #include <ruby/internal/value.h>
39
- #include <stdbool.h>
40
- #include <stddef.h>
41
- #include <stdlib.h>
42
- #include <time.h>
28
+ #include <ruby/internal/intern/variable.h>
29
+
30
+ #define ERROR \
31
+ rb_const_get (rb_const_get (rb_cObject, rb_intern ("GettextPO")), \
32
+ rb_intern ("Error"))
43
33
 
44
34
  VALUE rb_cMessage;
45
35
  VALUE rb_cMessageIterator;
46
36
  VALUE rb_cFilePos;
47
- VALUE rb_eError;
48
37
 
49
38
  /* ********** error ********** */
50
39
 
@@ -53,6 +42,7 @@ static struct
53
42
  bool error;
54
43
  VALUE *user_xerror;
55
44
  VALUE *user_xerror2;
45
+ VALUE *file;
56
46
  } gettextpo_xerror_context = {};
57
47
 
58
48
  static void
@@ -61,6 +51,7 @@ gettextpo_xerror_context_reset (void)
61
51
  gettextpo_xerror_context.error = false;
62
52
  gettextpo_xerror_context.user_xerror = NULL;
63
53
  gettextpo_xerror_context.user_xerror2 = NULL;
54
+ gettextpo_xerror_context.file = NULL;
64
55
  }
65
56
 
66
57
  static void
@@ -79,6 +70,8 @@ gettextpo_xerror (const int severity, const po_message_t message,
79
70
  {
80
71
  VALUE message_value = rb_obj_alloc (rb_cMessage);
81
72
  DATA_PTR (message_value) = message;
73
+ rb_ivar_set (message_value, rb_intern ("@file"),
74
+ *gettextpo_xerror_context.file);
82
75
  rb_hash_aset (kwargs, ID2SYM (rb_intern ("message")), message_value);
83
76
  }
84
77
  if (filename)
@@ -120,6 +113,8 @@ gettextpo_xerror2 (const int severity, const po_message_t message1,
120
113
  {
121
114
  VALUE message_value1 = rb_obj_alloc (rb_cMessage);
122
115
  DATA_PTR (message_value1) = message1;
116
+ rb_ivar_set (message_value1, rb_intern ("@file"),
117
+ *gettextpo_xerror_context.file);
123
118
  rb_hash_aset (kwargs, ID2SYM (rb_intern ("message1")), message_value1);
124
119
  }
125
120
  if (filename1)
@@ -138,6 +133,8 @@ gettextpo_xerror2 (const int severity, const po_message_t message1,
138
133
  {
139
134
  VALUE message_value2 = rb_obj_alloc (rb_cMessage);
140
135
  DATA_PTR (message_value2) = message2;
136
+ rb_ivar_set (message_value2, rb_intern ("@file"),
137
+ *gettextpo_xerror_context.file);
141
138
  rb_hash_aset (kwargs, ID2SYM (rb_intern ("message2")), message_value2);
142
139
  }
143
140
  if (filename2)
@@ -154,7 +151,7 @@ gettextpo_xerror2 (const int severity, const po_message_t message1,
154
151
  rb_str_new_cstr (message_text2));
155
152
  VALUE args = rb_ary_new ();
156
153
  rb_ary_push (args, kwargs);
157
- rb_proc_call_kw (*gettextpo_xerror_context.user_xerror, args,
154
+ rb_proc_call_kw (*gettextpo_xerror_context.user_xerror2, args,
158
155
  RB_PASS_KEYWORDS);
159
156
  if (severity == PO_SEVERITY_FATAL_ERROR)
160
157
  abort ();
@@ -423,7 +420,7 @@ gettextpo_po_message_m_format_set (int argc, VALUE *argv, VALUE self)
423
420
  bool opposite = !RB_UNDEF_P (kwargs_vals[0]) && RB_TEST (kwargs_vals[0]);
424
421
  bool remove = !RB_UNDEF_P (kwargs_vals[1]) && RB_TEST (kwargs_vals[1]);
425
422
  if (opposite && remove)
426
- rb_raise (rb_eError, "opposite and remove cannot be set at the same time");
423
+ rb_raise (ERROR, "opposite and remove cannot be set at the same time");
427
424
  po_message_set_format (DATA_PTR (self), StringValueCStr (format),
428
425
  opposite ? 0 : (remove ? -1 : 1));
429
426
  return Qnil;
@@ -470,6 +467,8 @@ gettextpo_po_message_m_filepos (VALUE self, VALUE index)
470
467
  {
471
468
  VALUE filepos = rb_obj_alloc (rb_cFilePos);
472
469
  DATA_PTR (filepos) = pos;
470
+ rb_ivar_set (filepos, rb_intern ("@file"),
471
+ rb_ivar_get (self, rb_intern ("@file")));
473
472
  return filepos;
474
473
  }
475
474
  else
@@ -500,7 +499,7 @@ gettextpo_po_message_m_add_filepos (VALUE self, VALUE file, VALUE start_line)
500
499
  /**
501
500
  * call-seq: check_all (iterator, xerror: nil, xerror2: nil)
502
501
  *
503
- * See also GettextPO::File.read.
502
+ * See also GettextPO::File.read for exception error handlings.
504
503
  */
505
504
  VALUE
506
505
  gettextpo_po_message_m_check_all (int argc, VALUE *argv, VALUE self)
@@ -511,6 +510,8 @@ gettextpo_po_message_m_check_all (int argc, VALUE *argv, VALUE self)
511
510
  VALUE kwargs_vals[] = { Qundef, Qundef };
512
511
  rb_get_kwargs (kwargs, kwargs_ids, 0, 2, kwargs_vals);
513
512
  gettextpo_xerror_context_reset ();
513
+ VALUE file = rb_ivar_get (self, rb_intern ("@file"));
514
+ gettextpo_xerror_context.file = &file;
514
515
  if (kwargs_vals[0] != Qundef)
515
516
  gettextpo_xerror_context.user_xerror = &kwargs_vals[0];
516
517
  if (kwargs_vals[1] != Qundef)
@@ -518,14 +519,14 @@ gettextpo_po_message_m_check_all (int argc, VALUE *argv, VALUE self)
518
519
  po_message_check_all (DATA_PTR (self), DATA_PTR (iterator),
519
520
  &gettextpo_xerror_handler);
520
521
  if (gettextpo_xerror_context.error)
521
- rb_raise (rb_eError, "check all for message failed");
522
+ rb_raise (ERROR, "check all for message failed");
522
523
  return Qnil;
523
524
  }
524
525
 
525
526
  /**
526
527
  * call-seq: check_format (xerror: nil, xerror2: nil)
527
528
  *
528
- * See also GettextPO::File.read.
529
+ * See also GettextPO::File.read for exception error handlings.
529
530
  */
530
531
  VALUE
531
532
  gettextpo_po_message_m_check_format (int argc, VALUE *argv, VALUE self)
@@ -536,13 +537,15 @@ gettextpo_po_message_m_check_format (int argc, VALUE *argv, VALUE self)
536
537
  VALUE kwargs_vals[] = { Qundef, Qundef };
537
538
  rb_get_kwargs (kwargs, kwargs_ids, 0, 2, kwargs_vals);
538
539
  gettextpo_xerror_context_reset ();
540
+ VALUE file = rb_ivar_get (self, rb_intern ("@file"));
541
+ gettextpo_xerror_context.file = &file;
539
542
  if (kwargs_vals[0] != Qundef)
540
543
  gettextpo_xerror_context.user_xerror = &kwargs_vals[0];
541
544
  if (kwargs_vals[1] != Qundef)
542
545
  gettextpo_xerror_context.user_xerror2 = &kwargs_vals[1];
543
546
  po_message_check_format (DATA_PTR (self), &gettextpo_xerror_handler);
544
547
  if (gettextpo_xerror_context.error)
545
- rb_raise (rb_eError, "check format for message failed");
548
+ rb_raise (ERROR, "check format for message failed");
546
549
  return Qnil;
547
550
  }
548
551
 
@@ -562,6 +565,10 @@ gettextpo_po_file_alloc (VALUE self)
562
565
  return TypedData_Wrap_Struct (self, &gettextpo_po_file_type, NULL);
563
566
  }
564
567
 
568
+ /**
569
+ * See also #message_iterator and GettextPO::MessageIterator#insert
570
+ * methods for the further manipulations.
571
+ */
565
572
  VALUE
566
573
  gettextpo_po_file_m_initialize (VALUE self)
567
574
  {
@@ -577,7 +584,8 @@ gettextpo_po_file_m_initialize (VALUE self)
577
584
  * +multiline+, and +message_text+. +xerror2+ takes keyword arguments
578
585
  * +severity+, +message1+, +filename1+, +lineno1+, +column1+,
579
586
  * +multiline1+, +message_text1+, +message2+, +filename2+, +lineno2+,
580
- * +column2+, +multiline2+, and +message_text2+.
587
+ * +column2+, +multiline2+, and +message_text2+. See also GettextPO
588
+ * for general exception handlings.
581
589
  */
582
590
  VALUE
583
591
  gettextpo_po_file_m_read (int argc, VALUE *argv, VALUE klass)
@@ -588,22 +596,25 @@ gettextpo_po_file_m_read (int argc, VALUE *argv, VALUE klass)
588
596
  VALUE kwargs_vals[] = { Qundef, Qundef };
589
597
  rb_get_kwargs (kwargs, kwargs_ids, 0, 2, kwargs_vals);
590
598
  gettextpo_xerror_context_reset ();
599
+ VALUE self = rb_obj_alloc (klass);
600
+ gettextpo_xerror_context.file = &self;
591
601
  if (kwargs_vals[0] != Qundef)
592
602
  gettextpo_xerror_context.user_xerror = &kwargs_vals[0];
593
603
  if (kwargs_vals[1] != Qundef)
594
604
  gettextpo_xerror_context.user_xerror2 = &kwargs_vals[1];
595
- VALUE self = rb_obj_alloc (klass);
605
+ /* TODO: po_file_read can return NULL when not found? Also check
606
+ null at file free. */
596
607
  DATA_PTR (self)
597
608
  = po_file_read (StringValueCStr (filename), &gettextpo_xerror_handler);
598
609
  if (gettextpo_xerror_context.error)
599
- rb_raise (rb_eError, "failed to read");
610
+ rb_raise (ERROR, "failed to read");
600
611
  return self;
601
612
  }
602
613
 
603
614
  /**
604
615
  * call-seq: write (filename, xerror: nil, xerror2: nil)
605
616
  *
606
- * See also ::read.
617
+ * See also ::read for exception error handlings.
607
618
  */
608
619
  VALUE
609
620
  gettextpo_po_file_m_write (int argc, VALUE *argv, VALUE self)
@@ -614,6 +625,7 @@ gettextpo_po_file_m_write (int argc, VALUE *argv, VALUE self)
614
625
  VALUE kwargs_vals[] = { Qundef, Qundef };
615
626
  rb_get_kwargs (kwargs, kwargs_ids, 0, 2, kwargs_vals);
616
627
  gettextpo_xerror_context_reset ();
628
+ gettextpo_xerror_context.file = &self;
617
629
  if (kwargs_vals[0] != Qundef)
618
630
  gettextpo_xerror_context.user_xerror = &kwargs_vals[0];
619
631
  if (kwargs_vals[1] != Qundef)
@@ -621,7 +633,7 @@ gettextpo_po_file_m_write (int argc, VALUE *argv, VALUE self)
621
633
  po_file_write (DATA_PTR (self), StringValueCStr (filename),
622
634
  &gettextpo_xerror_handler);
623
635
  if (gettextpo_xerror_context.error)
624
- rb_raise (rb_eError, "failed to write");
636
+ rb_raise (ERROR, "failed to write");
625
637
  return Qnil;
626
638
  }
627
639
 
@@ -646,11 +658,12 @@ gettextpo_po_file_m_message_iterator (int argc, VALUE *argv, VALUE self)
646
658
  VALUE iterator = rb_obj_alloc (rb_cMessageIterator);
647
659
  DATA_PTR (iterator) = po_message_iterator (
648
660
  DATA_PTR (self), NIL_P (domain) ? NULL : StringValueCStr (domain));
661
+ rb_ivar_set (iterator, rb_intern ("@file"), self);
649
662
  return iterator;
650
663
  }
651
664
 
652
665
  /**
653
- * call-seq: domain_header (domain)
666
+ * call-seq: domain_header (domain = nil)
654
667
  *
655
668
  * +domain+ can be +nil+ to use a default. Possibly returns +nil+.
656
669
  *
@@ -669,7 +682,7 @@ gettextpo_po_file_m_domain_header (int argc, VALUE *argv, VALUE self)
669
682
  /**
670
683
  * call-seq: check_all (xerror: nil, xerror2: nil)
671
684
  *
672
- * See also ::read.
685
+ * See also ::read for exception error handlings.
673
686
  */
674
687
  VALUE
675
688
  gettextpo_po_file_m_check_all (int argc, VALUE *argv, VALUE self)
@@ -680,13 +693,14 @@ gettextpo_po_file_m_check_all (int argc, VALUE *argv, VALUE self)
680
693
  VALUE kwargs_vals[] = { Qundef, Qundef };
681
694
  rb_get_kwargs (kwargs, kwargs_ids, 0, 2, kwargs_vals);
682
695
  gettextpo_xerror_context_reset ();
696
+ gettextpo_xerror_context.file = &self;
683
697
  if (kwargs_vals[0] != Qundef)
684
698
  gettextpo_xerror_context.user_xerror = &kwargs_vals[0];
685
699
  if (kwargs_vals[1] != Qundef)
686
700
  gettextpo_xerror_context.user_xerror2 = &kwargs_vals[1];
687
701
  po_file_check_all (DATA_PTR (self), &gettextpo_xerror_handler);
688
702
  if (gettextpo_xerror_context.error)
689
- rb_raise (rb_eError, "check all for file failed");
703
+ rb_raise (ERROR, "check all for file failed");
690
704
  return Qnil;
691
705
  }
692
706
 
@@ -718,6 +732,8 @@ gettextpo_po_message_iterator_m_next (VALUE self)
718
732
  {
719
733
  VALUE message_value = rb_obj_alloc (rb_cMessage);
720
734
  DATA_PTR (message_value) = message;
735
+ rb_ivar_set (message_value, rb_intern ("@file"),
736
+ rb_ivar_get (self, rb_intern ("@file")));
721
737
  return message_value;
722
738
  }
723
739
  else
@@ -736,6 +752,8 @@ gettextpo_po_message_iterator_m_insert (VALUE self, VALUE msgid, VALUE msgstr)
736
752
  po_message_insert (DATA_PTR (self), message);
737
753
  VALUE value = rb_obj_alloc (rb_cMessage);
738
754
  DATA_PTR (value) = message;
755
+ rb_ivar_set (value, rb_intern ("@file"),
756
+ rb_ivar_get (self, rb_intern ("@file")));
739
757
  return value;
740
758
  }
741
759
 
@@ -831,6 +849,8 @@ gettextpo_po_format_list (VALUE self)
831
849
  * call-seq: format_pretty_name (format)
832
850
  *
833
851
  * Possibly returns +nil+.
852
+ *
853
+ * See also ::formats method for available format types.
834
854
  */
835
855
  VALUE
836
856
  gettextpo_po_format_pretty_name (VALUE self, VALUE format)
@@ -845,6 +865,14 @@ RUBY_FUNC_EXPORTED void
845
865
  Init_gettextpo (void)
846
866
  {
847
867
  VALUE rb_mGettextPO = rb_define_module ("GettextPO");
868
+ rb_define_const (rb_mGettextPO, "LIBGETTEXTPO_VERSION",
869
+ INT2NUM (libgettextpo_version));
870
+ rb_define_const (rb_mGettextPO, "SEVERITY_WARNING",
871
+ INT2NUM (PO_SEVERITY_WARNING));
872
+ rb_define_const (rb_mGettextPO, "SEVERITY_ERROR",
873
+ INT2NUM (PO_SEVERITY_ERROR));
874
+ rb_define_const (rb_mGettextPO, "SEVERITY_FATAL_ERROR",
875
+ INT2NUM (PO_SEVERITY_FATAL_ERROR));
848
876
  rb_define_singleton_method (rb_mGettextPO, "header_entry_value",
849
877
  gettextpo_m_header_entry_value, 2);
850
878
  rb_define_singleton_method (rb_mGettextPO, "header_with_updated_entry_value",
@@ -940,6 +968,4 @@ Init_gettextpo (void)
940
968
  rb_define_method (rb_cFilePos, "file", gettextpo_po_filepos_m_file, 0);
941
969
  rb_define_method (rb_cFilePos, "start_line",
942
970
  gettextpo_po_filepos_m_start_line, 0);
943
- rb_eError
944
- = rb_define_class_under (rb_mGettextPO, "Error", rb_eStandardError);
945
971
  }
@@ -1,5 +1,4 @@
1
- /**
2
- * Copyright (C) 2026 gemmaro
1
+ /* Copyright (C) 2026 gemmaro
3
2
  *
4
3
  * This program is free software: you can redistribute it and/or modify
5
4
  * it under the terms of the GNU General Public License as published by
@@ -18,6 +17,7 @@
18
17
  #ifndef GETTEXTPO_H
19
18
  #define GETTEXTPO_H 1
20
19
 
21
- #include "ruby.h"
20
+ #include <gettext-po.h>
21
+ #include <ruby.h>
22
22
 
23
23
  #endif /* GETTEXTPO_H */
@@ -0,0 +1,24 @@
1
+ /* Copyright (C) 2026 gemmaro
2
+ *
3
+ * This program is free software: you can redistribute it and/or modify
4
+ * it under the terms of the GNU General Public License as published by
5
+ * the Free Software Foundation, either version 3 of the License, or
6
+ * (at your option) any later version.
7
+
8
+ * This program is distributed in the hope that it will be useful,
9
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ * GNU General Public License for more details.
12
+
13
+ * You should have received a copy of the GNU General Public License
14
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+ */
16
+
17
+ #ifndef MRB_GETTEXTPO_H
18
+ #define MRB_GETTEXTPO_H
19
+
20
+ #include <mruby.h>
21
+
22
+ void mrb_gettextpo_gem_init(mrb_state *mrb);
23
+
24
+ #endif
@@ -18,5 +18,6 @@
18
18
  #++
19
19
 
20
20
  module GettextPO
21
- VERSION = "0.1.0"
21
+ # Version of this gem.
22
+ VERSION = "0.1.2"
22
23
  end
data/lib/gettextpo.rb CHANGED
@@ -20,8 +20,25 @@ require_relative "gettextpo/gettextpo"
20
20
 
21
21
  # The main entrypoints to parse PO files are GettextPO::File.new and
22
22
  # GettextPO::File.read.
23
+ #
24
+ # == Error handling
25
+ #
26
+ # There are two kinds of errors in this gem. The first is for
27
+ # exception handlings of libgettextpo. The second is regular error
28
+ # raising from this gem.
29
+ #
30
+ # Some functions takes exception error handling paramters like
31
+ # +xerror+ and +xerror2+. These parameters expect +Proc+ object which
32
+ # takes several parameters. See also the description of
33
+ # GettextPO::File.read method. The +severity+ parameter among these
34
+ # parameters is either SEVERITY_WARNING, SEVERITY_ERROR, or
35
+ # SEVERITY_FATAL_ERROR.
36
+ #
37
+ # This gem normally raises +GettextPO::Error+ object, except for the
38
+ # standard ones, +StopIteration+ for example.
39
+ #
23
40
  module GettextPO
24
- class Error < StandardError; end
41
+ class Error < StandardError; end # :nodoc:
25
42
 
26
43
  # This class doesn't provide the +new+ class method. Refre to
27
44
  # GettextPO::MessageIterator#next or