com_err: correctly deal with lack of libintl
[openafs.git] / src / comerr / error_msg.c
1 /*
2  * $Locker$
3  *
4  * Copyright 1987 by the Student Information Processing Board
5  * of the Massachusetts Institute of Technology
6  *
7  * For copyright info, see "mit-sipb-cr.h".
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 #include <roken.h>
14
15 #ifdef HAVE_LIBINTL
16 #include <libintl.h>
17 #endif
18 #ifdef AFS_DARWIN_ENV
19 #include <CoreFoundation/CoreFoundation.h>
20 #endif
21
22 #include <afs/errors.h>
23 #include <afs/afsutil.h>
24
25 #include "internal.h"
26 #include "error_table.h"
27 #include "mit-sipb-cr.h"
28 #include "com_err.h"
29
30 static const char copyright[] =
31     "Copyright 1986, 1987, 1988 by the Student Information Processing Board\nand the department of Information Systems\nof the Massachusetts Institute of Technology";
32
33 static char buffer[64];
34
35 static struct et_list *_et_list = (struct et_list *)NULL;
36
37 #ifdef AFS_PTHREAD_ENV
38 #include <pthread.h>
39 #include <assert.h>
40
41 /*
42  * This mutex protects the following variables:
43  * _et_list
44  */
45
46 static pthread_mutex_t et_list_mutex;
47 static int et_list_done = 0;
48 static pthread_once_t et_list_once = PTHREAD_ONCE_INIT;
49
50 /*
51  * Function to initialize the et_list_mutex
52  */
53
54 static void
55 et_mutex_once(void)
56 {
57     assert(!pthread_mutex_init
58            (&et_list_mutex, (const pthread_mutexattr_t *)0));
59     et_list_done = 1;
60 }
61
62 #define LOCK_ET_LIST \
63         do { \
64             if (!et_list_done) \
65                 pthread_once(&et_list_once, et_mutex_once); \
66             assert(pthread_mutex_lock(&et_list_mutex)==0); \
67         } while (0)
68 #define UNLOCK_ET_LIST assert(pthread_mutex_unlock(&et_list_mutex)==0)
69 #else
70 #define LOCK_ET_LIST
71 #define UNLOCK_ET_LIST
72 #endif /* AFS_PTHREAD_ENV */
73
74
75 static char *vmsgs[] = {
76     "volume needs to be salvaged",      /* 101, in Pittsburghese */
77     "no such entry (vnode)",    /* 102 */
78     "volume does not exist / did not salvage",  /* 103 */
79     "volume already exists",    /* 104 */
80     "volume out of service",    /* 105 */
81     "volume offline (utility running)", /* 106 */
82     "volume already online",    /* 107 */
83     "unknown volume error 108", /* 108 */
84     "unknown volume error 109", /* 109 */
85     "volume temporarily busy",  /* 110 */
86     "volume moved",             /* 111 */
87     (char *)0
88 };
89
90 static char *
91 negative_message(int code)
92 {
93     if (code == -1)
94         return "server or network not responding";
95     else if (code == -2)
96         return "invalid RPC (RX) operation";
97     else if (code == -3)
98         return "server not responding promptly";
99     else if (code == -7)
100         return "port address already in use";
101     else if (code <= -450 && code > -500) {
102         sprintf(buffer, "RPC interface mismatch (%d)", code);
103         return buffer;
104     } else {
105         sprintf(buffer, "unknown RPC error (%d)", code);
106         return buffer;
107     }
108 }
109
110 static char *
111 volume_message(int code)
112 {
113     if (code >= 101 && code <= 111)
114         return vmsgs[code - 101];
115     else
116         return "unknown volume error";
117 }
118
119 #ifdef AFS_DARWIN_ENV
120 static_inline const char *
121 _intlize(const char *msg, int base, char *str, size_t len)
122 {
123     char domain[12 +20];
124     CFStringRef cfstring = CFStringCreateWithCString(kCFAllocatorSystemDefault,
125                                                      msg,
126                                                      kCFStringEncodingUTF8);
127     CFStringRef cfdomain;
128     CFBundleRef OpenAFSBundle = CFBundleGetBundleWithIdentifier(CFSTR("org.openafs.filesystems.afs"));
129
130     if (!str) {
131         CFRelease(cfstring);
132         return msg;
133     }
134
135     snprintf(domain, sizeof(domain), "heim_com_err%d", base);
136     cfdomain = CFStringCreateWithCString(kCFAllocatorSystemDefault, domain,
137                                          kCFStringEncodingUTF8);
138     if (OpenAFSBundle != NULL) {
139         CFStringRef cflocal;
140
141         cflocal = CFBundleCopyLocalizedString(OpenAFSBundle, cfstring,
142                                               cfstring, cfdomain);
143         CFStringGetCString(cflocal, str, len, kCFStringEncodingUTF8);
144         CFRelease(cflocal);
145     } else {
146         CFStringGetCString(cfstring, str, len, kCFStringEncodingUTF8);
147     }
148
149     CFRelease(cfstring);
150     CFRelease(cfdomain);
151     return str;
152 }
153 #else
154 static_inline const char *
155 _intlize(const char *msg, int base, char *str, size_t len)
156 {
157 #if defined(HAVE_LIBINTL)
158     char domain[12 +20];
159 #endif
160     if (!str)
161         return msg;
162 #if defined(HAVE_LIBINTL)
163     snprintf(domain, sizeof(domain), "heim_com_err%d", base);
164     strlcpy(str, dgettext(domain, msg), len);
165 #else
166     strlcpy(str, msg, len);
167 #endif
168     return str;
169 }
170 #endif
171
172 static const char *
173 afs_error_message_int(struct et_list *list, afs_int32 code, char *str, size_t len)
174 {
175     int offset;
176     struct et_list *et;
177     int table_num, unlock = 0;
178     int started = 0;
179     char *cp;
180     const char *err_msg;
181
182     /* check for rpc errors first */
183     if (code < 0)
184         return _intlize(negative_message(code), -1, str, len);
185
186     offset = code & ((1 << ERRCODE_RANGE) - 1);
187     table_num = code - offset;
188     if (!table_num) {
189         if ((err_msg = strerror(offset)) != NULL)
190             return _intlize(err_msg, 0, str, len);
191         else if (offset < 140)
192             return _intlize(volume_message(code), 0, str, len);
193         else
194             goto oops;
195     }
196     if (list) {
197         et = list;
198     } else {
199         LOCK_ET_LIST;
200         unlock = 1;
201         et = _et_list;
202     }
203     for (; et; et = et->next) {
204         if (et->table->base == table_num) {
205             /* This is the right table */
206             if (et->table->n_msgs <= offset)
207                 goto oops;
208             err_msg = _intlize(et->table->msgs[offset], et->table->base,
209                                str, len);
210             if (unlock)
211                 UNLOCK_ET_LIST;
212             return err_msg;
213         }
214     }
215   oops:
216     if (unlock)
217         UNLOCK_ET_LIST;
218     /* Unknown code can be included in the negative errors catalog */
219     _intlize("Unknown code ", -1, buffer, sizeof buffer);
220     if (table_num) {
221         strlcat(buffer, afs_error_table_name(table_num), sizeof buffer);
222         strlcat(buffer, " ", sizeof buffer);
223     }
224     for (cp = buffer; *cp; cp++);
225     if (offset >= 100) {
226         *cp++ = '0' + offset / 100;
227         offset %= 100;
228         started++;
229     }
230     if (started || offset >= 10) {
231         *cp++ = '0' + offset / 10;
232         offset %= 10;
233     }
234     *cp++ = '0' + offset;
235     if (code > -10000)
236         sprintf(cp, " (%d)", code);
237     else
238         *cp = '\0';
239     return (buffer);
240 }
241
242 const char *
243 afs_error_message_localize(afs_int32 code, char *str, size_t len)
244 {
245     return afs_error_message_int((struct et_list *)0, code, str, len);
246 }
247
248 const char *
249 afs_com_right_r(struct et_list *list, long code, char *str, size_t len)
250 {
251     return afs_error_message_int(list, (afs_int32)code, str, len);
252 }
253
254 const char *
255 afs_com_right(struct et_list *list, long code)
256 {
257     return afs_error_message_int(list, (afs_int32)code, (char *)0, 0);
258 }
259
260 const char *
261 afs_error_message(afs_int32 code)
262 {
263     return afs_error_message_int((struct et_list *)0, code, (char *)0, 0);
264 }
265
266 void
267 afs_add_to_error_table(struct et_list *new_table)
268 {
269     struct et_list *et;
270
271     LOCK_ET_LIST;
272     /*
273      * Protect against adding the same error table twice
274      */
275     for (et = _et_list; et; et = et->next) {
276         if (et->table->base == new_table->table->base) {
277             UNLOCK_ET_LIST;
278             return;
279         }
280     }
281
282     new_table->next = _et_list;
283     _et_list = new_table;
284     UNLOCK_ET_LIST;
285 }