winevt_c 0.7.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1069bcfe57cfd91d05fcff1a7314929c01c675d30678b8a624c926eb0a91b09f
4
- data.tar.gz: bea008c2cf452539b59a9d22800183797babcbb5158e5f6688dd3fd749dc4e3d
3
+ metadata.gz: fd29b4664130c80249811fb66df434aaa9219d6d375bcdaa7adfdd3fe72dd25e
4
+ data.tar.gz: 2a1e3ec7cb528269ebb65f4b1df72d6045f882d865956fb85def8bcb925d6944
5
5
  SHA512:
6
- metadata.gz: 81ef978d005e6c87607cc187509f8e5ae52b33b7687100148139b050c09d61cbb813fe1e77f8ccc0a02fa881d6a8066b6f7c959f45be3ff65c79e77b03dbd247
7
- data.tar.gz: c27480331ce8d8cfab82cd00ef4bd940e3a4224076db6641e9bb49019a2d88dac2ee73790153a20cf2bbafa1db6844b876f59cb53c249251460b6eb97b895c99
6
+ metadata.gz: 0f80978e4268c233d2e309c0e9556f38386b35300381c04f4d84226ce239a85ccb48b20a2c2a3c59c3d0557347e95ff11e0ca11945eaa128ba2c78c67ebc9250
7
+ data.tar.gz: a8ff4fae4995b41910352186898d54c929a918f86a6a4efe29c0a5c93433d9c95d91be5c9f81fdb52ff5f2307cbb2e2e61790a22fe076b7221ba03c89aed48d5
data/README.md CHANGED
@@ -31,6 +31,61 @@ Or install it yourself as:
31
31
  ## Usage
32
32
 
33
33
  Usage examples are found in [example directory](example).
34
+
35
+ ### Multilingual description
36
+
37
+ Currently, the following locales should be supported to output description:
38
+
39
+ locale | language
40
+ ---------:|:--------
41
+ bg\_BG | Bulgarian
42
+ zh\_CN | Simplified Chinese
43
+ zh\_TW | Traditional Chinese
44
+ zh\_HK | Chinese (Hong Kong)
45
+ zh\_SG | Chinese (Singapore)
46
+ hr\_HR | Croatian
47
+ cz\_CZ | Czech
48
+ da\_DK | Danish
49
+ nl\_NL | Dutch
50
+ nl\_BG | Dutch (Belgium)
51
+ en\_US | English (United States)
52
+ en\_GB | English (UK)
53
+ en\_AU | English (Australia)
54
+ en\_CA | English (Canada)
55
+ en\_NZ | English (New Zealand)
56
+ en\_IE | English (Ireland)
57
+ fi\_FI | Finnish
58
+ fr\_FR | French
59
+ fr\_BE | French (Belgium)
60
+ fr\_CA | French (Canada)
61
+ fr\_CH | French (Swiss)
62
+ de\_DE | German
63
+ de\_CH | German (Swiss)
64
+ de\_AT | German (Austria)
65
+ el\_GR | Greek (Ελληνικά)
66
+ hu\_HU | Hungarian
67
+ is\_IS | Icelandic
68
+ it\_IT | Italian (Italy)
69
+ it\_CH | Italian (Swiss)
70
+ ja\_JP | Japanese
71
+ ko\_KO | Korean
72
+ no\_NO | Norwegian (Bokmål)
73
+ nb\_NO | Norwegian (Bokmål)
74
+ nn\_NO | Norwegian (Nynorsk)
75
+ pl\_PL | Polish (Poland)
76
+ pt\_PT | Portuguese
77
+ pt\_BR | Portuguese (Brazil)
78
+ ro\_RO | Romanian
79
+ ru\_RU | Russian (русский язык)
80
+ sk\_SK | Slovak
81
+ sl\_SI | Slovenian
82
+ es\_ES | Spanish
83
+ es\_ES\_T | Spanish (Traditional)
84
+ es\_MX | Spanish (Mexico)
85
+ es\_ES\_M | Spanish (Modern)
86
+ sv\_SE | Swedish
87
+ tr\_TR | Turkish
88
+
34
89
  ## Development
35
90
 
36
91
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/Rakefile CHANGED
@@ -28,7 +28,7 @@ task 'gem:native' do
28
28
  # See RUBY_CC_VERSION in https://github.com/rake-compiler/rake-compiler-dock/blob/master/Dockerfile.mri
29
29
  RakeCompilerDock.sh <<-EOS
30
30
  gem install bundler yard --no-doc && bundle
