/* MSG.C

  Description:
    Utility to maintain different language versions with one source
    file.
    translates ;|MSGxx|;"msg" to different languages
    copies the translated file(s) to another dir

     (c) URBAN Reinhard, Graz 1995
    浜様様様様様様様様様様様様様様様様様様様様様様様様様様様様様融
     URBAN Utilities fr AutoCAD                                
     (c) Reinhard URBAN, A-Graz, 1991-95                        
     A-8010 GRAZ, Bergmanngasse 15                              
     E-Mail: rurban@sbox.tu-graz.ac.at                          
     AutoCAD R12 int. + deutsch Zusatzprogramme, 1991-95        
    藩様様様様様様様様様様様様様様様様様様様様様様様様様様様様様夕

Usage:
    MSG [/mmsgtable][Options] infile(s)... outdir\
Options:
    /? /h  help
    /mfile use message file as translation table
           without /m don't translate, just remove the inline comment
    /sfile additional msgfile to synchronize
           new msg's are here added too
    /ffile use file to translate FUNCTIONS
    /lname force language type {C | LISP | PASCAL}
		   ignore the extension
    /k     keep the comments
    /i     ignore the MSG numbers, set to those found in the msgfile
             without /k just adds new messages
    /u     update the MSG numbers, but don't translate the strings,
             to synchronize the MSG numbers with the msgfile
    /r     reset all MSG numbers to 0 (forces /k)
    /a     don't add to the msgfile
    /w     print warning on string translations or wrong numbers
    /t     errorlevel 0 on translations (only for WMAKE)
    /d     DEBUG mode (show everything)


  translates not only ;|MSG0|; numbered strings
  the function section [functions] too:
  searches for all words within the functions section with the delims
  appropriate to the selected language type

  Compile with the Symantec C 6.0 in large modell
		sc -ml -o+space -4 msg checkexe getopts ctoolsl.lib

    needed: file.c, filespec.c, list.c, mem.c (in \sc\src\ctools\)
			\sc\src\clib\mktemp.c
			checkexe.c, getopts.c (SNIPPETS)

  Credits:
	list.c, mem.c, file.c, filespec.c, mktemp.c
 		written by Walter Bright,
 		from the Symantec C++ 6.0 Compiler (CTOOLS)
	getopts.c
 		written by Paul Edwards, 16-Feb-1990
 		from the SNIPPETS package
	checkexe.c
 		written by Bobo Jarvis,
 		from the SNIPPETS package

  only backup msgfile, when before changing

  $Log: MSG.C $
  Revision 1.20  1995/05/01 16:33:22  Reini
  switch /f changed to /m
  new switch /f funcfile (nyi)
  Revision 1.19  1995/05/01 15:48:27  Reini
  better output
  Revision 1.18  1995/05/01 14:56:02  Reini
  V1.3:
  new switch /l=language added
  more language support (delims,identifiers,case-exact) for function
    replacement
  read_ini_file() doesn't work yet
  Revision 1.17  1995/04/25 07:32:07  Reini
  /t added
  Revision 1.16  1995/04/24 22:14:11  Reini
  more output
  Revision 1.15  1995/04/24 22:04:47  Reini
  outdir can end with / too (for MAKE)
  exit codes 0-5 for MAKE
  Revision 1.14  1995/04/23 21:50:55  Reini
  V1.2: getopts() verbessert
  Revision 1.13  1995/04/20 16:37:34  Reini
  V1.1
  warnings on /u with translation errors improved
  Revision 1.12  1995/04/20 03:49:12  Reini
  bugfixes with samedir
  backup now keeps the timestamp
  new switch /s for syncfile
  Revision 1.11  1995/04/19 19:44:42  Reini
  better sync error messages,
  new switch /w for string translation warnings
  better behavior of /u
  Revision 1.10  1995/04/19 14:39:49  Reini
  extension specific comments
  Revision 1.9  1995/04/19 03:21:58  Reini
  /a added, bugfix in backup of msgfile
  Revision 1.8  1995/04/19 02:59:09  Reini
  outdir auch ohne last \ wegen MAKE
  Revision 1.7  1995/04/18 22:49:20  Reini
  listfile support added: listchar @
  Revision 1.6  1995/04/18 22:12:00  Reini
  default extension LSP added
  Revision 1.5  1995/04/18 19:51:01  Reini
  div. assertion failures debugged
  Revision 1.4  1995/04/18 12:48:01  Reini
  /m new, /u coded
  Revision 1.3  1995/04/13 01:21:50  Reini
  virus checker added: checkexe
  Revision 1.2  1995/04/13 01:00:36  Reini
  various bugfixes and enhancements:
    correct backup, copy the msgfile, sync checking,
  	list_nth() with base 0, list_free(), list_searchstr(),
	create outdir
  Revision 1.1  1995/04/12 19:42:28  Reini
  Initial revision

*/
char ur_header[]="$Id: MSG.C 1.20 1995/05/01 16:33:22 Reini Exp $";
char ur_cpyrght[]="(c) Reinhard Urban, Graz - Austria, 1995";

#define PROTOTYPES 1
#undef FUNCS		//no function translation yet

#include  <stdio.h>
#include  <string.h>
#include  <assert.h>
#include  <malloc.h>
#include  <stdlib.h>

#ifdef __SC__
#include    "filespec.h"
#include    "file.h"
#endif

#ifndef __WINDOWS
#include    "checkexe.h"
#endif

#include    "getopts.h"

#ifdef __WATCOMC__
#include  <direct.h>    // opendir()
//#include  <conio.h>       // putch()
//#include  <ctype.h>       // toupper()
//#include  <time.h>        // time functions
#include  <errno.h>     // errno for fgets()
#endif

/**************************************************************************/
/*  GLOBAL DEFINES  */
/**************************************************************************/
#ifndef ELEMENTS
  #define ELEMENTS(array) (sizeof(array)/sizeof((array)[0]))
#endif

#define EXIT_SUCCESS  0     //none translated
#define EXIT_TRANS    1     //some translations made
#define EXIT_TRANSERR 2     //translation errors (msg not found)
#define EXIT_SYNCERR  3     //sync errors detected
#define EXIT_SAMEDIR  10    //same dir without /k or /r, aborted
#define EXIT_FAILURE  11    //wrong arguments, aborted

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

#define DEBUG_PRINT(str,what)  if (debug) printf (str,what)
#define DEBUG_PRINT2(str,what1,what2)  if (debug) printf (str,what1,what2)

#define MAXLINE 256

