db.c 10.5 KB
Newer Older
Rémy Oudompheng's avatar
Rémy Oudompheng committed
1
/**
2
 * db.c : wrapper class around alpm_db_t
Rémy Oudompheng's avatar
Rémy Oudompheng committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 *  Copyright (c) 2011 Rémy Oudompheng <remy@archlinux.org>
 *
 *  This file is part of pyalpm.
 *
 *  pyalpm is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  pyalpm is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with pyalpm.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

Jelle van der Waa's avatar
Jelle van der Waa committed
23
#include <pyconfig.h>
Rémy Oudompheng's avatar
Rémy Oudompheng committed
24
25
#include <alpm.h>
#include <Python.h>
26
#include "handle.h"
Rémy Oudompheng's avatar
Rémy Oudompheng committed
27
#include "db.h"
28
#include "package.h"
Rémy Oudompheng's avatar
Rémy Oudompheng committed
29
#include "util.h"
30
31
32

typedef struct _AlpmDB {
  PyObject_HEAD
33
  alpm_db_t *c_data;
34
  PyObject *handle;
35
} AlpmDB;
Rémy Oudompheng's avatar
Rémy Oudompheng committed
36

37
38
#define ALPM_DB(self) (((AlpmDB*)self)->c_data)

39
40
static PyTypeObject AlpmDBType;

Rémy Oudompheng's avatar
Rémy Oudompheng committed
41
static void pyalpm_db_dealloc(AlpmDB *self) {
42
43
  if (self->handle)
    Py_DECREF(self->handle);
44
  Py_TYPE(self)->tp_free((PyObject*)self);
Rémy Oudompheng's avatar
Rémy Oudompheng committed
45
46
}

47
static PyObject* _pyobject_from_pmgrp(void *group, PyObject *db) {
48
  const alpm_group_t* grp = (alpm_group_t*)group;
49
50
  if (!grp)
    Py_RETURN_NONE;
51
52
  else {
    PyObject *fst = PyUnicode_FromString(grp->name);
53
54
    PyObject *snd = alpmlist_to_pylist2(grp->packages,
              pyalpm_package_from_pmpkg, db);
55
56
57
58
59
    PyObject *tuple = PyTuple_Pack(2, fst, snd);
    Py_DECREF(fst);
    Py_DECREF(snd);
    return tuple;
  }
Rémy Oudompheng's avatar
Rémy Oudompheng committed
60
61
}

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/** Converts a Python list of databases to an alpm_list_t linked list.
 * return 0 on success, -1 on failure
 */
int pylist_db_to_alpmlist(PyObject *list, alpm_list_t **result) {
  alpm_list_t *ret = NULL;
  PyObject *iterator = PyObject_GetIter(list);
  PyObject *item;

  if(iterator == NULL) {
    PyErr_SetString(PyExc_TypeError, "object is not iterable");
    return -1;
  }

  while((item = PyIter_Next(iterator)))
  {
    if (PyObject_TypeCheck(item, &AlpmDBType)) {
      ret = alpm_list_add(ret, ((AlpmDB*)item)->c_data);
    } else {
      PyErr_SetString(PyExc_TypeError, "list must contain only Database objects");
      FREELIST(ret);
      Py_DECREF(item);
      Py_DECREF(iterator);
      return -1;
    }
    Py_DECREF(item);
  }
  Py_DECREF(iterator);

  *result = ret;
  return 0;
}

94
95
96
97
98
#define CHECK_IF_INITIALIZED() if (! self->c_data) { \
  PyErr_SetString(alpm_error, "data is not initialized"); \
  return NULL; \
  }

99
100
101
102
103
104
105
106
107
108
109
110
111
112
static PyObject* pyalpm_db_repr(PyObject *rawself) {
  AlpmDB *self = (AlpmDB *)rawself;
  return PyUnicode_FromFormat("<alpm.DB(\"%s\") at %p>",
			      alpm_db_get_name(self->c_data),
			      self);
}

static PyObject* pyalpm_db_str(PyObject *rawself) {
  AlpmDB *self = (AlpmDB *)rawself;
  return PyUnicode_FromFormat("alpm.DB(\"%s\")",
			      alpm_db_get_name(self->c_data),
            self);
}