31
- rake cross native gem RUBY_CC_VERSION=2.4.0:2.5.0:2.6.0
31
+ rake cross native gem RUBY_CC_VERSION=2.4.0:2.5.0:2.6.0:2.7.0
32
32
  EOS
33
33
  end
34
34
 
@@ -1,7 +1,13 @@
1
1
  require 'winevt'
2
2
 
3
- @query = Winevt::EventLog::Query.new("Application", "*[System[(Level <= 3) and TimeCreated[timediff(@SystemTime) <= 86400000]]]")
3
+ @session = Winevt::EventLog::Session.new("127.0.0.1") # Or remote box ip
4
+ # @session.domain = "<EXAMPLEGROUP>"
5
+ # @session.username = "<username>"
6
+ # @session.password = "<password>"
7
+ @query = Winevt::EventLog::Query.new("Application", "*[System[(Level <= 4) and TimeCreated[timediff(@SystemTime) <= 86400000]]]", @session)
4
8
 
9
+ @query.render_as_xml = true
10
+ @query.preserve_qualifiers = true
5
11
  @query.each do |eventlog, message, string_inserts|
6
12
  puts ({eventlog: eventlog, data: message})
7
13
  end
@@ -0,0 +1,13 @@
1
+ require 'winevt'
2
+
3
+ @locale = Winevt::EventLog::Locale.new
4
+
5
+ header = <<EOC
6
+ locale | language
7
+ ---------:|:--------
8
+ EOC
9
+
10
+ print header
11
+ @locale.each do |code, desc|
12
+ print "#{code.gsub("_", "\\_")}#{" "*(8 - code.size)}| #{desc}\n"
13
+ end
@@ -1,7 +1,7 @@
1
1
  require 'winevt'
2
2
 
3
3
  @subscribe = Winevt::EventLog::Subscribe.new
4
- @subscribe.tail = true
4
+ @subscribe.read_existing_events = true
5
5
  @subscribe.rate_limit = 80
6
6
  @subscribe.subscribe(
7
7
  "Application", "*[System[(Level <= 4) and TimeCreated[timediff(@SystemTime) <= 86400000]]]"
@@ -1,9 +1,17 @@
1
1
  require 'winevt'
2
2
 
3
+ @session = Winevt::EventLog::Session.new("127.0.0.1") # Or remote box ip
4
+ # @session.domain = "<EXAMPLEGROUP>"
5
+ # @session.username = "<username>"
6
+ # @session.password = "<password>"
7
+ @bookmark = Winevt::EventLog::Bookmark.new
3
8
  @subscribe = Winevt::EventLog::Subscribe.new
4
- @subscribe.tail = true
9
+ @subscribe.read_existing_events = true
10
+ @subscribe.preserve_qualifiers = true
11
+ @subscribe.render_as_xml = true
5
12
  @subscribe.subscribe(
6
- "Security", "*[System[(Level <= 4) and TimeCreated[timediff(@SystemTime) <= 86400000]]]"
13
+ "Security", "*[System[(Level <= 4) and TimeCreated[timediff(@SystemTime) <= 86400000]]]",
14
+ @bookmark, @session
7
15
  )
8
16
  while true do
9
17
  @subscribe.each do |eventlog, message, string_inserts|
@@ -1,7 +1,11 @@
1
1
  #include <winevt_c.h>
2
2
 
3
3
  VALUE rb_mWinevt;
4
+ VALUE rb_cQuery;
4
5
  VALUE rb_cEventLog;
6
+ VALUE rb_cSubscribe;
7
+ VALUE rb_eWinevtQueryError;
8
+ VALUE rb_eRemoteHandlerError;
5
9
 
6
10
  static ID id_call;
7
11
 
@@ -13,11 +17,14 @@ Init_winevt(void)
13
17
  rb_cQuery = rb_define_class_under(rb_cEventLog, "Query", rb_cObject);
14
18
  rb_cSubscribe = rb_define_class_under(rb_cEventLog, "Subscribe", rb_cObject);
15
19
  rb_eWinevtQueryError = rb_define_class_under(rb_cQuery, "Error", rb_eStandardError);
20
+ rb_eRemoteHandlerError = rb_define_class_under(rb_cSubscribe, "RemoteHandlerError", rb_eRuntimeError);
16
21
 
17
22
  Init_winevt_channel(rb_cEventLog);
18
23
  Init_winevt_bookmark(rb_cEventLog);
19
24
  Init_winevt_query(rb_cEventLog);
20
25
  Init_winevt_subscribe(rb_cEventLog);
26
+ Init_winevt_locale(rb_cEventLog);
27
+ Init_winevt_session(rb_cEventLog);
21
28
 
22
29
  id_call = rb_intern("call");
23
30
  }
