#include #include "managerpm.h" #include "managerpm.m" #include static HELP_FILE help_pkginfo ("managerpm","pkginfo"); static HELP_FILE help_removedeps ("managerpm","removedeps"); static HELP_FILE help_missingpkgs ("managerpm","missingpkgs"); static HELP_FILE help_requiredpkgs ("managerpm","requiredpkgs"); static HELP_FILE help_conflicts ("managerpm","conflicts"); /* Decompose the version string of a package so it is easier to compare. */ void mngrpm_parsever (const char *version, VERSION_ITEMS &v) { /* #Specification: package / version / parsing A package version is defined this way ver[.subver[.subsubver...]] Each member (between dots) is a number followed optionally by a string. managerpm split this sequence so it can compare the version numerically, instead of just doing a string compare. (This follows RPM specs as far as I can tell) */ strcpy_cut (v.str,version,sizeof(v.str)); for (int i=0; i<10; i++){ v.tb[i].num = 0; v.tb[i].suffix = ""; } char *pt = v.str; int nb = 0; while (*pt != '\0'){ v.tb[nb].num = atoi(pt); pt = str_skipdig(pt); v.tb[nb].suffix = pt; nb++; while (*pt != '\0' && *pt != '.') pt++; if (*pt == '.') *pt++ = '\0'; } v.nb = nb; #if 0 fprintf (stderr,"version :%s: -> :",version); for (int i=0; i0){ DIALOG_RECORDS dia; dia.setcontext (""); dia.newf_head ("",MSG_U(H_FILES ,"Permissions\tOwner\tGroup\tSize\tDate\tPath")); /* #Specification: package file list / format / rpm 3 and 4 The format changed between rpm version 3 and 4. Now version 4 include the link count, like "ls -l". */ int skip = rpm_version() >= 400 ? 1 : 0; for (int i=0; istrip_end(); SSTRINGS tbs; if (str_splitline (line->get(),' ',tbs)>=8+skip){ char buf[PATH_MAX]; snprintf (buf,sizeof(buf)-1,"%s\t%s\t%s\t%s %s %s\t%s" ,tbs.getitem(1+skip)->get() ,tbs.getitem(2+skip)->get() ,tbs.getitem(3+skip)->get() ,tbs.getitem(4+skip)->get() ,tbs.getitem(5+skip)->get() ,tbs.getitem(6+skip)->get() ,tbs.getitem(7+skip)->get()); dia.new_menuinfo (tbs.getitem(0)->get(),buf); } } int nof=0; while (1){ MENU_STATUS code = dia.editmenu (MSG_U(T_PKGFILES ,"Package files") ,"",help_nil,nof,0); if (code == MENU_QUIT || code == MENU_ESCAPE){ break; } } } } PUBLIC void PACKAGE::showinfo() { SSTRING args; bool installed = fullname.is_empty(); if (installed){ // Installed package args.setfromf ("-qi %s",name.get()); }else{ args.setfromf ("-qpi %s",fullname.get()); } SSTRINGS tb; mngrpm_execrpm (args,tb); if (tb.getnb()>0){ char outstr[PATH_MAX+100]; snprintf (outstr,sizeof(outstr)-1,MSG_U(I_OUTRPM ,"Output of \"rpm %s\""),args.get()); DIALOG_TEXTBOX dia; dia.setcontext (""); dia.newf_text ("",tb); dia.setbutinfo (MENU_USR1,MSG_U(B_FILES,"Show files") ,MSG_R(B_FILES)); int butmask = MENUBUT_USR1; if (installed){ butmask |= MENUBUT_USR2; dia.setbutinfo (MENU_USR2,MSG_U(B_UNINSTALL,"Uninstall") ,MSG_R(B_UNINSTALL)); }else{ butmask |= MENUBUT_USR3; dia.setbutinfo (MENU_USR3,MSG_U(B_INSTALL,"Install") ,MSG_R(B_INSTALL)); } int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_PKGINFO,"Package info") ,outstr ,help_pkginfo ,nof,MENUBUT_CANCEL|butmask); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (code == MENU_USR1){ showfiles(); }else if (code == MENU_USR2){ if (uninstall() == 0){ break; } }else if (code == MENU_USR3){ install(); break; } } }else{ xconf_error (MSG_U(E_NOOUTPUT ,"The command rpm %s\ndid not produced any output") ,args.get()); } } int package_cmpver (const VERSION_ITEMS &v1, const VERSION_ITEMS &v2) { int ret = 0; int nb = v1.nb; if (v2.nb < nb) nb = v2.nb; for (int i=0; iname); if (ret == 0){ ret = package_cmpver(v,p->v); if (ret == 0){ ret = relnum.num - p->relnum.num; if (ret == 0) ret = strcmp(relnum.suffix,p->relnum.suffix); } } return ret; } /* Add a dependencies to the package */ PUBLIC void PACKAGE::addrequire (const char *req) { STRENTRY *s = poolstr_addrequire (this,req); requires.add (s); } /* Add a "provide" entry to the package */ PUBLIC void PACKAGE::addprovide (const char *pro, const char *md5) { STRENTRY *s = poolstr_addprovide (this,pro,md5); provides.add (s); } /* Return true if the package is needed by another one */ PUBLIC bool PACKAGE::is_needed() const { return needed; } /* Return true if the package is selected for some operation */ PUBLIC bool PACKAGE::is_selected() const { return selected != 0; } /* Return true if the package is installed */ PUBLIC bool PACKAGE::is_installed() const { return installed; } /* Reset the "needed" flag of all package */ PUBLIC void PACKAGES::resetneeded() { int n = getnb(); for (int i=0; ineeded = false; } } class CONFLICT: public ARRAY_KEY{ /*~PROTOBEG~ CONFLICT */ public: CONFLICT (const char *key, PACKAGES *p); PACKAGES *getobj (void); /*~PROTOEND~ CONFLICT */ }; PUBLIC CONFLICT::CONFLICT (const char *key, PACKAGES *p) : ARRAY_KEY (key,p,false) { } PUBLIC PACKAGES *CONFLICT::getobj () { return (PACKAGES*)obj; } class CONFLICTS: public ARRAY_KEYS{ /*~PROTOBEG~ CONFLICTS */ public: CONFLICT *getitem (int no)const; /*~PROTOEND~ CONFLICTS */ }; PUBLIC CONFLICT *CONFLICTS::getitem (int no) const { return (CONFLICT*)ARRAY_KEYS::getitem(no); } /* Check if a package may be installed. The table needs will contain the list of packages needed to fullfill the install. Return true if the package may be installed (with the needed package) and false if the package can't be installed because some dependencies can't be solved. */ PUBLIC bool PACKAGE::testinstall( PACKAGES &needs, // Other package we must install to // satisfy the dependencies SSTRINGS &missing, // Stuff not provided by any packages CONFLICTS &conflicts) // Already installed package providing the // same capabilities than this one we want to // install { int ret = true; needs.neverdelete(); int n = requires.getnb(); for (int i=0; itestinstall(needs)){ missing.add (new SSTRING(s->val)); ret = false; } } n = provides.getnb(); PACKAGES *pkgs = new PACKAGES; for (int i=0; ival[0] != '/' || file_type(s->val)!=1){ pkgs->neverdelete(); if (s->testconflicts(name.get(),*pkgs)){ conflicts.add (s->val,pkgs); pkgs = new PACKAGES; ret = false; } } } delete pkgs; return ret; } /* Check if a package may be un-installed. The table needs will contain the list of packages needing this package (so it can't be uninstalled). Return true if the package may be uninstalled and false if the package can't be installed because it would break some dependencies. */ PUBLIC bool PACKAGE::testuninstall(PACKAGES &needs) { int ret = true; needs.neverdelete(); int n = provides.getnb(); for (int i=0; itestuninstall(needs)){ ret = false; } } return ret; } static bool package_showdeps ( PACKAGES &needs, const char *title, const char *intro, HELP_FILE &help, bool &nodeps) { bool ret = false; needs.sort(); DIALOG dia; for (int i=0; iselected = 1; dia.newf_chk (p->name.get(),p->selected,p->summary.get()); } dia.setbutinfo (MENU_USR1,MSG_R(B_NONE),MSG_R(B_NONE)); dia.setbutinfo (MENU_USR2,MSG_R(B_ALL),MSG_R(B_ALL)); int nof = 0; while (1){ MENU_STATUS code = dia.edit (title,intro,help ,nof,MENUBUT_ACCEPT|MENUBUT_CANCEL|MENUBUT_USR1|MENUBUT_USR2); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_USR1 || code == MENU_USR2){ char selected = code == MENU_USR1 ? 0 : 1; for (int i=0; iselected = selected; } dia.reload(); }else if (code == MENU_ACCEPT){ ret = true; // Force nodeps only if one package was not selected for // removal. for (int i=0; iis_selected()) nodeps = true; } break; } } return ret; } static bool package_showconflict ( CONFLICTS &conf, bool &replacefiles) { bool ret = false; replacefiles = false; conf.sort(); DIALOG_RECORDS dia; PACKAGES lookup; lookup.neverdelete(); int nof = 0; while (1){ if (dia.getnb()==0){ lookup.remove_all(); dia.newf_head ("",MSG_U(H_CONFLICT,"Resources/Files\tPackages\tDescriptions")); dia.setbutinfo (MENU_USR1,MSG_U(B_REPLACEFILE,"Install anyway") ,MSG_R(B_REPLACEFILE)); for (int i=0; igetobj(); const char *res = c->get(); for (int j=0; jgetnb(); j++){ PACKAGE *p = pkgs->getitem(j); if (p->is_installed()){ lookup.add (p); char buf[100]; snprintf (buf,sizeof(buf)-1,"%s\t%s",p->name.get() ,p->summary.get()); dia.new_menuitem (res,buf); res = ""; } } } if (lookup.getnb()==0){ xconf_notice (MSG_U(N_NOMORECONFLICTS ,"You have removed all the conflicting packages.\n" "We can proceed to the installation now.")); ret = true; break; } } MENU_STATUS code = dia.editmenu (MSG_U(T_CONFLICT,"Conflict packages") ,MSG_U(I_CONFLICT ,"Some already installed packages provide\n" "either the same files or functionalities.\n" "Installing is not recommended unless you really know\n" "what you are doing\n" "\n" "You may inspect the offending packages and even remove\n" "them to solve the problem as well.") ,help_conflicts ,nof,MENUBUT_USR1); if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else if (code == MENU_USR1){ replacefiles = true; ret = true; break; }else{ PACKAGE *p = lookup.getitem(nof); if (p != NULL){ p->showinfo(); if (!package_is_installed(p->name.get())){ // The package has been removed by the user // this may change the picture. p->installed = 0; dia.remove_all(); } } } } return ret; } /* Test if the selected packages may be un-installed without breaking any dependencies. Return true if ok. */ PUBLIC bool PACKAGES::testuninstall(bool &nodeps) { nodeps = false; int n = getnb(); PACKAGES needs; for (int i=0; iis_selected()){ p->testuninstall(needs); } } // Now check if the needed package do have dependencies of their own for (int i=0; itestuninstall(needs); } // Now remove the packages from the list // This solve the case where one package needs another one // and both will be removed anyway for (int i=0; iis_selected()){ while (needs.remove(p)!=-1); } } bool ret = needs.getnb()==0; if (!ret){ ret = package_showdeps (needs ,MSG_U(T_CANTREMOVE,"Can't remove") ,MSG_U(I_CANTREMOVE ,"We can't remove the package(s) because the following\n" "packages need them in one way or another\n." "\n" "Select the package you wish to remove as well\n" "(Either remove them all, or do nothing as this could leave\n" " your system unstable)\n") ,help_removedeps ,nodeps); } return ret; } /* Test if the selected packages may be installed. If some package are not selected but required, they will be proposed to the user. Return true if ok. nodeps will be true if the install must be performed with the nodeps RPM option. */ PUBLIC bool PACKAGES::testinstall( bool &nodeps, // Will be set to true if --nodeps is needed // and the user did confirmed bool &replacefiles) // Will be set to true also { PACKAGES needs; // Package which must be added SSTRINGS missing; // Requirements that prevent proper installation // The package providing those are unknown CONFLICTS conflicts; { // So the DIALOG become out of scope int n = getnb(); DIALOG dia; dia.settype (DIATYPE_POPUP); int sofar = 0,nof =0; dia.newf_gauge ("",sofar,n); dia.show (MSG_U(T_CHECKDEP,"Checking dependencies") ,MSG_U(I_CHECKDEP ,"We must check if other packages are needed\n" "This will take a moment...") ,help_nil,nof,0); diagui_flush(); for (int i=0; iis_selected()){ p->testinstall(needs,missing,conflicts); } sofar = i; dia.reload(); dia.show (MSG_R(T_CHECKDEP) ,MSG_R(I_CHECKDEP),help_nil,nof,0); diagui_flush(); } // Now check if the needed package do have dependencies of their own for (int i=0; itestinstall(needs,missing,conflicts); } // Now remove the packages from the list // This solve the case where one package needs another one // and both will be installed anyway for (int i=0; iis_selected()){ while (needs.remove(p)!=-1); } } diagui_flush(); } bool ret = false; if (!nodeps && missing.getnb()>0){ missing.sort(); missing.remove_dups(); DIALOG dia; for (int i=0; iget()); } int nof = 0; if (dia.edit (MSG_U(T_MISSINGPKGS,"Missing packages") ,MSG_U(I_MISSINGPKGS ,"We can't installed the package(s) because they require\n" "resources from un-available packages.\n." "Here is the list of missing resources." "\n" "Click yes to continue the install\n" "(Not recommended unless you really know what you are doing)") ,help_missingpkgs ,nof,MENUBUT_YES|MENUBUT_NO)==MENU_YES){ ret = true; nodeps = true; } }else if (!nodeps && needs.getnb() > 0){ ret = package_showdeps (needs ,MSG_U(T_REQUIREDPKGS,"Required packages") ,MSG_U(I_REQUIREDPKGS ,"We can't installed the package(s) successfully unless\n" "we add few more. Here they are.\n" "We suggest you installed them all." "\n" "(Unless you really know what you are doing, install them all)") ,help_requiredpkgs ,nodeps); }else if (!replacefiles && conflicts.getnb() > 0){ ret = package_showconflict (conflicts,replacefiles); }else{ ret = true; } return ret; }