Rémy Oudompheng's avatar
Rémy Oudompheng committed
113
/** Database properties */
Rémy Oudompheng's avatar
Rémy Oudompheng committed
114

115
static PyObject* pyalpm_db_get_name(AlpmDB* self, void* closure) {
116
  const char* name;
117
  CHECK_IF_INITIALIZED();
118
  name = alpm_db_get_name(self->c_data);
119
120
121
122
  if (!name)
    Py_RETURN_NONE;
  return PyUnicode_FromString(name);
}
Rémy Oudompheng's avatar
Rémy Oudompheng committed
123

124
static PyObject* pyalpm_db_get_servers(PyObject *self, void* closure) {
125
  alpm_db_t *db = ALPM_DB(self);
126
  return alpmlist_to_pylist(alpm_db_get_servers(db), pyobject_from_string);
127
128
}

129
static int pyalpm_db_set_servers(PyObject* self, PyObject* value, void* closure) {
130
  alpm_db_t *db = ALPM_DB(self);
131
132
  alpm_list_t *target;
  if (pylist_string_to_alpmlist(value, &target) == -1)
133
    return -1;
134
135
136
  if (alpm_db_set_servers(db, target) == -1)
    RET_ERR("unable to set servers", 0, -1);
  return 0;
137
138
}

139
140
static PyObject* pyalpm_db_get_pkgcache(AlpmDB* self, void* closure) {
  alpm_list_t *pkglist = alpm_db_get_pkgcache(self->c_data);
141
  return alpmlist_to_pylist2(pkglist, pyalpm_package_from_pmpkg, self);
Rémy Oudompheng's avatar
Rémy Oudompheng committed
142
143
}

144
static PyObject* pyalpm_db_get_grpcache(AlpmDB* self, void* closure) {
145
  alpm_list_t *grplist = alpm_db_get_groupcache(self->c_data);
146
  return alpmlist_to_pylist2(grplist, _pyobject_from_pmgrp, self);
147
148
}

Rémy Oudompheng's avatar
Rémy Oudompheng committed
149
150
151
152
/** Package get/set operations */

static PyObject* pyalpm_db_get_pkg(PyObject *rawself, PyObject* args) {
  char *pkgname;
153
  alpm_pkg_t *p;
Rémy Oudompheng's avatar
Rémy Oudompheng committed
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
  AlpmDB *self = (AlpmDB*)rawself;

  if(!PyArg_ParseTuple(args, "s", &pkgname))
  {
    PyErr_SetString(PyExc_TypeError, "get_pkg() takes a string argument");
    return NULL;
  }

  CHECK_IF_INITIALIZED();

  p = alpm_db_get_pkg(self->c_data, pkgname);

  if (p == NULL) {
    Py_RETURN_NONE;
  } else {
    PyObject *result;
170
    result = pyalpm_package_from_pmpkg(p, self);
Rémy Oudompheng's avatar
Rémy Oudompheng committed
171
172
173
174
175
176
177
178
    if (result == NULL) {
      return NULL;
    } else {
      return result;
    }
  }
}

179
static PyObject* pyalpm_db_get_group(PyObject* rawself, PyObject* args) {
180
181
  AlpmDB* self = (AlpmDB*)rawself;
  char *grpname;
182
  alpm_group_t *grp;
183
184
185
186
187
  if (!PyArg_ParseTuple(args, "s", &grpname)) {
    PyErr_SetString(PyExc_TypeError, "expected string argument");
    return NULL;
  }

188
  grp = alpm_db_get_group(self->c_data, grpname);
189
  return _pyobject_from_pmgrp(grp, self);
190
191
}

Rémy Oudompheng's avatar
Rémy Oudompheng committed
192
193
static PyObject *pyalpm_db_update(PyObject *rawself, PyObject *args, PyObject *kwargs) {
  AlpmDB* self = (AlpmDB*)rawself;
194
195
196
  alpm_db_t *db = ALPM_DB(self);
  alpm_handle_t *handle = ALPM_HANDLE(self->handle);
  alpm_list_t *dbs = NULL;
Rémy Oudompheng's avatar
Rémy Oudompheng committed
197
198
199
200
201
202
  char* keyword[] = {"force", NULL};
  int ret;
  PyObject *force;
  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", keyword, &PyBool_Type, &force))
    return NULL;

203
204
205
  dbs = alpm_list_add(dbs, db);
  ret = alpm_db_update(handle, dbs, (force == Py_True));
  alpm_list_free(dbs);
Rémy Oudompheng's avatar
Rémy Oudompheng committed
206
207
208

  switch(ret) {
  case -1:
209
    RET_ERR("unable to update database", 0, NULL);
Rémy Oudompheng's avatar
Rémy Oudompheng committed
210
211
212
213
214
  case 0:
    Py_RETURN_TRUE;
  case 1:
    Py_RETURN_FALSE;
  default:
215
    RET_ERR("invalid return code from alpm_db_update()", 0, NULL);
Rémy Oudompheng's avatar
Rémy Oudompheng committed
216
217
218
  }
}

