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