error_at_line (0, 0, fname, start_line, gettext ("duplicated message identifier")); error_at_line (0, 0, runp->fname, runp->line, gettext ("this is the first definition"));
if (any_space) {  error_at_line (0, 0, fname, start_line, gettext ("\ duplicated message identifier")); error_at_line (0, 0, runp->fname, runp->line, gettext ("this is the first definition")); } else {  if (lastp != NULL) lastp->next = runp->next; else current->current_set->messages = runp->next; free (runp); }
read_input_file (struct catalog *current, const char *fname){  FILE *fp;  char *buf;  size_t len;  size_t line_number;  if (strcmp (fname, "-") == 0 || strcmp (fname, "/dev/stdin") == 0)    {      fp = stdin;      fname = gettext ("*standard input*");    }  else    fp = fopen (fname, "r");  if (fp == NULL)    {      error (0, errno, gettext ("cannot open input file `%s'"), fname);      return current;    }  /* If we haven't seen anything yet, allocate result structure.  */  if (current == NULL)    {      current = (struct catalog *) xcalloc (1, sizeof (*current));#define obstack_chunk_alloc malloc#define obstack_chunk_free free      obstack_init (&current->mem_pool);      current->current_set = find_set (current, NL_SETD);    }  buf = NULL;  len = 0;  line_number = 0;  while (!feof (fp))    {      int continued;      int used;      size_t start_line = line_number + 1;      char *this_line;      do	{	  int act_len;	  act_len = getline (&buf, &len, fp);	  if (act_len <= 0)	    break;	  ++line_number;	  /* It the line continued?  */	  if (buf[act_len - 1] == '\n')	    {	      --act_len;	      continued = buf[act_len - 1] == '\\';	      if (continued)		--act_len;	    }	  else	    continued = 0;	  /* Append to currently selected line.  */	  obstack_grow (&current->mem_pool, buf, act_len);	}      while (continued);      obstack_1grow (&current->mem_pool, '\0');      this_line = (char *) obstack_finish (&current->mem_pool);      used = 0;      if (this_line[0] == '$')	{	  if (isspace (this_line[1]))	    /* This is a comment line.  Do nothing.  */;	  else if (strncmp (&this_line[1], "set", 3) == 0)	    {	      int cnt = sizeof ("set");	      int set_number;	      const char *symbol = NULL;	      while (isspace (this_line[cnt]))		++cnt;	      if (isdigit (this_line[cnt]))		{		  set_number = atol (&this_line[cnt]);		  /* If the given number for the character set is		     higher than any we used for symbolic set names		     avoid clashing by using only higher numbers for		     the following symbolic definitions.  */		  if (set_number > current->last_set)		    current->last_set = set_number;		}	      else		{		  /* See whether it is a reasonable identifier.  */		  int start = cnt;		  while (isalnum (this_line[cnt]) || this_line[cnt] == '_')		    ++cnt;		  if (cnt == start)		    {		      /* No correct character found.  */		      error_at_line (0, 0, fname, start_line,				     gettext ("illegal set number"));		      set_number = 0;		    }		  else		    {		      /* We have found seomthing that looks like a			 correct identifier.  */		      struct set_list *runp;		      this_line[cnt] = '\0';		      used = 1;		      symbol = &this_line[start];		      /* Test whether the identifier was already used.  */		      runp = current->all_sets;		      while (runp != 0)			if (runp->symbol != NULL			    && strcmp (runp->symbol, symbol) == 0)			  break;			else			  runp = runp->next;		      if (runp != NULL)			{			  /* We cannot allow duplicate identifiers for			     message sets.  */			  error_at_line (0, 0, fname, start_line,					 gettext ("duplicate set definition"));			  error_at_line (0, 0, runp->fname, runp->line,					 gettext ("\this is the first definition"));			  set_number = 0;			}		      else			/* Allocate next free message set for identifier.  */			set_number = ++current->last_set;		    }		}	      if (set_number != 0)		{		  /* We found a legal set number.  */		  current->current_set = find_set (current, set_number);		  if (symbol != NULL)		      used = 1;		  current->current_set->symbol = symbol;		  current->current_set->fname = fname;		  current->current_set->line = start_line;		}	    }	  else if (strncmp (&this_line[1], "delset", 6) == 0)	    {	      int cnt = sizeof ("delset");	      size_t set_number;	      while (isspace (this_line[cnt]))		++cnt;	      if (isdigit (this_line[cnt]))		{		  size_t set_number = atol (&this_line[cnt]);		  struct set_list *set;		  /* Mark the message set with the given number as		     deleted.  */		  set = find_set (current, set_number);		  set->deleted = 1;		}	      else		{		  /* See whether it is a reasonable identifier.  */		  int start = cnt;		  while (isalnum (this_line[cnt]) || this_line[cnt] == '_')		    ++cnt;		  if (cnt == start)		    {		      error_at_line (0, 0, fname, start_line,				     gettext ("illegal set number"));		      set_number = 0;		    }		  else		    {		      const char *symbol;		      struct set_list *runp;		      this_line[cnt] = '\0';		      used = 1;		      symbol = &this_line[start];		      /* We have a symbolic set name.  This name must			 appear somewhere else in the catalogs read so			 far.  */		      set_number = 0;		      for (runp = current->all_sets; runp != NULL;			   runp = runp->next)			{			  if (strcmp (runp->symbol, symbol) == 0)			    {			      runp->deleted = 1;			      break;			    }			}		      if (runp == NULL)			/* Name does not exist before.  */			error_at_line (0, 0, fname, start_line,				       gettext ("unknown set `%s'"), symbol);		    }		}	    }	  else if (strncmp (&this_line[1], "quote", 5) == 0)	    {	      int cnt = sizeof ("quote");	      while (isspace (this_line[cnt]))		++cnt;	      /* Yes, the quote char can be '\0'; this means no quote		 char.  */	      current->quote_char = this_line[cnt];	    }	  else	    {	      int cnt;	      cnt = 2;	      while (this_line[cnt] != '\0' && !isspace (this_line[cnt]))		++cnt;	      this_line[cnt] = '\0';	      error_at_line (0, 0, fname, start_line,			     gettext ("unknown directive `%s': line ignored"),			     &this_line[1]);	    }	}      else if (isalnum (this_line[0]) || this_line[0] == '_')	{	  const char *ident = this_line;	  int message_number;	  do	    ++this_line;	  while (this_line[0] != '\0' && !isspace (this_line[0]));;	  this_line[0] = '\0';	/* Terminate the identifier.  */	  do	    ++this_line;	  while (isspace (this_line[0]));	  /* Now we found the beginning of the message itself.  */	  if (isdigit (ident[0]))	    {	      struct message_list *runp;	      message_number = atoi (ident);	      /* Find location to insert the new message.  */	      runp = current->current_set->messages;	      while (runp != NULL)		if (runp->number == message_number)		  break;		else		  runp = runp->next;	      if (runp != NULL)		{		  /* Oh, oh.  There is already a message with this		     number is the message set.  */		  error_at_line (0, 0, fname, start_line,				 gettext ("duplicated message number"));		  error_at_line (0, 0, runp->fname, runp->line,				 gettext ("this is the first definition"));		  message_number = 0;		}	      ident = NULL;	/* We don't have a symbol.  */	      if (message_number != 0		  && message_number > current->current_set->last_message)		current->current_set->last_message = message_number;	    }	  else if (ident[0] != '\0')	    {	      struct message_list *runp;	      runp = current->current_set->messages;	      /* Test whether the symbolic name was not used for		 another message in this message set.  */	      while (runp != NULL)		if (runp->symbol != NULL && strcmp (ident, runp->symbol) == 0)		  break;		else		  runp = runp->next;	      if (runp != NULL)		{		  /* The name is already used.  */		  error_at_line (0, 0, fname, start_line,				 gettext ("duplicated message identifier"));		  error_at_line (0, 0, runp->fname, runp->line,				 gettext ("this is the first definition"));		  message_number = 0;		}	      else		/* Give the message the next unused number.  */		message_number = ++current->current_set->last_message;	    }	  else	    message_number = 0;	  if (message_number != 0)	    {	      struct message_list *newp;	      used = 1;	/* Yes, we use the line.  */	      /* Strip quote characters, change escape sequences into		 correct characters etc.  */	      normalize_line (fname, start_line, this_line,			      current->quote_char);	      newp = (struct message_list *) xmalloc (sizeof (*newp));	      newp->number = message_number;	      newp->message = this_line;	      /* Remember symbolic name; is NULL if no is given.  */	      newp->symbol = ident;	      /* Remember where we found the character.  */	      newp->fname = fname;	      newp->line = start_line;	      /* Find place to insert to message.  We keep them in a		 sorted single linked list.  */	      if (current->current_set->messages == NULL		  || current->current_set->messages->number > message_number)		{		  newp->next = current->current_set->messages;		  current->current_set->messages = newp;		}	      else		{		  struct message_list *runp;		  runp = current->current_set->messages;		  while (runp->next != NULL)		    if (runp->next->number > message_number)		      break;		    else		      runp = runp->next;		  newp->next = runp->next;		  runp->next = newp;		}	    }	  ++current->total_messages;	}      else	{	  size_t cnt;	  cnt = 0;	  /* See whether we have any non-white space character in this	     line.  */	  while (this_line[cnt] != '\0' && isspace (this_line[cnt]))	    ++cnt;	  if (this_line[cnt] != '\0')	    /* Yes, some unknown characters found.  */	    error_at_line (0, 0, fname, start_line,			   gettext ("malformed line ignored"));	}      /* We can save the memory for the line if it was not used.  */      if (!used)	obstack_free (&current->mem_pool, this_line);    }  if (fp != stdin)    fclose (fp);  return current;}