#define BACKUPCOPY 1
#define BACKUPMOVE 0

#define SYNCERRADD 0
#define SYNCERRUPD 1
#define SYNCERREND 2

#define TMPNAME "$MXXXXXX"

/**************************************************************************/
/*  EXTERNAL FUNCTIONS  DECLARATIONS */
/**************************************************************************/

char *mktemp(char *template);
#include "mem.c"
#include "list.c"

/***************************************************************************/
/*  GLOBAL STRUCTURES  */
/***************************************************************************/
// extension specific token delimiters for function renaming
typedef struct
{
	char ltype[8];
    char lbeg[3];
    char lend[3];
    char lidentifiers[20];	//valid identifier charbag
    char ldelims[20];	//token delimiter charbag
    char lcaseexact;	//are tokens case-exact
    char lstring;	 	//string char "
    char lliteral; 		//escape char
} lang_tbl_t;

// extension specific comments
typedef struct
{
	char cext[4];
    char cltype[8];
} ext_tbl_t;

typedef struct SECTION
{
	list_t list;		//list of strings
	struct SECTION *next;    //linked list
	char   *name;		//name of section: [name]
	long   pos;			//position in file
} section_t;


/**************************************************************************/
/*  LOCAL FUNCTIONS  DECLARATIONS */
/**************************************************************************/
int  file_ini_read (char *name, section_t *sections);
int  language_type (char *infile);
int  read_msgfile (char *msgfile, int test);
void doserr (char *msg);
int  add_msg (char *s);
int  list_searchstr (list_t list, char *str);
void terminate (void);
void file_backup (char *filespec, int copy);
void check_samedir (void);
void showhelp ();
void usage ();
void syncerror (int msgnum, int newnum, char *oldstr, char *newstr, int which);
char *change_line (char *line, int i, char *str, char *rest);
void split_line (char *line, char *s);
int  convert (char *infile);
void doit (char *arg);

/**************************************************************************/
/*  GLOBAL VARIABLES  */
/**************************************************************************/

char   	outdir[FILENAME_MAX] = "";
int    	actline;       	   // actual translated line
char   	actfile[FILENAME_MAX] = "";  // actual translated file
char   	msgfile[FILENAME_MAX] = "";
char   	funcfile[FILENAME_MAX] = ""; //function translation filename
char   	syncfile[FILENAME_MAX] = ""; //2nd msgfile to synchronize
char   	tmpstr[MAXLINE] = "";
char   	language[8] = "";  // set by /l
int    	msg_size = 0;      // number of messages in the list
int    	msg_creat = 0;     // number of new messages created
//section_t *sections;     // diff. ini sections
list_t 	msg_strings = NULL; //list of strings to translate
list_t 	msg_funcs   = NULL; //list of functions to translate
list_t 	msg_ftrans  = NULL; //list of translated functions

int    	numcopy = 0;       // number of translated files
int  	nummsg  = 0;       // number of translated MSGxx messages
int  	nummsg0 = 0;       // number of translated MSG0  messages
int  	translated = 0;    // number of really changed strings
int  	syncerr = 0;       // synchronicity errors

int  	samedir = FALSE;   // keep the copy in the same dir, overwrite it
                           // (do it only with -k, otherwise the marks are lost)
int  	msgtable= FALSE;   // is a msg file defined with -f
int  	debug   = FALSE;
int  	keep    = FALSE;   // keep the comment or delete it
int  	update  = FALSE;   // just update the MSG numbers to synchronize
int  	reset   = FALSE;   // creates new msgfile
int  	msgign  = FALSE;   // ignore the msg numbers
int  	msgdontadd  = FALSE;   // don't add to the msg file!
int  	warning = FALSE;   // warn on string translations
int  	quiet   = FALSE;   // print no messages at all
int  	trans   = FALSE;   // print no messages at all
int  	help    = FALSE;   // displays usgae

opt_t opttable[] =
{
	{ "?", OPTBOOL, &help },
	{ "h", OPTBOOL, &help },
	{ "m", OPTSTR,  msgfile },
	{ "s", OPTSTR,  syncfile },
#ifdef FUNCS
	{ "f", OPTSTR,  funcfile },
#endif
	{ "l", OPTSTR,  language },
   	{ "k", OPTBOOL, &keep },
   	{ "r", OPTBOOL, &reset },
   	{ "a", OPTBOOL, &msgdontadd },
   	{ "u", OPTBOOL, &update },
   	{ "d", OPTBOOL, &debug },
   	{ "i", OPTBOOL, &msgign },
   	{ "w", OPTBOOL, &warning },
   	{ "q", OPTBOOL, &quiet },
   	{ "t", OPTBOOL, &trans },
   	{ NULL, 0, NULL }
};
char *msg;
char str[MAXLINE];
char *rest;

//need the word delimiters to find the functions to translate
//normal delims are isspace() + the one defined in lang->ldelim

//valid word chars are isalpha() + '_' as the first char
//and isalnum()+ '_' as the next chars
//in LISP all the chars in identifiers are allowed too
//and isalnum() as first too
//in PLAIN files the strings are marked with MSGxx and no string chars
//!!the strings must begin with a space and end with a space!!
lang_tbl_t lang_tbl[] =  //the language table
{   //type    open  close identifiers
	//                         delims  caseexact str   lit
	{"C", 	  "/*", "*/", "",  "{}[]()'\"+-/@&%!*#`",
											 1, '\"', '\\' },
	{"LISP",  ";|", "|;", "+-@&%*#",
							   "{}[]()'\"",  0, '\"', '\\' },
	{"PLAIN", "",   "",   "",  "",           0,  ' ',   0  },
	{"MENU",  "{",  "}",  "",  "{}[]()'\"",  0,  ' ',   0  },
	{"PASCAL","(*", "*)", "",  "{}[]()'\"+-/@&%!*#`",
											 0, '\"', '\\' },
   	{ "",     NULL, NULL, NULL,NULL,		 0, 0, 0 }
};
lang_tbl_t *lang;	 	 //ptr to the actual language

ext_tbl_t cmts[] =
{
	{ "LSP", "LISP" },	 // valid lisp extensions
	{ "LLB", "LISP" },
	{ "MNL", "LISP" },
	{ "DCL", "C" },  	 // C like comments
	{ "C",   "C" },
	{ "H",   "C" },
	{ "PAS", "PASCAL" }, // Pascal like
	{ "INC", "PASCAL" },
	{ "MNU", "MENU" },   // AutoCAD Menufile
	{ "MND", "MENU" },
   	{ "",    NULL }
};

