More crash-resistant on network failure (patch from Lekensteyn)
[ppastats.git] / src / ppastats.c
1 /*
2     Copyright (C) 2011 jeanfi@gmail.com
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17     02110-1301 USA
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "list.h"
25 #include "lp_ws.h"
26 #include "ppastats.h"
27
28 static struct package_stats *get_package_stats(struct ppa_stats *stats,
29                                                const char *name)
30
31 {
32         struct package_stats *p, **p_cur;
33
34         p_cur = stats->packages;
35         while (p_cur && *p_cur) {
36                 struct package_stats *p = *p_cur;
37
38                 if (!strcmp(p->name, name))
39                         return p;
40
41                 p_cur++;
42         }
43
44         p = malloc(sizeof(struct package_stats));
45         p->name = strdup(name);
46         p->versions = NULL;
47         p->download_count = 0;
48         p->daily_download_totals = NULL;
49         p->distros = NULL;
50
51         stats->packages = (struct package_stats **)list_add
52                 ((void **)stats->packages, p);
53
54         return p;
55 }
56
57 static struct version_stats *get_version_stats(struct package_stats *package,
58                                                const char *version)
59 {
60         struct version_stats *v, **cur;
61
62         cur = package->versions;
63         while (cur && *cur) {
64                 struct version_stats *v = *cur;
65
66                 if (!strcmp(v->version, version))
67                         return v;
68
69                 cur++;
70         }
71
72         v = malloc(sizeof(struct version_stats));
73         v->version = strdup(version);
74         v->distros = NULL;
75         v->download_count = 0;
76         v->daily_download_totals = NULL;
77
78         package->versions
79                 = (struct version_stats **)list_add((void **)package->versions,
80                                                     v);
81
82         return v;
83 }
84
85 static struct distro_stats *distro_stats_new(const char *name)
86 {
87         struct distro_stats *d;
88
89         d = malloc(sizeof(struct distro_stats));
90         d->name = strdup(name);
91         d->archs = NULL;
92         d->download_count = 0;
93         d->ddts = NULL;
94
95         return d;
96 }
97
98 static struct distro_stats *get_distro_stats(struct version_stats *version,
99                                              const char *name)
100 {
101         struct distro_stats **cur = version->distros;
102         struct distro_stats *d;
103
104         while (cur && *cur) {
105                 d = *cur;
106
107                 if (!strcmp(d->name, name))
108                         return d;
109
110                 cur++;
111         }
112
113         d = distro_stats_new(name);
114
115         version->distros
116                 = (struct distro_stats **)list_add((void **)version->distros,
117                                                    d);
118
119         return d;
120 }
121
122 static struct arch_stats *get_arch_stats(struct distro_stats *distro,
123                                          const char *name)
124 {
125         struct arch_stats **cur = distro->archs;
126         struct arch_stats *a;
127
128         while (cur && *cur) {
129                 a = *cur;
130
131                 if (!strcmp(a->name, name))
132                         return a;
133
134                 cur++;
135         }
136
137         a = malloc(sizeof(struct arch_stats));
138         a->name = strdup(name);
139         a->download_count = 0;
140
141         distro->archs
142                 = (struct arch_stats **)list_add((void **)distro->archs,
143                                                  a);
144
145         return a;
146 }
147
148
149 static struct daily_download_total **add_total
150 (struct daily_download_total **totals, struct daily_download_total *total)
151 {
152         struct daily_download_total **cur;
153         struct daily_download_total *item;
154
155         if (totals) {
156                 cur = totals;
157                 while (*cur) {
158                         item = *cur;
159
160                         if (item->date.tm_year == total->date.tm_year &&
161                             item->date.tm_mon == total->date.tm_mon &&
162                             item->date.tm_mday == total->date.tm_mday) {
163                                 item->count += total->count;
164                                 return totals;
165                         }
166
167                         cur++;
168                 }
169         }
170
171         item = malloc(sizeof(struct daily_download_total));
172         memcpy(item, total, sizeof(struct daily_download_total));
173
174         return (struct daily_download_total **)
175                 list_add((void **)totals, (void *)item);
176 }
177
178 struct daily_download_total **add_totals
179 (struct daily_download_total **total1, struct daily_download_total **total2)
180 {
181         struct daily_download_total **cur;
182         struct daily_download_total **result;
183
184         result = total1;
185         cur = total2;
186         while (*cur) {
187                 result = add_total(result, *cur);
188
189                 cur++;
190         }
191
192         return result;
193 }
194
195 static void
196 pkg_add_distro(struct package_stats *pkg,
197                const char *distro_name,
198                int distro_count,
199                struct daily_download_total **ddts)
200 {
201         struct distro_stats **pkg_distros, *pkg_distro;
202
203         pkg_distros = pkg->distros;
204         pkg_distro = NULL;
205
206         if (pkg_distros)
207                 while (*pkg_distros)  {
208                         if (!strcmp((*pkg_distros)->name, distro_name)) {
209                                 pkg_distro = *pkg_distros;
210                                 break;
211                         }
212
213                         pkg_distros++;
214                 }
215
216         if (!pkg_distro) {
217                 pkg_distro = distro_stats_new(distro_name);
218                 pkg->distros
219                         = (struct distro_stats **)
220                         list_add((void **)pkg->distros, (void *)pkg_distro);
221         }
222
223         pkg_distro->download_count += distro_count;
224         pkg_distro->ddts = add_totals(pkg_distro->ddts, ddts);
225 }
226
227 struct ppa_stats *
228 create_ppa_stats(const char *owner,
229                  const char *ppa_name,
230                  const char *package_status)
231 {
232         struct ppa_stats *ppa;
233         struct binary_package_publishing_history **history, **h_cur, *h;
234         char *ppa_url, *pkg_name, *pkg_version;
235         struct package_stats *pkg;
236         struct version_stats *version;
237         const struct distro_series *distro_series;
238         const struct distro_arch_series *arch_series;
239         struct distro_stats *distro;
240         struct arch_stats *arch;
241         int count;
242         struct daily_download_total **totals;
243
244         ppa_url = get_archive_url(owner, ppa_name);
245         history = get_binary_package_publishing_history_list(ppa_url,
246                                                              package_status);
247         free(ppa_url);
248
249         if (!history) {
250                 fprintf(stderr, "Failed to retrieve PPA information\n");
251                 exit(EXIT_FAILURE);
252         }
253
254         ppa = malloc(sizeof(struct ppa_stats));
255         ppa->name = strdup(ppa_name);
256         ppa->owner = strdup(owner);
257         ppa->packages = NULL;
258         ppa->daily_download_totals = NULL;
259         ppa->download_count = 0;
260
261         for (h_cur = history; *h_cur; ++h_cur) {
262                 h = *h_cur;
263                 totals = get_daily_download_totals(h->self_link);
264                 if (!totals) {
265                         fprintf(stderr, "Failed to retrieve download totals"
266                                 " for %s\n", h->self_link);
267                         continue;
268                 }
269                 count = get_download_count(h->self_link);
270                 if (count == -1) {
271                         fprintf(stderr, "Failed to retrieve download count"
272                                 " for %s\n", h->self_link);
273                         count = 0;
274                 }
275                 pkg_name = h->binary_package_name;
276                 pkg_version = h->binary_package_version;
277                 arch_series
278                         = get_distro_arch_series(h->distro_arch_series_link);
279                 distro_series
280                         = get_distro_series(arch_series->distroseries_link);
281
282                 ppa->download_count += count;
283                 ppa->daily_download_totals
284                         = add_totals(ppa->daily_download_totals, totals);
285
286                 pkg = get_package_stats(ppa, pkg_name);
287                 pkg->download_count += count;
288                 pkg->daily_download_totals
289                         = add_totals(pkg->daily_download_totals, totals);
290
291                 version = get_version_stats(pkg, pkg_version);
292                 version->download_count += count;
293                 version->daily_download_totals
294                         = add_totals(version->daily_download_totals, totals);
295
296                 distro = get_distro_stats(version, distro_series->name);
297                 distro->download_count += count;
298
299                 arch = get_arch_stats(distro, arch_series->architecture_tag);
300                 arch->download_count += count;
301
302                 pkg_add_distro(pkg, distro_series->name, count, totals);
303
304                 daily_download_total_list_free(totals);
305         }
306
307         binary_package_publishing_history_list_free(history);
308
309         return ppa;
310 }
311
312 static void arch_stats_free(struct arch_stats *arch)
313 {
314         free(arch->name);
315         free(arch);
316 }
317
318 static void distro_stats_free(struct distro_stats *distro)
319 {
320         struct arch_stats **archs;
321
322         archs = distro->archs;
323         if (archs) {
324                 while (*archs) {
325                         arch_stats_free(*archs);
326                         archs++;
327                 }
328                 free(distro->archs);
329         }
330
331         free(distro->name);
332         free(distro);
333 }
334
335 static void version_stats_free(struct version_stats *version)
336 {
337         struct distro_stats **distros;
338
339         distros = version->distros;
340         if (distros) {
341                 while (*distros) {
342                         distro_stats_free(*distros);
343                         distros++;
344                 }
345                 free(version->distros);
346         }
347
348         free(version->version);
349         free(version);
350 }
351
352 static void package_stats_free(struct package_stats *package)
353 {
354         struct version_stats **versions;
355
356         versions = package->versions;
357         if (versions) {
358                 while (*versions) {
359                         version_stats_free(*versions);
360                         versions++;
361                 }
362                 free(package->versions);
363         }
364
365         free(package->name);
366         free(package);
367 }
368
369 void ppa_stats_free(struct ppa_stats *ppastats)
370 {
371         struct package_stats **packages;
372
373         packages = ppastats->packages;
374         if (packages) {
375                 while (*packages) {
376                         package_stats_free(*packages);
377                         packages++;
378                 }
379                 free(ppastats->packages);
380         }
381
382         free(ppastats->owner);
383         free(ppastats->name);
384
385         daily_download_total_list_free(ppastats->daily_download_totals);
386
387         free(ppastats);
388 }