| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 | /* msgfmt utility (C) 2012 rofl0r * released under the MIT license, see LICENSE for details */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <assert.h>#include "poparser.h"#include "StringEscape.h"__attribute__((noreturn))static void syntax(void) {	fprintf(stdout,	"Usage: msgmerge [OPTION] def.po ref.pot\n");	exit(1);}__attribute__((noreturn))static void version(void) {	fprintf(stdout,		"these are not (GNU gettext-tools) 99.9999.9999\n");	exit(0);}#define streq(A, B) (!strcmp(A, B))#define strstarts(S, W) (memcmp(S, W, sizeof(W) - 1) ? NULL : (S + (sizeof(W) - 1)))struct fiLes {	FILE *out;	/* we can haz 3 different input files:	* the .pot, which is the file containing only the ripped out strings from the program	* (and no translations)	* a .po, which contains translations and strings made from a previous .pot from that same source file,	* a compendium, which is basically a huge po file containing all sorts of strings (msgid's) and translations (msgstr's)	*/	FILE *po;	FILE *pot;	FILE *compend;	int plural_count;	char convbuf[16384];	enum po_entry prev_type;};/* currently we only output input strings as output strings * i.e. there is no translation lookup at all */int process_line_callback(struct po_info* info, void* user) {	struct fiLes* file = (struct fiLes*) user;	// escape what is unescaped automatically by lib	escape(info->text, file->convbuf, sizeof(file->convbuf));	switch (info->type) {	case pe_msgid:		file->plural_count = 1;		fprintf(file->out, "\nmsgid \"%s\"\n", file->convbuf);		file->prev_type = info->type;		break;	case pe_ctxt:		fprintf(file->out, "msgctxt \"%s\"\n", file->convbuf);		break;	case pe_plural:		fprintf(file->out, "msgid_plural \"%s\"\n", file->convbuf);		file->prev_type = info->type;		break;	case pe_msgstr:		if (file->prev_type == pe_plural) {			fprintf(file->out, "msgstr[%d] \"%s\"\n", file->plural_count++, file->convbuf);		} else {			fprintf(file->out, "msgstr \"%s\"\n", file->convbuf);		}		break;	}	return 0;}int process(struct fiLes *files, int update, int backup) {	(void) update; (void) backup;	struct po_parser pb, *p = &pb;	char line[4096], conv[8192], *lb;	poparser_init(p, conv, sizeof(conv), process_line_callback, files);	while((lb = fgets(line, sizeof(line), files->po))) {		poparser_feed_line(p, lb, sizeof(line));	}	poparser_finish(p);	return 0;}void set_file(int out, char* fn, FILE** dest) {	if(streq(fn, "-")) {		*dest = out ? stdout : stdin;	} else {		*dest = fopen(fn, out ? "w" : "r");	}	if(!*dest) {		perror("fopen");		exit(1);	}}int getbackuptype(char* str) {	if(!str || !*str || streq(str, "none") || streq(str, "off"))		return 0;	else if(streq(str, "t") || streq(str, "numbered"))		return 1;	else if(streq(str, "nil") || streq(str, "existing"))		return 2;	else if(streq(str, "simple") || streq(str, "never"))		return 3;	else syntax();}int main(int argc, char**argv) {	if(argc == 1) syntax();	int arg = 1;	struct expect {		int out;		int po;		int pot;		int compend;	} expect_fn = {		.out = 0,		.po = 1,		.pot = 0,		.compend = 0,	};	struct fiLes files = {0,0,0,0,1,0};	char* backup_suffix = getenv("SIMPLE_BACKUP_SUFFIX");	if(!backup_suffix) backup_suffix = "~";	int update = 0;	int backup = getbackuptype(getenv("VERSION_CONTROL"));	char* dest;	set_file(1, "-", &files.out);#define A argv[arg]	for(; arg < argc; arg++) {		if(A[0] == '-') {			if(A[1] == '-') {				if(					streq(A+2, "strict") ||					streq(A+2, "properties-input") ||					streq(A+2, "properties-output") ||					streq(A+2, "stringtable-input") ||					streq(A+2, "stringtable-output") ||					streq(A+2, "no-fuzzy-matching") ||					streq(A+2, "multi-domain") ||					streq(A+2, "previous") ||					streq(A+2, "escape") ||					streq(A+2, "no-escape") ||					streq(A+2, "force-po") ||					streq(A+2, "indent") ||					streq(A+2, "add-location") ||					streq(A+2, "no-location") ||					streq(A+2, "no-wrap") ||					streq(A+2, "sort-output") ||					streq(A+2, "sort-by-file") ||					strstarts(A+2, "lang=") ||					strstarts(A+2, "color") || // can be --color or --color=xxx					strstarts(A+2, "style=") ||					strstarts(A+2, "width=") ||					streq(A+2, "verbose") ||					streq(A+2, "quiet") ||					streq(A+2, "silent") ) {				} else if(streq(A+2, "version")) {					version();				} else if((dest = strstarts(A+2, "output-file="))) {					set_file(1, dest, &files.out);				} else if((dest = strstarts(A+2, "compendium="))) {					set_file(1, dest, &files.compend);				} else if((dest = strstarts(A+2, "suffix="))) {					backup_suffix = dest;				} else if((dest = strstarts(A+2, "directory="))) {					goto nodir;				} else if((dest = strstarts(A+2, "backup"))) {					if (*dest == '=')						backup = getbackuptype(dest + 1);					else						backup = 0;				} else if(streq(A+2, "update")) {					set_update:					update = 1;				} else if(streq(A+2, "help")) syntax();			} else if(streq(A + 1, "o")) {				expect_fn.out = 1;			} else if(streq(A + 1, "C")) {				expect_fn.compend = 1;			} else if(streq(A + 1, "U")) {				goto set_update;			} else if(				streq(A+1, "m") ||				streq(A+1, "N") ||				streq(A+1, "P") ||				streq(A+1, "e") ||				streq(A+1, "E") ||				streq(A+1, "i") ||				streq(A+1, "p") ||				streq(A+1, "w") ||				streq(A+1, "s") ||				streq(A+1, "F") ||				streq(A+1, "V") ||				streq(A+1, "q")			) {			} else if (streq(A+1, "v")) {				version();			} else if (streq(A+1, "D")) {				// no support for -D at this time				nodir:				fprintf(stderr, "EINVAL\n");				exit(1);			} else if (streq(A+1, "h")) {				syntax();			} else if(expect_fn.out) {				if(update && streq(A, "/dev/null")) return 0;				set_file(1, A, &files.out);				expect_fn.out = 0;			} else if(expect_fn.compend) {				set_file(1, A, &files.compend);				expect_fn.compend = 0;			} else if(expect_fn.po) {				if(update && streq(A, "/dev/null")) return 0;				set_file(0, A, &files.po);				expect_fn.po = 0;				expect_fn.pot = 1;			} else if(expect_fn.pot) {				if(update && streq(A, "/dev/null")) return 0;				set_file(0, A, &files.pot);				expect_fn.pot = 0;			}		} else if(expect_fn.out) {			if(update && streq(A, "/dev/null")) return 0;			set_file(1, A, &files.out);			expect_fn.out = 0;		} else if(expect_fn.compend) {			set_file(1, A, &files.compend);			expect_fn.compend = 0;		} else if(expect_fn.po) {			if(update && streq(A, "/dev/null")) return 0;			set_file(0, A, &files.po);			expect_fn.po = 0;			expect_fn.pot = 1;		} else if(expect_fn.pot) {			if(update && streq(A, "/dev/null")) return 0;			set_file(0, A, &files.pot);			expect_fn.pot = 0;		}	}	if(update) {		fprintf(stdout, "warning: update functionality unimplemented\n");		return 0;	}	if(!files.out || !files.po || !files.pot) syntax();	int ret = process(&files, update, backup);	FILE** filearr = (FILE**) &files;	unsigned i;	for (i = 0; i < 4; i++) {		if(filearr[i] != NULL) fflush(filearr[i]);	}	for (i = 0; i < 4; i++) {		if(			filearr[i] != NULL &&			filearr[i] != stdout &&			filearr[i] != stdin		) fclose(filearr[i]);	}	return ret;}
 |