char stringchar, litchar;
char cmtbeg[3];	 					// actual comment chars /*
char cmtend[3];   					// defaults to C-like   */
char firstmatch[12] = "/*MSG";		//to search in each line
char nullmatch[12]  = "/*MSG0*/";   //null MSG
char normmatch[12]  = "/*MSG%d*/";	//norm MSG, size max. 2+3+4+2

int ferr = 0;
int exitcode = EXIT_SUCCESS;

/**************************************************************************/
/* Function definitions */
/**************************************************************************/
void showhelp (int full)
{
	if (!quiet)
    puts (" \
MSG Translator V1.3, written by Reinhard Urban, Graz, 12.04.95\n \
Usage:\n \
   MSG [Options...] infile(s)... [@listfile] outdir\\ \n \
Description:\n \
   Maintainance of different language versions with one source file.\n \
   Translates messages within inline comments with use of a string table.\n \
   Supported extensions are LSP(default),LLB,MNL for LISP; PAS,INC for PASCAL\n \
   and C,H,DCL and the rest for C: /*MSGxx*/\"string\"\n \
Options:\n \
        /? or /h help\n \
        /mfile   use MESSAGE file as translation table\n \
        /sfile   additional msgfile to SYNCHRONIZE additions\n \
        /ffile   use file to translate FUNCTIONS\n \
        /lname   forces the LANGUAGE type { C,LISP,PASCAL,PLAIN,MENU }\n \
        /q       QUIET, print no messages\n \
        /k       KEEP the comments\n \
        /i       IGNORE the MSG numbers, set to those found in the msgfile\n \
        /u       UPDATE the MSG numbers, don't translate the strings,\n \
        /r       RESET all MSG numbers to 0 (forces /k)\n \
        /a       don't ADD to the msgfile\n \
        /w       print WARNING on string translations or wrong numbers\n \
        /t       return errorlevel 0 on TRANSLATIONS (only for WMAKE)\n \
        /d       DEBUG mode (show internal messages)\
");
	if (0)
	{
    	puts ("\n \
  Samples:\n \
    MSG *.lsp *.mnl *.dcl german\\ \n \
        removes just the comments without translation\n \
    MSG /menglish.msg /a *.lsp english\\\n \
        translates the strings to english\n \
    MSG /mgerman.msg /senglish.msg /u *.lsp *.mnl *.dcl \\german\\ \n \
        updates the german message table, if the table\n \
        is altered or a MSG is added or deleted in the source\n \
        updates english.msg too\n \
    MSG /mgerman.msg /k *.lsp *.mnl *.dcl .\\ \n \
        keeps the comments, eg. to add new messages,\n \
        same as /u, but translates strings if altered.\n \
    MSG /mgerman.msg /senglish.msg /u/k changed.lsp .\\ \n \
        updates the german message table, if the string was changed\n \
        in the source file\n \
    MSG /r *.lsp *.mnl *.dcl german\\ \n \
        resets all MSG numbers to 0, to create a new clear msgfile.\n \
    MSG @lisp.lst german\\\n \
	    removes the comments of all files in LISP.LST\n \
");
	}
	terminate();
	if ( !exitcode )
		exitcode=EXIT_FAILURE;
	if ( help )
		exitcode=EXIT_SUCCESS;
	DEBUG_PRINT ("EXIT CODE: %d ",exitcode);
    exit (exitcode);
}

void usage (void)
{
	if (!quiet)
    puts ("\n \
MSG Translator V1.3, written by Reinhard Urban, Graz, 12.04.95\n \
Usage:\n \
   MSG [Options...] infile(s)... [@listfile] outdir\\\n\
MSG /? for help\n");

	terminate();
	if ( !exitcode )
		exitcode=EXIT_FAILURE;
	DEBUG_PRINT ("EXIT CODE: %d",exitcode);
    exit (exitcode);
}

//get langauge specific information
lang_tbl_t *get_language (char *language)
{
	int k;

	for (k=0; *lang_tbl[k].ltype; k++)
	{	DEBUG_PRINT2 ("\ncmp language[%d]: \"%s\"",k,lang_tbl[k].ltype);
	 	if ( stricmp (lang_tbl[k].ltype,language) == 0)
	 	{	DEBUG_PRINT2  ("\nfound language type[%d]: %s ", k,lang_tbl[k].ltype);
	 		DEBUG_PRINT2 (" cmts: %s %s ", lang_tbl[k].lbeg, lang_tbl[k].lend);
			return &lang_tbl[k];
	 	}
	}
	return NULL;
}

// gets the language specific settings from the extension
int language_type (char *infile)
{
	int j,k;
	char *ext;

	//already set by /l, ignore the actual extension
	if ( *language )
	{	if ( !lang )
			lang = get_language (language);
		//else already defined
	}
	else
	//get extension and extension specific comments
	if ((ext = filespecdotext (infile)) != NULL)
	{	if (*ext) ext++;
		j=0;
		DEBUG_PRINT ("\ncheck ext: \"%s\"", ext);
        for (j=0; *cmts[j].cext; j++)
		{	DEBUG_PRINT2 ("\ncmp ext[%d]: \"%s\"",j,cmts[j].cext);
			if ( strnicmp (cmts[j].cext,ext,3) == 0)
			{	DEBUG_PRINT2 ("\nsupported extension[%d]: \"%s\"", j,cmts[j].cext);
				DEBUG_PRINT  (" language type: %s ", cmts[j].cltype);
				lang = get_language (cmts[j].cltype);
				goto go_on;
			}
		}
		DEBUG_PRINT ("\nnot supported extension",NULL);
	}
	if ( !lang )
	{	lang = &lang_tbl[0];
		DEBUG_PRINT2 ("\nnot supported language,\ntaking the default C-like comments: %sMSGxx%s",
					  lang->lbeg, lang->lend);
	}

  go_on:
	stringchar = lang->lstring;
	litchar    = lang->lliteral;
	strcpy (cmtbeg, lang->lbeg);
	strcpy (cmtend, lang->lend);
	strcpy (firstmatch,cmtbeg);
	strcat (firstmatch,"MSG");
	strcpy (nullmatch,cmtbeg);
	strcat (nullmatch,"MSG0");  strcat (nullmatch,cmtend);
	strcpy (normmatch,cmtbeg);
	strcat (normmatch,"MSG%d"); strcat (normmatch,cmtend);
	return TRUE;
}

// read file in the ini format
// creates list of lists
// for each section one
// the first section need not to be initiated by []

