libopkg: implement lightweight package listing logic
[oweals/opkg-lede.git] / libopkg / opkg_cmd.c
1 /* opkg_cmd.c - the opkg package management system
2
3    Carl D. Worth
4
5    Copyright (C) 2001 University of Southern California
6
7    This program is free software; you can redistribute it and/or
8    modify it under the terms of the GNU General Public License as
9    published by the Free Software Foundation; either version 2, or (at
10    your option) any later version.
11
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16 */
17
18 #include <stdio.h>
19 #include <dirent.h>
20 #include <glob.h>
21 #include <fnmatch.h>
22 #include <signal.h>
23 #include <unistd.h>
24
25 #include "opkg_conf.h"
26 #include "opkg_cmd.h"
27 #include "opkg_message.h"
28 #include "pkg.h"
29 #include "pkg_dest.h"
30 #include "pkg_parse.h"
31 #include "sprintf_alloc.h"
32 #include "pkg.h"
33 #include "file_util.h"
34 #include "libbb/libbb.h"
35 #include "opkg_utils.h"
36 #include "opkg_defines.h"
37 #include "opkg_download.h"
38 #include "opkg_install.h"
39 #include "opkg_upgrade.h"
40 #include "opkg_remove.h"
41 #include "opkg_configure.h"
42 #include "xsystem.h"
43
44 static void print_pkg(pkg_t * pkg)
45 {
46         char *version = pkg_version_str_alloc(pkg);
47         char *description = pkg_get_string(pkg, PKG_DESCRIPTION);
48         printf("%s - %s", pkg->name, version);
49         if (conf->size)
50                 printf(" - %lu", (unsigned long) pkg_get_int(pkg, PKG_SIZE));
51         if (description)
52                 printf(" - %s", description);
53         printf("\n");
54         free(version);
55 }
56
57 int opkg_state_changed;
58
59 static void write_status_files_if_changed(void)
60 {
61         if (opkg_state_changed && !conf->noaction) {
62                 opkg_msg(INFO, "Writing status file.\n");
63                 opkg_conf_write_status_files();
64                 pkg_write_changed_filelists();
65         } else {
66                 opkg_msg(DEBUG, "Nothing to be done.\n");
67         }
68 }
69
70 static void sigint_handler(int sig)
71 {
72         signal(sig, SIG_DFL);
73         opkg_msg(NOTICE, "Interrupted. Writing out status database.\n");
74         write_status_files_if_changed();
75         exit(128 + sig);
76 }
77
78 static int opkg_update_cmd(int argc, char **argv)
79 {
80         char *tmp;
81         int err;
82         int failures;
83         int pkglist_dl_error;
84         char *lists_dir;
85         pkg_src_list_elt_t *iter;
86         pkg_src_t *src;
87
88         sprintf_alloc(&lists_dir, "%s",
89                       conf->restrict_to_default_dest ? conf->default_dest->
90                       lists_dir : conf->lists_dir);
91
92         if (!file_is_dir(lists_dir)) {
93                 if (file_exists(lists_dir)) {
94                         opkg_msg(ERROR, "%s exists, but is not a directory.\n",
95                                  lists_dir);
96                         free(lists_dir);
97                         return -1;
98                 }
99                 err = file_mkdir_hier(lists_dir, 0755);
100                 if (err) {
101                         free(lists_dir);
102                         return -1;
103                 }
104         }
105
106         failures = 0;
107
108         sprintf_alloc(&tmp, "%s/update-XXXXXX", conf->tmp_dir);
109         if (mkdtemp(tmp) == NULL) {
110                 opkg_perror(ERROR, "Failed to make temp dir %s", conf->tmp_dir);
111                 return -1;
112         }
113
114         for (iter = void_list_first(&conf->pkg_src_list); iter;
115              iter = void_list_next(&conf->pkg_src_list, iter)) {
116                 char *url, *list_file_name;
117
118                 src = (pkg_src_t *) iter->data;
119
120                 if (src->extra_data && strcmp(src->extra_data, "__dummy__ "))
121                         continue;
122
123                 if (src->extra_data)    /* debian style? */
124                         sprintf_alloc(&url, "%s/%s/%s", src->value,
125                                       src->extra_data,
126                                       src->gzip ? "Packages.gz" : "Packages");
127                 else
128                         sprintf_alloc(&url, "%s/%s", src->value,
129                                       src->gzip ? "Packages.gz" : "Packages");
130
131                 sprintf_alloc(&list_file_name, "%s/%s", lists_dir, src->name);
132                 pkglist_dl_error = 0;
133                 if (opkg_download(url, list_file_name, 0)) {
134                         failures++;
135                         pkglist_dl_error = 1;
136                         opkg_msg(NOTICE,
137                                  "*** Failed to download the package list from %s\n\n",
138                                  url);
139                 } else {
140                         opkg_msg(NOTICE,
141                                  "Updated list of available packages in %s\n",
142                                  list_file_name);
143                 }
144                 free(url);
145 #if defined(HAVE_USIGN)
146                 if (pkglist_dl_error == 0 && conf->check_signature) {
147                         /* download detached signitures to verify the package lists */
148                         /* get the url for the sig file */
149                         if (src->extra_data)    /* debian style? */
150                                 sprintf_alloc(&url, "%s/%s/%s", src->value,
151                                               src->extra_data, "Packages.sig");
152                         else
153                                 sprintf_alloc(&url, "%s/%s", src->value,
154                                               "Packages.sig");
155
156                         /* create temporary file for it */
157                         char *tmp_file_name;
158
159                         /* Put the signature in the right place */
160                         sprintf_alloc(&tmp_file_name, "%s/%s.sig", lists_dir,
161                                       src->name);
162
163                         err = opkg_download(url, tmp_file_name, 0);
164                         if (err) {
165                                 failures++;
166                                 opkg_msg(NOTICE,
167                                          "Signature file download failed.\n");
168                         } else {
169                                 err =
170                                     opkg_verify_file(list_file_name,
171                                                      tmp_file_name);
172                                 if (err == 0)
173                                         opkg_msg(NOTICE,
174                                                  "Signature check passed.\n");
175                                 else
176                                         opkg_msg(NOTICE,
177                                                  "Signature check failed.\n");
178                         }
179                         if (err && !conf->force_signature) {
180                                 /* The signature was wrong so delete it */
181                                 opkg_msg(NOTICE,
182                                          "Remove wrong Signature file.\n");
183                                 unlink(tmp_file_name);
184                                 unlink(list_file_name);
185                         }
186                         /* We shouldn't unlink the signature ! */
187                         // unlink (tmp_file_name);
188                         free(tmp_file_name);
189                         free(url);
190                 }
191 #else
192                 // Do nothing
193 #endif
194                 free(list_file_name);
195         }
196         rmdir(tmp);
197         free(tmp);
198         free(lists_dir);
199
200         return failures;
201 }
202
203 struct opkg_intercept {
204         char *oldpath;
205         char *statedir;
206 };
207
208 typedef struct opkg_intercept *opkg_intercept_t;
209
210 static opkg_intercept_t opkg_prep_intercepts(void)
211 {
212         opkg_intercept_t ctx;
213         char *newpath;
214
215         ctx = xcalloc(1, sizeof(*ctx));
216         ctx->oldpath = xstrdup(getenv("PATH"));
217
218         sprintf_alloc(&newpath, "%s/opkg/intercept:%s", DATADIR,
219                       ctx->oldpath ? ctx->oldpath : PATH_SPEC);
220
221         sprintf_alloc(&ctx->statedir, "%s/opkg-intercept-XXXXXX",
222                       conf->tmp_dir);
223
224         if (mkdtemp(ctx->statedir) == NULL) {
225                 opkg_perror(ERROR, "Failed to make temp dir %s", ctx->statedir);
226
227                 if (ctx->oldpath)
228                         free(ctx->oldpath);
229
230                 free(ctx->statedir);
231                 free(newpath);
232                 free(ctx);
233                 return NULL;
234         }
235
236         setenv("OPKG_INTERCEPT_DIR", ctx->statedir, 1);
237         setenv("PATH", newpath, 1);
238         free(newpath);
239
240         return ctx;
241 }
242
243 static int opkg_finalize_intercepts(opkg_intercept_t ctx)
244 {
245         DIR *dir;
246         int err = 0;
247
248         if (ctx->oldpath) {
249                 setenv("PATH", ctx->oldpath, 1);
250                 free(ctx->oldpath);
251         }
252         else {
253                 unsetenv("PATH");
254         }
255
256         dir = opendir(ctx->statedir);
257         if (dir) {
258                 struct dirent *de;
259                 while (de = readdir(dir), de != NULL) {
260                         char *path;
261
262                         if (de->d_name[0] == '.')
263                                 continue;
264
265                         sprintf_alloc(&path, "%s/%s", ctx->statedir,
266                                       de->d_name);
267                         if (access(path, X_OK) == 0) {
268                                 const char *argv[] = { "/bin/sh", "-c", path, NULL };
269                                 xsystem(argv);
270                         }
271                         free(path);
272                 }
273                 closedir(dir);
274         } else
275                 opkg_perror(ERROR, "Failed to open dir %s", ctx->statedir);
276
277         rm_r(ctx->statedir);
278         free(ctx->statedir);
279         free(ctx);
280
281         return err;
282 }
283
284 /* For package pkg do the following: If it is already visited, return. If not,
285    add it in visited list and recurse to its deps. Finally, add it to ordered
286    list.
287    pkg_vec all contains all available packages in repos.
288    pkg_vec visited contains packages already visited by this function, and is
289    used to end recursion and avoid an infinite loop on graph cycles.
290    pkg_vec ordered will finally contain the ordered set of packages.
291 */
292 static int
293 opkg_recurse_pkgs_in_order(pkg_t * pkg, pkg_vec_t * all,
294                            pkg_vec_t * visited, pkg_vec_t * ordered)
295 {
296         int j, k, l, m;
297         pkg_t *dep;
298         compound_depend_t *compound_depend;
299         depend_t **possible_satisfiers;
300         abstract_pkg_t *abpkg;
301         abstract_pkg_t **dependents;
302
303         /* If it's just an available package, that is, not installed and not even
304            unpacked, skip it */
305         /* XXX: This is probably an overkill, since a state_status != SS_UNPACKED
306            would do here. However, if there is an intermediate node (pkg) that is
307            configured and installed between two unpacked packages, the latter
308            won't be properly reordered, unless all installed/unpacked pkgs are
309            checked */
310         if (pkg->state_status == SS_NOT_INSTALLED)
311                 return 0;
312
313         /* If the  package has already been visited (by this function), skip it */
314         for (j = 0; j < visited->len; j++)
315                 if (!strcmp(visited->pkgs[j]->name, pkg->name)) {
316                         opkg_msg(DEBUG, "pkg %s already visited, skipping.\n",
317                                  pkg->name);
318                         return 0;
319                 }
320
321         pkg_vec_insert(visited, pkg);
322
323         opkg_msg(DEBUG, "pkg %s.\n", pkg->name);
324
325         /* Iterate over all the dependencies of pkg. For each one, find a package
326            that is either installed or unpacked and satisfies this dependency.
327            (there should only be one such package per dependency installed or
328            unpacked). Then recurse to the dependency package */
329         for (compound_depend = pkg_get_ptr(pkg, PKG_DEPENDS); compound_depend && compound_depend->type; compound_depend++) {
330                 possible_satisfiers = compound_depend->possibilities;
331                 for (k = 0; k < compound_depend->possibility_count; k++) {
332                         abpkg = possible_satisfiers[k]->pkg;
333                         dependents = abpkg->provided_by->pkgs;
334                         l = 0;
335                         if (dependents != NULL)
336                                 while (l < abpkg->provided_by->len
337                                        && dependents[l] != NULL) {
338                                         opkg_msg(DEBUG,
339                                                  "Descending on pkg %s.\n",
340                                                  dependents[l]->name);
341
342                                         /* find whether dependent l is installed or unpacked,
343                                          * and then find which package in the list satisfies it */
344                                         for (m = 0; m < all->len; m++) {
345                                                 dep = all->pkgs[m];
346                                                 if (dep->state_status !=
347                                                     SS_NOT_INSTALLED)
348                                                         if (!strcmp
349                                                             (dep->name,
350                                                              dependents[l]->
351                                                              name)) {
352                                                                 opkg_recurse_pkgs_in_order
353                                                                     (dep, all,
354                                                                      visited,
355                                                                      ordered);
356                                                                 /* Stop the outer loop */
357                                                                 l = abpkg->
358                                                                     provided_by->
359                                                                     len;
360                                                                 /* break from the inner loop */
361                                                                 break;
362                                                         }
363                                         }
364                                         l++;
365                                 }
366                 }
367         }
368
369         /* When all recursions from this node down, are over, and all
370            dependencies have been added in proper order in the ordered array, add
371            also the package pkg to ordered array */
372         pkg_vec_insert(ordered, pkg);
373
374         return 0;
375
376 }
377
378 static int opkg_configure_packages(char *pkg_name)
379 {
380         pkg_vec_t *all, *ordered, *visited;
381         int i;
382         pkg_t *pkg;
383         opkg_intercept_t ic;
384         int r, err = 0;
385
386         opkg_msg(INFO, "Configuring unpacked packages.\n");
387
388         all = pkg_vec_alloc();
389
390         pkg_hash_fetch_available(all);
391
392         /* Reorder pkgs in order to be configured according to the Depends: tag
393            order */
394         opkg_msg(INFO, "Reordering packages before configuring them...\n");
395         ordered = pkg_vec_alloc();
396         visited = pkg_vec_alloc();
397         for (i = 0; i < all->len; i++) {
398                 pkg = all->pkgs[i];
399                 opkg_recurse_pkgs_in_order(pkg, all, visited, ordered);
400         }
401
402         ic = opkg_prep_intercepts();
403         if (ic == NULL) {
404                 err = -1;
405                 goto error;
406         }
407
408         for (i = 0; i < ordered->len; i++) {
409                 pkg = ordered->pkgs[i];
410
411                 if (pkg_name && fnmatch(pkg_name, pkg->name, conf->nocase))
412                         continue;
413
414                 if (pkg->state_status == SS_UNPACKED) {
415                         opkg_msg(NOTICE, "Configuring %s.\n", pkg->name);
416                         r = opkg_configure(pkg);
417                         if (r == 0) {
418                                 pkg->state_status = SS_INSTALLED;
419                                 pkg->parent->state_status = SS_INSTALLED;
420                                 pkg->state_flag &= ~SF_PREFER;
421                                 opkg_state_changed++;
422                         } else {
423                                 err = -1;
424                         }
425                 }
426         }
427
428         if (opkg_finalize_intercepts(ic))
429                 err = -1;
430
431 error:
432         pkg_vec_free(all);
433         pkg_vec_free(ordered);
434         pkg_vec_free(visited);
435
436         return err;
437 }
438
439 static int opkg_remove_cmd(int argc, char **argv);
440
441 static int opkg_install_cmd(int argc, char **argv)
442 {
443         int i;
444         char *arg;
445         int err = 0;
446
447         signal(SIGINT, sigint_handler);
448
449         /*
450          * Now scan through package names and install
451          */
452         for (i = 0; i < argc; i++) {
453                 arg = argv[i];
454
455                 opkg_msg(DEBUG2, "%s\n", arg);
456                 if (opkg_prepare_url_for_install(arg, &argv[i]))
457                         return -1;
458         }
459
460         pkg_hash_load_package_details();
461         pkg_hash_load_status_files(NULL, NULL);
462
463         if (conf->force_reinstall) {
464                 int saved_force_depends = conf->force_depends;
465                 conf->force_depends = 1;
466                 (void)opkg_remove_cmd(argc, argv);
467                 conf->force_depends = saved_force_depends;
468                 conf->force_reinstall = 0;
469         }
470
471         pkg_info_preinstall_check();
472
473         for (i = 0; i < argc; i++) {
474                 arg = argv[i];
475                 if (opkg_install_by_name(arg)) {
476                         opkg_msg(ERROR, "Cannot install package %s.\n", arg);
477                         err = -1;
478                 }
479         }
480
481         if (opkg_configure_packages(NULL))
482                 err = -1;
483
484         write_status_files_if_changed();
485
486         return err;
487 }
488
489 static int opkg_upgrade_cmd(int argc, char **argv)
490 {
491         int i;
492         pkg_t *pkg;
493         int err = 0;
494
495         signal(SIGINT, sigint_handler);
496
497         if (argc) {
498                 for (i = 0; i < argc; i++) {
499                         char *arg = argv[i];
500
501                         if (opkg_prepare_url_for_install(arg, &arg))
502                                 return -1;
503                 }
504                 pkg_info_preinstall_check();
505
506                 for (i = 0; i < argc; i++) {
507                         char *arg = argv[i];
508                         if (conf->restrict_to_default_dest) {
509                                 pkg =
510                                     pkg_hash_fetch_installed_by_name_dest(argv
511                                                                           [i],
512                                                                           conf->
513                                                                           default_dest);
514                                 if (pkg == NULL) {
515                                         opkg_msg(NOTICE,
516                                                  "Package %s not installed in %s.\n",
517                                                  argv[i],
518                                                  conf->default_dest->name);
519                                         continue;
520                                 }
521                         } else {
522                                 pkg = pkg_hash_fetch_installed_by_name(argv[i]);
523                         }
524                         if (pkg) {
525                                 if (opkg_upgrade_pkg(pkg))
526                                         err = -1;
527                         } else {
528                                 if (opkg_install_by_name(arg))
529                                         err = -1;
530                         }
531                 }
532         }
533
534         if (opkg_configure_packages(NULL))
535                 err = -1;
536
537         write_status_files_if_changed();
538
539         return err;
540 }
541
542 static int opkg_download_cmd(int argc, char **argv)
543 {
544         int i, err = 0;
545         char *arg;
546         pkg_t *pkg;
547
548         pkg_info_preinstall_check();
549         for (i = 0; i < argc; i++) {
550                 arg = argv[i];
551
552                 pkg = pkg_hash_fetch_best_installation_candidate_by_name(arg);
553                 if (pkg == NULL) {
554                         opkg_msg(ERROR, "Cannot find package %s.\n", arg);
555                         continue;
556                 }
557
558                 if (opkg_download_pkg(pkg, "."))
559                         err = -1;
560
561                 if (err) {
562                         opkg_msg(ERROR, "Failed to download %s.\n", pkg->name);
563                 } else {
564                         opkg_msg(NOTICE, "Downloaded %s as %s.\n",
565                                  pkg->name, pkg_get_string(pkg, PKG_LOCAL_FILENAME));
566                 }
567         }
568
569         return err;
570 }
571
572 struct opkg_list_find_cmd_item {
573         int size;
574         char *name;
575         char *version;
576         char *description;
577 };
578
579 struct opkg_list_find_cmd_args {
580         int use_desc;
581         int set_status;
582         char *pkg_name;
583         struct opkg_list_find_cmd_item **items;
584         size_t n_items;
585 };
586
587 static void opkg_list_find_cmd_cb(pkg_t *pkg, void *priv)
588 {
589         struct opkg_list_find_cmd_args *args = priv;
590         char *description = pkg_get_string(pkg, PKG_DESCRIPTION);
591         char *version = pkg_version_str_alloc(pkg);
592         struct opkg_list_find_cmd_item *item;
593         char *nameptr, *versionptr, *descriptionptr;
594         int i, found = 0;
595
596         /* if we have package name or pattern and pkg does not match, then skip it */
597         if (args->pkg_name && fnmatch(args->pkg_name, pkg->name, conf->nocase) &&
598             (!args->use_desc || !description
599              || fnmatch(args->pkg_name, description, conf->nocase)))
600                 goto out;
601
602         if (args->set_status) {
603                 for (i = 0; i < args->n_items; i++) {
604                         if (!strcmp(args->items[i]->name, pkg->name)) {
605                                 found = 1;
606                                 break;
607                         }
608                 }
609         }
610
611         if (!found) {
612                 item = calloc_a(sizeof(*item),
613                                 &nameptr, strlen(pkg->name) + 1,
614                                 &versionptr, strlen(version) + 1,
615                                 &descriptionptr, description ? strlen(description) + 1 : 0);
616
617                 item->name = strcpy(nameptr, pkg->name);
618                 item->size = pkg_get_int(pkg, PKG_SIZE);
619                 item->version = strcpy(versionptr, version);
620                 item->description = description ? strcpy(descriptionptr, description) : NULL;
621
622                 args->items = xrealloc(args->items, sizeof(item) * (args->n_items + 1));
623                 args->items[args->n_items++] = item;
624         }
625
626 out:
627         pkg_deinit(pkg);
628         free(pkg);
629         free(version);
630 }
631
632 static int opkg_list_find_cmd_sort(const void *a, const void *b)
633 {
634         const struct opkg_list_find_cmd_item *pkg_a = *(const struct opkg_list_find_cmd_item **)a;
635         const struct opkg_list_find_cmd_item *pkg_b = *(const struct opkg_list_find_cmd_item **)b;
636         return strcmp(pkg_a->name, pkg_b->name);
637 }
638
639 static int opkg_list_find_cmd(int argc, char **argv, int use_desc)
640 {
641         int i;
642         struct opkg_list_find_cmd_args args = {
643                 .use_desc = use_desc,
644                 .pkg_name = (argc > 0) ? argv[0] : NULL
645         };
646
647         args.set_status = 0;
648         pkg_hash_load_feeds(SF_NEED_DETAIL, opkg_list_find_cmd_cb, &args);
649
650         args.set_status = 1;
651         pkg_hash_load_status_files(opkg_list_find_cmd_cb, &args);
652
653         if (args.n_items > 1)
654                 qsort(args.items, args.n_items, sizeof(args.items[0]),
655                       opkg_list_find_cmd_sort);
656
657         for (i = 0; i < args.n_items; i++) {
658                 printf("%s - %s",
659                        args.items[i]->name,
660                        args.items[i]->version);
661
662                 if (conf->size)
663                         printf(" - %lu", (unsigned long) args.items[i]->size);
664
665                 if (args.items[i]->description)
666                         printf(" - %s", args.items[i]->description);
667
668                 printf("\n");
669
670                 free(args.items[i]);
671         }
672
673         free(args.items);
674
675         return 0;
676 }
677
678 static int opkg_list_cmd(int argc, char **argv)
679 {
680         return opkg_list_find_cmd(argc, argv, 0);
681 }
682
683 static int opkg_find_cmd(int argc, char **argv)
684 {
685         return opkg_list_find_cmd(argc, argv, 1);
686 }
687
688 static int opkg_list_installed_cmd(int argc, char **argv)
689 {
690         int i;
691         pkg_vec_t *available;
692         pkg_t *pkg;
693         char *pkg_name = NULL;
694
695         if (argc > 0) {
696                 pkg_name = argv[0];
697         }
698         available = pkg_vec_alloc();
699         pkg_hash_fetch_all_installed(available);
700         pkg_vec_sort(available, pkg_compare_names);
701         for (i = 0; i < available->len; i++) {
702                 pkg = available->pkgs[i];
703                 /* if we have package name or pattern and pkg does not match, then skip it */
704                 if (pkg_name && fnmatch(pkg_name, pkg->name, conf->nocase))
705                         continue;
706                 print_pkg(pkg);
707         }
708
709         pkg_vec_free(available);
710
711         return 0;
712 }
713
714 static int opkg_list_changed_conffiles_cmd(int argc, char **argv)
715 {
716         int i;
717         pkg_vec_t *available;
718         pkg_t *pkg;
719         char *pkg_name = NULL;
720         conffile_list_elt_t *iter;
721         conffile_list_t *cl;
722         conffile_t *cf;
723
724         if (argc > 0) {
725                 pkg_name = argv[0];
726         }
727         available = pkg_vec_alloc();
728         pkg_hash_fetch_all_installed(available);
729         pkg_vec_sort(available, pkg_compare_names);
730         for (i = 0; i < available->len; i++) {
731                 pkg = available->pkgs[i];
732                 cl = pkg_get_ptr(pkg, PKG_CONFFILES);
733                 /* if we have package name or pattern and pkg does not match, then skip it */
734                 if (pkg_name && fnmatch(pkg_name, pkg->name, conf->nocase))
735                         continue;
736                 if (!cl || nv_pair_list_empty(cl))
737                         continue;
738                 for (iter = nv_pair_list_first(cl); iter;
739                      iter = nv_pair_list_next(cl, iter)) {
740                         cf = (conffile_t *) iter->data;
741                         if (cf->name && cf->value
742                             && conffile_has_been_modified(cf))
743                                 printf("%s\n", cf->name);
744                 }
745         }
746         pkg_vec_free(available);
747         return 0;
748 }
749
750 static int opkg_list_upgradable_cmd(int argc, char **argv)
751 {
752         struct active_list *head = prepare_upgrade_list();
753         struct active_list *node = NULL;
754         pkg_t *_old_pkg, *_new_pkg;
755         char *old_v, *new_v;
756         for (node = active_list_next(head, head); node;
757              node = active_list_next(head, node)) {
758                 _old_pkg = node->pkg;
759                 _new_pkg =
760                     pkg_hash_fetch_best_installation_candidate_by_name
761                     (_old_pkg->name);
762                 if (_new_pkg == NULL)
763                         continue;
764                 old_v = pkg_version_str_alloc(_old_pkg);
765                 new_v = pkg_version_str_alloc(_new_pkg);
766                 printf("%s - %s - %s\n", _old_pkg->name, old_v, new_v);
767                 free(old_v);
768                 free(new_v);
769         }
770         active_list_head_delete(head);
771         return 0;
772 }
773
774 static int opkg_info_status_cmd(int argc, char **argv, int installed_only)
775 {
776         int i;
777         pkg_vec_t *available;
778         pkg_t *pkg;
779         char *pkg_name = NULL;
780         conffile_list_t *cl;
781
782         if (argc > 0) {
783                 pkg_name = argv[0];
784         }
785
786         available = pkg_vec_alloc();
787         if (installed_only)
788                 pkg_hash_fetch_all_installed(available);
789         else
790                 pkg_hash_fetch_available(available);
791
792         for (i = 0; i < available->len; i++) {
793                 pkg = available->pkgs[i];
794                 if (pkg_name && fnmatch(pkg_name, pkg->name, conf->nocase)) {
795                         continue;
796                 }
797
798                 pkg_formatted_info(stdout, pkg);
799
800                 cl = pkg_get_ptr(pkg, PKG_CONFFILES);
801
802                 if (conf->verbosity >= NOTICE && cl) {
803                         conffile_list_elt_t *iter;
804                         for (iter = nv_pair_list_first(cl); iter;
805                              iter = nv_pair_list_next(cl, iter)) {
806                                 conffile_t *cf = (conffile_t *) iter->data;
807                                 int modified = conffile_has_been_modified(cf);
808                                 if (cf->value)
809                                         opkg_msg(INFO,
810                                                  "conffile=%s md5sum=%s modified=%d.\n",
811                                                  cf->name, cf->value, modified);
812                         }
813                 }
814         }
815         pkg_vec_free(available);
816
817         return 0;
818 }
819
820 static int opkg_info_cmd(int argc, char **argv)
821 {
822         return opkg_info_status_cmd(argc, argv, 0);
823 }
824
825 static int opkg_status_cmd(int argc, char **argv)
826 {
827         return opkg_info_status_cmd(argc, argv, 1);
828 }
829
830 static int opkg_configure_cmd(int argc, char **argv)
831 {
832         int err;
833         char *pkg_name = NULL;
834
835         if (argc > 0)
836                 pkg_name = argv[0];
837
838         err = opkg_configure_packages(pkg_name);
839
840         write_status_files_if_changed();
841
842         return err;
843 }
844
845 static int opkg_remove_cmd(int argc, char **argv)
846 {
847         int i, a, done, err = 0;
848         pkg_t *pkg;
849         pkg_t *pkg_to_remove;
850         pkg_vec_t *available;
851
852         done = 0;
853
854         signal(SIGINT, sigint_handler);
855
856         pkg_info_preinstall_check();
857
858         available = pkg_vec_alloc();
859         pkg_hash_fetch_all_installed(available);
860
861         for (i = 0; i < argc; i++) {
862                 for (a = 0; a < available->len; a++) {
863                         pkg = available->pkgs[a];
864                         if (fnmatch(argv[i], pkg->name, conf->nocase)) {
865                                 continue;
866                         }
867                         if (conf->restrict_to_default_dest) {
868                                 pkg_to_remove =
869                                     pkg_hash_fetch_installed_by_name_dest(pkg->
870                                                                           name,
871                                                                           conf->
872                                                                           default_dest);
873                         } else {
874                                 pkg_to_remove =
875                                     pkg_hash_fetch_installed_by_name(pkg->name);
876                         }
877
878                         if (pkg_to_remove == NULL) {
879                                 opkg_msg(ERROR,
880                                          "Package %s is not installed.\n",
881                                          pkg->name);
882                                 continue;
883                         }
884                         if (pkg->state_status == SS_NOT_INSTALLED) {
885                                 opkg_msg(ERROR, "Package %s not installed.\n",
886                                          pkg->name);
887                                 continue;
888                         }
889
890                         if (opkg_remove_pkg(pkg_to_remove, 0))
891                                 err = -1;
892                         else
893                                 done = 1;
894                 }
895         }
896
897         pkg_vec_free(available);
898
899         if (done == 0)
900                 opkg_msg(NOTICE, "No packages removed.\n");
901
902         write_status_files_if_changed();
903         return err;
904 }
905
906 static int opkg_flag_cmd(int argc, char **argv)
907 {
908         int i;
909         pkg_t *pkg;
910         const char *flags = argv[0];
911
912         signal(SIGINT, sigint_handler);
913
914         for (i = 1; i < argc; i++) {
915                 if (conf->restrict_to_default_dest) {
916                         pkg = pkg_hash_fetch_installed_by_name_dest(argv[i],
917                                                                     conf->
918                                                                     default_dest);
919                 } else {
920                         pkg = pkg_hash_fetch_installed_by_name(argv[i]);
921                 }
922
923                 if (pkg == NULL) {
924                         opkg_msg(ERROR, "Package %s is not installed.\n",
925                                  argv[i]);
926                         continue;
927                 }
928                 if ((strcmp(flags, "hold") == 0)
929                     || (strcmp(flags, "noprune") == 0)
930                     || (strcmp(flags, "user") == 0)
931                     || (strcmp(flags, "ok") == 0)) {
932                         pkg->state_flag = pkg_state_flag_from_str(flags);
933                 }
934
935                 /*
936                  * Useful if a package is installed in an offline_root, and
937                  * should be configured by opkg-cl configure at a later date.
938                  */
939                 if ((strcmp(flags, "installed") == 0)
940                     || (strcmp(flags, "unpacked") == 0)) {
941                         pkg->state_status = pkg_state_status_from_str(flags);
942                 }
943
944                 opkg_state_changed++;
945                 opkg_msg(NOTICE, "Setting flags for package %s to %s.\n",
946                          pkg->name, flags);
947         }
948
949         write_status_files_if_changed();
950         return 0;
951 }
952
953 static int opkg_files_cmd(int argc, char **argv)
954 {
955         pkg_t *pkg;
956         str_list_t *files;
957         str_list_elt_t *iter;
958         char *pkg_version;
959
960         if (argc < 1) {
961                 return -1;
962         }
963
964         pkg = pkg_hash_fetch_installed_by_name(argv[0]);
965         if (pkg == NULL) {
966                 opkg_msg(ERROR, "Package %s not installed.\n", argv[0]);
967                 return 0;
968         }
969
970         files = pkg_get_installed_files(pkg);
971         pkg_version = pkg_version_str_alloc(pkg);
972
973         printf
974             ("Package %s (%s) is installed on %s and has the following files:\n",
975              pkg->name, pkg_version, pkg->dest->name);
976
977         for (iter = str_list_first(files); iter;
978              iter = str_list_next(files, iter))
979                 printf("%s\n", (char *)iter->data);
980
981         free(pkg_version);
982         pkg_free_installed_files(pkg);
983
984         return 0;
985 }
986
987 static int opkg_depends_cmd(int argc, char **argv)
988 {
989         int i, j, k;
990         pkg_vec_t *available_pkgs;
991         compound_depend_t *cdep;
992         pkg_t *pkg;
993         char *str;
994
995         pkg_info_preinstall_check();
996
997         available_pkgs = pkg_vec_alloc();
998         if (conf->query_all)
999                 pkg_hash_fetch_available(available_pkgs);
1000         else
1001                 pkg_hash_fetch_all_installed(available_pkgs);
1002
1003         for (i = 0; i < argc; i++) {
1004                 for (j = 0; j < available_pkgs->len; j++) {
1005                         pkg = available_pkgs->pkgs[j];
1006
1007                         if (fnmatch(argv[i], pkg->name, conf->nocase) != 0)
1008                                 continue;
1009
1010                         opkg_msg(NOTICE, "%s depends on:\n", pkg->name);
1011
1012                         for (k = 0, cdep = pkg_get_ptr(pkg, PKG_DEPENDS); cdep && cdep->type; k++, cdep++) {
1013                                 if (cdep->type != DEPEND)
1014                                         continue;
1015
1016                                 str = pkg_depend_str(pkg, k);
1017                                 opkg_msg(NOTICE, "\t%s\n", str);
1018                                 free(str);
1019                         }
1020
1021                 }
1022         }
1023
1024         pkg_vec_free(available_pkgs);
1025         return 0;
1026 }
1027
1028 static int pkg_mark_provides(pkg_t * pkg)
1029 {
1030         abstract_pkg_t **provider = pkg_get_ptr(pkg, PKG_PROVIDES);
1031
1032         pkg->parent->state_flag |= SF_MARKED;
1033
1034         while (provider && *provider) {
1035                 (*provider)->state_flag |= SF_MARKED;
1036                 provider++;
1037         }
1038
1039         return 0;
1040 }
1041
1042 enum what_field_type {
1043         WHATDEPENDS,
1044         WHATCONFLICTS,
1045         WHATPROVIDES,
1046         WHATREPLACES,
1047         WHATRECOMMENDS,
1048         WHATSUGGESTS
1049 };
1050
1051 static int
1052 opkg_what_depends_conflicts_cmd(enum depend_type what_field_type, int recursive,
1053                                 int argc, char **argv)
1054 {
1055         depend_t *possibility;
1056         compound_depend_t *cdep, *deps;
1057         pkg_vec_t *available_pkgs;
1058         pkg_t *pkg;
1059         int i, j, l;
1060         int changed;
1061         const char *rel_str = NULL;
1062         char *ver;
1063
1064         switch (what_field_type) {
1065         case DEPEND:
1066                 rel_str = "depends on";
1067                 break;
1068         case CONFLICTS:
1069                 rel_str = "conflicts with";
1070                 break;
1071         case SUGGEST:
1072                 rel_str = "suggests";
1073                 break;
1074         case RECOMMEND:
1075                 rel_str = "recommends";
1076                 break;
1077         default:
1078                 return -1;
1079         }
1080
1081         available_pkgs = pkg_vec_alloc();
1082
1083         if (conf->query_all)
1084                 pkg_hash_fetch_available(available_pkgs);
1085         else
1086                 pkg_hash_fetch_all_installed(available_pkgs);
1087
1088         /* mark the root set */
1089         pkg_vec_clear_marks(available_pkgs);
1090         opkg_msg(NOTICE, "Root set:\n");
1091         for (i = 0; i < argc; i++)
1092                 pkg_vec_mark_if_matches(available_pkgs, argv[i]);
1093
1094         for (i = 0; i < available_pkgs->len; i++) {
1095                 pkg = available_pkgs->pkgs[i];
1096                 if (pkg->state_flag & SF_MARKED) {
1097                         /* mark the parent (abstract) package */
1098                         pkg_mark_provides(pkg);
1099                         opkg_msg(NOTICE, "  %s\n", pkg->name);
1100                 }
1101         }
1102
1103         opkg_msg(NOTICE, "What %s root set\n", rel_str);
1104         do {
1105                 changed = 0;
1106
1107                 for (j = 0; j < available_pkgs->len; j++) {
1108
1109                         pkg = available_pkgs->pkgs[j];
1110                         /*
1111                         count = ((what_field_type == CONFLICTS)
1112                                  ? pkg->conflicts_count
1113                                  : pkg->pre_depends_count +
1114                                  pkg->depends_count +
1115                                  pkg->recommends_count + pkg->suggests_count);
1116                                  */
1117
1118                         /* skip this package if it is already marked */
1119                         if (pkg->parent->state_flag & SF_MARKED)
1120                                 continue;
1121
1122                         deps = pkg_get_ptr(pkg, (what_field_type == CONFLICTS) ? PKG_CONFLICTS : PKG_DEPENDS);
1123
1124                         for (cdep = deps; cdep && cdep->type; cdep++) {
1125                                 if (what_field_type != cdep->type)
1126                                         continue;
1127
1128                                 for (l = 0; l < cdep->possibility_count; l++) {
1129                                         possibility = cdep->possibilities[l];
1130
1131                                         if ((possibility->pkg->state_flag
1132                                              & SF_MARKED)
1133                                             != SF_MARKED)
1134                                                 continue;
1135
1136                                         /* mark the depending package so we
1137                                          * won't visit it again */
1138                                         pkg->state_flag |= SF_MARKED;
1139                                         pkg_mark_provides(pkg);
1140                                         changed++;
1141
1142                                         ver = pkg_version_str_alloc(pkg);
1143                                         opkg_msg(NOTICE, "\t%s %s\t%s %s",
1144                                                  pkg->name,
1145                                                  ver,
1146                                                  rel_str,
1147                                                  possibility->pkg->name);
1148                                         free(ver);
1149                                         if (possibility->version) {
1150                                                 opkg_msg(NOTICE, " (%s%s)",
1151                                                          constraint_to_str
1152                                                          (possibility->
1153                                                           constraint),
1154                                                          possibility->version);
1155                                         }
1156                                         if (!pkg_dependence_satisfiable
1157                                             (possibility))
1158                                                 opkg_msg(NOTICE,
1159                                                          " unsatisfiable");
1160                                         opkg_message(NOTICE, "\n");
1161                                         goto next_package;
1162                                 }
1163                         }
1164 next_package:
1165                         ;
1166                 }
1167         } while (changed && recursive);
1168
1169         pkg_vec_free(available_pkgs);
1170
1171         return 0;
1172 }
1173
1174 static int opkg_whatdepends_recursively_cmd(int argc, char **argv)
1175 {
1176         return opkg_what_depends_conflicts_cmd(DEPEND, 1, argc, argv);
1177 }
1178
1179 static int opkg_whatdepends_cmd(int argc, char **argv)
1180 {
1181         return opkg_what_depends_conflicts_cmd(DEPEND, 0, argc, argv);
1182 }
1183
1184 static int opkg_whatsuggests_cmd(int argc, char **argv)
1185 {
1186         return opkg_what_depends_conflicts_cmd(SUGGEST, 0, argc, argv);
1187 }
1188
1189 static int opkg_whatrecommends_cmd(int argc, char **argv)
1190 {
1191         return opkg_what_depends_conflicts_cmd(RECOMMEND, 0, argc, argv);
1192 }
1193
1194 static int opkg_whatconflicts_cmd(int argc, char **argv)
1195 {
1196         return opkg_what_depends_conflicts_cmd(CONFLICTS, 0, argc, argv);
1197 }
1198
1199 static int
1200 opkg_what_provides_replaces_cmd(enum what_field_type what_field_type, int argc,
1201                                 char **argv)
1202 {
1203         abstract_pkg_t *apkg, **abpkgs;
1204
1205         if (argc > 0) {
1206                 pkg_vec_t *available_pkgs = pkg_vec_alloc();
1207                 const char *rel_str =
1208                     (what_field_type == WHATPROVIDES ? "provides" : "replaces");
1209                 int i;
1210
1211                 pkg_info_preinstall_check();
1212
1213                 if (conf->query_all)
1214                         pkg_hash_fetch_available(available_pkgs);
1215                 else
1216                         pkg_hash_fetch_all_installed(available_pkgs);
1217                 for (i = 0; i < argc; i++) {
1218                         const char *target = argv[i];
1219                         int j;
1220
1221                         opkg_msg(NOTICE, "What %s %s\n", rel_str, target);
1222                         for (j = 0; j < available_pkgs->len; j++) {
1223                                 pkg_t *pkg = available_pkgs->pkgs[j];
1224                                 abpkgs = pkg_get_ptr(pkg, (what_field_type == WHATPROVIDES) ? PKG_PROVIDES : PKG_REPLACES);
1225
1226                                 while (abpkgs && *abpkgs) {
1227                                         apkg = *abpkgs++;
1228
1229                                         if (fnmatch(target, apkg->name, conf->nocase))
1230                                                 continue;
1231
1232                                         opkg_msg(NOTICE, "    %s", pkg->name);
1233
1234                                         if ((conf->nocase ? strcasecmp(target, apkg->name)
1235                                             : strcmp(target, apkg->name)))
1236                                                 opkg_msg(NOTICE, "\t%s %s\n", rel_str, apkg->name);
1237
1238                                         opkg_message(NOTICE, "\n");
1239                                 }
1240                         }
1241                 }
1242                 pkg_vec_free(available_pkgs);
1243         }
1244         return 0;
1245 }
1246
1247 static int opkg_whatprovides_cmd(int argc, char **argv)
1248 {
1249         return opkg_what_provides_replaces_cmd(WHATPROVIDES, argc, argv);
1250 }
1251
1252 static int opkg_whatreplaces_cmd(int argc, char **argv)
1253 {
1254         return opkg_what_provides_replaces_cmd(WHATREPLACES, argc, argv);
1255 }
1256
1257 static int opkg_search_cmd(int argc, char **argv)
1258 {
1259         int i;
1260
1261         pkg_vec_t *installed;
1262         pkg_t *pkg;
1263         str_list_t *installed_files;
1264         str_list_elt_t *iter;
1265         char *installed_file;
1266
1267         if (argc < 1) {
1268                 return -1;
1269         }
1270
1271         installed = pkg_vec_alloc();
1272         pkg_hash_fetch_all_installed(installed);
1273         pkg_vec_sort(installed, pkg_compare_names);
1274
1275         for (i = 0; i < installed->len; i++) {
1276                 pkg = installed->pkgs[i];
1277
1278                 installed_files = pkg_get_installed_files(pkg);
1279
1280                 for (iter = str_list_first(installed_files); iter;
1281                      iter = str_list_next(installed_files, iter)) {
1282                         installed_file = (char *)iter->data;
1283                         if (fnmatch(argv[0], installed_file, conf->nocase) == 0)
1284                                 print_pkg(pkg);
1285                 }
1286
1287                 pkg_free_installed_files(pkg);
1288         }
1289
1290         pkg_vec_free(installed);
1291
1292         return 0;
1293 }
1294
1295 static int opkg_compare_versions_cmd(int argc, char **argv)
1296 {
1297         int rc;
1298         pkg_t *p1, *p2;
1299
1300         if (argc == 3) {
1301                 /* this is a bit gross */
1302                 p1 = pkg_new();
1303                 p2 = pkg_new();
1304                 parse_version(p1, argv[0]);
1305                 parse_version(p2, argv[2]);
1306                 rc = pkg_version_satisfied(p1, p2, argv[1]);
1307                 pkg_deinit(p1);
1308                 pkg_deinit(p2);
1309                 free(p1);
1310                 free(p2);
1311                 return rc ? 0 : 1;
1312         } else {
1313                 opkg_msg(ERROR,
1314                          "opkg compare_versions <v1> <op> <v2>\n"
1315                          "<op> is one of <= >= << >> =\n");
1316                 return -1;
1317         }
1318 }
1319
1320 static int opkg_print_architecture_cmd(int argc, char **argv)
1321 {
1322         nv_pair_list_elt_t *l;
1323
1324         list_for_each_entry(l, &conf->arch_list.head, node) {
1325                 nv_pair_t *nv = (nv_pair_t *) l->data;
1326                 printf("arch %s %s\n", nv->name, nv->value);
1327         }
1328         return 0;
1329 }
1330
1331 /* XXX: CLEANUP: The usage strings should be incorporated into this
1332    array for easier maintenance */
1333 static opkg_cmd_t cmds[] = {
1334         {"update", 0, (opkg_cmd_fun_t) opkg_update_cmd,
1335          PFM_DESCRIPTION | PFM_SOURCE},
1336         {"upgrade", 1, (opkg_cmd_fun_t) opkg_upgrade_cmd,
1337          PFM_DESCRIPTION | PFM_SOURCE},
1338         {"list", 0, (opkg_cmd_fun_t) opkg_list_cmd, PFM_SOURCE},
1339         {"list_installed", 0, (opkg_cmd_fun_t) opkg_list_installed_cmd,
1340          PFM_SOURCE},
1341         {"list-installed", 0, (opkg_cmd_fun_t) opkg_list_installed_cmd,
1342          PFM_SOURCE},
1343         {"list_upgradable", 0, (opkg_cmd_fun_t) opkg_list_upgradable_cmd,
1344          PFM_SOURCE},
1345         {"list-upgradable", 0, (opkg_cmd_fun_t) opkg_list_upgradable_cmd,
1346          PFM_SOURCE},
1347         {"list_changed_conffiles", 0,
1348          (opkg_cmd_fun_t) opkg_list_changed_conffiles_cmd, PFM_SOURCE},
1349         {"list-changed-conffiles", 0,
1350          (opkg_cmd_fun_t) opkg_list_changed_conffiles_cmd, PFM_SOURCE},
1351         {"info", 0, (opkg_cmd_fun_t) opkg_info_cmd, 0},
1352         {"flag", 1, (opkg_cmd_fun_t) opkg_flag_cmd,
1353          PFM_DESCRIPTION | PFM_SOURCE},
1354         {"status", 0, (opkg_cmd_fun_t) opkg_status_cmd,
1355          PFM_DESCRIPTION | PFM_SOURCE},
1356         {"install", 1, (opkg_cmd_fun_t) opkg_install_cmd,
1357          PFM_DESCRIPTION | PFM_SOURCE},
1358         {"remove", 1, (opkg_cmd_fun_t) opkg_remove_cmd,
1359          PFM_DESCRIPTION | PFM_SOURCE},
1360         {"configure", 0, (opkg_cmd_fun_t) opkg_configure_cmd,
1361          PFM_DESCRIPTION | PFM_SOURCE},
1362         {"files", 1, (opkg_cmd_fun_t) opkg_files_cmd,
1363          PFM_DESCRIPTION | PFM_SOURCE},
1364         {"search", 1, (opkg_cmd_fun_t) opkg_search_cmd,
1365          PFM_DESCRIPTION | PFM_SOURCE},
1366         {"find", 1, (opkg_cmd_fun_t) opkg_find_cmd, PFM_SOURCE},
1367         {"download", 1, (opkg_cmd_fun_t) opkg_download_cmd,
1368          PFM_DESCRIPTION | PFM_SOURCE},
1369         {"compare_versions", 1, (opkg_cmd_fun_t) opkg_compare_versions_cmd, 0},
1370         {"compare-versions", 1, (opkg_cmd_fun_t) opkg_compare_versions_cmd, 0},
1371         {"print-architecture", 0, (opkg_cmd_fun_t) opkg_print_architecture_cmd,
1372          PFM_DESCRIPTION | PFM_SOURCE},
1373         {"print_architecture", 0, (opkg_cmd_fun_t) opkg_print_architecture_cmd,
1374          PFM_DESCRIPTION | PFM_SOURCE},
1375         {"print-installation-architecture", 0,
1376          (opkg_cmd_fun_t) opkg_print_architecture_cmd,
1377          PFM_DESCRIPTION | PFM_SOURCE},
1378         {"print_installation_architecture", 0,
1379          (opkg_cmd_fun_t) opkg_print_architecture_cmd,
1380          PFM_DESCRIPTION | PFM_SOURCE},
1381         {"depends", 1, (opkg_cmd_fun_t) opkg_depends_cmd,
1382          PFM_DESCRIPTION | PFM_SOURCE},
1383         {"whatdepends", 1, (opkg_cmd_fun_t) opkg_whatdepends_cmd,
1384          PFM_DESCRIPTION | PFM_SOURCE},
1385         {"whatdependsrec", 1, (opkg_cmd_fun_t) opkg_whatdepends_recursively_cmd,
1386          PFM_DESCRIPTION | PFM_SOURCE},
1387         {"whatrecommends", 1, (opkg_cmd_fun_t) opkg_whatrecommends_cmd,
1388          PFM_DESCRIPTION | PFM_SOURCE},
1389         {"whatsuggests", 1, (opkg_cmd_fun_t) opkg_whatsuggests_cmd,
1390          PFM_DESCRIPTION | PFM_SOURCE},
1391         {"whatprovides", 1, (opkg_cmd_fun_t) opkg_whatprovides_cmd,
1392          PFM_DESCRIPTION | PFM_SOURCE},
1393         {"whatreplaces", 1, (opkg_cmd_fun_t) opkg_whatreplaces_cmd,
1394          PFM_DESCRIPTION | PFM_SOURCE},
1395         {"whatconflicts", 1, (opkg_cmd_fun_t) opkg_whatconflicts_cmd,
1396          PFM_DESCRIPTION | PFM_SOURCE},
1397 };
1398
1399 opkg_cmd_t *opkg_cmd_find(const char *name)
1400 {
1401         int i;
1402         opkg_cmd_t *cmd;
1403         int num_cmds = sizeof(cmds) / sizeof(opkg_cmd_t);
1404
1405         for (i = 0; i < num_cmds; i++) {
1406                 cmd = &cmds[i];
1407                 if (strcmp(name, cmd->name) == 0)
1408                         return cmd;
1409         }
1410
1411         return NULL;
1412 }
1413
1414 int opkg_cmd_exec(opkg_cmd_t * cmd, int argc, const char **argv)
1415 {
1416         return (cmd->fun) (argc, argv);
1417 }