Running a script does not change the state, modifying the state flags does.
[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 "xsystem.h"
42
43 static void
44 print_pkg(pkg_t *pkg)
45 {
46         char *version = pkg_version_str_alloc(pkg);
47         if (pkg->description)
48                 printf("%s - %s - %s\n", pkg->name, version, pkg->description);
49         else
50                 printf("%s - %s\n", pkg->name, version);
51         free(version);
52 }
53
54 int opkg_state_changed;
55
56 static void
57 write_status_files_if_changed(void)
58 {
59      if (opkg_state_changed && !conf->noaction) {
60           opkg_msg(INFO, "Writing status file.\n");
61           opkg_conf_write_status_files();
62           pkg_write_changed_filelists();
63      } else { 
64           opkg_msg(DEBUG, "Nothing to be done.\n");
65      }
66 }
67
68 static void
69 sigint_handler(int sig)
70 {
71      signal(sig, SIG_DFL);
72      opkg_msg(NOTICE, "Interrupted. Writing out status database.\n");
73      write_status_files_if_changed();
74      exit(128 + sig);
75 }
76
77 static int
78 opkg_update_cmd(int argc, char **argv)
79 {
80      char *tmp;
81      int err;
82      int failures;
83      char *lists_dir;
84      pkg_src_list_elt_t *iter;
85      pkg_src_t *src;
86
87  
88     sprintf_alloc(&lists_dir, "%s", conf->restrict_to_default_dest ? conf->default_dest->lists_dir : conf->lists_dir);
89  
90     if (! file_is_dir(lists_dir)) {
91           if (file_exists(lists_dir)) {
92                opkg_msg(ERROR, "%s exists, but is not a directory.\n",
93                             lists_dir);
94                free(lists_dir);
95                return -1;
96           }
97           err = file_mkdir_hier(lists_dir, 0755);
98           if (err) {
99                free(lists_dir);
100                return -1;
101           }     
102      } 
103
104      failures = 0;
105
106      sprintf_alloc(&tmp, "%s/update-XXXXXX", conf->tmp_dir);
107      if (mkdtemp (tmp) == NULL) {
108          opkg_perror(ERROR, "Failed to make temp dir %s", conf->tmp_dir);
109          return -1;
110      }
111
112
113      for (iter = void_list_first(&conf->pkg_src_list); iter; iter = void_list_next(&conf->pkg_src_list, iter)) {
114           char *url, *list_file_name;
115
116           src = (pkg_src_t *)iter->data;
117
118           if (src->extra_data)  /* debian style? */
119               sprintf_alloc(&url, "%s/%s/%s", src->value, src->extra_data, 
120                             src->gzip ? "Packages.gz" : "Packages");
121           else
122               sprintf_alloc(&url, "%s/%s", src->value, src->gzip ? "Packages.gz" : "Packages");
123
124           sprintf_alloc(&list_file_name, "%s/%s", lists_dir, src->name);
125           if (src->gzip) {
126               char *tmp_file_name;
127               FILE *in, *out;
128               
129               sprintf_alloc (&tmp_file_name, "%s/%s.gz", tmp, src->name);
130               err = opkg_download(url, tmp_file_name, NULL, NULL);
131               if (err == 0) {
132                    opkg_msg(NOTICE, "Inflating %s.\n", url);
133                    in = fopen (tmp_file_name, "r");
134                    out = fopen (list_file_name, "w");
135                    if (in && out)
136                         unzip (in, out);
137                    else
138                         err = 1;
139                    if (in)
140                         fclose (in);
141                    if (out)
142                         fclose (out);
143                    unlink (tmp_file_name);
144               }
145               free(tmp_file_name);
146           } else
147               err = opkg_download(url, list_file_name, NULL, NULL);
148           if (err) {
149                failures++;
150           } else {
151                opkg_msg(NOTICE, "Updated list of available packages in %s.\n",
152                             list_file_name);
153           }
154           free(url);
155 #if defined(HAVE_GPGME) || defined(HAVE_OPENSSL)
156           if (conf->check_signature) {
157               /* download detached signitures to verify the package lists */
158               /* get the url for the sig file */
159               if (src->extra_data)      /* debian style? */
160                   sprintf_alloc(&url, "%s/%s/%s", src->value, src->extra_data,
161                           "Packages.sig");
162               else
163                   sprintf_alloc(&url, "%s/%s", src->value, "Packages.sig");
164
165               /* create temporary file for it */
166               char *tmp_file_name;
167
168               /* Put the signature in the right place */
169               sprintf_alloc (&tmp_file_name, "%s/%s.sig", lists_dir, src->name);
170
171               err = opkg_download(url, tmp_file_name, NULL, NULL);
172               if (err) {
173                   failures++;
174                   opkg_msg(NOTICE, "Signature check failed.\n");
175               } else {
176                   err = opkg_verify_file (list_file_name, tmp_file_name);
177                   if (err == 0)
178                       opkg_msg(NOTICE, "Signature check passed.\n");
179                   else
180                       opkg_msg(NOTICE, "Signature check failed.\n");
181               }
182               /* We shouldn't unlink the signature ! */
183               // unlink (tmp_file_name);
184               free (tmp_file_name);
185               free (url);
186           }
187 #else
188           // Do nothing
189 #endif
190           free(list_file_name);
191      }
192      rmdir (tmp);
193      free (tmp);
194      free(lists_dir);
195
196      return failures;
197 }
198
199
200 struct opkg_intercept
201 {
202     char *oldpath;
203     char *statedir;
204 };
205
206 typedef struct opkg_intercept *opkg_intercept_t;
207
208 static opkg_intercept_t
209 opkg_prep_intercepts(void)
210 {
211     opkg_intercept_t ctx;
212     char *newpath;
213
214     ctx = xcalloc(1, sizeof (*ctx));
215     ctx->oldpath = xstrdup(getenv("PATH"));
216     sprintf_alloc(&newpath, "%s/opkg/intercept:%s", DATADIR, ctx->oldpath);
217     sprintf_alloc(&ctx->statedir, "%s/opkg-intercept-XXXXXX", conf->tmp_dir);
218
219     if (mkdtemp(ctx->statedir) == NULL) {
220         opkg_perror(ERROR,"Failed to make temp dir %s", ctx->statedir);
221         free(ctx->oldpath);
222         free(ctx->statedir);
223         free(newpath);
224         free(ctx);
225         return NULL;
226     }
227
228     setenv("OPKG_INTERCEPT_DIR", ctx->statedir, 1);
229     setenv("PATH", newpath, 1);
230     free(newpath);
231
232     return ctx;
233 }
234
235 static int
236 opkg_finalize_intercepts(opkg_intercept_t ctx)
237 {
238     DIR *dir;
239     int err = 0;
240
241     setenv ("PATH", ctx->oldpath, 1);
242     free (ctx->oldpath);
243
244     dir = opendir (ctx->statedir);
245     if (dir) {
246         struct dirent *de;
247         while (de = readdir (dir), de != NULL) {
248             char *path;
249
250             if (de->d_name[0] == '.')
251                 continue;
252             
253             sprintf_alloc (&path, "%s/%s", ctx->statedir, de->d_name);
254             if (access (path, X_OK) == 0) {
255                 const char *argv[] = {"sh", "-c", path, NULL};
256                 xsystem (argv);
257             }
258             free (path);
259         }
260         closedir(dir);
261     } else
262         opkg_perror(ERROR, "Failed to open dir %s", ctx->statedir);
263         
264     rm_r(ctx->statedir);
265     free (ctx->statedir);
266     free (ctx);
267
268     return err;
269 }
270
271 /* For package pkg do the following: If it is already visited, return. If not,
272    add it in visited list and recurse to its deps. Finally, add it to ordered 
273    list.
274    pkg_vec all contains all available packages in repos.
275    pkg_vec visited contains packages already visited by this function, and is 
276    used to end recursion and avoid an infinite loop on graph cycles.
277    pkg_vec ordered will finally contain the ordered set of packages.
278 */
279 static int
280 opkg_recurse_pkgs_in_order(pkg_t *pkg, pkg_vec_t *all,
281                                pkg_vec_t *visited, pkg_vec_t *ordered)
282 {
283     int j,k,l,m;
284     int count;
285     pkg_t *dep;
286     compound_depend_t * compound_depend;
287     depend_t ** possible_satisfiers;
288     abstract_pkg_t *abpkg;
289     abstract_pkg_t **dependents;
290
291     /* If it's just an available package, that is, not installed and not even
292        unpacked, skip it */
293     /* XXX: This is probably an overkill, since a state_status != SS_UNPACKED 
294        would do here. However, if there is an intermediate node (pkg) that is 
295        configured and installed between two unpacked packages, the latter 
296        won't be properly reordered, unless all installed/unpacked pkgs are
297        checked */
298     if (pkg->state_status == SS_NOT_INSTALLED) 
299         return 0;
300
301     /* If the  package has already been visited (by this function), skip it */
302     for(j = 0; j < visited->len; j++) 
303         if ( ! strcmp(visited->pkgs[j]->name, pkg->name)) {
304             opkg_msg(DEBUG, "pkg %s already visited, skipping.\n", pkg->name);
305             return 0;
306         }
307     
308     pkg_vec_insert(visited, pkg);
309
310     count = pkg->pre_depends_count + pkg->depends_count + \
311         pkg->recommends_count + pkg->suggests_count;
312
313     opkg_msg(DEBUG, "pkg %s.\n", pkg->name);
314
315     /* Iterate over all the dependencies of pkg. For each one, find a package 
316        that is either installed or unpacked and satisfies this dependency.
317        (there should only be one such package per dependency installed or 
318        unpacked). Then recurse to the dependency package */
319     for (j=0; j < count ; j++) {
320         compound_depend = &pkg->depends[j];
321         possible_satisfiers = compound_depend->possibilities;
322         for (k=0; k < compound_depend->possibility_count ; k++) {
323             abpkg = possible_satisfiers[k]->pkg;
324             dependents = abpkg->provided_by->pkgs;
325             l = 0;
326             if (dependents != NULL)
327                 while (l < abpkg->provided_by->len && dependents[l] != NULL) {
328                     opkg_msg(DEBUG, "Descending on pkg %s.\n", 
329                                  dependents [l]->name);
330     
331                     /* find whether dependent l is installed or unpacked,
332                      * and then find which package in the list satisfies it */
333                     for(m = 0; m < all->len; m++) {
334                         dep = all->pkgs[m];
335                         if ( dep->state_status != SS_NOT_INSTALLED)
336                             if ( ! strcmp(dep->name, dependents[l]->name)) {
337                                 opkg_recurse_pkgs_in_order(dep, all, 
338                                                            visited, ordered);
339                                 /* Stop the outer loop */
340                                 l = abpkg->provided_by->len;
341                                 /* break from the inner loop */
342                                 break;
343                             }
344                     }
345                     l++;
346                 }
347         }
348     }
349
350     /* When all recursions from this node down, are over, and all 
351        dependencies have been added in proper order in the ordered array, add
352        also the package pkg to ordered array */
353     pkg_vec_insert(ordered, pkg);
354
355     return 0;
356
357 }
358
359 static int
360 opkg_configure_packages(char *pkg_name)
361 {
362      pkg_vec_t *all, *ordered, *visited;
363      int i;
364      pkg_t *pkg;
365      opkg_intercept_t ic;
366      int r, err = 0;
367
368      opkg_msg(INFO, "Configuring unpacked packages.\n");
369
370      all = pkg_vec_alloc();
371
372      pkg_hash_fetch_available(all);
373
374      /* Reorder pkgs in order to be configured according to the Depends: tag
375         order */
376      opkg_msg(INFO, "Reordering packages before configuring them...\n");
377      ordered = pkg_vec_alloc();
378      visited = pkg_vec_alloc();
379      for(i = 0; i < all->len; i++) {
380          pkg = all->pkgs[i];
381          opkg_recurse_pkgs_in_order(pkg, all, visited, ordered);
382      }
383
384      ic = opkg_prep_intercepts();
385      if (ic == NULL) {
386              err = -1;
387              goto error;
388      }
389     
390      for(i = 0; i < all->len; i++) {
391           pkg = all->pkgs[i];
392
393           if (pkg_name && fnmatch(pkg_name, pkg->name, 0)) 
394                continue;
395
396           if (pkg->state_status == SS_UNPACKED) {
397                opkg_msg(NOTICE, "Configuring %s.\n", pkg->name);
398                r = opkg_configure(pkg);
399                if (r == 0) {
400                     pkg->state_status = SS_INSTALLED;
401                     pkg->parent->state_status = SS_INSTALLED;
402                     pkg->state_flag &= ~SF_PREFER;
403                     opkg_state_changed++;
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 }