// buggy code!!
int file_ini_read (char *name, section_t *sections)
{
	section_t *sec;
	list_t lines = NULL;    //actual list
    list_t list;			//actual line
    FILE *fp;
    char *buffer = NULL;
    int buflen = 0;
    int bufi = 0;
	int seci = 0;
	long fpos;
    int c;
	char *p;

    fp = fopen(name,"r");
    if (!fp) return NULL;
	sec = (section_t *) mem_fmalloc (sizeof(struct SECTION));
	sections = sec;
	sec->next = NULL;
	sec->name = NULL;
	sec->pos = 0;
	while (1)
	{   if (bufi + 1 > buflen)
		{	buflen += 80;
			buffer = (char *) mem_realloc(buffer,buflen);
			assert(buffer);
		}
		c = fgetc(fp);
		switch (c)
		{
		case EOF:
			goto eof;
		case 0:
			break;		/* ignore nulls			*/
		case '\n':
			buffer[bufi] = 0;
			p = mem_strdup(buffer);
			assert(p);
			DEBUG_PRINT ("read msg line: %s", p);
			list = list_append(&lines,p);
			assert(list);
			bufi = 0;
			break;
		case '[':
			if ( bufi == 0 )
			{	fpos = ftell (fp);
				fgets (buffer,buflen,fp);
			    assert(buffer);
				if ((p = strchr (buffer,']')) != NULL)
				{	// new sections
					*p = 0;
					p = mem_strdup (buffer);
					assert(p);
					DEBUG_PRINT ("new section: %s", p);
					//save the old sections
					if ( lines )
					{	section_t *sec1;

						sec->list = lines;
						//start a new list
						lines = NULL;
						sec1 = sec;
						//room for a new section_t
						sec = (section_t *) mem_malloc (sizeof(section_t));
						assert (sec);
						sec1->next = sec;
						seci++;
					}
					//initialize new section
					sec->list = NULL;
					sec->next = NULL;
					sec->name = p;
					sec->pos  = fpos;	// for fseek
				}
				else // ] not found
				{	fseek (fp, SEEK_SET, fpos);
					DEBUG_PRINT ("read section error: %s", buffer);
				}
			}
			break;
		case '#':	//ignore the rest of the line
			if ( bufi == 0 )
			{	fgets (buffer,buflen,fp);
			    assert(buffer);
				DEBUG_PRINT ("read comment: %s", buffer);
			}
			break;
		default:
			buffer[bufi++] = c;
			break;
		}
	}
	eof:
	if (bufi)
	{   buffer[bufi++] = 0;
		buffer = (char *) mem_realloc(buffer,bufi);
		assert(buffer);
		list = list_append(&lines,buffer);
		assert(list);
	}
	else
		mem_free(buffer);
	fclose(fp);
    return seci;
}

//read two sections (list of lists) from the message file
//the first in no group are the strings >msg_strings
//and the second group [functions] into the msg_funcs
int read_msgfile (char *msgfile, int test)
{
	list_t list;
//	section_t *sec, *tokens;
	int j,k;

	msg_strings = file_read (msgfile);
/*
	if (!file_ini_read (msgfile, sections))		//too buggy for now!!
		return FALSE;
	msg_strings = sections->list;
   	if (stricmp (sections->name,"functions") != 0)
	sec = sections; j = 1;
	sec++;
	while (sec)
	{	if (stricmp ("functions",sec->name) == 0)
		{
			tokens = sections;
			msg_funcs = tokens->list;
			if ( j != 1 )
			{
				DEBUG_PRINT ("\n%d sections ignored",j-1);
			}
		}
		sec = sec->next;
	}
*/
    msg_size = list_nitems (msg_strings);
	DEBUG_PRINT2 ("\n%d strings read from file %s",msg_size,msgfile);

	if ( debug && test )		// test the lists and list functions
	{   printf("\ncheck list_nth():");
		for ( j=1; j < ((10 < msg_size) ? 10 : msg_size); j++)
		{	list = list_nth (msg_strings,j-1);
           	printf ("\nmsg[%d]: %s",j,(char *)list_ptr( list ));
		}
	    printf("\ncheck list_searchstr():");
		for ( j=1; j < ((10 < msg_size) ? 10 : msg_size); j++)
		{	int k;
			list = list_nth (msg_strings,j-1);
			strcpy (str,(char *)list_ptr( list ));
			k = list_searchstr (msg_strings,str);
			list = list_nth (msg_strings,k-1);
           	printf ("\nfound[%d]: %s",k,(char *)list_ptr( list ));
		}
		//check for [functions]
/*		if ( msg_funcs )
		{   printf("\ncheck functions:");
    		k = list_nitems (msg_funcs);
			for ( j=1; j < ((10 < k) ? 10 : k); j++)
 			{   list = list_nth (msg_funcs,j-1);
	           	printf ("\ntok[%d]: \"%s\"",j,(char *)list_ptr( list ));
			}
		}
*/	}
}

int read_funcfile (char *file)
{
	list_t temp, temp1, list;
	char delims[] = " \t=";
	char *p, *p1, *p2;

	temp = file_read (file);
	DEBUG_PRINT2 ("\n%d function translations read from file %s",
				  list_nitems(list),file);
	//split the list in first and second token
	list = temp;
	while ( temp )
	{   p1 = strtok ((char *)list_ptr( list ), delims);
		p2 = strtok (NULL, delims);
		if ( p1 && p2 )
		{	p = mem_strdup(p1);
			assert(p);
			temp1 = list_append(&msg_funcs,p1);  //external function
			assert(temp1);
		 	p = mem_strdup(p2);
			assert(p);
			temp1 = list_append(&msg_ftrans,p1);
			assert(temp1);
		}
		temp = list_next (temp);
	}
	temp = list;
	while ( list ) 			// free the temp. list
	{   mem_free (list_ptr (list));
		list = list_next (list);
	}
    list_free(&temp,FPNULL);		// free list
	list_term();     				// free the freelist
}

void doserr (char *msg)
{
	exitcode = errno+10;
	perror (msg);
	usage ();
}

