xref: /illumos-gate/usr/src/cmd/msgfmt/check_header.c (revision d48be21240dfd051b689384ce2b23479d757f2d8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #include "sun_msgfmt.h"
28 
29 static const char	*mandatory_fields[] = {
30 	"Project-Id-Version",
31 	"PO-Revision-Date",
32 	"Last-Translator",
33 	"Language-Team",
34 	"Content-Type",
35 	"Content-Transfer-Encoding",
36 	NULL
37 };
38 
39 static const char	*mandatory_fields_new[] = {
40 	"POT-Creation-Date",
41 	"Plural-Forms",
42 	NULL
43 };
44 
45 extern int	verbose;
46 
47 extern void	invoke_gnu_msgfmt(void);
48 
49 static size_t
50 get_one_line(char **bufhead, char **mbuf, size_t *fsize)
51 {
52 	size_t	len;
53 	char	*p = *mbuf;
54 	char	*q, *tmp;
55 
56 	if (*bufhead) {
57 		free(*bufhead);
58 		*bufhead = NULL;
59 	}
60 
61 	if (*fsize == 0) {
62 		/* eof */
63 		return (0);
64 	}
65 
66 	q = p;
67 	while (((*fsize) != 0) && (*p++ != '\n')) {
68 		(*fsize)--;
69 	}
70 	len = p - q;
71 	if (len == 0) {
72 		return (0);
73 	}
74 	tmp = (char *)Xmalloc(len + 1);
75 	(void) memcpy(tmp, q, len);
76 	tmp[len] = '\0';
77 	*bufhead = tmp;
78 	*mbuf = p;
79 	return (len);
80 }
81 
82 void
83 check_gnu(char *addr, size_t fsize)
84 {
85 	int	i;
86 	char	c, mc;
87 	char	*linebuf;
88 	char	*mbuf, *p, *buf;
89 	unsigned int	n;
90 	size_t	ln_size;
91 	size_t	bufsize, index;
92 	size_t	size = fsize;
93 	int	quotefound = 0;
94 	const char	*field;
95 
96 	buf = NULL;
97 	linebuf = NULL;
98 	mbuf = addr;
99 
100 loop:
101 	ln_size = get_one_line(&linebuf, &mbuf, &size);
102 	if ((ln_size == (size_t)-1) ||
103 		(ln_size == 0)) {
104 		goto no_gnu;
105 	}
106 	p = linebuf;
107 
108 	while ((*p == '#') || (*p == '\n')) {
109 		ln_size = get_one_line(&linebuf, &mbuf, &size);
110 		if ((ln_size == (size_t)-1) ||
111 			(ln_size == 0)) {
112 			goto no_gnu;
113 		}
114 		p = linebuf;
115 	}
116 
117 	if (strncmp(p, "domain", 6) == 0)
118 		goto loop;
119 
120 	if (strncmp(p, "msgid", 5) != 0) {
121 		/* error */
122 		goto no_gnu;
123 	}
124 
125 	p += 5;
126 	if ((*p != ' ') && (*p != '\t') &&
127 		(*p != '\n') && (*p != '\0')) {
128 		/* no space after msgid */
129 		goto no_gnu;
130 	}
131 	/* skip spaces */
132 	while ((*p == ' ') || (*p == '\t'))
133 		p++;
134 
135 	/* check if this entry is an empty string */
136 	if ((*p != '\"') || (*(p + 1) != '\"')) {
137 		/* this is not an empty string */
138 		goto no_gnu;
139 	}
140 	p += 2;
141 	while (*p && ((*p == ' ') || (*p == '\t'))) {
142 		p++;
143 	}
144 	if ((*p != '\n') && (*p != '\0')) {
145 		/* other characters than '\n' and '\0' found */
146 		goto no_gnu;
147 	}
148 
149 	for (; ; ) {
150 		ln_size = get_one_line(&linebuf, &mbuf, &size);
151 		if ((ln_size == (size_t)-1) ||
152 			(ln_size == 0)) {
153 			goto no_gnu;
154 		}
155 		p = linebuf;
156 		/* skip leading spaces */
157 		while ((*p == ' ') || (*p == '\t'))
158 			p++;
159 
160 		if (*p != '\"') {
161 			if (strncmp(p, "msgstr", 6) == 0) {
162 				break;
163 			}
164 			/* not a valid entry */
165 			goto no_gnu;
166 		}
167 		if (*(p + 1) != '\"') {
168 			/* not an empty string */
169 			goto no_gnu;
170 		}
171 		p += 2;
172 		while ((*p == ' ') || (*p == '\t'))
173 			p++;
174 
175 		if ((*p != '\n') && (*p != '\0')) {
176 			/* other characters than '\n' and '\0' found */
177 			goto no_gnu;
178 		}
179 	}
180 
181 	/*
182 	 * msgid for the header entry found
183 	 * Now p points to "msgstr"
184 	 */
185 	p += 6;
186 	if ((*p != ' ') && (*p != '\t') &&
187 		(*p != '\n') && (*p != '\0')) {
188 		/* no space after msgid */
189 		goto no_gnu;
190 	}
191 
192 	/* skip spaces */
193 	while ((*p == ' ') || (*p == '\t'))
194 		p++;
195 
196 	if (*p != '\"') {
197 		/* no quote */
198 		goto no_gnu;
199 	}
200 
201 	bufsize = ln_size + 1;
202 	index = 0;
203 	buf = (char *)Xmalloc(bufsize);
204 
205 	for (; ; ) {
206 		if (*p != '\"') {
207 			/* msgstr entry ends */
208 			buf[index] = '\0';
209 			break;
210 		}
211 
212 		if (*p++ != '\"') {
213 			/* no beginning quote */
214 			goto no_gnu;
215 		}
216 		while (*p) {
217 			switch (mc = *p++) {
218 			case '\n':
219 				if (!quotefound) {
220 					/* error */
221 					goto no_gnu;
222 				}
223 				break;
224 			case '\"':
225 				quotefound = 1;
226 				break;
227 			case '\\':
228 				if (!*p)
229 					break;
230 				switch (c = *p++) {
231 				case 'b':
232 					buf[index++] = '\b';
233 					break;
234 				case 'f':
235 					buf[index++] = '\f';
236 					break;
237 				case 'n':
238 					buf[index++] = '\n';
239 					break;
240 				case 'r':
241 					buf[index++] = '\r';
242 					break;
243 				case 't':
244 					buf[index++] = '\t';
245 					break;
246 				case 'v':
247 					buf[index++] = '\v';
248 					break;
249 				case 'a':
250 					buf[index++] = '\a';
251 					break;
252 				case '\"':
253 				case '\\':
254 				case '\'':
255 				case '?':
256 					buf[index++] = c;
257 					break;
258 				default:
259 					if (isdigit((unsigned char)c)) {
260 						unsigned int	x;
261 						unsigned char	*up =
262 							(unsigned char *)p;
263 						n = c - '0';
264 						if (isdigit(*up)) {
265 							x = *up++ - '0';
266 							n = 8 * n + x;
267 							if (isdigit(*up)) {
268 								x = *up++ - '0';
269 								n = 8 * n + x;
270 							}
271 						}
272 						p = (char *)up;
273 						buf[index++] = n;
274 					}
275 					break;
276 				}
277 				break;
278 			default:
279 				buf[index++] = mc;
280 				break;
281 			}
282 			if (quotefound) {
283 				while (*p && ((*p == ' ') || (*p == '\t'))) {
284 					p++;
285 				}
286 				if ((*p != '\n') && (*p != '\0')) {
287 					goto no_gnu;
288 				}
289 				quotefound = 0;
290 				break;
291 			}
292 		}
293 		ln_size = get_one_line(&linebuf, &mbuf, &size);
294 		if ((ln_size == (size_t)-1) ||
295 			(ln_size == 0)) {
296 			goto no_gnu;
297 		}
298 		p = linebuf;
299 		/* skip spaces */
300 		while ((*p == ' ') || (*p == '\t'))
301 			p++;
302 		bufsize += ln_size;
303 		buf = (char *)Xrealloc(buf, bufsize);
304 	}
305 
306 	for (i = 0; (field = mandatory_fields[i]) != NULL; i++) {
307 		if (strstr(buf, field) == NULL)
308 			continue;
309 		/* one of mandatory fields found */
310 		free(linebuf);
311 		free(buf);
312 		(void) munmap(addr, fsize);
313 		if (verbose)
314 			diag(gettext(DIAG_GNU_FOUND));
315 		invoke_gnu_msgfmt();
316 		/* NOTREACHED */
317 	}
318 	for (i = 0; (field = mandatory_fields_new[i]) != NULL; i++) {
319 		if (strstr(buf, field) == NULL)
320 			continue;
321 		/* one of mandatory fields found */
322 		free(linebuf);
323 		free(buf);
324 		(void) munmap(addr, fsize);
325 		if (verbose)
326 			diag(gettext(DIAG_GNU_FOUND));
327 		invoke_gnu_msgfmt();
328 		/* NOTREACHED */
329 	}
330 
331 no_gnu:
332 	free(linebuf);
333 	if (buf)
334 		free(buf);
335 }
336