From 475f7aa0e62e2e24753121f28d32843e983df4c6 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Sat, 10 Jun 2023 11:53:02 +0100 Subject: [PATCH] Now works on (my) Windows; upversioned to 1.0.0 --- README.md | 24 ++++++++++- project.clj | 4 +- src/cc/journeyman/real_name/core.clj | 64 ++++++++++++++++++++++++++-- 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ba87763..25b3965 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,8 @@ cc.journeyman.real-name.core=> (get-real-name "simon") ## Errors -If it doesn't work, an `ex-info` will be thrown, with at least the keys `:username` and `:os-name`, and a (hopefully useful) diagnostic message: +If it doesn't work, an `ex-info` will be thrown, with at least the keys `:username` +and `:os-name`, and a (hopefully useful) diagnostic message: ```clojure cc.journeyman.real-name.core=> (get-real-name "fred") @@ -77,6 +78,21 @@ If it does not work, please raise an issue on this project, giving the full `ex- from the exception you experienced. I don't guarantee I can fix it, because I don't have access to all platforms to test, but I will do my best. +### Caution: Windows + +There doesn't seem to be a mechanism to retrieve just the real name of the user +on a Windows system in locale-independent way. + +There must be some system call that Windows is using to localise the string +'Full Name', but I haven't found it. Consequently I'm using a map to do my own +resolution of locale-specific translations of 'Full Name' into known translations +used by Microsoft for different locales, but it is far from complete and may not +be accurate. + +In this case the ex-data from the exception thrown will include both the `locale` +and the `localised-key` used, and I need both of these to be able to resolve the +problem. + ## Invoking from Java As there doesn't seem to be a Java-native solution, you may want to use this @@ -94,6 +110,12 @@ public class Main { } ``` +## Policy on pull requests + +I obviously cannot test this on every possible platform. Pull requests which +extend this to work on platforms I don't have access to will be gratefully +received. + ## License Copyright © 2023 Simon Brooke diff --git a/project.clj b/project.clj index 60d4f8e..850aabb 100644 --- a/project.clj +++ b/project.clj @@ -1,9 +1,9 @@ -(defproject org.clojars.simon_brooke/real-name "0.1.0-SNAPSHOT" +(defproject org.clojars.simon_brooke/real-name "1.0.0" :codox {:metadata {:doc "**TODO**: write docs" :doc/format :markdown} :output-path "docs" :source-uri "https://github.com/simon-brooke/real-name/blob/master/{filepath}#L{line}"} - :dependencies [[org.clojure/clojure "1.10.3"]] + :dependencies [[org.clojure/clojure "1.11.1"]] :description "Resolve real names from user names in a platform independent fashion." :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" :url "https://www.eclipse.org/legal/epl-2.0/"} diff --git a/src/cc/journeyman/real_name/core.clj b/src/cc/journeyman/real_name/core.clj index f5b5924..531f077 100644 --- a/src/cc/journeyman/real_name/core.clj +++ b/src/cc/journeyman/real_name/core.clj @@ -2,7 +2,9 @@ "Resolve real names from user names in a platform independent fashion." (:require [clojure.java.io :refer [reader]] [clojure.java.shell :refer [sh]] - [clojure.string :refer [split starts-with? trim]]) + [clojure.string :refer [lower-case split starts-with? trim]]) + (:import [java.io StringReader] + [java.util Locale]) (:gen-class)) (defn- mac-os-x @@ -59,19 +61,73 @@ :os-name (System/getProperty "os.name") :os-version (System/getProperty "os.version")})))))) +(def full-name-in-locale + "Microsoft's own translations of `Full Name` into a variety of languages, + taken from their language portal. Clearly there must be some os-call that + they're using to resolve the translation, but I don't know it; so this is + the best I can currently do." + {"ar" "الاسم الكامل" + "bg" "Пълно име" + "ca" "Nom complet" + "cs" "Celé jméno" + "da" "Fulde navn" + "de" "Vollständiger Name" + "el" "Ονοματεπώνυμο" ;; Greek + "en" "Full Name" + "es" "Nombre completo" + "et" "Täielik nimi" ;; Microsoft also uses 'Täisnimi' in some contexts! + "fi" "Koko nimi" + "fr" "Nom complet" + "ga" "Ainm agus Sloinne" ;; Microsoft also uses 'Ainm iomlán' in some contexts! + "hu" "Teljes név" + "is" "Fullt Nafn" + "it" "Nome completo" + "jp" "氏名" ;; Microsoft also uses 'フル ネーム' and '完全名' in some contexts! + "lt" "Vardas, pavardė" ;; or 'Visas pavadinimas', or 'Visas vardas', or 'vardas ir pavardė'! + "lv" "Pilns vārds" ;; or 'Pilnais vārds', or 'Pilns nosaukums' + "nb" "Fullt navn" ;; Norse + "nl" "Volledige naam" + "nn" "Fullt namn" ;; also Norse + "no" "Fullt namn" ;; also Norse + "pl" "Imię i nazwisko" ;; or 'Pełna nazwa' + "pt" "Nome completo" + "ro" "Nume complet" + "ru" "Полное название" ;; or 'ФИО', or 'Полное имя' + "se" "Fullständigt namn" + "sk" "Celé meno" + "sl" "Polno ime" ;; or 'ime in priimek' + "tr" "Tam Adı" ;; or 'Tam Ad', or 'Adı-soyadı', or 'Ad ve soyadı', or 'Adı ve Soyadı' + "uk" "Повне ім’я" ;; or 'Ім'я та прізвище' + "zh" "全名"}) + (defn- windows7+ - "Very experimental, probably wrong." + "Very experimental, probably wrong, certainly works only in selected + locales." [username] - (let [response (sh "net" "user" username "/domain" "|" "FIND" "/I" "Full Name")] + (let [response (sh "net" "user" username) + full-name (full-name-in-locale + (first (split (str (Locale/getDefault)) #"_")))] (if (zero? (:exit response)) - (trim (:out response)) + (trim + (subs + (first (filter + ;; Cast to lower-case because although in English, + ;; Microsoft uses the capitalisation 'Full Name', + ;; according to their own language portal in other + ;; languages they don't; and I don't trust them! + #(starts-with? (lower-case %) (lower-case full-name)) + (line-seq (reader (StringReader. (:out response)))))) + (count full-name))) (throw (ex-info (format "Real name for `%s` not found" username) {:username username :response response + :locale (Locale/getDefault) + :localised-key full-name :os-name (System/getProperty "os.name") :os-version (System/getProperty "os.version")}))))) + (defn username->real-name "Given this `username`, return the associated real name if found; else throw exception."