/*-----------------11.04.95 19:39-------------------
   add str to msg_strings, to the end of the file and
   advance msg_size
--------------------------------------------------*/
int add_msg (char *s)
{   list_t line;
    //section_t *sec;
	void *p;
    FILE *fp = NULL;
	static int backedup = FALSE;

	if ( msgtable && !msgdontadd)
	{
/*
		if ( msg_funcs )
		{   //cannot append to the end, have to insert
			if ((fp = fopen (msgfile, "r+")) != NULL)
			{	sec = &sections[1];	//start search with next section
				fseek (fp,SEEK_SET,sec->pos);
				while (sec)
				{	fputs (s, fp);
					//update the new position
					sec->pos = ftell (fp);
					line = sec->list;
					//write the section
					fputc ('[', fp);
					fputs (sec->name,fp);
					fputc (']', fp);
					while ( line )
					{	fputs (s, fp);
						line = list_next (line);
					}
					sec = sec->next;
				}
			    fclose (fp);
				if (syncfile && !quiet)
					printf("\ncannot add msg to syncfile %s",syncfile);
			}
			else goto err;
		}
		else
*/
		if ( !backedup )
		{	file_backup (msgfile, BACKUPCOPY);
			if (*syncfile)
				file_backup (syncfile, BACKUPCOPY);
			backedup = TRUE;
		}
		if ((fp = fopen (msgfile, "at")) != NULL)
		{
			fprintf (fp, "%s\n", s);
		    fclose (fp);
			if (*syncfile  && ((fp = fopen (syncfile, "at")) != NULL))
			{	fprintf (fp, "%s\n", s);
		    	fclose (fp);
			}
		}
		else goto err;
	}
	//lines = msg_strings;
	p = (char *)mem_strdup(s);
	assert (p);
#ifdef MEM_DEBUG
    line = list_append_debug(&msg_strings,p,__FILE__,__LINE__);
#else
	line = list_append (&msg_strings,p);
#endif
	assert (msg_strings);
	msg_creat++; msg_size++;

	DEBUG_PRINT2 ("\n%-45.45s  added to list at pos: %d",s,msg_size);
	//msg_strings = lines;
	return;
err:
	if (!quiet)
		printf ("\nerror appending a string to msgfile %s",msgfile);
	doserr (msgfile);
}

// look for the string in list, base 1
// return the index (starting with 1) or 0 if not found
// Attention! list_nth() counts with basis 0
int list_searchstr (list_t list, char *str)
{   int i = 1;
    list_t tmp = NULL;  // a list to compare

    tmp = list;
    while ( tmp )
    {	if (strcmp((char *)list_ptr(tmp),str)==0)
            break;
        tmp = list_next(tmp);
        i++;
    }
    if (tmp)
		return i;
	else
		return 0;
}

void terminate (void)
{   list_t list;

	list = msg_strings;
	if (list) DEBUG_PRINT ("\nfree the list\n",NULL);
	while ( list ) 			// free the strings
	{   //DEBUG_PRINT2 ("\nfree the string %p in %p",list_ptr(list),list);
		mem_free (list_ptr (list));
		list = list_next (list);
	}
    list_free(&msg_strings,FPNULL);	// free list
	list_term();     				// free the freelist
    mem_term();
}


void file_backup (char *filespec, int copy)
{   char *backup;

	backup = filespecbackup (filespec);
	if ( file_exists (backup) ) remove (backup);
	if (copy == BACKUPCOPY)
	{   DEBUG_PRINT2 ("\nbacking up %s to: %s", filespec, backup);
		file_rename (filespec, backup);	// to keep the old timestamp
		file_copy (backup, filespec);
	}
	else
	{	DEBUG_PRINT2 ("\nmoving %s to: %s", filespec, backup);
		file_rename (filespec, backup);
	}
	mem_free (backup);
}


void check_samedir (void)
{
    if ( !keep && samedir)
	{	if (!quiet)
			puts ("\nWarning! Can't copy the the file(s) into the same dir\n\
without the /k or /r option.");
        exit (EXIT_SAMEDIR);
    }
}


/*
int list_cmpfun (void *s1, void *s2)
{
    return (strcmp ((char *) s1, (char *) s2) == 0);
}
*/

/***************************************************
    splits the line into line, str and rest
				  0            0
    (test ;|MSG0|;"Teststring: ")
    ^             ^str          ^rest
    line
->  (test "translated: ")

	very destructive!
***************************************************/
void split_line (char *line, char *s)
{
	char c;

	//sc  = lang->lstring;	// usually '\"' (' ' for one word)
	//lit = lang->lliteral;   // usually '\\'
	//str is at ;|
    *s++ = 0;       //cut line before the cmt
    //advance str up to the string
    while ( *s != stringchar ) s++;
    //search for the string ending, check string end too
    for (rest = s+1;
		*rest && ((*rest!=stringchar)||(*(rest-1)==litchar)); rest++) ;
	if ( !*rest )
	{	printf ("\n%-25.25s in line %d: unexpected end-of-string  in: %s",
				 actfile, actline,str);
	}
	rest++; 				//advance after "
	c = *rest; *rest='\0';	//terminate str for strdup()
	strcpy (str, s);
	*rest = c;				//restore rest
	if ( strstr (rest,firstmatch))
	{	printf ("\n%-25.25s in line %d: 2nd ;|MSGxx|; ignored in: %s",
				actfile, actline, rest);
	}
#ifdef DEBUG
    //printf ("\nsplitted to: %s;|MSGxx|;%s%s",line,str,rest);
#endif
}
/***************************************************
    translates the string and the comment number
				  0            0
    (test ;|MSG0|;"Teststring: ")
    ^             ^str          ^rest
    line
->  (test "translated: ")

	again very destructive!
***************************************************/
char *change_line (char *line, int i, char *str, char *rest)
{
    char tmp[MAXLINE];

	if ( reset ) i = 0;
    if ( keep )         //keep the comment, change the number
        sprintf (tmp, "%s%sMSG%d%s%s%s",line,cmtbeg,i,cmtend,str,rest);
    else
        sprintf (tmp, "%s%s%s",line,str,rest);
    if (i==0) nummsg0++;
    else      nummsg++;
    return strcpy (line, tmp);
}

