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