- follow /etc/products.d/baseproduct and mark product as 'product:type = base'
[platform/upstream/libsolv.git] / tools / repo_products.c
1 /*
2  * repo_products.c
3  * 
4  * Parses all files below 'proddir'
5  * See http://en.opensuse.org/Product_Management/Code11
6  * 
7  * 
8  * Copyright (c) 2007, Novell Inc.
9  *
10  * This program is licensed under the BSD license, read LICENSE.BSD
11  * for further information
12  */
13
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 #include <limits.h>
18 #include <fcntl.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <dirent.h>
24
25 #include "pool.h"
26 #include "repo.h"
27 #include "util.h"
28 #include "tools_util.h"
29 #include "repo_content.h"
30
31
32 static ino_t baseproduct = 0;
33
34 struct parsedata {
35   Repo *repo;
36   char *tmp;
37   int tmpl;
38   Id langcache[ID_NUM_INTERNAL];
39 };
40
41
42 /*
43  * create localized tag
44  */
45
46 static Id
47 langtag(struct parsedata *pd, Id tag, const char *language)
48 {
49     if (language && !language[0])
50         language = 0;
51     if (!language || tag >= ID_NUM_INTERNAL)
52         return pool_id2langid(pd->repo->pool, tag, language, 1);
53     return pool_id2langid(pd->repo->pool, tag, language, 1);
54     if (!pd->langcache[tag])
55         pd->langcache[tag] = pool_id2langid(pd->repo->pool, tag, language, 1);
56     return pd->langcache[tag];
57 }
58
59
60
61
62 enum sections 
63 {
64   SECTION_UNKNOWN,
65   SECTION_PRODUCT,
66   SECTION_TRANSLATED,
67   SECTION_UPDATE
68 };
69
70
71 /*
72  * add single product to repo
73  *
74  */
75
76 static void
77 repo_add_product(struct parsedata *pd, Repodata *data, FILE *fp, int code11)
78 {
79   Repo *repo = pd->repo;
80   Pool *pool = repo->pool;
81   char *line, *linep;
82   int aline;
83   Solvable *s = 0;
84   Id handle = 0;
85
86   enum sections current_section = SECTION_UNKNOWN;
87
88   line = sat_malloc(1024);
89   aline = 1024;
90
91   linep = line;
92   s = 0;
93
94   for (;;)
95     {      
96       /* read line into big-enough buffer */
97       if (linep - line + 16 > aline)
98         {
99           aline = linep - line;
100           line = sat_realloc(line, aline + 512);
101           linep = line + aline;
102           aline += 512;
103         }
104       if (!fgets(linep, aline - (linep - line), fp))
105         break;
106       linep += strlen(linep);
107       if (linep == line || linep[-1] != '\n')
108         continue;
109       *--linep = 0;
110       linep = line;
111
112       if (!code11)
113         {
114           /* non-code11, assume /etc/xyz-release
115            * just parse first line
116            * as "<name> <version> (<arch>)"
117            */
118           char *sp[3];
119
120           if (split(linep, sp, 3) == 3)
121             {
122               if (!s)
123                 {
124                   struct stat st;
125                   
126                   s = pool_id2solvable(pool, repo_add_solvable(repo));
127                   repodata_extend(data, s - pool->solvables);
128                   handle = repodata_get_handle(data, s - pool->solvables - repo->start);
129                   if (!fstat(fileno(fp), &st))
130                     {
131                       repodata_set_num(data, handle, SOLVABLE_INSTALLTIME, st.st_ctime);
132                     }
133                   else
134                     {
135                       perror("Can't stat()");
136                     }
137                 }
138               s->name = str2id(pool, join2("product", ":", sp[0]), 1);
139               s->evr = makeevr(pool, sp[1]);
140             }
141           else
142             {
143               fprintf(stderr, "Can't recognize -release line '%s'\n", linep);
144             }     
145           break; /* just parse single line */
146         }
147       
148       /*
149        * Very trivial .ini parser
150        */
151       
152       /* skip empty and comment lines */
153       if (*linep == '#'
154           || *linep == 0)
155         {
156           continue;
157         }
158       
159       /* sections must start at column 0 */
160       if (*linep == '[')
161         {
162           char *secp = linep+1;
163           char *endp = linep;
164           endp = strchr(secp, ']');
165           if (!endp)
166             {
167               fprintf(stderr, "Skipping unclosed section '%s'\n", line);
168               continue;
169             }
170           *endp = 0;
171           if (!strcmp(secp, "product"))
172             current_section = SECTION_PRODUCT;
173           else if (!strcmp(secp, "translated"))
174             current_section = SECTION_TRANSLATED;
175           else if (!strcmp(secp, "update"))
176             current_section = SECTION_UPDATE;
177           else
178             {
179               fprintf(stderr, "Skipping unknown section '%s'\n", secp);
180               current_section = SECTION_UNKNOWN;
181             }
182           continue;
183         }
184       else if (current_section != SECTION_UNKNOWN)
185         {
186           char *ptr = linep;
187           char *key, *value, *lang;
188
189           lang = 0;
190           
191           /* split line into '<key>[<lang>] = <value>' */
192           while (*ptr && (*ptr == ' ' || *ptr == '\t'))
193             ++ptr;
194           key = ptr;
195           while (*ptr && !(*ptr == ' ' || *ptr == '\t' || *ptr == '=' || *ptr == '['))
196             ++ptr;
197           if (*ptr == '[')
198             {
199               *ptr++ = 0;
200               lang = ptr;
201               while (*ptr && !(*ptr == ']'))
202                 ++ptr;
203               *ptr++ = 0;
204             }
205           if (*ptr != '=')
206             *ptr++ = 0;
207           while (*ptr && !(*ptr == '='))
208             ++ptr;
209           if (*ptr == '=')
210             *ptr++ = 0;
211           while (*ptr && (*ptr == ' ' || *ptr == '\t'))
212             ++ptr;
213           value = ptr;
214
215           /*
216            * [product]
217            */
218           
219           if (current_section == SECTION_PRODUCT)
220             {
221               if (!s)
222                 {
223                   struct stat st;
224                   
225                   s = pool_id2solvable(pool, repo_add_solvable(repo));
226                   repodata_extend(data, s - pool->solvables);
227                   handle = repodata_get_handle(data, s - pool->solvables - repo->start);
228                   if (!fstat(fileno(fp), &st))
229                     {
230                       repodata_set_num(data, handle, SOLVABLE_INSTALLTIME, st.st_ctime);
231                       /* this is where <productsdir>/baseproduct points to */
232                       if (st.st_ino == baseproduct)
233                         repodata_set_str(data, handle, PRODUCT_TYPE, "base");
234                     }
235                   else
236                     {
237                       perror("Can't stat()");
238                     }
239                 }
240               if (!strcmp(key, "name"))
241                   s->name = str2id(pool, join2("product", ":", value), 1);
242               else if (!strcmp(key, "version"))
243                 s->evr = makeevr(pool, value);
244               else if (!strcmp(key, "vendor"))
245                 s->vendor = str2id(pool, value, 1);
246               else if (!strcmp(key, "distribution"))
247                 repo_set_str(repo, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
248               else if (!strcmp (key, "flavor"))
249                 repo_set_str(repo, s - pool->solvables, PRODUCT_FLAVOR, value);     
250             }
251             /*
252              * [translated]
253              */
254           else if (current_section == SECTION_TRANSLATED)
255             {
256               if (!strcmp(key, "summary"))
257                 {
258                   repodata_set_str(data, handle, langtag(pd, SOLVABLE_SUMMARY, lang), value );
259                 }
260               else if (!strcmp(key, "description"))
261                 repodata_set_str(data, handle, langtag(pd, SOLVABLE_DESCRIPTION, lang), value );
262             }
263             /*
264              * [update]
265              */
266           else if (current_section == SECTION_UPDATE)
267             {
268             }
269         }
270       else
271         fprintf (stderr, "malformed line: %s\n", line);
272     }
273
274   if (!s)
275     {
276       fprintf(stderr, "No product solvable created !\n");
277       exit(1);
278     }
279
280   if (!s->arch)
281     s->arch = ARCH_NOARCH;
282   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
283     {
284       s->provides = repo_addid_dep(pd->repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
285     }
286   
287   sat_free(line);
288 }
289
290
291 /*
292  * parse dir looking for files ending in suffix
293  */
294
295 static void
296 parse_dir(DIR *dir, const char *path, struct parsedata *pd, Repodata *repodata, int code11)
297 {
298   struct dirent *entry;
299   char *suffix = code11 ? ".prod" : "-release";
300   int slen = code11 ? 5 : 8;  /* strlen(".prod") : strlen("-release") */
301   struct stat st;
302   
303   /* check for <productsdir>/baseproduct on code11 and remember its target inode */
304   if (code11
305       && stat(join2(path, "/", "baseproduct"), &st) == 0) /* follow symlink */
306     {
307       baseproduct = st.st_ino;
308     }
309   
310   while ((entry = readdir(dir)))
311     {
312       int len;
313       len = strlen(entry->d_name);
314       
315       /* skip /etc/lsb-release, thats not a product per-se */
316       if (!code11
317           && strcmp(entry->d_name, "lsb-release") == 0)
318         {
319           continue;
320         }
321       
322       if (len > slen
323           && strcmp(entry->d_name+len-slen, suffix) == 0)
324         {
325           char *fullpath = join2(path, "/", entry->d_name);
326           FILE *fp = fopen(fullpath, "r");
327           if (!fp)
328             {
329               perror(fullpath);
330               break;
331             }
332           repo_add_product(pd, repodata, fp, code11);
333           fclose(fp);
334         }
335     }
336 }
337
338
339 /*
340  * read all installed products
341  * 
342  * try proddir (reading all .prod files from this directory) first
343  * if not available, assume non-code11 layout and parse /etc/xyz-release
344  *
345  * parse each one as a product
346  */
347
348 void
349 repo_add_products(Repo *repo, Repodata *repodata, const char *proddir, const char *root)
350 {
351   const char *fullpath = proddir;
352   int code11 = 1;
353   DIR *dir = opendir(fullpath);
354   struct parsedata pd;
355   
356   memset(&pd, 0, sizeof(pd));
357   pd.repo = repo;
358
359   if (!dir)
360     {
361       fullpath = root ? join2(root, "", "/etc") : "/etc";
362       dir = opendir(fullpath);
363       code11 = 0;
364     }
365   if (!dir)
366     {
367       perror(fullpath);
368     }
369   else
370     {
371       parse_dir(dir, fullpath, &pd, repodata, code11);
372     }
373   
374   if (pd.tmp)
375     sat_free(pd.tmp);
376   join_freemem();
377   closedir(dir);
378 }