//write all sync errors to actfile.ERR
void syncerror (int msgnum, int newnum, char *oldstr, char *newstr, int which)
{
	FILE *ferr;
	char *err;
	static char acterr[FILENAME_MAX] = "";	//actual error file

	err = filespecforceext (actfile, "ERR");
	if ( !err ) return;
	//delete the old .ERR file if not initialized (first time)
	if ( strcmp (err, acterr) != 0) 	// if not equal
	{	remove (err);
		strcpy (acterr, err);
	}

	if ( (ferr = fopen (err,"at")) != NULL)
	{   fprintf (ferr,"\nLine %d with MSG%d:",actline,msgnum);
		fprintf (ferr,"\n\tthe actual string is: %s",oldstr);
		switch ( which )
		{
		case SYNCERRADD:
			// a msg number greater than the in the list is defined
			if (keep)
				fprintf (ferr,"\n\tkeep the number     : MSG%d",msgnum);
			else
				fprintf (ferr,"\n\tthe new number would be: MSG%d",newnum);
			fprintf (ferr,"\n\tthe last valid msg number is %3d",msg_size);
			break;

		case SYNCERRUPD:
			// only with update, not with translation, diff. msg and string
			if (keep)
			{	fprintf (ferr,"\n\tthe proposed is     : %s",newstr);
				fprintf (ferr,"\n\tthe new number is   : MSG%d",msgnum);
			}
			else
				if (msgnum!=newnum)
				{	fprintf (ferr,"\n\tthe new number would be: MSG%d",newnum);
					fprintf (ferr,"\n\tbut without the /k option, the MSG number is not written.");
				}
				else
				{   fprintf (ferr,"\n\tbut the translation is: %s",newstr);
				}
			break;
		case SYNCERREND:
			//append a closing message to actfile.ERR
			if (update) fprintf (ferr,"\n\tnothing changed, don't use the /u option.");
			fprintf (ferr,"\n\n use /u/k to keep the strings and to change the numbers");
			fprintf (ferr,"\n or use just /k to keep the numbers and to change the strings.");
			fprintf (ferr,"\n\tif the actual string is okay, use: MSG /m%s /u/k %s .\\",msgfile,actfile);
			fprintf (ferr,"\n\tif the translation   is okay, use: MSG /m%s /k %s .\\",msgfile,actfile);
			break;
		}
		fclose (ferr);
	}
	else
		doserr (err);
	mem_free (err);
}

/*-----------------11.04.95 15:46-------------------
the actual program:
  parse the source file
  on a ;|MSG0|;"%s"
    look up the string in the msgtable
    if found change 0 to the index
    else create a new entry
  on a ;|MSGxx|;"%s"
    if a msgtable is defined
      translate the string to that of the index xx
  if keep don't destroy the comment
  if no msgfile is defined, don't convert MSG0

usage:
  create a new msgtable with:
    MSG -fnew.msg -k *.* .\
  translate to english:
    MSG -fenglish.msg -k *.* l:\english\
  delete the comment:
    MSG *.* l:\german\
--------------------------------------------------*/
int convert (char *infile)
{
    FILE *fin, *fout;
    char line[MAXLINE];
	char tmpstr[MAXLINE];
    char outfile[FILENAME_MAX];
    char *tmp, *str1;
    int i, j;
    list_t found;
	char *ext;

    //msg_size = 0;   // number of messages in the list
    //msg_creat = 0;  // number of new messages created
    //nummsg = 0;     // number of translated messages
    //nummsg0 = 0;    // number of translated 0 messages

	//assume LSP as default extension
	if ( !file_exists(infile))
	{	tmp =  filespecdefaultext (infile, "LSP");
		if (file_exists (tmp)) strcpy (infile, tmp);
		mem_free (tmp);
	}
	//on samedir: infile -> temp
	//if okay: ren infile *.bak
	//         ren temp infile
    if ( samedir )
	{	strcpy (outfile,TMPNAME);
		mktemp (TMPNAME);
	}
    else
	{	strcpy (outfile, outdir);
		strcat (outfile, filespecname (infile));        //external function
	}
    if ( stricmp (infile,outfile) == 0)
	{	strcpy (outfile,TMPNAME);
		mktemp (TMPNAME);
        samedir = TRUE;
    }
	if ( samedir )
    {   check_samedir();
	}
	/*	file_backup (infile, BACKUPMOVE);
		strcpy (actfile, infile);
		tmp = filespecbackup (infile);
		strcpy (infile, tmp);
		mem_free (tmp);
	*/
	strcpy (actfile, infile);
    fin = fopen (infile, "rt");
    if (!fin)
    {   if (!quiet) printf ("\nError: couldn't open file %s ", infile);
		ferr = 1;
        return FALSE;
    }
	DEBUG_PRINT ("\ninput file: %s", infile);

	language_type (infile);

	if ( file_exists (outfile) )				//external function
		file_backup (outfile, BACKUPMOVE);
    fout = fopen (outfile, "wt");
    if (!fout)
    {   if (!quiet) printf ("\nError: couldn't open output file %s ", outfile);
		ferr = 1;
        return FALSE;
    }
	DEBUG_PRINT ("\noutput file: %s", outfile);
	if (!quiet) printf ("\r%s    \r",infile);	//print for each file
	actline = 0;
	/*
	DEBUG_PRINT ("\nfirstmatch: %s", firstmatch);
	DEBUG_PRINT ("\nnullmatch : %s", nullmatch);
	DEBUG_PRINT ("\nnormmatch : %s", normmatch);
	*/

    while ( fgets(line, MAXLINE, fin) != NULL)
	{	actline++;
        if ( (msg=strstr (line, firstmatch)) != NULL)
		{	strcpy (tmpstr, msg);
			split_line (line, msg);	//sets str
			//DEBUG_PRINT ("\nwith cmt: \"%10s\"",tmpstr);
			//DEBUG_PRINT ("\nonly the str: \"%s\"",str);
            if (reset || !msgtable || msgign || (strncmp (tmpstr, nullmatch, 8) == 0))
			{   //search for the string start
                if ((i = list_searchstr (msg_strings,str)) == 0)
                {	add_msg (str);  //not found, add to msgfile
                    change_line (line,msg_size,str,rest); //if keep, add number
                }
                else  //translate, if keep change number
				{	DEBUG_PRINT2 ("\n%-45.45s        in list at pos: %d",str,i);
                    change_line (line,i,str,rest);
                }
            }
            else
            {	// search the number in the msg_strings
                sscanf ( tmpstr, normmatch, &i);
                if ( i > msg_size)
				{	//check for sync error,
					//MSGxx says not in list, but look for it anyway
                	if ((j = list_searchstr (msg_strings,str)) != 0)
					{   found = list_nth (msg_strings,j-1);
				        strcpy (tmpstr, (char *)list_ptr( found ));
						syncerr++;
						if ( warning )
						{   //print warning anyway
							printf ("\nsynchronization error in file %s in line %d with MSG%d",actfile,actline,i);
							printf ("\n\tthe actual string is: %s",str);
								if (keep)
							printf ("\n\tkeep the number     : MSG%d",j);
								else
							printf ("\n\tthe new number would be: MSG%d",j);
							printf ("\n\tthe %3d-th translation is not valid",i);
						}
						syncerror (i,j,str,NULL,SYNCERRADD);
					}
                	add_msg (str);  //not found, add to msgfile
                    change_line (line,msg_size,str,rest); //if keep, add number
                }
				else
				{	if ((found=list_nth (msg_strings,i-1)) != NULL)
					{   // found the number in the msgfile, check for sync error
                    	str1 = (char *)mem_strdup ((char *)list_ptr( found ));
						if ( strcmp (str1,str) != 0 )
						{
                			j = list_searchstr (msg_strings,str);
							if ( j==0 )
							{	//not found, new string, add_msg
								if ( update )
								{	add_msg (str);  //not found, add to msgfile
									j=msg_size;
								}
							}
							else
							{	if (update)
								{	syncerr++; //or just a translation
									printf ("\nsynchronization error:");
									syncerror (i,j,str,str1,SYNCERRUPD);
								}
							}
							if ( debug || warning )
							{
		//print warning only on /w or /d
		printf ("\ntranslation in file %s in line %d with MSG%d",actfile,actline,i);
		printf ("\n\tthe actual string is: %s",str);
		if (update)
		{	if (keep)
			{   printf ("\n\tthe new number is: MSG%d",j);
			}
			else
			{   if (i!=j)
				{	printf ("\n\tthe new number would be: MSG%d",j);
					printf ("\n\tbut without the /k option, the MSG number is not written.");
				}
				else
				{	printf ("\n\tbut the translation is: %s",str1);
				}
			}
		}
		else
			printf ("\n\tis translated to    : %s",str1);
		                    }
							//change the number
							if (update)
								i=j;
							//or translate the string
							else
								translated++;
		                }
						if ( update )
						{	//don't change the string, but the number (if keep)
							change_line (line,i,str,rest);
						}
                    	else
						{	//get the string from the list, keep the number
							change_line (line,i,str1,rest);
						}
						mem_free (str1);
					}
					else
					{	// the str was not found in the msgfile
						exitcode = EXIT_TRANSERR;
						if ( debug || warning )
						{
		printf ("\nwrong MSG%d in file %s in line %d",i,actfile,actline);
		printf ("\n\tthe %d-th MSG couldn't be found",i);
		                }
						//not found, don't change the string
	                	add_msg (str);  //not found, add to msgfile
                    	change_line (line,i,str,rest);
					}
                }
            }
        }
        fputs (line, fout);
    }
	DEBUG_PRINT ("\nclose file: %s", infile);
    fclose (fin);
	DEBUG_PRINT ("\nclose file: %s", outfile);
    fclose (fout);
	//if okay: ren infile *.bak
	//         ren temp infile
    if (samedir)
	{   file_backup (infile, BACKUPMOVE);
		DEBUG_PRINT2 ("\nrenaming file %s to %s", outfile, infile);
        file_rename (outfile,infile);
    }
	if (syncerr) syncerror (0,0,"","",SYNCERREND);
}

