/* * Copyright (c) 2005 * iMil . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by iMil. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY iMil AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL iMil OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * $Id: main.c,v 1.47 2005/12/09 15:28:12 imil Exp $ */ #include #ifndef lint #if 0 static char *rcsid = "$Id: main.c,v 1.47 2005/12/09 15:28:12 imil Exp $"; #else __RCSID("$Id: main.c,v 1.47 2005/12/09 15:28:12 imil Exp $"); #endif #endif #include #include #include "pkg_select.h" #include "curses_input.h" /* #include "menus.h" */ /* reset hl_index and clear screen */ #define DO_RESET 1 #define DONT_RESET 0 static void usage(void); static void init_curses(void); static void init_windows(void); static void finish(int); static void clear_tree(Etree ***, int); static void list_win_refresh(void); static int print_list(Etree **, HL_datas *, char *, int); static void print_bindings(int); static int nlines, ncols, top_line; static WINDOW *list_win; static void usage() { (void) fprintf(stderr, "%s\n%s\n", "usage: pkg_select [-h] [-b pkgsrcdir] [-K pkg_dbdir] [-c conf file]", " [-l [-m] [-u NetBSD ftp mirror]] [-d url] [-s]"); exit(1); } static void init_curses() { initscr(); nonl(); cbreak(); noecho(); curs_set(0); init_keymaps(); } static void init_windows() { nlines = MAINWIN_LINES; ncols = MAINWIN_COLS; top_line = 1; /* create a window 1 char smaller than term */ list_win = newwin(nlines + 2, ncols, 1, 1); } static void finish(int sig) { /* do something useful with sig so compiler does not complain on unused variable (FreeBSD) */ if (sig) sig &= 0; /* free dir list */ free_pkgdb(); /* free "to be installed" package list */ free_tbi_pkgs(); /* free conf file members */ freeconf(); /* live ftp */ ftp_stop(); curs_set(1); delwin(list_win); reset_shell_mode(); free_keymaps(); endwin(); exit(EXIT_SUCCESS); } int toplevel(const char *path) { if (strcmp(path, pkgsrcbase) != 0) return(0); else return(1); } /* etree's address is passed so we can NULL **etree */ static void clear_tree(Etree ***etree, const int where) { if (where == IN_DEPENDS) free_nodir_tree(etree); else free_tree(etree); } /* refresh and resize screen (coming from info window) */ static void list_win_refresh() { wclear(list_win); wrefresh(list_win); wresize(list_win, nlines + 2, ncols); } /* parse directory listing */ static int print_list(Etree **etree, HL_datas *hl, char *path, const int where) { int i = 0; char buf[MAXLEN], *ppath, *item; /* don't show path when listing PKGDB */ if (where == IN_PKGDB) draw_box(list_win, INST_PKGS); /* etree == NULL, no directories found */ if (etree == NULL || etree[0] == NULL) { /* there was no entries, are we in a pkg dir ? */ snprintf(buf, MAXLEN, "%s/Makefile", path); if ((item = strrchr(path, '/')) == NULL) /* should never happen */ return(IN_DESCR); item++; if (where == IN_FTP || file_exists(buf)) { /* remember initial path */ ppath = path; /* switch to information window */ i = info_win(list_win, item, path); } /* back from information window */ /* clean and restore window size */ list_win_refresh(); /* return IN_QUIT from info window, quit*/ if (i == IN_QUIT) /* etree == NULL, nothing to free() */ finish(0); if (where == IN_PKGDB) /* redraw borders */ draw_box(list_win, INST_PKGS); if (where == IN_DEPENDS) { if ((ppath = strstr(path, "/..")) != NULL) /* dependency */ *ppath = '\0'; else /* pkgfind */ strcpy(path, pkgsrcbase); draw_box(list_win, path); } if (where == IN_PKGSRC || where == IN_SYSINST) { /* rewind to parent dir */ if ((ppath = strrchr(path, '/')) != NULL) *ppath = '\0'; /* redraw borders */ draw_box(list_win, path); } /* jump back to main loop with etree = NULL */ return(IN_DESCR); } /* etree == NULL */ /* draw the combo list */ return(combo_list(list_win, etree, hl, path)); } /* print key shortcuts */ static void print_bindings(int page) { int spacing, up, down, i; static int last_page = 1; #define MAXCMDPAGES 2 spacing = strlen(ps_installed.descr); up = nlines + 3; down = nlines + 4; if (page != last_page) { /* clear last 2 lines, nicer than a clrscr() */ for (i = 0; i < ncols; i++) { mvprintw(LINES - 2, i, " "); mvprintw(LINES - 3, i, " "); } } switch (page) { case 1: print_kb(ps_enter.icon, ps_enter.descr, up, 2); print_kb(ps_back.icon, ps_back.descr, down, 2); print_kb(ps_search.icon, ps_search.descr, up, spacing); print_kb(ps_quit.icon, ps_quit.descr, down, spacing); print_kb(ps_next.icon, ps_next.descr, up, spacing * 2); print_kb(ps_find.icon, ps_find.descr, down, spacing * 2); print_kb(ps_other.icon, ps_other.descr, up, spacing * 3); print_kb(ps_installed.icon, ps_installed.descr, down, spacing * 3); break; case 2: print_kb(ps_update.icon, ps_update.descr, up, 2); print_kb(ps_tag.icon, ps_tag.descr, down, 2); print_kb(ps_install.icon, ps_install.descr, up, spacing * 1.5); print_kb(ps_deinstall.icon, ps_deinstall.descr, down, spacing * 1.5); print_kb(ps_prefs.icon, ps_prefs.descr, down, spacing * 3); print_kb(ps_other.icon, ps_other.descr, up, spacing * 3); break; } refresh(); last_page = page; } /* * non-directory based loop, * we build etree with **list, a char ** flat list * comments are built from *path/ * this is used for pkgfind() and show deps */ void nodir_loop(const char *path, char **list) { Etree **etree; if (list != NULL && list[0] != '\0') { /* call main loop with tree built from deps */ etree = get_nodir_tree(path, list); if (etree != NULL) etree = main_loop(etree, list, path, IN_DEPENDS); /* at this point etree should be NULL */ } } Etree ** main_loop(Etree **etree, char **list, const char *basepath, const int where) { int c, tmp, page; Etree **etree_sav; HL_datas hl; char wpath[MAXLEN], tstr[MAXLEN], *p, **pkglist; /* init highlight index */ hl.hl_index = hl.old_index = 0; /* save screen props */ hl.nlines = nlines; hl.ncols = ncols; hl.top_line = 1; /* init working path */ strcpy(wpath, basepath); /* we are called from show-deps, clean display from info window */ if (where == IN_DEPENDS) list_win_refresh(); /* redraw box */ draw_box(list_win, wpath); /* command page */ page = 1; /* main loop */ for (;;) { WINDOW *popup; if (print_list(etree, &hl, wpath, where) == IN_DESCR) /* recursive call led to DESCR, return NULL */ return(NULL); /* print bindings */ print_bindings(page); c = wgetch(list_win); switch(c) { /* macro defining up, down, pgup and pgdown */ BASIC_NAV case KEY_RIGHT: case KEY_ENTER: if (where == IN_FTP) { /* save etree pointer */ etree_sav = etree; snprintf(wpath, MAXLEN, "%s/%s", basepath, hl.hl_entry); etree = live_ftp(wpath); etree = main_loop(etree, NULL, wpath, IN_FTP); /* restore etree */ etree = etree_sav; break; } if (where == IN_PKGDB) { /* point to corresponding pkgdb line */ p = getpkginfo(hl.hl_entry, PKG_CATEGORY); if (p == NULL) break; snprintf(wpath, MAXLEN, "%s/%s", pkgsrcbase, p); } if (where == IN_DEPENDS) snprintf(wpath, MAXLEN, "%s/%s", basepath, etree[hl.hl_index]->dep_path); if (where == IN_PKGSRC || where == IN_SYSINST) snprintf(wpath, MAXLEN, "%s/%s", basepath, hl.hl_entry); /* display a wait message */ popup = mid_info_popup("parsing pkgsrc", PARSE_PKGSRC); if (popup != NULL) wrefresh(popup); /* load new basepath tree */ clear_tree(&etree, where); etree = get_tree(wpath, where); /* re-enter main_loop with new tree */ etree = main_loop(etree, NULL, wpath, where); /* back from recursion with NULL etree */ break; case KEY_LEFT: if (!toplevel(basepath) || where == IN_DEPENDS) { clear_tree(&etree, where); return(NULL); } break; case 'n': tmp= entry_search(etree,1); if (tmp >= 0) hl.hl_index = tmp; break; case '/': tmp = entry_search(etree, 0); if (tmp >= 0) hl.hl_index = tmp; break; case 'f': /* show popup, p gets result */ p = getstr_popup("pkgfind", 5, 30, (LINES / 2) - 2, (COLS / 2) - 15); /* show waiting popup */ popup = mid_info_popup("pkgfind", "searching..."); wrefresh(popup); /* split p into malloc'ed char * pieces */ pkglist = pkgfind(pkgsrcbase, p, 0); /* destroy wait popup */ clr_del_win(popup); XFREE(p); if (pkglist == NULL) { (void) mid_getch_popup("pkgfind", NO_PKG_FOUND); break; } /* non dir-listing loop */ nodir_loop(pkgsrcbase, pkglist); /* back from pkgfind browsing, free the pkglist */ free_list(&pkglist); break; case 'l': if (where != IN_PKGDB) { /* set basepath to pkgdb_dir */ strncpy(wpath, pkg_dbdir, strlen(pkg_dbdir) + 1); clear_tree(&etree, IN_PKGDB); etree = get_tree(wpath, IN_PKGDB); /* recurse */ etree = main_loop(etree, NULL, wpath, IN_PKGDB); } break; case 't': if (!toplevel(basepath)) { snprintf(tstr, MAXLEN, "%s/%s", basepath, hl.hl_entry); add_pkg(tstr); } break; case 'i': clr_allscr(list_win); process_many(COMBO_INST); /* this overwrites current box, redraw */ clr_allscr(list_win); draw_box(list_win, basepath); break; case 'd': clr_allscr(list_win); process_many(COMBO_DEINST); /* this overwrites current box, redraw */ clr_allscr(list_win); draw_box(list_win, basepath); break; case 'u': clear_tree(&etree, where); cvs_up(basepath); /* safety clear */ break; case 'p': clear_tree(&etree, where); prefs_screen(); clr_allscr(list_win); break; case 'o': if (page < MAXCMDPAGES) page++; else page = 1; break; case 'q': clear_tree(&etree, where); finish(0); break; } /* switch */ /* if we return from DESCR section, etree == NULL */ if (etree == NULL) { /* coming from DESCR, show basepath (parent) */ if (where == IN_DEPENDS) etree = get_nodir_tree(basepath, list); else if (where == IN_FTP) etree = live_ftp(basepath); else etree = get_tree(basepath, where); } /* redraw border */ draw_box(list_win, basepath); } /* for (;;) */ /* NOTREACHED */ } int main(int argc, char *argv[]) { Etree **etree; char ch, *confpath, *ftp_url, *dl_path, basepath[MAXLEN]; int where, use_live_ftp, read_makefiles, console_output, mmenu; WINDOW *popup; pkgsrcbase = pkg_dbdir = confpath = ftp_url = dl_path = NULL; use_live_ftp = console_output = mmenu = T_FALSE; read_makefiles = T_TRUE; /* live_ftp option */ /* command line handling */ while ((ch = getopt(argc, argv, "c:b:K:u:d:lmshi")) != -1) switch(ch) { case 'b': pkgsrcbase = optarg; break; case 'K': pkg_dbdir = optarg; break; case 'c': confpath = optarg; break; case 'l': use_live_ftp = 1; case 'u': XSTRDUP(ftp_url, optarg); break; case 'm': read_makefiles = T_FALSE; break; case 'd': dl_path = optarg; break; case 's': console_output = T_TRUE; break; case 'i': mmenu = T_TRUE; break; case 'h': usage(); /* NOTREACHED */ } argc -= optind; argv += optind; /* end of command line handlig */ /* confpath given to command line or NULL*/ if (confpath == NULL) { conf.confpath = CONFPATH; } else conf.confpath = confpath; loadconf(); if (pkgsrcbase == NULL) { /* there was no -b flag */ if (conf.pkgsrcdir != NULL) /* conf file ? */ pkgsrcbase = conf.pkgsrcdir; else /* env variable ? */ if ((pkgsrcbase = getenv("PKGSRCDIR")) == NULL) /* default basepath */ pkgsrcbase = PKGSRCBASE; } conf.pkgsrcdir = pkgsrcbase; if (pkg_dbdir == NULL) { /* no -K flag */ if (conf.pkg_dbdir != NULL) pkg_dbdir = conf.pkg_dbdir; else if ((pkg_dbdir = getenv("PKG_DBDIR")) == NULL) /* default pkg_dbdir */ pkg_dbdir = PKGDB; } conf.pkg_dbdir = pkg_dbdir; if (console_output == T_TRUE) conf.shell_output = T_TRUE; /* default basepath */ strncpy(basepath, pkgsrcbase, MAXLEN); signal(SIGINT, finish); /* init ncurses */ init_curses(); /* set BG color */ if (has_colors() == TRUE) { start_color(); init_pair(1, FGCOLOR, BGCOLOR); bkgd(COLOR_PAIR(1)); attrset(COLOR_PAIR(1)); clear(); refresh(); } /* init different windows */ init_windows(); /* set BG color */ set_colors(list_win); /* enable KEY_* */ keypad(list_win, TRUE); popup = mid_info_popup("loading pkgdb", LOAD_PKGDB); wrefresh(popup); /* load installed package list */ load_pkgdb(); clr_del_win(popup); /* enter main menu mode */ /*if (mmenu) mainmenu(main_menu);*/ /* pkg_select was called with -l, entering live FTP mode */ if (use_live_ftp) { /* had a -l flag */ /* ensure basepath is empty */ memset(basepath, 0, MAXLEN); if (read_makefiles == T_FALSE) /* dont read makefiles (no comments) */ conf.live_ftp_read_makefiles = T_FALSE; if (ftp_url == NULL) { /* had no -u flag, try conf */ if (conf.live_ftp_pkgsrc != NULL) XSTRDUP(ftp_url, conf.live_ftp_pkgsrc); else { /* give mirror list */ if ((ftp_url = list_mirrors("ftp")) == NULL) finish(0); snprintf(basepath, MAXLEN, "%s/NetBSD-current/pkgsrc", ftp_url); XFREE(ftp_url); XSTRDUP(ftp_url, basepath); } } else { int len; /* validate url as ftp://host */ len = strlen(ftp_url) - 1; /* flush terminating /'s */ for (; len > 0 && ftp_url[len] == '/'; len--) ftp_url[len] = '\0'; /* ftp_url has no beginning ftp:// */ if (strncmp("ftp://", ftp_url, 6) != 0) snprintf(basepath, MAXLEN, "ftp://%s", ftp_url); } where = IN_FTP; /* basepath was not filled by previous snprintf */ if (basepath[0] == '\0') XSTRCPY(basepath, ftp_url); conf.live_ftp = ftp_url; if ((etree = live_ftp(basepath)) == NULL) { warn("error while negociating with %s", basepath); finish(0); } /* set pkgsrc base to ftp base */ pkgsrcbase = conf.live_ftp; } /* end if ftp */ else if (getenv("PACKAGES") != NULL) { char **rmout, rmcmd[MIDLEN]; where = IN_SYSINST; /* as of 12 / 2005, this feature allows only local packages * manipulation, but it could quite easily do the same with * remote packages (see live ftp feature) */ setenv("PKG_PATH", getenv("PACKAGES"), 1); if (getenv("PKGSRCDIR") == NULL) setenv("PKGSRCDIR", TMPPKGSRCDIR, 1); else { snprintf(rmcmd, MIDLEN, WARN_RM_PKGSRC, getenv("PKGSRCDIR")); if (mid_getch_popup("warning", rmcmd) != 'y') finish(0); } XSTRCPY(basepath, getenv("PKGSRCDIR")); /* remove any existing tmp pkgsrc */ snprintf(rmcmd, MIDLEN, "%s -rf %s", RM, getenv("PKGSRCDIR")); /* we really don't want to cut/paste rm.c... */ rmout = exec_list(rmcmd, NULL); /* we don't need output */ free_list(&rmout); if (mkpkgsrc(getenv("PKG_PATH")) < 0) err(EXIT_FAILURE, "%s", getenv("PKG_PATH")); /* read etree from binaries directory*/ etree = get_tree(basepath, where); } /* end sysinst mode */ else { /* was pkgsrc download asked ? */ if (dl_path != NULL) { if (download_pkgsrc(dl_path, basepath) < 0) { warnx("pkgsrc download failed"); finish(0); } } else /* check for pkgsrc presence */ if (pkgsrc_chk(basepath) < 0) { warnx("%s does not exists", basepath); /* pkgsrc fetch failed */ finish(0); } where = IN_PKGSRC; /* display a wait message */ popup = mid_info_popup("parsing pkgsrc", PARSE_PKGSRC); wrefresh(popup); /* get directory listing */ etree = get_tree(basepath, where); /* destroy wait popup */ clr_del_win(popup); } /* enter browser */ (void) main_loop(etree, NULL, basepath, where); finish(0); /* NOTREACHED */ exit(EXIT_SUCCESS); }