GNU file utilities
[gnulib.git] / lib / idcache.c
1 /* idcache.c -- map user and group IDs, cached for speed
2    Copyright (C) 1985, 1988, 1989, 1990 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
17
18 #ifdef HAVE_CONFIG_H
19 #if defined (CONFIG_BROKETS)
20 /* We use <config.h> instead of "config.h" so that a compilation
21    using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
22    (which it would do because it found this file in $srcdir).  */
23 #include <config.h>
24 #else
25 #include "config.h"
26 #endif
27 #endif
28
29 #include <stdio.h>
30 #include <sys/types.h>
31 #include <pwd.h>
32 #include <grp.h>
33
34 #if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
35 #include <string.h>
36 #else
37 #include <strings.h>
38 #endif
39
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #ifndef _POSIX_VERSION
44 struct passwd *getpwuid ();
45 struct passwd *getpwnam ();
46 struct group *getgrgid ();
47 struct group *getgrnam ();
48 #endif
49
50 char *xmalloc ();
51 char *xstrdup ();
52
53 struct userid
54 {
55   union
56     {
57       uid_t u;
58       gid_t g;
59     } id;
60   char *name;
61   struct userid *next;
62 };
63
64 static struct userid *user_alist;
65
66 /* The members of this list have names not in the local passwd file.  */
67 static struct userid *nouser_alist;
68
69 /* Translate UID to a login name or a stringified number,
70    with cache.  */
71
72 char *
73 getuser (uid)
74      uid_t uid;
75 {
76   register struct userid *tail;
77   struct passwd *pwent;
78   char usernum_string[20];
79
80   for (tail = user_alist; tail; tail = tail->next)
81     if (tail->id.u == uid)
82       return tail->name;
83
84   pwent = getpwuid (uid);
85   tail = (struct userid *) xmalloc (sizeof (struct userid));
86   tail->id.u = uid;
87   if (pwent == 0)
88     {
89       sprintf (usernum_string, "%u", (unsigned) uid);
90       tail->name = xstrdup (usernum_string);
91     }
92   else
93     tail->name = xstrdup (pwent->pw_name);
94
95   /* Add to the head of the list, so most recently used is first.  */
96   tail->next = user_alist;
97   user_alist = tail;
98   return tail->name;
99 }
100
101 /* Translate USER to a UID, with cache.
102    Return NULL if there is no such user.
103    (We also cache which user names have no passwd entry,
104    so we don't keep looking them up.)  */
105
106 uid_t *
107 getuidbyname (user)
108      char *user;
109 {
110   register struct userid *tail;
111   struct passwd *pwent;
112
113   for (tail = user_alist; tail; tail = tail->next)
114     /* Avoid a function call for the most common case.  */
115     if (*tail->name == *user && !strcmp (tail->name, user))
116       return &tail->id.u;
117
118   for (tail = nouser_alist; tail; tail = tail->next)
119     /* Avoid a function call for the most common case.  */
120     if (*tail->name == *user && !strcmp (tail->name, user))
121       return 0;
122
123   pwent = getpwnam (user);
124
125   tail = (struct userid *) xmalloc (sizeof (struct userid));
126   tail->name = xstrdup (user);
127
128   /* Add to the head of the list, so most recently used is first.  */
129   if (pwent)
130     {
131       tail->id.u = pwent->pw_uid;
132       tail->next = user_alist;
133       user_alist = tail;
134       return &tail->id.u;
135     }
136
137   tail->next = nouser_alist;
138   nouser_alist = tail;
139   return 0;
140 }
141
142 /* Use the same struct as for userids.  */
143 static struct userid *group_alist;
144 static struct userid *nogroup_alist;
145
146 /* Translate GID to a group name or a stringified number,
147    with cache.  */
148
149 char *
150 getgroup (gid)
151      gid_t gid;
152 {
153   register struct userid *tail;
154   struct group *grent;
155   char groupnum_string[20];
156
157   for (tail = group_alist; tail; tail = tail->next)
158     if (tail->id.g == gid)
159       return tail->name;
160
161   grent = getgrgid (gid);
162   tail = (struct userid *) xmalloc (sizeof (struct userid));
163   tail->id.g = gid;
164   if (grent == 0)
165     {
166       sprintf (groupnum_string, "%u", (unsigned int) gid);
167       tail->name = xstrdup (groupnum_string);
168     }
169   else
170     tail->name = xstrdup (grent->gr_name);
171
172   /* Add to the head of the list, so most recently used is first.  */
173   tail->next = group_alist;
174   group_alist = tail;
175   return tail->name;
176 }
177
178 /* Translate GROUP to a UID, with cache.
179    Return NULL if there is no such group.
180    (We also cache which group names have no group entry,
181    so we don't keep looking them up.)  */
182
183 gid_t *
184 getgidbyname (group)
185      char *group;
186 {
187   register struct userid *tail;
188   struct group *grent;
189
190   for (tail = group_alist; tail; tail = tail->next)
191     /* Avoid a function call for the most common case.  */
192     if (*tail->name == *group && !strcmp (tail->name, group))
193       return &tail->id.g;
194
195   for (tail = nogroup_alist; tail; tail = tail->next)
196     /* Avoid a function call for the most common case.  */
197     if (*tail->name == *group && !strcmp (tail->name, group))
198       return 0;
199
200   grent = getgrnam (group);
201
202   tail = (struct userid *) xmalloc (sizeof (struct userid));
203   tail->name = xstrdup (group);
204
205   /* Add to the head of the list, so most recently used is first.  */
206   if (grent)
207     {
208       tail->id.g = grent->gr_gid;
209       tail->next = group_alist;
210       group_alist = tail;
211       return &tail->id.g;
212     }
213
214   tail->next = nogroup_alist;
215   nogroup_alist = tail;
216   return 0;
217 }