Raymond Toy pushed to branch issue-269-unix-get-user-homedir at cmucl / cmucl

Commits:

4 changed files:

Changes:

  • src/code/unix.lisp
    ... ... @@ -2902,22 +2902,25 @@
    2902 2902
     	c-string))
    
    2903 2903
     
    
    2904 2904
     (defun unix-get-user-homedir (name)
    
    2905
    -  _N"Get the user home directory for user named NAME.  If there's no such
    
    2906
    -  user or if we don't have enough space to store the path, return NIL."
    
    2907
    -  (with-alien ((homedir (array c-call:char 1024)))
    
    2905
    +  _N"Get the user home directory for user named NAME.  Two values are
    
    2906
    +  returned: the pathname of the home directory and a status code.  If
    
    2907
    +  the home directory does not exist NIL is returned.  The status is 0
    
    2908
    +  if no errors occurred.  Otherwise a non-zero value is returned.
    
    2909
    +  Examining errno may give information about what failed."
    
    2910
    +  (with-alien ((status c-call:int))
    
    2908 2911
         (let ((result
    
    2909 2912
                 (alien-funcall
    
    2910 2913
                  (extern-alien "os_get_user_homedir"
    
    2911
    -                           (function c-call:int
    
    2914
    +                           (function c-call:c-string
    
    2912 2915
                                          c-call:c-string
    
    2913
    -                                     (* char)
    
    2914
    -                                     c-call:int))
    
    2916
    +                                     (* c-call:int)))
    
    2915 2917
                  name
    
    2916
    -             (cast homedir (* c-call:char))
    
    2917
    -             1024)))
    
    2918
    -      (when (zerop result)
    
    2919
    -        (pathname
    
    2920
    -         (concatenate 'string
    
    2921
    -                      (cast homedir c-call:c-string)
    
    2922
    -                      "/"))))))
    
    2918
    +             (addr status))))
    
    2919
    +      (if (and (zerop status) result)
    
    2920
    +          (values (pathname
    
    2921
    +                   (concatenate 'string
    
    2922
    +                                result
    
    2923
    +                                "/"))
    
    2924
    +                  status)
    
    2925
    +          (values result status)))))
    
    2923 2926
       

  • src/i18n/locale/cmucl-unix.pot
    ... ... @@ -1434,7 +1434,10 @@ msgstr ""
    1434 1434
     
    
    1435 1435
     #: src/code/unix.lisp
    
    1436 1436
     msgid ""
    
    1437
    -"Get the user home directory for user named NAME.  If there's no such\n"
    
    1438
    -"  user or if we don't have enough space to store the path, return NIL."
    
    1437
    +"Get the user home directory for user named NAME.  Two values are\n"
    
    1438
    +"  returned: the pathname of the home directory and a status code.  If\n"
    
    1439
    +"  the home directory does not exist NIL is returned.  The status is 0\n"
    
    1440
    +"  if no errors occurred.  Otherwise a non-zero value is returned.\n"
    
    1441
    +"  Examining errno may give information about what failed."
    
    1439 1442
     msgstr ""
    
    1440 1443
     

  • src/lisp/os-common.c
    ... ... @@ -864,43 +864,65 @@ os_software_version(void)
    864 864
         return result;
    
    865 865
     }
    
    866 866
     
    
    867
    -int
    
    868
    -os_get_user_homedir(const char* name, char* homedir, int len)
    
    867
    +char *
    
    868
    +os_get_user_homedir(const char* name, int *status)
    
    869 869
     {
    
    870
    -    int rc;
    
    871 870
         int buflen;
    
    872 871
         char * buf;
    
    873 872
         struct passwd pwd;
    
    874 873
         struct passwd *result;
    
    875 874
     
    
    876 875
         buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
    
    876
    +    /*
    
    877
    +     * If sysconf failed, just try some possibly large enough value
    
    878
    +     */
    
    879
    +    if (buflen == -1) {
    
    880
    +        buflen = 1024;
    
    881
    +    }
    
    877 882
     
    
    878
    -    buf = malloc(buflen);
    
    883
    +    /*
    
    884
    +     * sysconf may return a value that is not large enough, so start
    
    885
    +     * with the given value and keep increasing it until we reach some
    
    886
    +     * upper limit and give up.
    
    887
    +     */
    
    888
    +    while (buflen <= (1 << 20)) {
    
    889
    +        errno = 0;
    
    890
    +        buf = malloc(buflen);
    
    879 891
     
    
    880
    -    if (buf == NULL) {
    
    881
    -        return -1;
    
    882
    -    }
    
    892
    +        if (buf == NULL) {
    
    893
    +            *status = -1;
    
    894
    +            return NULL;
    
    895
    +        }
    
    896
    +
    
    897
    +        *status = getpwnam_r(name, &pwd, buf, buflen, &result);
    
    883 898
     
    
    884
    -    rc = getpwnam_r(name, &pwd, buf, buflen, &result);
    
    899
    +        if (*status == 0) {
    
    900
    +            /*
    
    901
    +             * Success, or entry was not found.  If found the result
    
    902
    +             * is not NULL.  Return the result or NULL
    
    903
    +             */
    
    904
    +            fprintf(stderr, "dir = %s\n", pwd.pw_dir);
    
    905
    +            return result ? strdup(pwd.pw_dir) : NULL;
    
    906
    +        }
    
    885 907
     
    
    886
    -    if ((rc == 0) && result != NULL) {
    
    887 908
             /*
    
    888
    -         * Found a matching entry.  Copy it to the output buffer if we
    
    889
    -         * have room.  If not, set code to -1
    
    909
    +         * Check errno for ERANGE.  If so, the buffer was too small, so grow it.
    
    890 910
              */
    
    891
    -        if (strlen(pwd.pw_dir) < len) {
    
    892
    -            strcpy(homedir, pwd.pw_dir);
    
    911
    +        if (errno == ERANGE) {
    
    912
    +            free(buf);
    
    913
    +            buflen *= 2;
    
    893 914
             } else {
    
    894
    -            rc = -1;
    
    915
    +            /*
    
    916
    +             * Some other error.  Just return NULL
    
    917
    +             */
    
    918
    +            return NULL;
    
    895 919
             }
    
    896
    -    } else {
    
    897
    -        rc = -1;
    
    898 920
         }
    
    899 921
     
    
    900
    -    if (buf) {
    
    901
    -        free(buf);
    
    902
    -    }
    
    903
    -    
    
    904
    -    return rc;
    
    922
    +    /*
    
    923
    +     * Ran out of space.  Just return NULL and set status to -1.
    
    924
    +     */
    
    925
    +    *status = -1;
    
    926
    +    return NULL;
    
    905 927
     }
    
    906 928
         

  • tests/os.lisp
    ... ... @@ -3,7 +3,7 @@
    3 3
     
    
    4 4
     (in-package "OS-TESTS")
    
    5 5
     
    
    6
    -(define-test user-homedir
    
    6
    +(define-test user-homedir.1
    
    7 7
       "Test user-homedir"
    
    8 8
       (:tag :issues)
    
    9 9
       ;; Simple test to see if unix-get-user-homedir returns the expected
    
    ... ... @@ -16,9 +16,22 @@
    16 16
         (let* ((info-dir (unix:user-info-dir user-info))
    
    17 17
                (info-name (unix:user-info-name user-info))
    
    18 18
                (expected-home-pathname (pathname
    
    19
    -                                    (concatenate 'string info-dir "/")))
    
    20
    -           (home-pathname (unix:unix-get-user-homedir info-name)))
    
    21
    -      (assert-true info-dir)
    
    22
    -      (assert-true info-name)
    
    19
    +                                    (concatenate 'string info-dir "/"))))
    
    20
    +      (multiple-value-bind (home-pathname status)
    
    21
    +          (unix:unix-get-user-homedir info-name)
    
    22
    +        (assert-true info-dir)
    
    23
    +        (assert-true info-name)
    
    23 24
     
    
    24
    -      (assert-equal home-pathname expected-home-pathname))))
    25
    +        (assert-equal home-pathname expected-home-pathname)
    
    26
    +        (assert-eql status 0)))))
    
    27
    +
    
    28
    +(define-test user-homedir.2
    
    29
    +  "Test user-homedir"
    
    30
    +  (:tag :issues)
    
    31
    +  ;; Simple test to see if unix-get-user-homedir returns the expected
    
    32
    +  ;; value for a user that does not exist.  Well, we assume such a
    
    33
    +  ;; user doesn't exist.
    
    34
    +      (multiple-value-bind (home-pathname status)
    
    35
    +          (unix:unix-get-user-homedir "zotuserunknown")
    
    36
    +        (assert-eql home-pathname nil)
    
    37
    +        (assert-eql status 0)))