root/usr/src/cmd/msgfmt/check_header.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 2001 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#include "sun_msgfmt.h"

static const char       *mandatory_fields[] = {
        "Project-Id-Version",
        "PO-Revision-Date",
        "Last-Translator",
        "Language-Team",
        "Content-Type",
        "Content-Transfer-Encoding",
        NULL
};

static const char       *mandatory_fields_new[] = {
        "POT-Creation-Date",
        "Plural-Forms",
        NULL
};

extern int      verbose;

extern void     invoke_gnu_msgfmt(void);

static size_t
get_one_line(char **bufhead, char **mbuf, size_t *fsize)
{
        size_t  len;
        char    *p = *mbuf;
        char    *q, *tmp;

        if (*bufhead) {
                free(*bufhead);
                *bufhead = NULL;
        }

        if (*fsize == 0) {
                /* eof */
                return (0);
        }

        q = p;
        while (((*fsize) != 0) && (*p++ != '\n')) {
                (*fsize)--;
        }
        len = p - q;
        if (len == 0) {
                return (0);
        }
        tmp = (char *)Xmalloc(len + 1);
        (void) memcpy(tmp, q, len);
        tmp[len] = '\0';
        *bufhead = tmp;
        *mbuf = p;
        return (len);
}

void
check_gnu(char *addr, size_t fsize)
{
        int     i;
        char    c, mc;
        char    *linebuf;
        char    *mbuf, *p, *buf;
        unsigned int    n;
        size_t  ln_size;
        size_t  bufsize, index;
        size_t  size = fsize;
        int     quotefound = 0;
        const char      *field;

        buf = NULL;
        linebuf = NULL;
        mbuf = addr;

loop:
        ln_size = get_one_line(&linebuf, &mbuf, &size);
        if ((ln_size == (size_t)-1) ||
                (ln_size == 0)) {
                goto no_gnu;
        }
        p = linebuf;

        while ((*p == '#') || (*p == '\n')) {
                ln_size = get_one_line(&linebuf, &mbuf, &size);
                if ((ln_size == (size_t)-1) ||
                        (ln_size == 0)) {
                        goto no_gnu;
                }
                p = linebuf;
        }

        if (strncmp(p, "domain", 6) == 0)
                goto loop;

        if (strncmp(p, "msgid", 5) != 0) {
                /* error */
                goto no_gnu;
        }

        p += 5;
        if ((*p != ' ') && (*p != '\t') &&
                (*p != '\n') && (*p != '\0')) {
                /* no space after msgid */
                goto no_gnu;
        }
        /* skip spaces */
        while ((*p == ' ') || (*p == '\t'))
                p++;

        /* check if this entry is an empty string */
        if ((*p != '\"') || (*(p + 1) != '\"')) {
                /* this is not an empty string */
                goto no_gnu;
        }
        p += 2;
        while (*p && ((*p == ' ') || (*p == '\t'))) {
                p++;
        }
        if ((*p != '\n') && (*p != '\0')) {
                /* other characters than '\n' and '\0' found */
                goto no_gnu;
        }

        for (; ; ) {
                ln_size = get_one_line(&linebuf, &mbuf, &size);
                if ((ln_size == (size_t)-1) ||
                        (ln_size == 0)) {
                        goto no_gnu;
                }
                p = linebuf;
                /* skip leading spaces */
                while ((*p == ' ') || (*p == '\t'))
                        p++;

                if (*p != '\"') {
                        if (strncmp(p, "msgstr", 6) == 0) {
                                break;
                        }
                        /* not a valid entry */
                        goto no_gnu;
                }
                if (*(p + 1) != '\"') {
                        /* not an empty string */
                        goto no_gnu;
                }
                p += 2;
                while ((*p == ' ') || (*p == '\t'))
                        p++;

                if ((*p != '\n') && (*p != '\0')) {
                        /* other characters than '\n' and '\0' found */
                        goto no_gnu;
                }
        }

        /*
         * msgid for the header entry found
         * Now p points to "msgstr"
         */
        p += 6;
        if ((*p != ' ') && (*p != '\t') &&
                (*p != '\n') && (*p != '\0')) {
                /* no space after msgid */
                goto no_gnu;
        }

        /* skip spaces */
        while ((*p == ' ') || (*p == '\t'))
                p++;

        if (*p != '\"') {
                /* no quote */
                goto no_gnu;
        }

        bufsize = ln_size + 1;
        index = 0;
        buf = (char *)Xmalloc(bufsize);

        for (; ; ) {
                if (*p != '\"') {
                        /* msgstr entry ends */
                        buf[index] = '\0';
                        break;
                }

                if (*p++ != '\"') {
                        /* no beginning quote */
                        goto no_gnu;
                }
                while (*p) {
                        switch (mc = *p++) {
                        case '\n':
                                if (!quotefound) {
                                        /* error */
                                        goto no_gnu;
                                }
                                break;
                        case '\"':
                                quotefound = 1;
                                break;
                        case '\\':
                                if (!*p)
                                        break;
                                switch (c = *p++) {
                                case 'b':
                                        buf[index++] = '\b';
                                        break;
                                case 'f':
                                        buf[index++] = '\f';
                                        break;
                                case 'n':
                                        buf[index++] = '\n';
                                        break;
                                case 'r':
                                        buf[index++] = '\r';
                                        break;
                                case 't':
                                        buf[index++] = '\t';
                                        break;
                                case 'v':
                                        buf[index++] = '\v';
                                        break;
                                case 'a':
                                        buf[index++] = '\a';
                                        break;
                                case '\"':
                                case '\\':
                                case '\'':
                                case '?':
                                        buf[index++] = c;
                                        break;
                                default:
                                        if (isdigit((unsigned char)c)) {
                                                unsigned int    x;
                                                unsigned char   *up =
                                                        (unsigned char *)p;
                                                n = c - '0';
                                                if (isdigit(*up)) {
                                                        x = *up++ - '0';
                                                        n = 8 * n + x;
                                                        if (isdigit(*up)) {
                                                                x = *up++ - '0';
                                                                n = 8 * n + x;
                                                        }
                                                }
                                                p = (char *)up;
                                                buf[index++] = n;
                                        }
                                        break;
                                }
                                break;
                        default:
                                buf[index++] = mc;
                                break;
                        }
                        if (quotefound) {
                                while (*p && ((*p == ' ') || (*p == '\t'))) {
                                        p++;
                                }
                                if ((*p != '\n') && (*p != '\0')) {
                                        goto no_gnu;
                                }
                                quotefound = 0;
                                break;
                        }
                }
                ln_size = get_one_line(&linebuf, &mbuf, &size);
                if ((ln_size == (size_t)-1) ||
                        (ln_size == 0)) {
                        goto no_gnu;
                }
                p = linebuf;
                /* skip spaces */
                while ((*p == ' ') || (*p == '\t'))
                        p++;
                bufsize += ln_size;
                buf = (char *)Xrealloc(buf, bufsize);
        }

        for (i = 0; (field = mandatory_fields[i]) != NULL; i++) {
                if (strstr(buf, field) == NULL)
                        continue;
                /* one of mandatory fields found */
                free(linebuf);
                free(buf);
                (void) munmap(addr, fsize);
                if (verbose)
                        diag(gettext(DIAG_GNU_FOUND));
                invoke_gnu_msgfmt();
                /* NOTREACHED */
        }
        for (i = 0; (field = mandatory_fields_new[i]) != NULL; i++) {
                if (strstr(buf, field) == NULL)
                        continue;
                /* one of mandatory fields found */
                free(linebuf);
                free(buf);
                (void) munmap(addr, fsize);
                if (verbose)
                        diag(gettext(DIAG_GNU_FOUND));
                invoke_gnu_msgfmt();
                /* NOTREACHED */
        }

no_gnu:
        free(linebuf);
        if (buf)
                free(buf);
}