Rémy Oudompheng's avatar
Rémy Oudompheng committed
219
220
221
static PyObject* pyalpm_db_search(PyObject *rawself, PyObject *args) {
  AlpmDB* self = (AlpmDB *)rawself;
  alpm_list_t* rawargs;
222
  alpm_list_t* result = NULL;
Rémy Oudompheng's avatar
Rémy Oudompheng committed
223
224
225
  int ok = pylist_string_to_alpmlist(args, &rawargs);
  if (ok == -1) return NULL;

226
227
228
229
230
  ok = alpm_db_search(self->c_data, rawargs, &result);
  FREELIST(rawargs);
  // TODO: handle pm_errno being set and throw an exception
  if (ok == -1) return NULL;

231
  return alpmlist_to_pylist2(result, pyalpm_package_from_pmpkg, self);
Rémy Oudompheng's avatar
Rémy Oudompheng committed
232
233
}

Rémy Oudompheng's avatar
Rémy Oudompheng committed
234
static struct PyMethodDef db_methods[] = {
235
236
  { "get_pkg", pyalpm_db_get_pkg, METH_VARARGS,
    "get a package by name\n"
Rémy Oudompheng's avatar
Rémy Oudompheng committed
237
238
    "args: a package name (string)\n"
    "returns: a Package object or None if not found" },
Rémy Oudompheng's avatar
Rémy Oudompheng committed
239
240
241
242
  { "search", pyalpm_db_search, METH_VARARGS,
    "search for packages matching a list of regexps\n"
    "args: a variable number of regexps (strings)\n"
    "returns: packages matching all these regexps" },
243
  { "read_grp", pyalpm_db_get_group, METH_VARARGS,
244
245
246
    "get contents of a group\n"
    "args: a group name (string)\n"
    "returns: a tuple (group name, list of packages)" },
247
  { "update", pyalpm_db_update, METH_VARARGS | METH_KEYWORDS,
Rémy Oudompheng's avatar
Rémy Oudompheng committed
248
249
250
    "update a database from its url attribute\n"
    "args: force (update even if DB is up to date, boolean)\n"
    "returns: True if an update has been done" },
Rémy Oudompheng's avatar
Rémy Oudompheng committed
251
252
  { NULL },
};
Rémy Oudompheng's avatar
Rémy Oudompheng committed
253

254
255
struct PyGetSetDef db_getset[] = {
  /* description properties */
256
257
  { "name", (getter)pyalpm_db_get_name, 0,
    "database name (e.g. \"core\", \"extra\")", NULL } ,
258
259
  { "servers", (getter)pyalpm_db_get_servers, (setter)pyalpm_db_set_servers,
    "a list of URLs (for sync DBs)", NULL } ,
260
261
  { "pkgcache", (getter)pyalpm_db_get_pkgcache, 0, "(read only) list of packages", NULL } ,
  { "grpcache", (getter)pyalpm_db_get_grpcache, 0, "(read only) list of package groups", NULL } ,
262
263
264
  { NULL }
};

265
static PyTypeObject AlpmDBType = {
Rémy Oudompheng's avatar
Rémy Oudompheng committed
266
267
268
269
  PyVarObject_HEAD_INIT(NULL, 0)
  "alpm.DB",             /*tp_name*/
  sizeof(AlpmDB),        /*tp_basicsize*/
  0,                          /*tp_itemsize*/
270
  .tp_dealloc = (destructor)pyalpm_db_dealloc,
271
272
  .tp_repr = pyalpm_db_repr,
  .tp_str = pyalpm_db_str,
273
274
275
276
  .tp_flags = Py_TPFLAGS_DEFAULT,
  .tp_doc = "libalpm DB object",
  .tp_methods = db_methods,
  .tp_getset = db_getset,
Rémy Oudompheng's avatar
Rémy Oudompheng committed
277
278
279
280
281
282
283
284
285
286
287
};

