gettextpo 0.1.2 → 0.2.0

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: 939f88fac034fa8ae14d06943e12163df3a34ca1797c625c7d5c6c591c18756b
4
- data.tar.gz: 972a70b2e9f8c5c623e839a7e8c5d54e2b95cd62d1fe0d3f6c26fe4f3d53b429
3
+ metadata.gz: d84207e6d2a84f0dd156d3a0638c3637aa5ca90340c3806d384706ded2a974c2
4
+ data.tar.gz: 7228e514562ed2bd8ebcdbd5c2ba2dbfb7068d3865621bfde7780c93bf970676
5
5
  SHA512:
6
- metadata.gz: 9d1f6fbf4c89921e78629b4f23b2f120671fb04db1dfcb09d73db5f28c6396d37f4bf792a9f69e034ce5ab077ac5c79b3d310beb0deb7707966be306065e8a2f
7
- data.tar.gz: b9bdbc78813ad3e20c783e5a2745297624c7c629fc2b97a55081a5b7f904fa3be99df556dc0b6664199e4d71e2e4e02c0d59340129f6f273ff3ae2e8607de3ad
6
+ metadata.gz: df67f19d3165af0fd428092d2cc35aeb15a55f7300e14440c3cc12222795fd479c7621cb30baf4473768b674549511785b23803b953063621f2526e946d8382d
7
+ data.tar.gz: b6d7a3cc904323f13a9d971daad33f197b916d042b9c5bcb4d88d0dec295e2af1cdde36bd9b8405b5a614831a2b99667fd154ed3a68d51b4fa626fe8e3bedbe7
data/.clangd ADDED
@@ -0,0 +1,2 @@
1
+ CompileFlags:
2
+ Add: [-DHAVE_PO_MESSAGE_GET_FORMAT]
data/.envrc CHANGED
@@ -13,14 +13,7 @@
13
13
  # You should have received a copy of the GNU General Public License
14
14
  # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
15
 
16
- use guix \
17
- clang \
18
- gcc-toolchain \
19
- ruby \
20
- ruby-rake-compiler \
21
- make \
22
- gettext \
23
- gdb
16
+ use guix
24
17
 
25
18
  dotenv
26
19
 
data/.rdoc_options CHANGED
@@ -5,3 +5,10 @@ exclude:
5
5
  - Rakefile
6
6
  - Gemfile
7
7
  - vendor
8
+ - test.cruby/**/*
9
+ - build_config.rb.lock
10
+ - mrbgem.rake
11
+ - mrblib/mrb_gettextpo.rb
12
+ - mrblib/mrb_gettextpo_version.rb
13
+ - manifest.scm
14
+ - ext/gettextpo/extconf.rb
data/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## CRuby and mruby version 0.2.0 - 2026-03-15
6
+
7
+ - CRuby and mruby: Add `GettextPO::Message#file_positions`,
8
+ `GettextPO::Message#workflow_flag?`,
9
+ `GettextPO::Message#update_workflow_flag`,
10
+ `GettextPO::Message#workflow_flag_iterator`,
11
+ `GettextPO::Message#sticky_flag?`,
12
+ `GettextPO::Message#update_sticky_flag`, and
13
+ `GettextPO::Message#sticky_flag_iterator` method.
14
+ - CRuby and mruby: Add `GettextPO::FlagIterator#next` method (and the
15
+ class, which is `Enumerable`).
16
+ - mruby: Requires GNU gettext 1.0 or later.
17
+
18
+ ## CRuby version 0.1.3
19
+
20
+ - CRuby: Fix file not found case.
21
+
5
22
  ## CRuby version 0.1.2 and mruby version 0.1.0 - 2026-03-11
6
23
 
7
24
  - Add mruby version.
data/README.md CHANGED
@@ -3,15 +3,15 @@
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
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.
6
+ idiomatic coding style while paying attensiton to memory safety.
7
+ Currently this supports both CRuby and mruby.
8
8
 
9
9
  ## Installation
10
10
 
11
- Prerequisites: `libgettextpo` library. On Linux and similar systems,
12
- you should be able to install it as a distribution package on most
13
- platforms. For example, on Ubuntu it was as follows
14
- (2026-03-07T15:48:13+09:00):
11
+ Prerequisites: `libgettextpo` library (mruby: version 1.0 or later).
12
+ On Linux and similar systems, you should be able to install it as a
13
+ distribution package on most platforms. For example, on Ubuntu it was
14
+ as follows (2026-03-07T15:48:13+09:00):
15
15
 