void doit (char *arg)
{
/*
#ifdef __WATCOMC__
    DIR     *dirp;
    struct dirent *dir;
    FILE    *f=NULL;


// POSIX compliant directory handling: opendir(),readdir()

    DEBUG_PRINT ("\ntry it with: %s",arg);
    if ((dirp = opendir (arg) ))
 	{	for(;;)
 		{	dir = readdir ( dirp );
            if( dir == NULL ) break;
            if ( (dir->d_attr & 0x1E) !=0 ) continue;   //only normal files 0x21
                                                        // and read-only, archived
			numcopy++;
            DEBUG_PRINT2 ( "\n  doit(%d): %s ",numcopy,dir->d_name );
            convert (dir->d_name);
        }
        closedir ( dirp );
    }
#endif
*/
	unsigned nfiles = 0;
    char **files = NULL;
    unsigned u;

    DEBUG_PRINT ("\nnext infile argument: %s",arg);
	if ( *arg=='@' )
	{   list_t file_list, list;

		file_list = file_read (&arg[1]);
		if ( file_list ) {
	        char *fromspec;

			list = file_list;
			while (list) {
				fromspec = (char *)list_ptr (list);
	        	DEBUG_PRINT2 ( "\n  convert(%d): %s ",numcopy+1,fromspec );
	        	if (convert (fromspec))
					numcopy++;
				list = list_next (list);
			}
			list = file_list;
			while ( list )
			{   mem_free (list_ptr (list));
				list = list_next (list);
			}
			list_free (&file_list, FPNULL);
		}
		else
			DEBUG_PRINT ("\nlistfile %s is empty, one infile per line ", &arg[1]);
	}
	else
	{   file_directory(arg,0,&files,&nfiles);
		if ( nfiles>0 )
		{   for (u = 0; u < nfiles; u++)
		    {   char *fromspec;

		        fromspec = files[u];
				assert (fromspec);
		        DEBUG_PRINT2 ( "\n  convert(%d): %s ",numcopy+1,fromspec );
		        if (convert (fromspec))
					numcopy++;
		    }
		}
		else
		{   DEBUG_PRINT2 ( "\n  convert(%d): %s ",numcopy+1,arg );
			if (convert (arg))
				numcopy++;
		}
    	file_directory(NULL,0,&files,&nfiles);
	}
}


