1 /* Message list header manipulation.
2 Copyright (C) 2007, 2016-2017 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2007.
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 3 of the License, or
8 (at your option) 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, see <https://www.gnu.org/licenses/>. */
17
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 /* Specification. */
24 #include "msgl-header.h"
25
26 #include <string.h>
27
28 #include "xalloc.h"
29
30 #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
31
32
33 /* The known fields in their usual order. */
34 static const struct
35 {
36 const char *name;
37 size_t len;
38 }
39 known_fields[] =
40 {
41 { "Project-Id-Version:", sizeof ("Project-Id-Version:") - 1 },
42 { "Report-Msgid-Bugs-To:", sizeof ("Report-Msgid-Bugs-To:") - 1 },
43 { "POT-Creation-Date:", sizeof ("POT-Creation-Date:") - 1 },
44 { "PO-Revision-Date:", sizeof ("PO-Revision-Date:") - 1 },
45 { "Last-Translator:", sizeof ("Last-Translator:") - 1 },
46 { "Language-Team:", sizeof ("Language-Team:") - 1 },
47 { "Language:", sizeof ("Language:") - 1 },
48 { "MIME-Version:", sizeof ("MIME-Version:") - 1 },
49 { "Content-Type:", sizeof ("Content-Type:") - 1 },
50 { "Content-Transfer-Encoding:", sizeof ("Content-Transfer-Encoding:") - 1 }
51 };
52
53
54 void
msgdomain_list_set_header_field(msgdomain_list_ty * mdlp,const char * field,const char * value)55 msgdomain_list_set_header_field (msgdomain_list_ty *mdlp,
56 const char *field, const char *value)
57 {
58 size_t field_len;
59 int field_index;
60 size_t k, i;
61
62 field_len = strlen (field);
63
64 /* Search the field in known_fields[]. */
65 field_index = -1;
66 for (k = 0; k < SIZEOF (known_fields); k++)
67 if (strcmp (known_fields[k].name, field) == 0)
68 {
69 field_index = k;
70 break;
71 }
72
73 for (i = 0; i < mdlp->nitems; i++)
74 {
75 message_list_ty *mlp = mdlp->item[i]->messages;
76 size_t j;
77
78 /* Search the header entry. */
79 for (j = 0; j < mlp->nitems; j++)
80 if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
81 {
82 message_ty *mp = mlp->item[j];
83
84 /* Modify the header entry. */
85 const char *header = mp->msgstr;
86 char *new_header =
87 XNMALLOC (strlen (header) + 1
88 + strlen (field) + 1 + strlen (value) + 1 + 1,
89 char);
90
91 /* Test whether the field already occurs in the header entry. */
92 const char *h;
93
94 for (h = header; *h != '\0'; )
95 {
96 if (strncmp (h, field, field_len) == 0)
97 break;
98 h = strchr (h, '\n');
99 if (h == NULL)
100 break;
101 h++;
102 }
103 if (h != NULL && *h != '\0')
104 {
105 /* Replace the field. */
106 char *p = new_header;
107 memcpy (p, header, h - header);
108 p += h - header;
109 p = stpcpy (p, field);
110 p = stpcpy (stpcpy (stpcpy (p, " "), value), "\n");
111 h = strchr (h, '\n');
112 if (h != NULL)
113 {
114 h++;
115 stpcpy (p, h);
116 }
117 }
118 else if (field_index < 0)
119 {
120 /* An unknown field. Append it at the end. */
121 char *p = new_header;
122 p = stpcpy (p, header);
123 if (p > new_header && p[-1] != '\n')
124 *p++ = '\n';
125 p = stpcpy (p, field);
126 stpcpy (stpcpy (stpcpy (p, " "), value), "\n");
127 }
128 else
129 {
130 /* Find the appropriate position for inserting the field. */
131 for (h = header; *h != '\0'; )
132 {
133 /* Test whether h starts with a field name whose index is
134 > field_index. */
135 for (k = field_index + 1; k < SIZEOF (known_fields); k++)
136 if (strncmp (h, known_fields[k].name, known_fields[k].len)
137 == 0)
138 break;
139 if (k < SIZEOF (known_fields))
140 break;
141 h = strchr (h, '\n');
142 if (h == NULL)
143 break;
144 h++;
145 }
146 if (h != NULL && *h != '\0')
147 {
148 /* Insert the field at position h. */
149 char *p = new_header;
150 memcpy (p, header, h - header);
151 p += h - header;
152 p = stpcpy (p, field);
153 p = stpcpy (stpcpy (stpcpy (p, " "), value), "\n");
154 stpcpy (p, h);
155 }
156 else
157 {
158 /* Append it at the end. */
159 char *p = new_header;
160 p = stpcpy (p, header);
161 if (p > new_header && p[-1] != '\n')
162 *p++ = '\n';
163 p = stpcpy (p, field);
164 stpcpy (stpcpy (stpcpy (p, " "), value), "\n");
165 }
166 }
167
168 mp->msgstr = new_header;
169 mp->msgstr_len = strlen (new_header) + 1;
170 }
171 }
172 }
173
174
175 void
message_list_delete_header_field(message_list_ty * mlp,const char * field)176 message_list_delete_header_field (message_list_ty *mlp,
177 const char *field)
178 {
179 size_t field_len = strlen (field);
180 size_t j;
181
182 /* Search the header entry. */
183 for (j = 0; j < mlp->nitems; j++)
184 if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
185 {
186 message_ty *mp = mlp->item[j];
187
188 /* Modify the header entry. */
189 const char *header = mp->msgstr;
190
191 /* Test whether the field occurs in the header entry. */
192 const char *h;
193
194 for (h = header; *h != '\0'; )
195 {
196 if (strncmp (h, field, field_len) == 0)
197 break;
198 h = strchr (h, '\n');
199 if (h == NULL)
200 break;
201 h++;
202 }
203 if (h != NULL && *h != '\0')
204 {
205 /* Delete the field. */
206 char *new_header = XCALLOC (strlen (header) + 1, char);
207
208 char *p = new_header;
209 memcpy (p, header, h - header);
210 p += h - header;
211 h = strchr (h, '\n');
212 if (h != NULL)
213 {
214 h++;
215 strcpy (p, h);
216 }
217 else
218 *p = '\0';
219
220 mp->msgstr = new_header;
221 mp->msgstr_len = strlen (new_header) + 1;
222 }
223 }
224 }
225