なかだです。

Etc.each_passwdやEtc.each_groupというものを思いつきました。
また、別名としてEtc::Passwd.eachやEtc.::Group.eachも。

  # パスワードを設定していないユーザ名のリスト
  Etc::Passwd.find_all {|u| u.passwd == ""}.map(&:name)

とか。


Index: ext/etc/etc.c =================================================================== --- ext/etc/etc.c (revision 22104) +++ ext/etc/etc.c (working copy) @@ -187,4 +187,14 @@ passwd_iterate(void) return Qnil; } + +static void +each_passwd(void) +{ + if (passwd_blocking) { + rb_raise(rb_eRuntimeError, "parallel passwd iteration"); + } + passwd_blocking = Qtrue; + rb_ensure(passwd_iterate, 0, passwd_ensure, 0); +} #endif @@ -212,11 +222,7 @@ etc_passwd(VALUE obj) rb_secure(4); if (rb_block_given_p()) { - if (passwd_blocking) { - rb_raise(rb_eRuntimeError, "parallel passwd iteration"); - } - passwd_blocking = Qtrue; - rb_ensure(passwd_iterate, 0, passwd_ensure, 0); + each_passwd(); } - if (pw = getpwent()) { + else if (pw = getpwent()) { return setup_passwd(pw); } @@ -225,4 +231,32 @@ etc_passwd(VALUE obj) } +/* Iterates for each entry in the /etc/passwd file if a block is given. + * If no block is given, returns the enumerator. + * + * The code block is passed an Struct::Passwd struct; see getpwent above for + * details. + * + * Example: + * + * require 'etc' + * + * Etc.each_passwd {|u| + * puts u.name + " = " + u.gecos + * } + * + * Etc.each_passwd.collect {|u| u.gecos} + * Etc::Passwd.collect {|u| u.gecos} + * + */ +static VALUE +etc_each_passwd(VALUE obj) +{ +#ifdef HAVE_GETPWENT + RETURN_ENUMERATOR(obj, 0, 0); + each_passwd(); +#endif + return obj; +} + /* Resets the process of reading the /etc/passwd file, so that the next call * to getpwent will return the first entry again. @@ -391,4 +425,14 @@ group_iterate(void) return Qnil; } + +static void +each_group(void) +{ + if (group_blocking) { + rb_raise(rb_eRuntimeError, "parallel group iteration"); + } + group_blocking = Qtrue; + rb_ensure(group_iterate, 0, group_ensure, 0); +} #endif @@ -416,11 +460,7 @@ etc_group(VALUE obj) rb_secure(4); if (rb_block_given_p()) { - if (group_blocking) { - rb_raise(rb_eRuntimeError, "parallel group iteration"); - } - group_blocking = Qtrue; - rb_ensure(group_iterate, 0, group_ensure, 0); + each_group(); } - if (grp = getgrent()) { + else if (grp = getgrent()) { return setup_group(grp); } @@ -429,4 +469,32 @@ etc_group(VALUE obj) } +/* Iterates for each entry in the /etc/group file if a block is given. + * If no block is given, returns the enumerator. + * + * The code block is passed an Struct::Group struct; see getpwent above for + * details. + * + * Example: + * + * require 'etc' + * + * Etc.each_group {|g| + * puts g.name + ": " + g.mem.join(', ') + * } + * + * Etc.each_group.collect {|g| g.name} + * Etc::Group.select {|g| !g.mem.empty?} + * + */ +static VALUE +etc_each_group(VALUE obj) +{ +#ifdef HAVE_GETPWENT + RETURN_ENUMERATOR(obj, 0, 0); + each_group(); +#endif + return obj; +} + /* Resets the process of reading the /etc/group file, so that the next call * to getgrent will return the first entry again. @@ -505,8 +573,10 @@ Init_etc(void) rb_define_module_function(mEtc, "getpwent", etc_getpwent, 0); rb_define_module_function(mEtc, "passwd", etc_passwd, 0); + rb_define_module_function(mEtc, "each_passwd", etc_each_passwd, 0); rb_define_module_function(mEtc, "getgrgid", etc_getgrgid, -1); rb_define_module_function(mEtc, "getgrnam", etc_getgrnam, 1); rb_define_module_function(mEtc, "group", etc_group, 0); + rb_define_module_function(mEtc, "each_group", etc_each_group, 0); rb_define_module_function(mEtc, "setgrent", etc_setgrent, 0); rb_define_module_function(mEtc, "endgrent", etc_endgrent, 0); @@ -539,4 +609,6 @@ Init_etc(void) NULL); rb_define_const(mEtc, "Passwd", sPasswd); + rb_extend_object(sPasswd, rb_mEnumerable); + rb_define_singleton_method(sPasswd, "each", etc_each_passwd, 0); #ifdef HAVE_GETGRENT @@ -548,4 +620,6 @@ Init_etc(void) rb_define_const(mEtc, "Group", sGroup); + rb_extend_object(sGroup, rb_mEnumerable); + rb_define_singleton_method(sGroup, "each", etc_each_group, 0); #endif }
-- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