summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/meta-normalize-stdio192
1 files changed, 164 insertions, 28 deletions
diff --git a/bin/meta-normalize-stdio b/bin/meta-normalize-stdio
index 901ca0b..c49dcd9 100755
--- a/bin/meta-normalize-stdio
+++ b/bin/meta-normalize-stdio
@@ -1,33 +1,169 @@
#!/usr/bin/env ruby
+
+# First we define a bunch of code-generators, then at the end is a
+# very neat and readable definition of the format of the YAML files.
+
require 'yaml'
-core_order = [ "username",
- "fullname",
- "email", # ordered list
- "groups", # ordered list
- "pgp_keyid",
- "pgp_revoked_keyids", # unordered list
- "ssh_keys", # unordered map
- "extra" ] # unordered map
-
-extra_order = [ "alias",
- "other_contact",
- "roles",
- "website",
- "occupation",
- "yob",
- "location",
- "languages",
- "interests",
- "favorite_distros" ]
-
-_core_order = Hash[[*core_order.map.with_index]]
-_extra_order = Hash[[*extra_order.map.with_index]]
-
-user = YAML::load(STDIN)
-user = Hash[user.sort_by{|k,v| _core_order[k]}]}
-user["pgp_revoked_keyids"] = user["pgp_revoked_keyids"].sort if user["extra"]}
-user["ssh_keys"] = Hash[user["ssh_keys"].sort_by{|k,v| k}] if user["ssh_keys"]}
-user["extra"] = Hash[user["extra"].sort_by{|k,v| _extra_order[k]}] if user["extra"]}
+def error(msg)
+ $stderr.puts "ERROR: #{msg}"
+ @err = 1
+end
+
+def warning(msg)
+ $stderr.puts "WARNING: #{msg}"
+end
+
+
+# Generic validators/formatters
+
+def semiordered_list(cnt, validator)
+ lambda {|name,ary|
+ if ary.class != Array
+ error "`#{name}' must be a list"
+ else
+ ary.each_index{|i| ary[i] = validator.call("#{name}[#{i}]", ary[i])}
+ ary = ary.first(cnt).concat(ary.last(ary.count-cnt).sort)
+ end
+ ary
+ }
+end
+
+def unordered_list(validator)
+ semiordered_list(0, validator)
+end
+
+def _unknown(map_name, key)
+ error "Unknown item: #{map_name}[#{key.inspect}]"
+ 0
+end
+def unordered_map1(validator)
+ lambda {|name,hash|
+ if hash.class != Hash
+ error "`#{name}' must be a map"
+ else
+ order = Hash[[*validator.keys.map.with_index]]
+ hash = Hash[hash.sort_by{|k,v| order[k] || _unknown(name,k) }]
+ hash.keys.each{|k|
+ hash[k] = validator[k].call("#{name}[#{k.inspect}]", hash[k])
+ }
+ end
+ hash
+ }
+end
+
+def unordered_map2(key_validator, val_validator)
+ lambda {|name,hash|
+ if hash.class != Hash
+ error "`#{name}' must be a map"
+ else
+ hash = Hash[hash.sort_by{|k,v| k}]
+ hash.keys.each{|k|
+ key_validator.call("#{name} key #{k.inspect}", k)
+ hash[k] = val_validator.call("#{name}[#{k.inspect}]", hash[k])
+ }
+ end
+ hash
+ }
+end
+
+string = lambda {|name,str|
+ if str.class != String
+ error "`#{name}' must be a string"
+ else
+ str
+ end
+}
+
+# Regular Expression String
+def restring(re)
+ lambda {|name,str|
+ if str.class != String
+ error "`#{name}' must be a string"
+ else
+ unless re =~ str
+ error "`#{name}' does not match #{re.inspect}: #{str}"
+ end
+ str
+ end
+ }
+end
+
+
+# Specific validators/formatters
+
+year = lambda {|name, num|
+ if num.class != Fixnum
+ error "`#{name}' must be a year"
+ else
+ if (num < 1900 || num > 3000)
+ error "`#{name}' is a number, but doesn't look like a year"
+ end
+ num
+ end
+}
+
+# This regex is taken from http://www.w3.org/TR/html5/forms.html#valid-e-mail-address
+_email_regex = /^[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
+email_list = lambda {|name, ary|
+ if ary.class != Array
+ error "`#{name}' must be a list"
+ elsif ary.count > 1
+ preserve = 1
+ if ary.first.end_with?("@parabola.nu") and ary.count > 2
+ preserve = 2
+ end
+ ary = semiordered_list(preserve, restring(_email_regex)).call(name, ary)
+ end
+ ary
+}
+
+shell = lambda {|name, sh|
+ if sh.class != String
+ error "`#{name}' must be a string"
+ else
+ @valid_shells ||= open("/etc/shells").read.split("\n")
+ .find_all{|line| /^[^\#]/ =~ line}
+ unless @valid_shells.include?(sh)
+ warning "shell not listed in /etc/shells: #{sh}"
+ end
+ end
+ sh
+}
+
+
+# The format of the YAML files
+
+format = unordered_map1(
+ {
+ "username" => restring(/^[a-z][a-z0-9]*$/),
+ "fullname" => string,
+ "email" => email_list,
+ "groups" => semiordered_list(1, string),
+ "pgp_keyid" => restring(/^[0-9A-F]{40}$/),
+ "pgp_revoked_keyids" => unordered_list(restring(/^[0-9A-F]{40}$/)),
+ "ssh_keys" => unordered_map2(string, string),
+ "shell" => shell,
+ "extra" => unordered_map1(
+ {
+ "alias" => string,
+ "other_contact" => string,
+ "roles" => string,
+ "website" => string,
+ "occupation" => string,
+ "yob" => year,
+ "location" => string,
+ "languages" => string,
+ "interests" => string,
+ "favorite_distros" => string,
+ })
+ })
+
+
+@err = 0
+user = format.call("user", YAML::load(STDIN))
+if @err != 0
+ exit @err
+end
print user.to_yaml