Fix Werrors with GCC-14.1.0
[platform/upstream/rpm.git] / python / rpmfiles-py.c
1 #include "rpmsystem-py.h"
2
3 #include <rpm/rpmtypes.h>
4 #include <rpm/rpmpgp.h>
5
6 #include "header-py.h"
7 #include "rpmfi-py.h"
8 #include "rpmfiles-py.h"
9 #include "rpmfd-py.h"
10 #include "rpmarchive-py.h"
11 #include "rpmstrpool-py.h"
12
13 /* A single file from rpmfiles set, can't be independently instanciated */
14 struct rpmfileObject_s {
15     PyObject_HEAD
16     PyObject *md_dict;
17     rpmfiles files;             /* reference to rpmfiles */
18     int ix;                     /* index to rpmfiles */
19 };
20
21 static void rpmfile_dealloc(rpmfileObject * s)
22 {
23     s->files = rpmfilesFree(s->files);
24     Py_TYPE(s)->tp_free((PyObject *)s);
25 }
26
27 static char rpmfile_doc[] =
28     "Gives access to the meta data of a single file.\n\n"
29     "Instances of this class are only available through an rpm.files object.";
30
31 static PyObject *rpmfile_fx(rpmfileObject *s)
32 {
33     return Py_BuildValue("i", s->ix);
34 }
35
36 static PyObject *rpmfile_dx(rpmfileObject *s)
37 {
38     return Py_BuildValue("i", rpmfilesDI(s->files, s->ix));
39 }
40
41 static PyObject *rpmfile_name(rpmfileObject *s)
42 {
43     char * fn = rpmfilesFN(s->files, s->ix);
44     PyObject *o = Py_BuildValue("s", fn);
45     free(fn);
46     return o;
47 }
48
49 static PyObject *rpmfile_basename(rpmfileObject *s)
50 {
51     return Py_BuildValue("s", rpmfilesBN(s->files, s->ix));
52 }
53
54 static PyObject *rpmfile_dirname(rpmfileObject *s)
55 {
56     return Py_BuildValue("s", rpmfilesDN(s->files, rpmfilesDI(s->files, s->ix)));
57 }
58
59 static PyObject *rpmfile_orig_name(rpmfileObject *s)
60 {
61     char * fn = rpmfilesOFN(s->files, s->ix);
62     PyObject *o = Py_BuildValue("s", fn);
63     free(fn);
64     return o;
65 }
66
67 static PyObject *rpmfile_orig_basename(rpmfileObject *s)
68 {
69     return Py_BuildValue("s", rpmfilesOBN(s->files, s->ix));
70 }
71
72 static PyObject *rpmfile_orig_dirname(rpmfileObject *s)
73 {
74     return Py_BuildValue("s", rpmfilesODN(s->files, rpmfilesODI(s->files, s->ix)));
75 }
76 static PyObject *rpmfile_mode(rpmfileObject *s)
77 {
78     return Py_BuildValue("i", rpmfilesFMode(s->files, s->ix));
79 }
80
81 static PyObject *rpmfile_size(rpmfileObject *s)
82 {
83     return Py_BuildValue("L", rpmfilesFSize(s->files, s->ix));
84 }
85
86 static PyObject *rpmfile_mtime(rpmfileObject *s)
87 {
88     return Py_BuildValue("i", rpmfilesFMtime(s->files, s->ix));
89 }
90
91 static PyObject *rpmfile_rdev(rpmfileObject *s)
92 {
93     return Py_BuildValue("i", rpmfilesFRdev(s->files, s->ix));
94 }
95
96 static PyObject *rpmfile_inode(rpmfileObject *s)
97 {
98     return Py_BuildValue("i", rpmfilesFInode(s->files, s->ix));
99 }
100
101 static PyObject *rpmfile_nlink(rpmfileObject *s)
102 {
103     return Py_BuildValue("i", rpmfilesFNlink(s->files, s->ix));
104 }
105
106 static PyObject *rpmfile_linkto(rpmfileObject *s)
107 {
108     return Py_BuildValue("s", rpmfilesFLink(s->files, s->ix));
109 }
110
111 static PyObject *rpmfile_user(rpmfileObject *s)
112 {
113     return Py_BuildValue("s", rpmfilesFUser(s->files, s->ix));
114 }
115
116 static PyObject *rpmfile_group(rpmfileObject *s)
117 {
118     return Py_BuildValue("s", rpmfilesFGroup(s->files, s->ix));
119 }
120
121 static PyObject *rpmfile_fflags(rpmfileObject *s)
122 {
123     return Py_BuildValue("i", rpmfilesFFlags(s->files, s->ix));
124 }
125
126 static PyObject *rpmfile_vflags(rpmfileObject *s)
127 {
128     return Py_BuildValue("i", rpmfilesVFlags(s->files, s->ix));
129 }
130
131 static PyObject *rpmfile_color(rpmfileObject *s)
132 {
133     return Py_BuildValue("i", rpmfilesFColor(s->files, s->ix));
134 }
135
136 static PyObject *rpmfile_state(rpmfileObject *s)
137 {
138     return Py_BuildValue("i", rpmfilesFState(s->files, s->ix));
139 }
140
141 static PyObject *rpmfile_digest(rpmfileObject *s)
142 {
143     size_t diglen = 0;
144     const unsigned char *digest = rpmfilesFDigest(s->files, s->ix,
145                                                   NULL, &diglen);
146     if (digest) {
147         char * hex = pgpHexStr(digest, diglen);
148         PyObject *o = Py_BuildValue("s", hex);
149         free(hex);
150         return o;
151     }
152     Py_RETURN_NONE;
153 }
154
155 static PyObject *rpmfile_class(rpmfileObject *s)
156 {
157     return Py_BuildValue("s", rpmfilesFClass(s->files, s->ix));
158 }
159
160 static PyObject *rpmfile_caps(rpmfileObject *s)
161 {
162     return Py_BuildValue("s", rpmfilesFCaps(s->files, s->ix));
163 }
164
165 static PyObject *rpmfile_langs(rpmfileObject *s)
166 {
167     return Py_BuildValue("s", rpmfilesFLangs(s->files, s->ix));
168 }
169
170 static PyObject *rpmfile_links(rpmfileObject *s)
171 {
172     PyObject *result = NULL;
173     const int * links = NULL;
174     uint32_t nlinks = rpmfilesFLinks(s->files, s->ix, &links);
175
176     if (nlinks == 0)
177         Py_RETURN_NONE;
178     else if (nlinks == 1)
179         links = &s->ix; /* file itself */
180
181     result = PyTuple_New(nlinks);
182     if (result) {
183         for (uint32_t i = 0; i < nlinks; i++) {
184             int lix = links[i];
185             PyObject * o;
186
187             if (lix == s->ix) {
188                 /* file itself, return a reference instead of new object */
189                 Py_INCREF(s);
190                 o = (PyObject *) s;
191             } else {
192                 o = rpmfile_Wrap(s->files, lix);
193             }
194
195             PyTuple_SET_ITEM(result, i, o);
196         }
197     }
198     return result;
199 }
200
201 /*
202  * Exported as "matches" instead of a rich comparison operator or such
203  * as this cannot be used for comparing file *object* equality,
204  * rpmfilesCompare() determines whether the file *contents* match.
205  */
206 static PyObject *rpmfile_matches(rpmfileObject *s, PyObject *o)
207 {
208     PyObject *result = NULL;
209     if (rpmfileObject_Check(o)) {
210         rpmfileObject *of = (rpmfileObject *)o;
211         int rc = rpmfilesCompare(s->files, s->ix, of->files, of->ix);
212         result = PyBool_FromLong(rc == 0);
213     } else {
214         PyErr_SetObject(PyExc_TypeError, o);
215     }
216     return result;
217 }
218
219 static PyObject *rpmfile_verify(rpmfileObject *s, PyObject *args, PyObject *kwds)
220 {
221     static char *kwlist[] = { "omitMask", NULL };
222     rpmVerifyAttrs omitMask = 0;
223
224     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &omitMask))
225         return NULL;
226
227     return Py_BuildValue("i", rpmfilesVerify(s->files, s->ix, omitMask));
228 }
229
230 static PyGetSetDef rpmfile_getseters[] = {
231     { "fx",             (getter) rpmfile_fx,            NULL,
232       "index in header and rpm.files object" },
233     { "dx",             (getter) rpmfile_dx,            NULL,
234       "index of dirname entry" },
235     { "name",           (getter) rpmfile_name,          NULL,
236       "file name (path)" },
237     { "basename",       (getter) rpmfile_basename,      NULL, NULL },
238     { "dirname",        (getter) rpmfile_dirname,       NULL, NULL },
239     { "orig_name",      (getter) rpmfile_orig_name,     NULL,
240       "original file name (may differ due to relocation)" },
241     { "orig_basename",  (getter) rpmfile_orig_basename, NULL,
242       "original base name (may differ due to relocation)" },
243     { "orig_dirname",   (getter) rpmfile_orig_dirname,  NULL,
244       "original dir name (may differ due to relocation)" },
245     { "mode",           (getter) rpmfile_mode,          NULL,
246       "mode flags / unix permissions" },
247     { "mtime",          (getter) rpmfile_mtime,         NULL,
248       "modification time (in unix time)" },
249     { "size",           (getter) rpmfile_size,          NULL,
250       "file size" },
251     { "rdev",           (getter) rpmfile_rdev,          NULL,
252       "device number - for device files only" },
253     { "inode",          (getter) rpmfile_inode,         NULL,
254       "inode number - contains fake, data used to identify hard liked files" },
255     { "fflags",         (getter) rpmfile_fflags,        NULL,
256       "file flags - see RPMFILE_* constants" },
257     { "vflags",         (getter) rpmfile_vflags,        NULL,
258       "verification flags - see RPMVERIFY_* (in rpmvf.h)" },
259     { "linkto",         (getter) rpmfile_linkto,        NULL,
260       "link target - symlinks only" },
261     { "color",          (getter) rpmfile_color,         NULL,
262       "file color - 2 for 64 bit binaries, 1 for 32 bit binaries, 0 else" },
263     { "nlink",          (getter) rpmfile_nlink,         NULL,
264       "number of hardlinks pointing to the same content as this file" },
265     { "links",          (getter) rpmfile_links,         NULL,
266       "list of file indexes that are hardlinked with this file" },
267     { "user",           (getter) rpmfile_user,          NULL,
268       "user name owning this file" },
269     { "group",          (getter) rpmfile_group,         NULL,
270       "group name owning this file" },
271     { "digest",         (getter) rpmfile_digest,        NULL,
272       "check sum of file content" },
273     { "class",          (getter) rpmfile_class,         NULL,
274       "classfication of file content based on libmagic/file(1)" },
275     { "state",          (getter) rpmfile_state,         NULL,
276       "file state  - see RPMFILE_STATE_* constants" },
277     { "langs",          (getter) rpmfile_langs,         NULL,
278       "language the file provides (typically for doc files)" },
279     { "caps",           (getter) rpmfile_caps,          NULL,
280       "file capabilities" },
281     { NULL, NULL, NULL, NULL }
282 };
283
284 static struct PyMethodDef rpmfile_methods[] = {
285     { "matches", (PyCFunction) rpmfile_matches,         METH_O,
286         NULL },
287     { "verify", (PyCFunction) rpmfile_verify,   METH_VARARGS|METH_KEYWORDS,
288         NULL },
289     { NULL, NULL, 0, NULL }
290 };
291
292 PyTypeObject rpmfile_Type = {
293         PyVarObject_HEAD_INIT(&PyType_Type, 0)
294         "rpm.file",                     /* tp_name */
295         sizeof(rpmfileObject),          /* tp_basicsize */
296         0,                              /* tp_itemsize */
297         /* methods */
298         (destructor) rpmfile_dealloc,   /* tp_dealloc */
299         0,                              /* tp_print */
300         0,                              /* tp_getattr */
301         0,                              /* tp_setattr */
302         0,                              /* tp_compare */
303         0,                              /* tp_repr */
304         0,                              /* tp_as_number */
305         0,                              /* tp_as_sequence */
306         0,                              /* tp_as_mapping */
307         0,                              /* tp_hash */
308         0,                              /* tp_call */
309         (reprfunc)rpmfile_name,         /* tp_str */
310         PyObject_GenericGetAttr,        /* tp_getattro */
311         PyObject_GenericSetAttr,        /* tp_setattro */
312         0,                              /* tp_as_buffer */
313         Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
314         rpmfile_doc,                    /* tp_doc */
315         0,                              /* tp_traverse */
316         0,                              /* tp_clear */
317         0,                              /* tp_richcompare */
318         0,                              /* tp_weaklistoffset */
319         0,                              /* tp_iter */
320         0,                              /* tp_iternext */
321         rpmfile_methods,                /* tp_methods */
322         0,                              /* tp_members */
323         rpmfile_getseters,              /* tp_getset */
324         0,                              /* tp_base */
325         0,                              /* tp_dict */
326         0,                              /* tp_descr_get */
327         0,                              /* tp_descr_set */
328         0,                              /* tp_dictoffset */
329         0,                              /* tp_init */
330         0,                              /* tp_alloc */
331         0,                              /* tp_new */
332         0,                              /* tp_free */
333         0,                              /* tp_is_gc */
334 };
335
336 PyObject * rpmfile_Wrap(rpmfiles files, int ix)
337 {
338     rpmfileObject *s = PyObject_New(rpmfileObject, &rpmfile_Type);
339     if (s == NULL) return NULL;
340
341     s->files = rpmfilesLink(files);
342     s->ix = ix;
343     return (PyObject *) s;
344 }
345
346 /* The actual rpmfiles info set */
347 struct rpmfilesObject_s {
348     PyObject_HEAD
349     PyObject *md_dict;          /*!< to look like PyModuleObject */
350     rpmfiles files;
351 };
352
353 static void rpmfiles_dealloc(rpmfilesObject * s)
354 {
355     s->files = rpmfilesFree(s->files);
356     Py_TYPE(s)->tp_free((PyObject *)s);
357 }
358
359 static PyObject * rpmfiles_new(PyTypeObject * subtype, PyObject *args, PyObject *kwds)
360 {
361     PyObject * to = NULL;
362     Header h = NULL;
363     rpmfiles files = NULL;
364     rpmTagVal tagN = RPMTAG_BASENAMES;
365     int flags = 0;
366     rpmstrPool pool = NULL;
367     char * kwlist[] = {"header", "tag", "flags", "pool", NULL};
368
369     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|OiO&:rpmfiles_new", kwlist,
370                                 hdrFromPyObject, &h, &to, &flags,
371                                 poolFromPyObject, &pool))
372         return NULL;
373
374     files = rpmfilesNew(pool, h, tagN, flags);
375
376     if (files == NULL) {
377         PyErr_SetString(PyExc_ValueError, "invalid file data in header");
378         return NULL;
379     }
380
381     return rpmfiles_Wrap(subtype, files);
382 }
383
384 static Py_ssize_t rpmfiles_length(rpmfilesObject *s)
385 {
386     return rpmfilesFC(s->files);
387 }
388
389 static PyObject * rpmfiles_getitem(rpmfilesObject *s, Py_ssize_t ix)
390 {
391     if (ix >= 0 && ix < rpmfilesFC(s->files))
392         return rpmfile_Wrap(s->files, ix);
393
394     PyErr_SetObject(PyExc_IndexError, Py_BuildValue("i", ix));
395     return NULL;
396 }
397
398 static int rpmfiles_contains(rpmfilesObject *s, PyObject *value)
399 {
400     const char *fn = NULL;
401
402     if (!PyArg_Parse(value, "s", &fn))
403         return -1;
404
405     return (rpmfilesFindFN(s->files, fn) >= 0) ? 1 : 0;
406 }
407
408 static PySequenceMethods rpmfiles_as_sequence = {
409     (lenfunc)rpmfiles_length,           /* sq_length */
410     0,                                  /* sq_concat */
411     0,                                  /* sq_repeat */
412     (ssizeargfunc) rpmfiles_getitem,    /* sq_item */
413     0,                                  /* sq_slice */
414     0,                                  /* sq_ass_item */
415     0,                                  /* sq_ass_slice */
416     (objobjproc)rpmfiles_contains,      /* sq_contains */
417     0,                                  /* sq_inplace_concat */
418     0,                                  /* sq_inplace_repeat */
419 };
420
421 static PyObject * rpmfiles_find(rpmfileObject *s,
422                                 PyObject *args, PyObject *kwds)
423 {
424     const char *fn = NULL;
425     int fx, orig = 0;
426     char * kwlist[] = {"filename", "orig", NULL};
427
428     if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &fn, &orig))
429         return NULL;
430
431     if (orig)
432         fx = rpmfilesFindOFN(s->files, fn);
433     else
434         fx = rpmfilesFindFN(s->files, fn);
435
436     if (fx >= 0)
437         return rpmfile_Wrap(s->files, fx);
438
439     Py_RETURN_NONE;
440 }
441
442 static PyObject *rpmfiles_archive(rpmfilesObject *s,
443                                   PyObject *args, PyObject *kwds)
444 {
445     char * kwlist[] = {"fd", "write", NULL};
446     rpmfdObject *fdo = NULL;
447     FD_t fd = NULL;
448     rpmfi archive = NULL;
449     int writer = 0;
450
451     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|i", kwlist,
452                                      rpmfdFromPyObject, &fdo, &writer)) {
453         return NULL;
454     }
455
456     fd = rpmfdGetFd(fdo);
457     if (writer) {
458         archive = rpmfiNewArchiveWriter(fd, s->files);
459     } else {
460         archive = rpmfiNewArchiveReader(fd, s->files, RPMFI_ITER_READ_ARCHIVE);
461     }
462
463     return rpmarchive_Wrap(&rpmarchive_Type, s->files, archive);
464 }
465
466 static PyObject *rpmfiles_subscript(rpmfilesObject *s, PyObject *item)
467 {
468     PyObject *str = NULL;
469
470     /* treat numbers as sequence accesses */
471     if (PyInt_Check(item)) {
472         return rpmfiles_getitem(s, PyInt_AsSsize_t(item));
473     } else if (PyLong_Check(item)) {
474         return rpmfiles_getitem(s, PyLong_AsSsize_t(item));
475     }
476
477     /* handle slices by returning tuples of rpm.file items */
478     if (PySlice_Check(item)) {
479         Py_ssize_t start, stop, step, slicelength, i, cur;
480         PyObject * result;
481         
482         if (PySlice_GetIndicesEx(
483 #if PY_MAJOR_VERSION < 3
484                                  (PySliceObject*)
485 #endif
486                                  item, rpmfiles_length(s),
487                                  &start, &stop, &step, &slicelength) < 0) {
488             return NULL;
489         }
490
491         result = PyTuple_New(slicelength);
492         if (result) {
493             for (cur = start, i = 0; i < slicelength; cur += step, i++) {
494                 PyTuple_SET_ITEM(result, i, rpmfiles_getitem(s, cur));
495             }
496         }
497         return result;
498     }
499
500     /* ... and strings as mapping access */
501     if (utf8FromPyObject(item, &str)) {
502         int fx = rpmfilesFindFN(s->files, PyBytes_AsString(str));
503         Py_DECREF(str);
504
505         if (fx >= 0) {
506             return rpmfile_Wrap(s->files, fx);
507         } else {
508             PyErr_SetObject(PyExc_KeyError, item);
509         }
510     } else {
511         PyErr_SetObject(PyExc_TypeError, item);
512     }
513
514     return NULL;
515 }
516
517 static PyMappingMethods rpmfiles_as_mapping = {
518     (lenfunc) rpmfiles_length,          /* mp_length */
519     (binaryfunc) rpmfiles_subscript,    /* mp_subscript */
520     0,                                  /* mp_ass_subscript */
521 };
522
523 static struct PyMethodDef rpmfiles_methods[] = {
524     { "archive", (PyCFunction) rpmfiles_archive, METH_VARARGS|METH_KEYWORDS,
525       "files.archive(fd, write=False) -- Return a rpm.archive object\n\n"
526       "Args:\n"
527       "  fd : File to read from or write to.\n"
528       "  write : True to get an archive writer, False for an archive reader"},
529     { "find",   (PyCFunction) rpmfiles_find,    METH_VARARGS|METH_KEYWORDS,
530       "files.find(filename, orig=False) -- Return index of given file name.\n\n"
531       "  Return -1 if file is not found.\n"
532       "  Leading \".\" in filename is ignored."},
533     { NULL, NULL, 0, NULL }
534 };
535
536 static char rpmfiles_doc[] =
537     "rpm.files(hdr, tag=RPMTAG_BASENAMES, flags=None, pool=None)\n\n"
538     "Stores the meta data of a package's files.\n\n"
539     "Args:\n"
540     "\thdr: The header object to get the data from.\n"
541     "\tflags : Controls which data to store and whether to create\n\t\tcopies or use the data from the header.\n\t\tBy default all data is copied.\n\t\tSee RPMFI_* constants in rpmfiles.h.\n"
542     "\tpool : rpm.strpool object to store the strings in.\n\t\tLeave empty to use global pool.\n"
543     "\ttag : Obsolete. Leave alone!\n\n"
544     "rpm.files is basically a sequence of rpm.file objects.\nNote that this is a read only data structure. To write file data you\nhave to write it directly into aheader object.";
545
546 PyTypeObject rpmfiles_Type = {
547         PyVarObject_HEAD_INIT(&PyType_Type, 0)
548         "rpm.files",                    /* tp_name */
549         sizeof(rpmfilesObject),         /* tp_basicsize */
550         0,                              /* tp_itemsize */
551         /* methods */
552         (destructor) rpmfiles_dealloc,  /* tp_dealloc */
553         0,                              /* tp_print */
554         0,                              /* tp_getattr */
555         0,                              /* tp_setattr */
556         0,                              /* tp_compare */
557         0,                              /* tp_repr */
558         0,                              /* tp_as_number */
559         &rpmfiles_as_sequence,          /* tp_as_sequence */
560         &rpmfiles_as_mapping,           /* tp_as_mapping */
561         0,                              /* tp_hash */
562         0,                              /* tp_call */
563         0,                              /* tp_str */
564         PyObject_GenericGetAttr,        /* tp_getattro */
565         PyObject_GenericSetAttr,        /* tp_setattro */
566         0,                              /* tp_as_buffer */
567         Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
568         rpmfiles_doc,                   /* tp_doc */
569         0,                              /* tp_traverse */
570         0,                              /* tp_clear */
571         0,                              /* tp_richcompare */
572         0,                              /* tp_weaklistoffset */
573         0,                              /* tp_iter */
574         0,                              /* tp_iternext */
575         rpmfiles_methods,               /* tp_methods */
576         0,                              /* tp_members */
577         0,                              /* tp_getset */
578         0,                              /* tp_base */
579         0,                              /* tp_dict */
580         0,                              /* tp_descr_get */
581         0,                              /* tp_descr_set */
582         0,                              /* tp_dictoffset */
583         0,                              /* tp_init */
584         0,                              /* tp_alloc */
585         (newfunc) rpmfiles_new,         /* tp_new */
586         0,                              /* tp_free */
587         0,                              /* tp_is_gc */
588 };
589
590 PyObject * rpmfiles_Wrap(PyTypeObject *subtype, rpmfiles files)
591 {
592     rpmfilesObject *s = (rpmfilesObject *)subtype->tp_alloc(subtype, 0);
593     if (s == NULL) return NULL;
594
595     s->files = files;
596     return (PyObject *) s;
597 }
598