1 /* changelogs.c:
2  *
3  ****************************************************************
4  * Copyright (C) 2003 Tom Lord
5  *
6  * See the file "COPYING" for further information about
7  * the copyright and warranty status of this work.
8  */
9 
10 
11 #include "hackerlab/bugs/panic.h"
12 #include "hackerlab/char/char-class.h"
13 #include "hackerlab/char/str.h"
14 #include "hackerlab/vu/safe.h"
15 #include "hackerlab/rx-posix/regex.h"
16 #include "tla/libarch/patch-logs.h"
17 #include "tla/libarch/inv-ids.h"
18 #include "tla/libarch/namespace.h"
19 #include "tla/libarch/changelogs.h"
20 
21 
22 
23 #define arch_changelog_id_re "^([ix]_automatic-ChangeLog--)(" arch_archive_re ")/(" arch_base_re ")(--" arch_base_re ")?(--" arch_vsn_re ")$"
24 
25 
26 /* __STDC__ prototypes for static functions */
27 static regex_t * changelog_id_pattern (void);
28 static void generate_changelog_entry (int out_fd, const t_uchar * archive, const t_uchar * version, const t_uchar * patch_lvl, const t_uchar * file, int no_files);
29 static void changelog_file_list (int out_fd, assoc_table headers, const t_uchar * header, const t_uchar * exclude);
30 static void changelog_file_pair_list (int out_fd, assoc_table headers, const t_uchar * header);
31 
32 
33 
34 int
arch_id_indicates_changelog(const t_uchar * id)35 arch_id_indicates_changelog (const t_uchar * id)
36 {
37   regex_t * pattern;
38 
39   pattern = changelog_id_pattern ();
40 
41   if (!regexec (pattern, id, 0, 0, 0))
42     return 1;
43   else
44     return 0;
45 }
46 
47 void
arch_parse_changelog_id(t_uchar ** archive,t_uchar ** version,const t_uchar * id)48 arch_parse_changelog_id (t_uchar ** archive, t_uchar ** version, const t_uchar * id)
49 {
50   regex_t * pattern;
51   regmatch_t pmatch[6];
52 
53   pattern = changelog_id_pattern ();
54 
55   invariant (!regexec (pattern, id, 6, pmatch, 0));
56   *archive = str_save_n (0, id + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so);
57   *version = str_save_n (0, id + pmatch[3].rm_so, pmatch[5].rm_eo - pmatch[3].rm_so);
58 }
59 
60 
61 void
arch_generate_changelog(int out_fd,const t_uchar * tree_root,int no_files,int untagged,const t_uchar * new_entry_patch_lvl,const t_uchar * new_entry_file,const t_uchar * archive,const t_uchar * version)62 arch_generate_changelog (int out_fd,
63                          const t_uchar * tree_root,
64                          int no_files,
65                          int untagged,
66                          const t_uchar * new_entry_patch_lvl,
67                          const t_uchar * new_entry_file,
68                          const t_uchar * archive,
69                          const t_uchar * version)
70 {
71   rel_table log_ls;
72   int lim;
73   int x;
74 
75   if (!arch_valid_package_name (version, arch_no_archive, arch_req_version, 0))
76     {
77       safe_printfmt (2, "changelog: invalid version name (%s)\n", version);
78       exit (1);
79     }
80 
81   log_ls = arch_logs (tree_root, archive, version, 0);
82   arch_sort_table_by_patch_level_field (1, log_ls, 0);
83 
84   {
85     t_uchar * tag_name;
86 
87     if (untagged)
88       tag_name = "non-id";
89     else if (arch_implicit_id_tagging == arch_tree_id_tagging_method (0, tree_root, 0))
90       tag_name = "tag";
91     else
92       tag_name = "arch-tag";
93 
94     safe_printfmt (out_fd, "# do not edit -- automatically generated by arch changelog\n# %s: automatic-ChangeLog--%s/%s\n#\n\n", tag_name, archive, version);
95   }
96 
97   if (new_entry_patch_lvl)
98     {
99       generate_changelog_entry (out_fd, archive, version, new_entry_patch_lvl, new_entry_file, no_files);
100     }
101 
102   lim = rel_n_records (log_ls);
103   for (x = 0; x < lim; ++x)
104     {
105       generate_changelog_entry (out_fd, archive, version, rel_peek_str (log_ls, x, 0), rel_peek_str (log_ls, x, 1), no_files);
106     }
107 
108   rel_free_table (log_ls);
109 }
110 
111 
112 
113 
114 
115 static regex_t *
changelog_id_pattern(void)116 changelog_id_pattern (void)
117 {
118   static int compiled = 0;
119   static regex_t pattern;
120 
121   if (!compiled)
122     {
123       if (regcomp (&pattern, arch_changelog_id_re, REG_EXTENDED))
124         panic ("unable to compile arch_changelog_id_re");
125       compiled = 1;
126     }
127 
128   return &pattern;
129 }
130 
131 
132 
133 static void
generate_changelog_entry(int out_fd,const t_uchar * archive,const t_uchar * version,const t_uchar * patch_lvl,const t_uchar * file,int no_files)134 generate_changelog_entry (int out_fd, const t_uchar * archive, const t_uchar * version, const t_uchar * patch_lvl, const t_uchar * file, int no_files)
135 {
136   int in_fd;
137   t_uchar * log = 0;
138   size_t len;
139   assoc_table headers = 0;
140   t_uchar * body = 0;
141 
142   in_fd = safe_open (file, O_RDONLY, 0);
143   safe_file_to_string (&log, &len, in_fd);
144   safe_close (in_fd);
145   log = lim_realloc (0, log, len + 1);
146   log[len] = 0;
147   headers = 0;
148   body = 0;
149   arch_parse_log (0, &headers, (const t_uchar **)&body, log);
150   lim_free (0, log);
151 
152   /* print the first line (date, creator, patch level)
153    */
154 
155   {
156     const t_uchar * raw_date;
157     t_uchar * cooked_date = 0;
158     const t_uchar * creator;
159 
160     raw_date = assoc_get_str_taking (headers, rel_make_field_str ("standard-date"));
161     if (raw_date)
162       {
163         const t_uchar * from;
164         const t_uchar * to;
165 
166         from = raw_date;
167         while (*from && char_is_space (*from))
168           ++from;
169 
170         to = from;
171         while (*to && !char_is_space (*to))
172           ++to;
173         while (*to && char_is_space (*to))
174           ++to;
175         while (*to && !char_is_space (*to))
176           ++to;
177 
178         cooked_date = str_save_n (0, from, to - from);
179         cooked_date = str_realloc_cat (0, cooked_date, " GMT");
180       }
181     else
182       {
183         cooked_date = str_save (0, "\?\?\?\?-\?\?-\?\? GMT");
184       }
185 
186     creator = assoc_get_str_taking (headers, rel_make_field_str ("creator"));
187     if (!creator)
188       creator = "????";
189 
190     safe_printfmt (out_fd, "%s\t%s\t%s\n", cooked_date, creator, patch_lvl);
191     lim_free (0, cooked_date);
192   }
193 
194   safe_printfmt (out_fd, "\n");
195 
196   /* Print the summary: */
197 
198   {
199     const t_uchar * summary;
200 
201     summary = assoc_get_str_taking (headers, rel_make_field_str ("summary"));
202     if (!summary)
203       summary = "";
204 
205     safe_printfmt (out_fd, "    Summary:\n      %s\n", summary);
206   }
207 
208   /* Print the revision name: */
209   {
210     const t_uchar * revision;
211 
212     revision = assoc_get_str_taking (headers, rel_make_field_str ("revision"));
213     if (!revision)
214       revision = "";
215 
216     safe_printfmt (out_fd, "    Revision:\n      %s\n", revision);
217   }
218 
219   safe_printfmt (out_fd, "\n");
220 
221   /* Print the revision body: */
222   {
223     t_uchar * eol;
224     t_uchar * pos;
225 
226     pos = body;
227     while (*pos)
228       {
229         eol = str_chr_index (pos, '\n');
230 
231         if (eol)
232           {
233             safe_printfmt (out_fd, "    %.*s\n", (int)(eol - pos), pos);
234             pos = eol + 1;
235           }
236         else
237           {
238             safe_printfmt (out_fd, "    %s\n", pos);
239             break;
240           }
241       }
242   }
243 
244   safe_printfmt (out_fd, "\n");
245 
246   /* file lists */
247 
248   if (!no_files)
249     {
250       t_uchar * revision = 0;
251       t_uchar * fqrev = 0;
252       t_uchar * excluded_patch_file = 0;
253 
254       revision = str_alloc_cat_many (0, version, "--", patch_lvl, str_end);
255       fqrev = arch_fully_qualify (archive, revision);
256 
257       excluded_patch_file = arch_log_file (".", archive, revision);
258 
259       changelog_file_list (out_fd, headers, "new-files", excluded_patch_file + 2);
260       changelog_file_list (out_fd, headers, "removed-files", 0);
261       changelog_file_list (out_fd, headers, "modified-files", 0);
262       changelog_file_pair_list (out_fd, headers, "renamed-files");
263       changelog_file_list (out_fd, headers, "new-directories", 0);
264       changelog_file_list (out_fd, headers, "removed-directories", 0);
265       changelog_file_list (out_fd, headers, "modified-directories", 0);
266       changelog_file_pair_list (out_fd, headers, "renamed-directories");
267       changelog_file_list (out_fd, headers, "new-patches", fqrev);
268 
269       lim_free (0, revision);
270       lim_free (0, fqrev);
271       lim_free (0, excluded_patch_file);
272     }
273 
274   safe_printfmt (out_fd, "\n");
275 
276   lim_free (0, body);
277   free_assoc_table (headers);
278 }
279 
280 static void
changelog_file_list(int out_fd,assoc_table headers,const t_uchar * header,const t_uchar * exclude)281 changelog_file_list (int out_fd, assoc_table headers, const t_uchar * header, const t_uchar * exclude)
282 {
283   const t_uchar * value;
284   int width;
285   int items_on_line;
286   int printed_header;
287 
288 
289   value = assoc_get_str_taking (headers, rel_make_field_str (header));
290   if (!value)
291     return;
292 
293   printed_header = 0;
294   width = 0;
295   items_on_line = 0;
296 
297   while (1)
298     {
299       const t_uchar * end;
300 
301       while (char_is_space (*value))
302         ++value;
303 
304       if (!*value)
305         break;
306 
307       end = value;
308       while (*end && !char_is_space (*end))
309         ++end;
310 
311       if (!exclude || str_cmp_n (exclude, str_length (exclude), value, end - value))
312         {
313           if (!printed_header)
314             {
315               t_uchar * fixed_header = 0;
316 
317               fixed_header = str_save (0, header);
318               {
319                 t_uchar * h;
320                 for (h = fixed_header; *h; ++h)
321                   if (*h == '-')
322                     *h = ' ';
323                 safe_printfmt (out_fd, "    %s:\n    ", fixed_header);
324               }
325               lim_free (0, fixed_header);
326               printed_header = 1;
327             }
328 
329 
330           if ((items_on_line == 0) || ((width + (1 + end - value)) < 64))
331             {
332               width += (end - value) + 1;
333               ++items_on_line;
334               safe_printfmt (out_fd, " %.*s", (int)(end - value), value);
335             }
336           else
337             {
338               safe_printfmt (out_fd, "\n     %.*s", (int)(end - value), value);
339               width = (end - value) + 1;
340               items_on_line = 1;
341             }
342         }
343 
344       value = end;
345     }
346 
347   if (printed_header)
348     safe_printfmt (out_fd, "\n\n");
349 }
350 
351 
352 static void
changelog_file_pair_list(int out_fd,assoc_table headers,const t_uchar * header)353 changelog_file_pair_list (int out_fd, assoc_table headers, const t_uchar * header)
354 {
355   const t_uchar * value;
356   int printed_header = 0;
357 
358   value = assoc_get_str_taking (headers, rel_make_field_str (header));
359   if (!value)
360     return;
361 
362   while (1)
363     {
364       const t_uchar * end_1;
365       const t_uchar * start_2;
366       const t_uchar * end_2;
367 
368       while (char_is_space (*value))
369         ++value;
370 
371       if (!*value)
372         break;
373 
374       end_1 = value;
375       while (*end_1 && !char_is_space (*end_1))
376         ++end_1;
377 
378       if (!*end_1)
379         panic ("corrupt log file");
380 
381       start_2 = end_1;
382 
383       while (char_is_space (*start_2))
384         ++start_2;
385 
386       if (!*start_2)
387         panic ("corrupt log file");
388 
389       end_2 = start_2;
390       while (*end_2 && !char_is_space (*end_2))
391         ++end_2;
392 
393       if (!printed_header)
394         {
395           t_uchar * fixed_header = 0;
396 
397           fixed_header = str_save (0, header);
398           {
399             t_uchar * h;
400             for (h = fixed_header; *h; ++h)
401               if (*h == '-')
402                 *h = ' ';
403             safe_printfmt (out_fd, "    %s:\n", fixed_header);
404           }
405           lim_free (0, fixed_header);
406           printed_header = 1;
407         }
408 
409       safe_printfmt (out_fd, "     %.*s\n       ==> %.*s\n", (end_1 - value), value, (end_2 - start_2), start_2);
410       value = end_2;
411     }
412 
413   if (printed_header)
414     safe_printfmt (out_fd, "\n");
415 }
416 
417 
418 
419 
420 
421 /* tag: Tom Lord Wed May 14 12:14:01 2003 (changelogs.c)
422  */
423