/** Initializes Pb class in module */
void init_pyalpm_db(PyObject *module) {
  PyObject *type;

  if (PyType_Ready(&AlpmDBType) < 0)
    return;
  type = (PyObject*)&AlpmDBType;
  Py_INCREF(type);
  PyModule_AddObject(module, "DB", type);
288
289
290
291
292
293

  /* signature check levels */
  PyModule_AddIntConstant(module, "SIG_DATABASE", ALPM_SIG_DATABASE);
  PyModule_AddIntConstant(module, "SIG_DATABASE_OPTIONAL", ALPM_SIG_DATABASE_OPTIONAL);
  PyModule_AddIntConstant(module, "SIG_DATABASE_MARGINAL_OK", ALPM_SIG_DATABASE_MARGINAL_OK);
  PyModule_AddIntConstant(module, "SIG_DATABASE_UNKNOWN_OK", ALPM_SIG_DATABASE_UNKNOWN_OK);
Rémy Oudompheng's avatar
Rémy Oudompheng committed
294
295
}

296

297
PyObject *pyalpm_db_from_pmdb(void* data, PyObject *handle) {
298
  alpm_db_t *db = (alpm_db_t*)data;
299
300
301
302
303
304
305
  AlpmDB *self;
  self = (AlpmDB*)AlpmDBType.tp_alloc(&AlpmDBType, 0);
  if (self == NULL) {
    PyErr_SetString(PyExc_RuntimeError, "unable to create DB object");
    return NULL;
  }

306
307
308
309
310
  /* TODO: Remove check when src/package.c also has a refcount to a handle */
  if (handle != NULL) {
    Py_INCREF(handle);
    self->handle = handle;
  }
311
312
313
314
  self->c_data = db;
  return (PyObject *)self;
}

315
316
/** non-class methods */
PyObject* pyalpm_find_grp_pkgs(PyObject* self, PyObject *args) {
317
  PyObject *dbs;
318
319
320
321
322
323
324
325
326
327
  char *grpname;
  alpm_list_t *db_list = NULL;
  alpm_list_t *pkg_list;
  PyObject *result;

  if (!PyArg_ParseTuple(args, "Os", &dbs, &grpname)) {
    PyErr_SetString(PyExc_TypeError, "expected arguments (list of dbs, group name)");
    return NULL;
  }

328
329
330
331
332
  {
    int ret = pylist_db_to_alpmlist(dbs, &db_list);
    if (ret == -1)
      return NULL;
  }
333
  pkg_list = alpm_find_group_pkgs(db_list, grpname);
Jelle van der Waa's avatar
Jelle van der Waa committed
334
  result = alpmlist_to_pylist2(pkg_list, pyalpm_package_from_pmpkg, NULL);
335
336
337
338
339
  alpm_list_free(db_list);
  alpm_list_free(pkg_list);
  return result;
}

340
/** Finds an available upgrade for a package in a list of databases */
341
PyObject* pyalpm_sync_get_new_version(PyObject *self, PyObject* args) {
342
343
344
  PyObject *pkg;
  PyObject *dbs;
  alpm_list_t *db_list;
345
  alpm_pkg_t *result = NULL;
346
347
348
349
350
351
352
353
  if(!PyArg_ParseTuple(args, "OO", &pkg, &dbs)
      || !PyAlpmPkg_Check(pkg)
      || pylist_db_to_alpmlist(dbs, &db_list) == -1)
  {
    PyErr_SetString(PyExc_TypeError, "sync_newversion() takes a Package and a list of DBs");
    return NULL;
  }

354
355
  {
    alpm_pkg_t *rawpkg = pmpkg_from_pyalpm_pkg(pkg);
356
    if (rawpkg) {
357
      result = alpm_sync_get_new_version(rawpkg, db_list);
358
359
    }
    alpm_list_free(db_list);
360
  }
361
362
363
  if (!result)
    Py_RETURN_NONE;
  else
364
    return pyalpm_package_from_pmpkg(result, NULL);
365
366
}

367
/* vim: set ts=2 sw=2 et: */