@@ -19,6 +19,8 @@
19
19
  */
20
20
  /* clang-format pn */
21
21
 
22
+ VALUE rb_cBookmark;
23
+
22
24
  static void bookmark_free(void* ptr);
23
25
 
24
26
  static const rb_data_type_t rb_winevt_bookmark_type = { "winevt/bookmark",
@@ -21,28 +21,58 @@
21
21
  #define EventQuery(object) ((struct WinevtQuery*)DATA_PTR(object))
22
22
  #define EventBookMark(object) ((struct WinevtBookmark*)DATA_PTR(object))
23
23
  #define EventChannel(object) ((struct WinevtChannel*)DATA_PTR(object))
24
+ #define EventSession(object) ((struct WinevtSession*)DATA_PTR(object))
25
+
26
+ typedef struct {
27
+ LANGID langID;
28
+ CHAR* langCode;
29
+ CHAR* description;
30
+ } LocaleInfo;
24
31
 
25
32
  #ifdef __cplusplus
26
33
  extern "C" {
27
34
  #endif /* __cplusplus */
28
35
 
29
36
  VALUE wstr_to_rb_str(UINT cp, const WCHAR* wstr, int clen);
37
+ #if defined(__cplusplus)
38
+ [[ noreturn ]]
39
+ #endif /* __cplusplus */
30
40
  void raise_system_error(VALUE error, DWORD errorCode);
31
41
  VALUE render_to_rb_str(EVT_HANDLE handle, DWORD flags);
32
- WCHAR* get_description(EVT_HANDLE handle);
42
+ EVT_HANDLE connect_to_remote(LPWSTR computerName, LPWSTR domain,
43
+ LPWSTR username, LPWSTR password,
44
+ EVT_RPC_LOGIN_FLAGS flags);
45
+ WCHAR* get_description(EVT_HANDLE handle, LANGID langID, EVT_HANDLE hRemote);
33
46
  VALUE get_values(EVT_HANDLE handle);
34
- VALUE render_system_event(EVT_HANDLE handle);
47
+ VALUE render_system_event(EVT_HANDLE handle, BOOL preserve_qualifiers);
48
+ LocaleInfo* get_locale_info_from_rb_str(VALUE rb_locale_str);
35
49
 
36
50
  #ifdef __cplusplus
37
51
  }
38
52
  #endif /* __cplusplus */
39
53
 
40
- VALUE rb_cQuery;
41
- VALUE rb_cFlag;
42
- VALUE rb_cChannel;
43
- VALUE rb_cBookmark;
44
- VALUE rb_cSubscribe;
45
- VALUE rb_eWinevtQueryError;
54
+ extern VALUE rb_cQuery;
55
+ extern VALUE rb_cFlag;
56
+ extern VALUE rb_cChannel;
57
+ extern VALUE rb_cBookmark;
58
+ extern VALUE rb_cSubscribe;
59
+ extern VALUE rb_eWinevtQueryError;
60
+ extern VALUE rb_eRemoteHandlerError;
61
+ extern VALUE rb_cLocale;
62
+ extern VALUE rb_cSession;
63
+
64
+ struct WinevtSession {
65
+ LPWSTR server;
66
+ LPWSTR domain;
67
+ LPWSTR username;
68
+ LPWSTR password;
69
+ EVT_RPC_LOGIN_FLAGS flags;
70
+ };
71
+
72
+ extern LocaleInfo localeInfoTable[];
73
+ extern LocaleInfo default_locale;
74
+
75
+ struct WinevtLocale {};
46
76
 
47
77
  struct WinevtChannel
48
78
  {
@@ -66,6 +96,9 @@ struct WinevtQuery
66
96
  LONG offset;
67
97
  LONG timeout;
68
98
  BOOL renderAsXML;
99
+ BOOL preserveQualifiers;
100
+ LocaleInfo *localeInfo;
101
+ EVT_HANDLE remoteHandle;
69
102
  };
70
103
 
71
104
  #define SUBSCRIBE_ARRAY_SIZE 10
@@ -84,11 +117,16 @@ struct WinevtSubscribe
84
117
  time_t lastTime;
85
118
  DWORD currentRate;
86
119
  BOOL renderAsXML;
120
+ BOOL preserveQualifiers;
121
+ LocaleInfo* localeInfo;
122
+ EVT_HANDLE remoteHandle;
87
123
  };
88
124
 
89
125
  void Init_winevt_query(VALUE rb_cEventLog);