16
16
  ``` shell
17
17
  apt search libgettextpo
@@ -22,8 +22,11 @@ require "mkmf"
22
22
  # selectively, or entirely remove this flag.
23
23
  append_cflags("-fvisibility=hidden")
24
24
 
25
- have_header("gettext-po.h") or raise "gettext-po.h not found"
25
+ GETTEXTPO = "gettext-po.h"
26
+ have_header(GETTEXTPO) or raise "gettext-po.h not found"
26
27
  have_library("gettextpo") or raise "gettextpo library not found"
27
- # TODO: check library function
28
+
29
+ # Indicates 1.0 or later. <https://savannah.gnu.org/news/?id=10853>
30
+ have_func('po_message_get_format', GETTEXTPO)
28
31
 
29
32
  create_makefile("gettextpo/gettextpo")
@@ -24,8 +24,15 @@
24
24
  */
25
25
 
26
26
  #include "gettextpo.h"
27
+ #include <gettext-po.h>
27
28
  #include <ruby/internal/core/rdata.h>
28
- #include <ruby/internal/intern/variable.h>
29
+ #include <ruby/internal/core/rstring.h>
30
+ #include <ruby/internal/error.h>
31
+ #include <ruby/internal/globals.h>
32
+ #include <ruby/internal/intern/string.h>
33
+ #include <ruby/internal/module.h>
34
+ #include <ruby/internal/scan_args.h>
35
+ #include <ruby/internal/special_consts.h>
29
36
 
30
37
  #define ERROR \
31
38
  rb_const_get (rb_const_get (rb_cObject, rb_intern ("GettextPO")), \
@@ -34,6 +41,7 @@
34
41
  VALUE rb_cMessage;
35
42
  VALUE rb_cMessageIterator;
36
43
  VALUE rb_cFilePos;
44
+ VALUE rb_cFlagIterator;
37
45
 
38
46
  /* ********** error ********** */
39
47
 
@@ -162,6 +170,41 @@ static const struct po_xerror_handler gettextpo_xerror_handler = {
162
170
  .xerror2 = gettextpo_xerror2,
163
171
  };
164
172
 
173
+ /**
174
+ * Document-class: GettextPO::FlagIterator
175
+ */
176
+
177
+ void
178
+ gettextpo_flag_iterator_free (void *iter)
179
+ {
180
+ if (iter)
181
+ po_flag_iterator_free (iter);
182
+ }
183
+
184
+ static const rb_data_type_t gettextpo_po_flag_iterator_type = {
185
+ .wrap_struct_name = "gettextpo PO flag iterator",
186
+ .function = {
187
+ .dfree = gettextpo_flag_iterator_free,
188
+ },
189
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
190
+ };
191
+
192
+ VALUE
193
+ gettextpo_po_flag_iterator_alloc (VALUE self)
194
+ {
195
+ return TypedData_Wrap_Struct (self, &gettextpo_po_flag_iterator_type, NULL);
196
+ }
197
+
198
+ static VALUE
199
+ gettextpo_po_flag_iterator_m_next (VALUE self)
200
+ {
201
+ const char *flag = po_flag_next (DATA_PTR (self));
202
+ if (flag)
203
+ return rb_str_new_cstr (flag);
204
+ else
205
+ rb_raise (rb_eStopIteration, "no more flag");
206
+ }
207
+
165
208
  /**
166
209
  * Document-class: GettextPO::Message
167
210
  */
@@ -393,11 +436,90 @@ BOOL_GETTER (fuzzy);
393
436
  */
394
437
  BOOL_SETTER (fuzzy);
395
438
 
439
+ #ifdef HAVE_PO_MESSAGE_GET_FORMAT
440
+ #define FLAG_Q(name) \
441
+ static VALUE gettextpo_po_message_m_##name##_flag_q (VALUE self, \
442
+ VALUE flag) \
443
+ { \
444
+ return po_message_has_##name##_flag (DATA_PTR (self), \
445
+ StringValueCStr (flag)) \
446
+ ? Qtrue \
447
+ : Qfalse; \
448
+ }
449
+ #define UPDATE_FLAG(name) \
450
+ static VALUE gettextpo_po_message_m_update_##name##_flag ( \
451
+ int argc, VALUE *argv, VALUE self) \
452
+ { \
453
+ VALUE flag, kwargs; \
454
+ rb_scan_args (argc, argv, "1:", &flag, &kwargs); \
455
+ ID kwargs_ids[] = { rb_intern ("set") }; \
456
+ VALUE kwargs_vals[] = { Qundef }; \
457
+ rb_get_kwargs (kwargs, kwargs_ids, 0, \
458
+ sizeof (kwargs_ids) / sizeof (kwargs_ids[0]), \
459
+ kwargs_vals); \
460
+ \
461
+ po_message_set_##name##_flag (DATA_PTR (self), StringValueCStr (flag), \
462
+ RB_UNDEF_P (kwargs_vals[0]) \
463
+ && RB_TEST (kwargs_vals[0])); \
464
+ return Qnil; \
465
+ }
466
+ #define FLAG_ITER(name) \
467
+ static VALUE gettextpo_po_message_m_##name##_flags_iterator (VALUE self) \
468
+ { \
469
+ VALUE iter = rb_obj_alloc (rb_cFlagIterator); \
470
+ DATA_PTR (iter) = po_message_##name##_flags_iterator (DATA_PTR (self)); \
471
+ return iter; \
472
+ }
473
+
474
+ /**
475
+ * Document-method: workflow_flag?
476
+ * call-seq: workflow_flag? (flag)
477
+ */
478
+ FLAG_Q (workflow);
479
+
480
+ /**
481
+ * Document-method: update_workflow_flag
482
+ * call-seq: update_workflow_flag (flag, set: true)
483
+ */
484
+ UPDATE_FLAG (workflow);
485
+
486
+ /**
487
+ * Document-method: workflow_flag_iterator
488
+ */
489
+ FLAG_ITER (workflow);
490
+
491
+ /**
492
+ * call-seq: format (type)
493
+ *
494
+ * Returns +true+, +false+ (opposite), or +nil+ (none).
495
+ */
496
+ static VALUE
497
+ gettextpo_po_message_m_format (VALUE self, VALUE format)
498
+ {
499
+ int result
500
+ = po_message_get_format (DATA_PTR (self), StringValueCStr (format));
501
+ switch (result)
502
+ {
503
+ case 1:
504
+ return Qtrue;
505
+ break;
506
+ case 0:
507
+ return Qfalse;
508
+ break;
509
+ case -1:
510
+ return Qnil;
511
+ break;
512
+ default:
513
+ rb_raise (ERROR, "unreachable");
514
+ }
515
+ }
516
+ #endif
517
+
396
518
  /**
397
519
  * call-seq: format? (type)
398
520
  */
399
521
  VALUE
400
- gettextpo_po_message_m_format (VALUE self, VALUE format)
522
+ gettextpo_po_message_m_format_q (VALUE self, VALUE format)
401
523
  {
402
524
  return po_message_is_format (DATA_PTR (self), StringValueCStr (format))
403
525
  ? Qtrue
@@ -426,6 +548,25 @@ gettextpo_po_message_m_format_set (int argc, VALUE *argv, VALUE self)
426
548
  return Qnil;
427
549
  }
428
550
 
551
+ #ifdef HAVE_PO_MESSAGE_GET_FORMAT
552
+ /**
553
+ * Document-method: sticky_flag?
554
+ * call-seq: sticky_flag? (flag)
555
+ */
556
+ FLAG_Q (sticky);
557
+
558
+ /**
559
+ * Document-method: update_sticky_flag
560
+ * call-seq: update_sticky_flag (flag, set: true)
561
+ */
562
+ UPDATE_FLAG (sticky);
563
+
564
+ /**
565
+ * Document-method: sticky_flag_iterator
566
+ */
567
+ FLAG_ITER (sticky);
568
+ #endif
569
+
429
570
  /**
430
571
  * call-seq: range? (range)
431
572
  */
@@ -551,10 +692,17 @@ gettextpo_po_message_m_check_format (int argc, VALUE *argv, VALUE self)
551
692
 
552
693
  /* ********** file ********** */
553
694
 
695
+ void
696
+ gettextpo_file_free (void *file)
697
+ {
698
+ if (file)
699
+ po_file_free (file);
700
+ }
701
+
554
702
  static const rb_data_type_t gettextpo_po_file_type = {
555
703
  .wrap_struct_name = "gettextpo PO file",
556
704
  .function = {
557
- .dfree = (void (*)(void *)) po_file_free,
705
+ .dfree = gettextpo_file_free,
558
706
  },
559
707
  .flags = RUBY_TYPED_FREE_IMMEDIATELY,
560
708
  };
@@ -602,13 +750,17 @@ gettextpo_po_file_m_read (int argc, VALUE *argv, VALUE klass)
602
750
  gettextpo_xerror_context.user_xerror = &kwargs_vals[0];
603
751
  if (kwargs_vals[1] != Qundef)
604
752
  gettextpo_xerror_context.user_xerror2 = &kwargs_vals[1];
605
- /* TODO: po_file_read can return NULL when not found? Also check
606
- null at file free. */
607
- DATA_PTR (self)
753
+ po_file_t file
608
754
  = po_file_read (StringValueCStr (filename), &gettextpo_xerror_handler);
609
- if (gettextpo_xerror_context.error)
610
- rb_raise (ERROR, "failed to read");
611
- return self;
755
+ if (file)
756
+ {
757
+ DATA_PTR (self) = file;
758
+ if (gettextpo_xerror_context.error)
759
+ rb_raise (ERROR, "failed to read");
760
+ return self;
761
+ }
762
+ else
763
+ rb_raise (ERROR, "failed to read file, maybe file not found?");
612
764
  }
613
765
 
614
766
  /**
@@ -940,9 +1092,33 @@ Init_gettextpo (void)
940
1092
  rb_define_method (rb_cMessage, "fuzzy?", gettextpo_po_message_m_fuzzy, 0);
941
1093
  rb_define_method (rb_cMessage, "fuzzy=", gettextpo_po_message_m_fuzzy_set,
942
1094
  1);
943
- rb_define_method (rb_cMessage, "format?", gettextpo_po_message_m_format, 1);
1095
+
1096
+ #ifdef HAVE_PO_MESSAGE_GET_FORMAT
1097
+ rb_define_method (rb_cMessage, "workflow_flag?",
1098
+ gettextpo_po_message_m_workflow_flag_q, 1);
1099
+ rb_define_method (rb_cMessage, "update_workflow_flag",
1100
+ gettextpo_po_message_m_update_workflow_flag, -1);
1101
+ rb_define_method (rb_cMessage, "workflow_flag_iterator",
1102
+ gettextpo_po_message_m_workflow_flags_iterator, 0);
1103
+ #endif
1104
+
1105
+ rb_define_method (rb_cMessage, "format?", gettextpo_po_message_m_format_q,
1106
+ 1);
1107
+ #ifdef HAVE_PO_MESSAGE_GET_FORMAT
1108
+ rb_define_method (rb_cMessage, "format", gettextpo_po_message_m_format, 1);
1109
+ #endif
944
1110
  rb_define_method (rb_cMessage, "update_format",
945
1111
  gettextpo_po_message_m_format_set, -1);
1112
+
1113
+ #ifdef HAVE_PO_MESSAGE_GET_FORMAT
1114
+ rb_define_method (rb_cMessage, "sticky_flag?",
1115
+ gettextpo_po_message_m_sticky_flag_q, 1);
1116
+ rb_define_method (rb_cMessage, "update_sticky_flag",
1117
+ gettextpo_po_message_m_update_sticky_flag, -1);
1118
+ rb_define_method (rb_cMessage, "sticky_flag_iterator",
1119
+ gettextpo_po_message_m_sticky_flags_iterator, 0);
1120
+ #endif
1121
+
946
1122
  rb_define_method (rb_cMessage, "range?", gettextpo_po_message_m_range, 1);
947
1123
  rb_define_method (rb_cMessage, "range=", gettextpo_po_message_m_range_set,
948
1124
  1);
@@ -968,4 +1144,9 @@ Init_gettextpo (void)
968
1144
  rb_define_method (rb_cFilePos, "file", gettextpo_po_filepos_m_file, 0);
969
1145
  rb_define_method (rb_cFilePos, "start_line",
970
1146
  gettextpo_po_filepos_m_start_line, 0);
1147
+ rb_cFlagIterator
1148
+ = rb_define_class_under (rb_mGettextPO, "FlagIterator", rb_cObject);
1149
+ rb_define_alloc_func (rb_cFlagIterator, gettextpo_po_flag_iterator_alloc);
1150
+ rb_define_method (rb_cFlagIterator, "next",
1151
+ gettextpo_po_flag_iterator_m_next, 0);
971
1152
  }
@@ -19,5 +19,5 @@
19
19
 
20
20
  module GettextPO
21
21
  # Version of this gem.
22
- VERSION = "0.1.2"
22
+ VERSION = "0.2.0"
23
23
  end
data/lib/gettextpo.rb CHANGED
@@ -17,57 +17,4 @@
17
17
 
18
18
  require_relative "gettextpo/version"
19
19
  require_relative "gettextpo/gettextpo"
20
-
21
- # The main entrypoints to parse PO files are GettextPO::File.new and
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
- #
40
- module GettextPO
41
- class Error < StandardError; end # :nodoc:
42
-
43
- # This class doesn't provide the +new+ class method. Refre to
44
- # GettextPO::MessageIterator#next or
45
- # GettextPO::MessageIterator#insert.
46
- class Message
47
- private_class_method :new
48
- end
49
-
50
- # This class doesn't provide the +new+ class method. See also
51
- # GettextPO::File#message_iterator.
52
- class MessageIterator
53
- private_class_method :new
54
-
55
- def each # yields: message
56
- while true
57
- begin
58
- yield self.next
59
- rescue StopIteration
60
- return self
61
- end
62
- end
63
- end
64
-
65
- include Enumerable
66
- end
67
-
68
- # This class doesn't provide the +new+ class method. See also
69
- # GettextPO::Message#filepos.
70
- class FilePos
71
- private_class_method :new
72
- end
73
- end
20
+ require_relative "../mrblib/mrb_gettextpo_common"
data/manifest.scm ADDED
@@ -0,0 +1,43 @@
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
+ (use-modules (guix packages)
17
+ (guix download)
18
+ (guix profiles)
19
+ (gnu packages gettext)
20
+ (gnu packages gdb)
21
+ (gnu packages commencement)
22
+ (gnu packages llvm)
23
+ (gnu packages ruby)
24
+ (gnu packages ruby-check)
25
+ (gnu packages base))
26
+
27
+ (define-public gnu-gettext-1.0
28
+ (package/inherit gnu-gettext
29
+ (version "1.0")
30
+ (source (origin
31
+ (method url-fetch)
32
+ (uri (string-append "mirror://gnu/gettext/gettext-" version
33
+ ".tar.gz"))
34
+ (sha256 (base32
35
+ "06jgjxnm5xhw0d0zwl9birlwfxfgfqhk9q029j3h9941r5wrpnc5"))))))
36
+
37
+ (packages->manifest (list gnu-gettext-1.0
38
+ gcc-toolchain
39
+ gdb
40
+ clang
41
+ ruby-3.3
42
+ ruby-rake-compiler
43
+ gnu-make))
@@ -37,31 +37,7 @@ module GettextPO
37
37
  end
38
38
  end
39
39
 
40
- class MessageIterator
41
- def self.new
42
- raise NoMethodError,
43
- "please use other methods instead, such as GettextPO::File#message_iterator"
44
- end
45
-
46
- def each # yields: message
47
- while true
48
- begin
49
- yield self.next
50
- rescue StopIteration
51
- return self
52
- end
53
- end
54
- end
55
-
56
- include Enumerable
57
- end
58
-
59
40
  class Message
60
- def self.new
61
- raise NoMethodError,
62
- "please use other methods instead, such as GettextPO::MessageIterator#next"
63
- end
64
-
65
41
  # It seems that calling Proc with keyword arguments is not yet
66
42
  # supported.
67
43
  alias original_check_all check_all
@@ -79,11 +55,4 @@ module GettextPO
79
55
  xerror2: xerror2 && Proc.new { |kwargs| xerror2.(**kwargs) })
80
56
  end
81
57
  end
82
-
83
- class FilePos
84
- def self.new
85
- raise NoMethodError,
86
- "please use other methods instead, such as GettextPO::Message#filepos"
87
- end
88
- end
89
58
  end
@@ -0,0 +1,111 @@
1
+ #--
2
+ # Copyright (C) 2026 gemmaro
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
16
+ #++
17
+
18
+ # The main entrypoints to parse PO files are GettextPO::File.new and
19
+ # GettextPO::File.read.
20
+ #
21
+ # == Error handling
22
+ #
23
+ # There are two kinds of errors in this gem. The first is for
24
+ # exception handlings of libgettextpo. The second is regular error
25
+ # raising from this gem.
26
+ #
27
+ # Some functions takes exception error handling paramters like
28
+ # +xerror+ and +xerror2+. These parameters expect +Proc+ object which
29
+ # takes several parameters. See also the description of
30
+ # GettextPO::File.read method. The +severity+ parameter among these
31
+ # parameters is either SEVERITY_WARNING, SEVERITY_ERROR, or
32
+ # SEVERITY_FATAL_ERROR.
33
+ #
34
+ # This gem normally raises +GettextPO::Error+ object, except for the
35
+ # standard ones, +StopIteration+ for example.
36
+ #
37
+ module GettextPO
38
+ Error = Class.new(StandardError) # :nodoc:
39
+
40
+ # This class doesn't provide the +new+ class method. See also
41
+ # GettextPO::File#message_iterator.
42
+ class MessageIterator
43
+ def self.new # :nodoc:
44
+ raise NoMethodError,
45
+ "please use other methods instead, such as GettextPO::File#message_iterator"
46
+ end
47
+
48
+ def each # yields: message
49
+ while true
50
+ begin
51
+ yield self.next
52
+ rescue StopIteration
53
+ return self
54
+ end
55
+ end
56
+ end
57
+
58
+ include Enumerable
59
+ end
60
+
61
+ # This class doesn't provide the +new+ class method. Refer to
62
+ # GettextPO::MessageIterator#next or
63
+ # GettextPO::MessageIterator#insert.
64
+ class Message
65
+ def self.new # :nodoc:
66
+ raise NoMethodError,
67
+ "please use other methods instead, such as GettextPO::MessageIterator#next"
68
+ end
69
+
70
+ def file_positions
71
+ positions = []
72
+ index = 0
73
+ while (pos = filepos(index))
74
+ positions << pos
75
+ index += 1
76
+ end
77
+ positions
78
+ end
79
+ end
80
+
81
+ # This class doesn't provide the +new+ class method. See also
82
+ # GettextPO::Message#filepos.
83
+ class FilePos
84
+ def self.new # :nodoc:
85
+ raise NoMethodError,
86
+ "please use other methods instead, such as GettextPO::Message#filepos"
87
+ end
88
+ end
89
+
90
+ # This class doesn't provide the +new+ class method. See also
91
+ # GettextPO::Message#workflow_flag_iterator and
92
+ # GettextPO::Message#sticky_flag_iterator.
93
+ class FlagIterator
94
+ def self.new # :nodoc:
95
+ raise NoMethodError,
96
+ "please use other methods instead, such as GettextPO::Message#workflow_flag_iterator"
97
+ end
98
+
99
+ def each # yields: flag
100
+ while true
101
+ begin
102
+ yield self.next
103
+ rescue StopIteration
104
+ return self
105
+ end
106
+ end
107
+ end
108
+
109
+ include Enumerable
110
+ end
111
+ end
@@ -19,5 +19,5 @@
19
19
 
20
20
  module GettextPO
21
21
  # Version of this gem.
22
- VERSION = "0.1.0"
22
+ VERSION = "0.2.0"
23
23
  end
data/src/mrb_gettextpo.c CHANGED
@@ -37,9 +37,12 @@
37
37
 
38
38
  #define MODULE mrb_module_get_id (mrb, MRB_SYM (GettextPO))
39
39
  #define MESSAGE mrb_class_get_under_id (mrb, MODULE, MRB_SYM (Message))
40
+ #define FLAG_ITERATOR \
41
+ mrb_class_get_under_id (mrb, MODULE, MRB_SYM (FlagIterator))
40
42
  #define ERROR mrb_class_get_under_id (mrb, MODULE, MRB_SYM (Error))
41
43
 
42
44
  #define NIL mrb_nil_value ()
45
+ #define STOP_ITERATION mrb_class_get_id (mrb, MRB_SYM (StopIteration))
43
46
 
44
47
  static mrb_value
45
48
  gettextpo_m_header_entry_value (mrb_state *mrb, mrb_value klass)
@@ -70,6 +73,28 @@ gettextpo_m_header_with_updated_entry_value (mrb_state *mrb, mrb_value klass)
70
73
  return header_value;
71
74
  }
72
75
 
76
+ static void
77
+ gettextpo_flag_iterator_free (mrb_state *mrb, void *iter)
78
+ {
79
+ if (iter)
80
+ po_flag_iterator_free (iter);
81
+ }
82
+
83
+ static const struct mrb_data_type gettextpo_flag_iterator_data_type = {
84
+ .struct_name = "gettextpo flag iterator",
85
+ .dfree = gettextpo_flag_iterator_free,
86
+ };
87
+
88
+ static mrb_value
89
+ gettextpo_flag_iterator_m_next (mrb_state *mrb, mrb_value self)
90
+ {
91
+ const char *flag = po_flag_next (DATA_PTR (self));
92
+ if (flag)
93
+ return mrb_str_new_cstr (mrb, flag);
94
+ else
95
+ mrb_raise (mrb, STOP_ITERATION, "no more flag");
96
+ }
97
+
73
98
  static void
74
99
  gettextpo_message_free (mrb_state *mrb, void *message)
75
100
  {
@@ -179,8 +204,48 @@ BOOL_SETTER (obsolete);
179
204
  BOOL_GETTER (fuzzy);
180
205
  BOOL_SETTER (fuzzy);
181
206
 
207
+ #define QUERY_FLAG(name) \
208
+ static mrb_value gettextpo_message_m_##name##_flag_q (mrb_state *mrb, \
209
+ mrb_value self) \
210
+ { \
211
+ char *flag = NULL; \
212
+ mrb_get_args (mrb, "z", &flag); \
213
+ \
214
+ return mrb_bool_value ( \
215
+ po_message_has_##name##_flag (DATA_PTR (self), flag)); \
216
+ }
217
+ #define UPDATE_FLAG(name) \
218
+ static mrb_value gettextpo_message_m_##name##_flag_update (mrb_state *mrb, \
219
+ mrb_value self) \
220
+ { \
221
+ char *flag = NULL; \
222
+ mrb_sym table[] = { MRB_SYM (set) }; \
223
+ size_t num = sizeof (table) / sizeof (table[0]); \
224
+ mrb_value values[] = { mrb_undef_value () }; \
225
+ mrb_kwargs kwargs = { .num = num, .table = table, .values = values }; \
226
+ mrb_get_args (mrb, "z:", &flag, &kwargs); \
227
+ \
228
+ po_message_set_##name##_flag (DATA_PTR (self), flag, \
229
+ mrb_undef_p (values[0]) \
230
+ || mrb_test (values[0])); \
231
+ return mrb_nil_value (); \
232
+ }
233
+ #define FLAG_ITERATOR_METHOD(name) \
234
+ static mrb_value gettextpo_message_m_##name##_flag_iterator ( \
235
+ mrb_state *mrb, mrb_value self) \
236
+ { \
237
+ return mrb_obj_value (mrb_data_object_alloc ( \
238
+ mrb, FLAG_ITERATOR, \
239
+ po_message_##name##_flags_iterator (DATA_PTR (self)), \
240
+ &gettextpo_flag_iterator_data_type)); \
241
+ }
242
+
243
+ QUERY_FLAG (workflow);
244
+ UPDATE_FLAG (workflow);
245
+ FLAG_ITERATOR_METHOD (workflow);
246
+
182
247
  static mrb_value
183
- gettextpo_message_m_format (mrb_state *mrb, mrb_value self)
248
+ gettextpo_message_m_format_q (mrb_state *mrb, mrb_value self)
184
249
  {
185
250
  char *format = NULL;
186
251
  mrb_get_args (mrb, "z", &format);
@@ -188,6 +253,28 @@ gettextpo_message_m_format (mrb_state *mrb, mrb_value self)
188
253
  return mrb_bool_value (po_message_is_format (DATA_PTR (self), format));
189
254
  }
190
255
 
256
+ static mrb_value
257
+ gettextpo_message_m_format (mrb_state *mrb, mrb_value self)
258
+ {
259
+ char *format = NULL;
260
+ mrb_get_args (mrb, "z", &format);
261
+
262
+ switch (po_message_get_format (DATA_PTR (self), format))
263
+ {
264
+ case 1:
265
+ return mrb_true_value ();
266
+ break;
267
+ case 0:
268
+ return mrb_false_value ();
269
+ break;
270
+ case -1:
271
+ return mrb_nil_value ();
272
+ break;
273
+ default:
274
+ mrb_raise (mrb, ERROR, "unreachable");
275
+ }
276
+ }
277
+
191
278
  static mrb_value
192
279
  gettextpo_message_m_format_set (mrb_state *mrb, mrb_value self)
193
280
  {
@@ -208,6 +295,10 @@ gettextpo_message_m_format_set (mrb_state *mrb, mrb_value self)
208
295
  return NIL;
209
296
  }
210
297
 
298
+ QUERY_FLAG (sticky);
299
+ UPDATE_FLAG (sticky);
300
+ FLAG_ITERATOR_METHOD (sticky);
301
+
211
302
  static mrb_value
212
303
  gettextpo_message_m_range (mrb_state *mrb, mrb_value self)
213
304
  {
@@ -528,8 +619,7 @@ gettextpo_message_iterator_m_next (mrb_state *mrb, mrb_value message_iterator)
528
619
  return value;
529
620
  }
530
621
  else
531
- mrb_raise (mrb, mrb_class_get_id (mrb, MRB_SYM (StopIteration)),
532
- "end of PO message iterator");
622
+ mrb_raise (mrb, STOP_ITERATION, "end of PO message iterator");
533
623
  }
534
624
 
535
625
  static mrb_value
@@ -784,10 +874,32 @@ mrb_mruby_gettextpo_gem_init (mrb_state *const mrb)
784
874
  gettextpo_message_m_fuzzy, MRB_ARGS_NONE ());
785
875
  mrb_define_method_id (mrb, mrb_cMessage, MRB_SYM_E (fuzzy),
786
876
  gettextpo_message_m_fuzzy_set, MRB_ARGS_REQ (1));
877
+
878
+ mrb_define_method_id (mrb, mrb_cMessage, MRB_SYM_Q (workflow_flag),
879
+ gettextpo_message_m_workflow_flag_q, MRB_ARGS_REQ (1));
880
+ mrb_define_method_id (mrb, mrb_cMessage, MRB_SYM (update_workflow_flag),
881
+ gettextpo_message_m_workflow_flag_update,
882
+ MRB_ARGS_ANY ());
883
+ mrb_define_method_id (mrb, mrb_cMessage, MRB_SYM (workflow_flag_iterator),
884
+ gettextpo_message_m_workflow_flag_iterator,
885
+ MRB_ARGS_NONE ());
886
+
787
887
  mrb_define_method_id (mrb, mrb_cMessage, MRB_SYM_Q (format),
888
+ gettextpo_message_m_format_q, MRB_ARGS_REQ (1));
889
+ mrb_define_method_id (mrb, mrb_cMessage, MRB_SYM (format),
788
890
  gettextpo_message_m_format, MRB_ARGS_REQ (1));
789
891
  mrb_define_method_id (mrb, mrb_cMessage, MRB_SYM (update_format),
790
892
  gettextpo_message_m_format_set, MRB_ARGS_ANY ());
893
+
894
+ mrb_define_method_id (mrb, mrb_cMessage, MRB_SYM_Q (sticky_flag),
895
+ gettextpo_message_m_sticky_flag_q, MRB_ARGS_REQ (1));
896
+ mrb_define_method_id (mrb, mrb_cMessage, MRB_SYM (update_sticky_flag),
897
+ gettextpo_message_m_sticky_flag_update,
898
+ MRB_ARGS_ANY ());
899
+ mrb_define_method_id (mrb, mrb_cMessage, MRB_SYM (sticky_flag_iterator),
900
+ gettextpo_message_m_sticky_flag_iterator,
901
+ MRB_ARGS_NONE ());
902
+
791
903
  mrb_define_method_id (mrb, mrb_cMessage, MRB_SYM_Q (range),
792
904
  gettextpo_message_m_range, MRB_ARGS_REQ (1));
793
905
  mrb_define_method_id (mrb, mrb_cMessage, MRB_SYM_E (range),
@@ -816,6 +928,11 @@ mrb_mruby_gettextpo_gem_init (mrb_state *const mrb)
816
928
  gettextpo_filepos_m_file, MRB_ARGS_NONE ());
817
929
  mrb_define_method_id (mrb, mrb_cFilePos, MRB_SYM (start_line),
818
930
  gettextpo_filepos_m_start_line, MRB_ARGS_NONE ());
931
+ struct RClass *const mrb_cFlagIterator = mrb_define_class_under_id (
932
+ mrb, mrb_mGettextPO, MRB_SYM (FlagIterator), mrb->object_class);
933
+ MRB_SET_INSTANCE_TT (mrb_cFlagIterator, MRB_TT_CDATA);
934
+ mrb_define_method_id (mrb, mrb_cFlagIterator, MRB_SYM (next),
935
+ gettextpo_flag_iterator_m_next, MRB_ARGS_NONE ());
819
936
  DONE;
820
937
  }
821
938
 
@@ -28,6 +28,7 @@ class GettextPOTest < Test::Unit::TestCase
28
28
  check
29
29
  file
30
30
  filepos
31
+ flag
31
32
  format
32
33
  header
33
34
  message
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gettextpo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - gemmaro
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-03-15 00:00:00.000000000 Z
11
12
  dependencies: []
12
13
  description: This gem is a Ruby library for the GNU gettext PO files. This is a C
13
14
  binding of the libgettextpo library, which is part of the GNU gettext package.
@@ -19,6 +20,7 @@ extensions:
19
20
  extra_rdoc_files: []
20
21
  files:
21
22
  - ".clang-format"
23
+ - ".clangd"
22
24
  - ".dir-locals.el"
23
25
  - ".env.example"
24
26
  - ".envrc"
@@ -34,9 +36,11 @@ files:
34
36
  - include/mrb_gettextpo.h
35
37
  - lib/gettextpo.rb
36
38
  - lib/gettextpo/version.rb
39
+ - manifest.scm
37
40
  - mrbgem.rake
38
41
  - mrblib/mrb_gettextpo.rb
39
- - mrblib/version.rb
42
+ - mrblib/mrb_gettextpo_common.rb
43
+ - mrblib/mrb_gettextpo_version.rb
40
44
  - sig/gettextpo.rbs
41
45
  - src/mrb_gettextpo.c
42
46
  - test.cruby/gettextpo_test.rb
@@ -58,6 +62,7 @@ metadata:
58
62
  homepage_uri: https://git.disroot.org/gemmaro/ruby-gettextpo
59
63
  source_code_uri: https://git.disroot.org/gemmaro/ruby-gettextpo
60
64
  wiki_uri: https://git.disroot.org/gemmaro/ruby-gettextpo/wiki
65
+ post_install_message:
61
66
  rdoc_options: []
62
67
  require_paths:
63
68
  - lib
@@ -71,8 +76,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
71
76
  - - ">="
72
77
  - !ruby/object:Gem::Version
73
78
  version: '0'
74
- requirements: []
75
- rubygems_version: 3.6.9
79
+ requirements:
80
+ - libgettextpo
81
+ rubygems_version: 3.5.22
82
+ signing_key:
76
83
  specification_version: 4
77
84
  summary: GNU gettext PO file parser library
78
85
  test_files: []