msgcat.c revision 71579
1/* $FreeBSD: head/lib/libc/nls/msgcat.c 71579 2001-01-24 13:01:12Z deischen $ */
2
3/***********************************************************
4Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
5
6                        All Rights Reserved
7
8Permission to use, copy, modify, and distribute this software and its
9documentation for any purpose and without fee is hereby granted,
10provided that the above copyright notice appear in all copies and that
11both that copyright notice and this permission notice appear in
12supporting documentation, and that Alfalfa's name not be used in
13advertising or publicity pertaining to distribution of the software
14without specific, written prior permission.
15
16ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
17ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
18ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
19ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
22SOFTWARE.
23
24If you make any modifications, bugfixes or other changes to this software
25we'd appreciate it if you could send a copy to us so we can keep things
26up-to-date.  Many thanks.
27				Kee Hinckley
28				Alfalfa Software, Inc.
29				267 Allston St., #3
30				Cambridge, MA 02139  USA
31				nazgul@alfalfa.com
32
33******************************************************************/
34
35#if defined(LIBC_SCCS) && !defined(lint)
36static char *rcsid = "$FreeBSD: head/lib/libc/nls/msgcat.c 71579 2001-01-24 13:01:12Z deischen $";
37#endif /* LIBC_SCCS and not lint */
38
39/*
40 * We need a better way of handling errors than printing text.  I need
41 * to add an error handling routine.
42 */
43
44#include "namespace.h"
45#include <sys/types.h>
46#include <sys/stat.h>
47#include <sys/syslimits.h>
48#include <errno.h>
49#include <fcntl.h>
50#include <locale.h>
51#include <nl_types.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include <unistd.h>
56#include "un-namespace.h"
57
58#include "msgcat.h"
59
60#define _DEFAULT_NLS_PATH "/usr/share/nls/%L/%N.cat:/usr/share/nls/%N/%L:/usr/local/share/nls/%L/%N.cat:/usr/local/share/nls/%N/%L"
61
62#define	TRUE	1
63#define	FALSE	0
64
65#define	NLERR		((nl_catd) -1)
66#define	NLRETERR(errc)	errno = errc; return(NLERR);
67
68static nl_catd loadCat();
69static int loadSet();
70static void __nls_free_resources();
71
72nl_catd
73catopen( name, type)
74	__const char *name;
75	int type;
76{
77  int		spcleft;
78  char		path[PATH_MAX];
79  char		*nlspath, *lang, *base, *cptr, *pathP, *tmpptr;
80  char          *cptr1, *plang, *pter, *pcode;
81  struct stat	sbuf;
82
83  if (name == NULL || *name == '\0') {
84	NLRETERR(ENOENT);
85  }
86
87  /* is it absolute path ? if yes, load immidiately */
88  if (strchr(name, '/'))
89	return loadCat(name);
90
91  if (type == NL_CAT_LOCALE)
92	lang = setlocale(LC_MESSAGES, NULL);
93  else
94	lang = getenv("LANG");
95  if (lang == NULL || *lang == '\0' || strchr(lang, '/') != NULL)
96	lang = "C";
97
98  if ((plang = cptr1 = strdup(lang)) == NULL)
99	return (NLERR);
100  if ((cptr = strchr(cptr1, '@')) != NULL)
101	*cptr = '\0';
102  pter = pcode = "";
103  if ((cptr = strchr(cptr1, '_')) != NULL) {
104	*cptr++ = '\0';
105	pter = cptr1 = cptr;
106  }
107  if ((cptr = strchr(cptr1, '.')) != NULL) {
108	*cptr++ = '\0';
109	pcode = cptr;
110  }
111
112  if ((nlspath = getenv("NLSPATH")) == NULL
113#ifndef __NETBSD_SYSCALLS
114    || issetugid()
115#endif
116    )
117    nlspath = _DEFAULT_NLS_PATH;
118
119  if ((base = cptr = strdup(nlspath)) == NULL) {
120	free(plang);
121	return (NLERR);
122  }
123
124  while ((nlspath = strsep(&cptr, ":")) != NULL) {
125	pathP = path;
126	if (*nlspath) {
127		for ( ; *nlspath; ++nlspath) {
128	    	  if (*nlspath == '%') {
129			switch (*(nlspath + 1)) {
130			    case 'l':
131				tmpptr = plang;
132				break;
133			    case 't':
134				tmpptr = pter;
135				break;
136			    case 'c':
137				tmpptr = pcode;
138				break;
139			    case 'L':
140				tmpptr = lang;
141				break;
142			    case 'N':
143				tmpptr = (char*)name;
144				break;
145			    case '%':
146				++nlspath;
147				/* fallthrough */
148			    default:
149				if (pathP - path >= sizeof(path) - 1)
150					goto too_long;
151				*(pathP++) = *nlspath;
152				continue;
153			}
154			++nlspath;
155		put_tmpptr:
156	        	spcleft = sizeof(path) - (pathP - path) - 1;
157		    	if (strlcpy(pathP, tmpptr, spcleft) >= spcleft) {
158		too_long:
159				free(plang);
160				free(base);
161				NLRETERR(ENAMETOOLONG);
162			}
163		    	pathP += strlen(tmpptr);
164		  } else {
165			if (pathP - path >= sizeof(path) - 1)
166				goto too_long;
167			*(pathP++) = *nlspath;
168		  }
169		}
170		*pathP = '\0';
171		if (stat(path, &sbuf) == 0) {
172			free(plang);
173			free(base);
174			return loadCat(path);
175		}
176	} else {
177		tmpptr = (char*)name;
178		--nlspath;
179		goto put_tmpptr;
180	}
181  }
182  free(plang);
183  free(base);
184  NLRETERR(ENOENT);
185}
186
187/*
188 * We've got an odd situation here.  The odds are real good that the
189 * number we are looking for is almost the same as the index.  We could
190 * use the index, check the difference and do something intelligent, but
191 * I haven't quite figured out what's intelligent.
192 *
193 * Here's a start.
194 *	Take an id N.  If there are > N items in the list, then N cannot
195 *	be more than N items from the start, since otherwise there would
196 *	have to be duplicate items.  So we can safely set the top to N+1
197 *	(after taking into account that ids start at 1, and arrays at 0)
198 *
199 *	Let's say we are at position P, and we are looking for N, but have
200 *	V.  If N > V, then the furthest away that N could be is
201 *	P + (N-V).  So we can safely set hi to P+(N-V)+1.  For example:
202 *		We are looking for 10, but have 8
203 *		8	?	?	?	?
204 *			>=9	>=10	>=11
205 *
206 */
207
208#define LOOKUP(PARENT, CHILD, ID, NUM, SET) {	\
209    lo = 0; 					\
210    if (ID - 1 < PARENT->NUM) {			\
211	cur = ID - 1; hi = ID;			\
212    } else {					\
213	hi = PARENT->NUM; cur = (hi - lo) / 2;	\
214    }						\
215    while (TRUE) {				\
216	CHILD = PARENT->SET + cur;		\
217	if (CHILD->ID == ID) break;		\
218	if (CHILD->ID < ID) {			\
219	    lo = cur+1;				\
220	    if (hi > cur+(ID-CHILD->ID)+1)	\
221		hi = cur+(ID-CHILD->ID)+1;	\
222	    dir = 1;				\
223	} else {				\
224	    hi = cur; dir = -1;			\
225	}					\
226	if (lo >= hi) return(NULL);		\
227	if (hi - lo == 1) cur += dir;		\
228	else cur += ((hi - lo) / 2) * dir;	\
229    }						\
230  }
231
232static MCSetT*
233MCGetSet( cat, setId)
234	MCCatT *cat;
235	int setId;
236{
237    MCSetT	*set;
238    long	lo, hi, cur, dir;
239
240    if (cat == NULL || setId <= 0) return(NULL);
241    LOOKUP(cat, set, setId, numSets, sets);
242    if (set->invalid && loadSet(cat, set) <= 0)
243	return(NULL);
244    return(set);
245}
246
247
248static MCMsgT*
249MCGetMsg( set, msgId)
250	MCSetT *set;
251	int msgId;
252{
253    MCMsgT	*msg;
254    long	lo, hi, cur, dir;
255
256    if (set == NULL || set->invalid || msgId <= 0) return(NULL);
257    LOOKUP(set, msg, msgId, numMsgs, u.msgs);
258    return(msg);
259}
260
261char*
262catgets( catd, setId, msgId, dflt)
263	nl_catd catd;
264	int setId;
265	int msgId;
266	__const char *dflt;
267{
268    MCMsgT	*msg;
269    MCCatT	*cat = (MCCatT *) catd;
270    __const char *cptr;
271
272    if (catd == NULL || catd == NLERR)
273	return((char *)dflt);
274    msg = MCGetMsg(MCGetSet(cat, setId), msgId);
275    if (msg != NULL) cptr = msg->msg.str;
276    else cptr = dflt;
277    return((char *)cptr);
278}
279
280
281int
282catclose( catd)
283	nl_catd catd;
284{
285    MCCatT	*cat = (MCCatT *) catd;
286
287    if (catd == NULL || catd == NLERR) {
288	errno = EBADF; return(-1);
289    }
290
291#if 0
292    if (cat->loadType != MCLoadAll)
293#endif
294	(void) fclose(cat->fp);
295    __nls_free_resources(cat, cat->numSets);
296    free(cat);
297
298    return(0);
299}
300
301/*
302 * Internal routines
303 */
304
305/* Note that only malloc failures are allowed to return an error */
306static char* _errowner = "Message Catalog System";;
307#define CORRUPT() { 						\
308	fprintf(stderr, "%s: corrupt file.", _errowner);	\
309		free(cat);					\
310		NLRETERR(EINVAL);				\
311	}
312
313#define NOSPACE() {						\
314	fprintf(stderr, "%s: no more memory.", _errowner);	\
315		free(cat);					\
316		return(NLERR);					\
317	}
318
319static void
320__nls_free_resources(cat, i)
321	MCCatT *cat;
322	int i;
323{
324  MCSetT	*set;
325  int		j;
326
327  for (j = 0; j < i; j++) {
328	set = cat->sets + j;
329	if (!set->invalid) {
330	    free(set->data.str);
331	    free(set->u.msgs);
332	}
333  }
334  free(cat->sets);
335}
336
337static nl_catd
338loadCat(catpath)
339	__const char *catpath;
340{
341    MCHeaderT	header;
342    MCCatT	*cat;
343    MCSetT	*set;
344    long        i;
345    off_t	nextSet;
346
347    cat = (MCCatT *) malloc(sizeof(MCCatT));
348    if (cat == NULL) return(NLERR);
349    cat->loadType = MCLoadBySet;
350
351    if ((cat->fp = fopen(catpath, "r")) == NULL) {
352	free(cat);
353	return(NLERR);
354    }
355
356    (void) _fcntl(fileno(cat->fp), F_SETFD, FD_CLOEXEC);
357
358    if (fread(&header, sizeof(header), 1, cat->fp) != 1)
359    	CORRUPT();
360
361    if (strncmp(header.magic, MCMagic, MCMagicLen) != 0) CORRUPT();
362
363    if (header.majorVer != MCMajorVer) {
364	free(cat);
365	fprintf(stderr, "%s: %s is version %ld, we need %ld.\n", _errowner,
366		catpath, header.majorVer, MCMajorVer);
367	NLRETERR(EINVAL);
368    }
369
370    if (header.numSets <= 0) {
371	free(cat);
372	fprintf(stderr, "%s: %s has %ld sets!\n", _errowner, catpath,
373		header.numSets);
374	NLRETERR(EINVAL);
375    }
376
377    cat->numSets = header.numSets;
378    cat->sets = (MCSetT *) malloc(sizeof(MCSetT) * header.numSets);
379    if (cat->sets == NULL) NOSPACE();
380
381    nextSet = header.firstSet;
382    for (i = 0; i < cat->numSets; ++i) {
383	if (fseeko(cat->fp, nextSet, SEEK_SET) == -1) {
384		__nls_free_resources(cat, i);
385		CORRUPT();
386	}
387
388	/* read in the set header */
389	set = cat->sets + i;
390	if (fread(set, sizeof(*set), 1, cat->fp) != 1) {
391		__nls_free_resources(cat, i);
392		CORRUPT();
393	}
394
395	/* if it's invalid, skip over it (and backup 'i') */
396	if (set->invalid) {
397	    --i;
398	    nextSet = set->nextSet;
399	    continue;
400	}
401
402#if 0
403	if (cat->loadType == MCLoadAll) {
404	    int res;
405
406	    if ((res = loadSet(cat, set)) <= 0) {
407		__nls_free_resources(cat, i);
408		if (res < 0) NOSPACE();
409		CORRUPT();
410	    }
411	} else
412#endif
413		set->invalid = TRUE;
414	nextSet = set->nextSet;
415    }
416#if 0
417    if (cat->loadType == MCLoadAll) {
418	(void) fclose(cat->fp);
419	cat->fp = NULL;
420    }
421#endif
422    return((nl_catd) cat);
423}
424
425static int
426loadSet(cat, set)
427	MCCatT *cat;
428	MCSetT *set;
429{
430    MCMsgT	*msg;
431    int		i;
432
433    /* Get the data */
434    if (fseeko(cat->fp, set->data.off, SEEK_SET) == -1) return(0);
435    if ((set->data.str = malloc(set->dataLen)) == NULL) return(-1);
436    if (fread(set->data.str, set->dataLen, 1, cat->fp) != 1) {
437	free(set->data.str); return(0);
438    }
439
440    /* Get the messages */
441    if (fseeko(cat->fp, set->u.firstMsg, SEEK_SET) == -1) {
442	free(set->data.str); return(0);
443    }
444    if ((set->u.msgs = (MCMsgT *) malloc(sizeof(MCMsgT) * set->numMsgs)) == NULL) {
445	free(set->data.str); return(-1);
446    }
447
448    for (i = 0; i < set->numMsgs; ++i) {
449	msg = set->u.msgs + i;
450	if (fread(msg, sizeof(*msg), 1, cat->fp) != 1) {
451	    free(set->u.msgs); free(set->data.str); return(0);
452	}
453	if (msg->invalid) {
454	    --i;
455	    continue;
456	}
457	msg->msg.str = (char *) (set->data.str + msg->msg.off);
458    }
459    set->invalid = FALSE;
460    return(1);
461}
462