1/* Message list test for equality.
2   Copyright (C) 2001-2002, 2005-2006 Free Software Foundation, Inc.
3   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2, or (at your option)
8   any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software Foundation,
17   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19
20#ifdef HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24/* Specification.  */
25#include "msgl-equal.h"
26
27#include <stddef.h>
28#include <string.h>
29
30
31static inline bool
32msgstr_equal (const char *msgstr1, size_t msgstr1_len,
33	      const char *msgstr2, size_t msgstr2_len)
34{
35  return (msgstr1_len == msgstr2_len
36	  && memcmp (msgstr1, msgstr2, msgstr1_len) == 0);
37}
38
39static bool
40msgstr_equal_ignoring_potcdate (const char *msgstr1, size_t msgstr1_len,
41				const char *msgstr2, size_t msgstr2_len)
42{
43  const char *msgstr1_end = msgstr1 + msgstr1_len;
44  const char *msgstr2_end = msgstr2 + msgstr2_len;
45  const char *ptr1;
46  const char *ptr2;
47  const char *const field = "POT-Creation-Date:";
48  const ptrdiff_t fieldlen = sizeof ("POT-Creation-Date:") - 1;
49
50  /* Search for the occurrence of field in msgstr1.  */
51  for (ptr1 = msgstr1;;)
52    {
53      if (msgstr1_end - ptr1 < fieldlen)
54	{
55	  ptr1 = NULL;
56	  break;
57	}
58      if (memcmp (ptr1, field, fieldlen) == 0)
59	break;
60      ptr1 = memchr (ptr1, '\n', msgstr1_end - ptr1);
61      if (ptr1 == NULL)
62	break;
63      ptr1++;
64    }
65
66  /* Search for the occurrence of field in msgstr2.  */
67  for (ptr2 = msgstr2;;)
68    {
69      if (msgstr2_end - ptr2 < fieldlen)
70	{
71	  ptr2 = NULL;
72	  break;
73	}
74      if (memcmp (ptr2, field, fieldlen) == 0)
75	break;
76      ptr2 = memchr (ptr2, '\n', msgstr2_end - ptr2);
77      if (ptr2 == NULL)
78	break;
79      ptr2++;
80    }
81
82  if (ptr1 == NULL)
83    {
84      if (ptr2 == NULL)
85	return msgstr_equal (msgstr1, msgstr1_len, msgstr2, msgstr2_len);
86    }
87  else
88    {
89      if (ptr2 != NULL)
90	{
91	  /* Compare, ignoring the lines starting at ptr1 and ptr2.  */
92	  if (msgstr_equal (msgstr1, ptr1 - msgstr1, msgstr2, ptr2 - msgstr2))
93	    {
94	      ptr1 = memchr (ptr1, '\n', msgstr1_end - ptr1);
95	      if (ptr1 == NULL)
96		ptr1 = msgstr1_end;
97
98	      ptr2 = memchr (ptr2, '\n', msgstr2_end - ptr2);
99	      if (ptr2 == NULL)
100		ptr2 = msgstr2_end;
101
102	      return msgstr_equal (ptr1, msgstr1_end - ptr1,
103				   ptr2, msgstr2_end - ptr2);
104	    }
105	}
106    }
107  return false;
108}
109
110static inline bool
111pos_equal (const lex_pos_ty *pos1, const lex_pos_ty *pos2)
112{
113  return ((pos1->file_name == pos2->file_name
114	   || strcmp (pos1->file_name, pos2->file_name) == 0)
115	  && pos1->line_number == pos2->line_number);
116}
117
118bool
119string_list_equal (const string_list_ty *slp1, const string_list_ty *slp2)
120{
121  size_t i, i1, i2;
122
123  i1 = (slp1 != NULL ? slp1->nitems : 0);
124  i2 = (slp2 != NULL ? slp2->nitems : 0);
125  if (i1 != i2)
126    return false;
127  for (i = 0; i < i1; i++)
128    if (strcmp (slp1->item[i], slp2->item[i]) != 0)
129      return false;
130  return true;
131}
132
133bool
134message_equal (const message_ty *mp1, const message_ty *mp2,
135	       bool ignore_potcdate)
136{
137  size_t i, i1, i2;
138
139  if (!(mp1->msgctxt != NULL
140	? mp2->msgctxt != NULL && strcmp (mp1->msgctxt, mp2->msgctxt) == 0
141	: mp2->msgctxt == NULL))
142    return false;
143
144  if (strcmp (mp1->msgid, mp2->msgid) != 0)
145    return false;
146
147  if (!(mp1->msgid_plural != NULL
148	? mp2->msgid_plural != NULL
149	  && strcmp (mp1->msgid_plural, mp2->msgid_plural) == 0
150	: mp2->msgid_plural == NULL))
151    return false;
152
153  if (is_header (mp1) && ignore_potcdate
154      ? !msgstr_equal_ignoring_potcdate (mp1->msgstr, mp1->msgstr_len,
155					 mp2->msgstr, mp2->msgstr_len)
156      : !msgstr_equal (mp1->msgstr, mp1->msgstr_len,
157		       mp2->msgstr, mp2->msgstr_len))
158    return false;
159
160  if (!pos_equal (&mp1->pos, &mp2->pos))
161    return false;
162
163  if (!string_list_equal (mp1->comment, mp2->comment))
164    return false;
165
166  if (!string_list_equal (mp1->comment_dot, mp2->comment_dot))
167    return false;
168
169  i1 = mp1->filepos_count;
170  i2 = mp2->filepos_count;
171  if (i1 != i2)
172    return false;
173  for (i = 0; i < i1; i++)
174    if (!pos_equal (&mp1->filepos[i], &mp2->filepos[i]))
175      return false;
176
177  if (mp1->is_fuzzy != mp2->is_fuzzy)
178    return false;
179
180  for (i = 0; i < NFORMATS; i++)
181    if (mp1->is_format[i] != mp2->is_format[i])
182      return false;
183
184  if (!(mp1->prev_msgctxt != NULL
185	? mp2->prev_msgctxt != NULL
186	  && strcmp (mp1->prev_msgctxt, mp2->prev_msgctxt) == 0
187	: mp2->prev_msgctxt == NULL))
188    return false;
189
190  if (!(mp1->prev_msgid != NULL
191	? mp2->prev_msgid != NULL
192	  && strcmp (mp1->prev_msgid, mp2->prev_msgid) == 0
193	: mp2->prev_msgid == NULL))
194    return false;
195
196  if (!(mp1->prev_msgid_plural != NULL
197	? mp2->prev_msgid_plural != NULL
198	  && strcmp (mp1->prev_msgid_plural, mp2->prev_msgid_plural) == 0
199	: mp2->prev_msgid_plural == NULL))
200    return false;
201
202  if (mp1->obsolete != mp2->obsolete)
203    return false;
204
205  return true;
206}
207
208bool
209message_list_equal (const message_list_ty *mlp1, const message_list_ty *mlp2,
210		    bool ignore_potcdate)
211{
212  size_t i, i1, i2;
213
214  i1 = mlp1->nitems;
215  i2 = mlp2->nitems;
216  if (i1 != i2)
217    return false;
218  for (i = 0; i < i1; i++)
219    if (!message_equal (mlp1->item[i], mlp2->item[i], ignore_potcdate))
220      return false;
221  return true;
222}
223
224static inline bool
225msgdomain_equal (const msgdomain_ty *mdp1, const msgdomain_ty *mdp2,
226		 bool ignore_potcdate)
227{
228  return (strcmp (mdp1->domain, mdp2->domain) == 0
229	  && message_list_equal (mdp1->messages, mdp2->messages,
230				 ignore_potcdate));
231}
232
233bool
234msgdomain_list_equal (const msgdomain_list_ty *mdlp1,
235		      const msgdomain_list_ty *mdlp2,
236		      bool ignore_potcdate)
237{
238  size_t i, i1, i2;
239
240  i1 = mdlp1->nitems;
241  i2 = mdlp2->nitems;
242  if (i1 != i2)
243    return false;
244  for (i = 0; i < i1; i++)
245    if (!msgdomain_equal (mdlp1->item[i], mdlp2->item[i], ignore_potcdate))
246      return false;
247  return true;
248}
249