int main (int argc, char *argv[])
{
	int nextarg = 1,j,k,l;
	list_t list;

    checkexe(argv[0]);						//external function

	mem_init();                             //external function
	list_init();							//external function
	lang = NULL;

    if (argc < 2)
	{   if (!quiet) puts ("arguments missing");
        showhelp(0);
    }
  	nextarg = getopts(argc,argv,opttable);	//external function
	//debug = TRUE;
    //check params for getopts() error
    for ( l=0; l<argc; l++ )
	{   if ( argv[l][0]=='-') argv[l][0]='/';
        DEBUG_PRINT2 ("\nargv[%d]: \"%s\"",l,argv[l]);
    }
	if ( reset )
	{   *msgfile = NULL;
		msgtable = FALSE;
		keep = TRUE;
	}
	if ( quiet )
	{	warning = FALSE;
	}
    if ( strcmp(msgfile,"") != 0)
		msgtable = TRUE;
	else
		*msgfile = NULL;
    DEBUG_PRINT ("\nmsgfile : \"%s\"",msgfile);
	if ( *syncfile && !msgtable )
	{	if (!quiet) printf ("\nsyncfile %s without /mmsgfile ignored ", syncfile);
	}
	if ( *syncfile )
    {	DEBUG_PRINT ("\nsyncfile: \"%s\"",syncfile);
	}
	if ( *funcfile )
    {	DEBUG_PRINT ("\nfuncfile: \"%s\"",funcfile);
	}
	if ( update && !keep )
    {	DEBUG_PRINT ("\nupdate without keep will not change the MSG numbers!",NULL);
    	DEBUG_PRINT ("\nmessage numbers will not be changed ",NULL);
	}
	if ( *language )
	{   strupr (language);
	    DEBUG_PRINT ("\nlanguage: %s",language);
	}
	else
	{	DEBUG_PRINT ("\nlanguage: get from extension",NULL);
	}
    DEBUG_PRINT ("\nkeep    : %s",(keep == TRUE) ? "TRUE" : "FALSE");
    DEBUG_PRINT ("\nreset   : %s",(reset == TRUE) ? "TRUE" : "FALSE");
    DEBUG_PRINT ("\nupdate  : %s",(update == TRUE) ? "TRUE" : "FALSE");
    DEBUG_PRINT ("\nwarning : %s",(warning == TRUE) ? "TRUE" : "FALSE");
    DEBUG_PRINT ("\nignore  : %s",(msgign == TRUE) ? "TRUE" : "FALSE");
    DEBUG_PRINT ("\ndont add: %s",(msgdontadd == TRUE) ? "TRUE" : "FALSE");
    DEBUG_PRINT ("\nDEBUG   : %s",(debug == TRUE) ? "TRUE" : "FALSE");
    DEBUG_PRINT ("\nhelp    : %s",(help == TRUE) ? "TRUE" : "FALSE");
	DEBUG_PRINT2 ("\nnext argument %d: %s",nextarg,argv[nextarg]);
	DEBUG_PRINT ("\n",NULL);
	if ( debug && msgfile && ( argc <= nextarg ))
	{	read_msgfile (msgfile,1);
        showhelp(0);
	}
	if ( help )
		showhelp(1);
	if ( msgign && !msgtable )
	{	if (!quiet) puts ("\ncannot use the /i option without any msgfile");
        usage();
	}
	if ( update && (argc-1 == nextarg))
	{	strcpy (outdir, ".\\");
		argc++;
	}
	else
	{   if ( argc < nextarg +1 )  //infile + outdir
	    {   if (!quiet) puts ("\nsome arguments missing: what files?");
	        showhelp(0);
	    }
	    strcpy (outdir, argv[argc-1]);  //win\ the last arg
	    l = strlen (outdir);
	    if (outdir[l-1] == '/')
			outdir[l-1]='\\';
	    if (outdir[l-1] != '\\')
		{   if ( file_exists (strcat (outdir,"\\NUL"))) {
				outdir[l]='\\';
				outdir[l+1]='\0';
			}
			else
			{
	        	if (!quiet) puts ("\nlast argument is no directory");
	        	usage();
			}
	    }
	}
    DEBUG_PRINT ("\noutdir  : %s",outdir);
    if ( stricmp(outdir,".\\") == 0)  samedir = TRUE;
    DEBUG_PRINT ("\nmsgtable: %s",(msgtable != FALSE) ? "TRUE" : "FALSE");
    DEBUG_PRINT ("\nsamedir : %s",(samedir  != FALSE) ? "TRUE" : "FALSE");
    DEBUG_PRINT ("\n", NULL);


	if ( reset && !msgtable && !quiet )
	{   printf ("\nWarning! MSG numbers reset but no MSG file defined");
		printf ("\n\tThe MSG files, if existing, become unsynchronized!");
		printf ("\n\tThey should be deleted or rebuild with the /m option");
	}
	if ( reset && msgtable && !msgdontadd)
    {	if (!quiet) printf ("\nmsgfile: %s  deleted",msgfile);
		file_backup (msgfile,BACKUPMOVE);   //external function
		//remove (msgfile);
	}
	check_samedir();
	//check and create outdir
	strcpy (tmpstr,outdir);
	if ( !file_exists (strcat (tmpstr,"NUL")))  //external function
	{   l = strlen (outdir);
	    outdir[l-1] = '\0';
		if (mkdir (outdir))
			doserr (outdir);
	    outdir[l-1] = '\\';
    	DEBUG_PRINT ("\noutdir %s created",outdir);
	}

    if ( !reset && msgtable) // fill the msg_strings
    {   read_msgfile (msgfile,0);
    }

    DEBUG_PRINT ("\n", NULL);
    for ( ; nextarg < argc-1; nextarg++ )
    {   doit (argv[nextarg]);
    }

    if (!quiet)
	{   printf ("\n  %3d File(s) translated.",numcopy);
	    printf ("\n  %3d messages at all, %3d empty messages",nummsg+nummsg0,nummsg0);
		if (msgtable)
    	{	if ( msgdontadd )
				printf ("\n  %3d messages would be added,  %3d messages in msgfile",msg_creat,msg_size);
			else
				printf ("\n  %3d messages added,  %3d messages in msgfile",msg_creat,msg_size);
		}
	    else
			printf ("\n  %3d different messages",msg_size); //created = size
	    if (translated)
		{	printf ("\n  %3d strings translated",translated);
			if (!exitcode && !trans) exitcode = EXIT_TRANS;
		}
		if (syncerr)
		{	printf ("\n  %3d synchronization errors detected",syncerr);
			printf ("\n examine the .ERR files");
			if (update)
				printf ("\n\tnothing changed, don't use the /u option.");
			printf ("\n use /u/k to keep the strings and to change the numbers");
			printf ("\n or use just /k to keep the numbers and to change the strings.");
			printf ("\n if the actual strings are okay: MSG /m%s /u/k %s .\\",msgfile,actfile);
			printf ("\n if the translation is okay    : MSG /m%s /k %s .\\",msgfile,actfile);
			exitcode = EXIT_SYNCERR;
		}
	}
	if ( ferr && !numcopy )
		doserr ("");
	if (!quiet) printf ("\n");
	if ( msgtable && !samedir)
	{	//copy the msgfile to the outdir
		strcpy (tmpstr, outdir);
		strcat (tmpstr, filespecname (msgfile));
		//DEBUG_PRINT2 ("\ncompare %s to %s",msgfile, tmpstr);
		//if ( file_same (tmpstr, msgfile))
		//{  if (!msgdontadd)
		//		file_backup (tmpstr, BACKUPCOPY);
		//}
		//else
		//{
			DEBUG_PRINT2 ("\ncopy msgfile %s to %s",msgfile, tmpstr);
			if (file_copy (msgfile, tmpstr))    //external function
				doserr (msgfile);
		//}
	}
	terminate();
    return exitcode;
}