90
126
  void Init_winevt_channel(VALUE rb_cEventLog);
91
127
  void Init_winevt_bookmark(VALUE rb_cEventLog);
92
128
  void Init_winevt_subscribe(VALUE rb_cEventLog);
129
+ void Init_winevt_locale(VALUE rb_cEventLog);
130
+ void Init_winevt_session(VALUE rb_cEventLog);
93
131
 
94
132
  #endif // _WINEVT_C_H
@@ -19,6 +19,8 @@
19
19
  * print channels
20
20
  */
21
21
 
22
+ VALUE rb_cChannel;
23
+
22
24
  DWORD is_subscribable_channel_p(EVT_HANDLE hChannel, BOOL force_enumerate);
23
25
  DWORD check_subscribable_with_channel_config_type(int Id, PEVT_VARIANT pProperty, BOOL force_enumerate);
24
26
  static void channel_free(void* ptr);
@@ -0,0 +1,92 @@
1
+ #include <winevt_c.h>
2
+
3
+
4
+ /* clang-format off */
5
+ /*
6
+ * Document-class: Winevt::EventLog::Locale
7
+ *
8
+ * handle locales for Windows EventLog's description.
9
+ *
10
+ * @example
11
+ * require 'winevt'
12
+ *
13
+ * @locale = Winevt::EventLog::Locale.new
14
+ * @locale.each {|code, desc|
15
+ * print code, desc
16
+ * }
17
+ * @since v0.8.1
18
+ */
19
+ /* clang-format on */
20
+
21
+ VALUE rb_cLocale;
22
+
23
+ static void locale_free(void* ptr);
24
+
25
+ static const rb_data_type_t rb_winevt_locale_type = { "winevt/locale",
26
+ {
27
+ 0,
28
+ locale_free,
29
+ 0,
30
+ },
31
+ NULL,
32
+ NULL,
33
+ RUBY_TYPED_FREE_IMMEDIATELY };
34
+
35
+ static void
36
+ locale_free(void* ptr)
37
+ {
38
+ xfree(ptr);
39
+ }
40
+
41
+ static VALUE
42
+ rb_winevt_locale_alloc(VALUE klass)
43
+ {
44
+ VALUE obj;
45
+ struct WinevtLocale* winevtLocale;
46
+ obj = TypedData_Make_Struct(
47
+ klass, struct WinevtLocale, &rb_winevt_locale_type, winevtLocale);
48
+ return obj;
49
+ }
50
+
51
+ /*
52
+ * Initalize Locale class.
53
+ *
54
+ * @return [Locale]
55
+ *
56
+ */
57
+ static VALUE
58
+ rb_winevt_locale_initialize(VALUE self)
59
+ {
60
+ return Qnil;
61
+ }
62
+
63
+ /*
64
+ * Enumerate supported locales and its descriptions
65
+ *
66
+ * @yield (String, String)
67
+ *
68
+ */
69
+ static VALUE
70
+ rb_winevt_locale_each(VALUE self)
71
+ {
72
+ RETURN_ENUMERATOR(self, 0, 0);
73
+
74
+ for (int i = 0; localeInfoTable[i].langCode != NULL; i++) {
75
+ rb_yield_values(2,
76
+ rb_utf8_str_new_cstr(localeInfoTable[i].langCode),
77
+ rb_utf8_str_new_cstr(localeInfoTable[i].description));
78
+ }
79
+
80
+ return Qnil;
81
+ }
82
+
83
+ void
84
+ Init_winevt_locale(VALUE rb_cEventLog)
85
+ {
86
+ rb_cLocale = rb_define_class_under(rb_cEventLog, "Locale", rb_cObject);
87
+
88
+ rb_define_alloc_func(rb_cLocale, rb_winevt_locale_alloc);
89
+
90
+ rb_define_method(rb_cLocale, "initialize", rb_winevt_locale_initialize, 0);
91
+ rb_define_method(rb_cLocale, "each", rb_winevt_locale_each, 0);
92
+ }
@@ -0,0 +1,68 @@
1
+ #include <winevt_c.h>
2
+
3
+ LocaleInfo localeInfoTable [] = {
4
+ { MAKELANGID(LANG_BULGARIAN, SUBLANG_DEFAULT), "bg_BG", "Bulgarian"},
5
+ { MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), "zh_CN", "Simplified Chinese "},
6
+ { MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL), "zh_TW", "Traditional Chinese"},
7
+ { MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG), "zh_HK", "Chinese (Hong Kong)"},
8
+ { MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE), "zh_SG", "Chinese (Singapore)"},
9
+ { MAKELANGID(LANG_CROATIAN, SUBLANG_DEFAULT), "hr_HR", "Croatian"},
10
+ { MAKELANGID(LANG_CZECH, SUBLANG_DEFAULT), "cs_CZ", "Czech"},
11
+ { MAKELANGID(LANG_DANISH, SUBLANG_DEFAULT), "da_DK", "Dannish"},
12
+ { MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), "nl_NL", "Dutch"},
13
+ { MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN), "nl_BE", "Dutch (Belgium)"},
14
+ { MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), "en_US", "English (United States)"},
15
+ { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_UK), "en_GB", "English (UK)"},
16
+ { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_AUS), "en_AU", "English (Australia)"},
17
+ { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_CAN), "en_CA", "English (Canada)"},
18
+ { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_NZ), "en_NZ", "English (New Zealand)"},
19
+ { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_EIRE), "en_IE", "English (Ireland)"},
20
+ { MAKELANGID(LANG_FINNISH, SUBLANG_DEFAULT), "fi_FI", "Finnish"},
21
+ { MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH), "fr_FR", "French"},
22
+ { MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_BELGIAN), "fr_BE", "French (Belgium)"},
23
+ { MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_CANADIAN), "fr_CA", "French (Canada)"},
24
+ { MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_SWISS), "fr_CH", "French (Swiss)"},
25
+ { MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), "de_DE", "German"},
26
+ { MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_SWISS), "de_CH", "German (Swiss)"},
27
+ { MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_AUSTRIAN), "de_AT", "German (Austria))"},
28
+ { MAKELANGID(LANG_GREEK, SUBLANG_DEFAULT), "el_GR", "Greek (Ελληνικά)"},
29
+ { MAKELANGID(LANG_HUNGARIAN, SUBLANG_DEFAULT), "hu_HU", "Hungarian"},
30
+ { MAKELANGID(LANG_ICELANDIC, SUBLANG_DEFAULT), "is_IS", "Icelandic"},
31
+ { MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), "it_IT", "Italian (Italy)"},
32
+ { MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN_SWISS), "it_CH", "Italian (Swiss)"},
33
+ { MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT), "ja_JP", "Japanases"},
34
+ { MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT), "ko_KO", "Korean"},
35
+ { MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL), "no_NO", "Norwegian (Bokmål)"},
36
+ { MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL), "nb_NO", "Norwegian (Bokmål)"},
37
+ { MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK), "nn_NO", "Norwegian (Nynorsk)"},
38
+ { MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT), "pl_PL", "Polish"},
39
+ { MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE), "pt_PT", "Portuguese"},
40
+ { MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), "pt_BR", "Portuguese (Brazil)"},
41
+ { MAKELANGID(LANG_ROMANIAN, SUBLANG_DEFAULT), "ro_RO", "Romanian"},
42
+ { MAKELANGID(LANG_RUSSIAN, SUBLANG_DEFAULT), "ru_RU", "Russian (русский язык)"},
43
+ { MAKELANGID(LANG_SLOVAK, SUBLANG_DEFAULT), "sk_SK", "Slovak"},
44
+ { MAKELANGID(LANG_SLOVENIAN, SUBLANG_DEFAULT), "sl_SI", "Slovenian"},
45
+ { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH), "es_ES", "Spanish"},
46
+ { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH), "es_ES_T", "Spanish (Traditional)"},
47
+ { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MEXICAN), "es_MX", "Spanish (Mexico)"},
48
+ { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), "es_ES_M", "Spanish (Modern)"},
49
+ { MAKELANGID(LANG_SWEDISH, SUBLANG_DEFAULT), "sv_SE", "Swedish"},
50
+ { MAKELANGID(LANG_TURKISH, SUBLANG_DEFAULT), "tr_TR", "Turkish"},
51
+ { 0, NULL, NULL}
52
+ };
53
+
54
+ LocaleInfo default_locale = {MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), "neutral", "Default"};
55
+
56
+ LocaleInfo*
57
+ get_locale_info_from_rb_str(VALUE rb_locale_str)
58
+ {
59
+ CHAR* locale_str = StringValuePtr(rb_locale_str);
60
+
61
+ for (int i = 0; localeInfoTable[i].langCode != NULL; i++) {
62
+ if (stricmp(localeInfoTable[i].langCode, locale_str) == 0) {
63
+ return &localeInfoTable[i];
64
+ }
65
+ }
66
+
67
+ rb_raise(rb_eArgError, "Unknown locale: %s", locale_str);
68
+ }