"Fossies" - the Fresh Open Source Software Archive

Member "ncdc-1.22.1/src/uit_search.c" (26 Mar 2019, 13592 Bytes) of package /linux/privat/ncdc-1.22.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "uit_search.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.20_vs_1.21.

    1 /* ncdc - NCurses Direct Connect client
    2 
    3   Copyright (c) 2011-2019 Yoran Heling
    4 
    5   Permission is hereby granted, free of charge, to any person obtaining
    6   a copy of this software and associated documentation files (the
    7   "Software"), to deal in the Software without restriction, including
    8   without limitation the rights to use, copy, modify, merge, publish,
    9   distribute, sublicense, and/or sell copies of the Software, and to
   10   permit persons to whom the Software is furnished to do so, subject to
   11   the following conditions:
   12 
   13   The above copyright notice and this permission notice shall be included
   14   in all copies or substantial portions of the Software.
   15 
   16   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   17   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   18   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   19   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   20   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   21   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   22   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   23 
   24 */
   25 
   26 
   27 #include "ncdc.h"
   28 #include "uit_search.h"
   29 
   30 
   31 ui_tab_type_t uit_search[1];
   32 
   33 
   34 typedef struct tab_t {
   35   ui_tab_t tab;
   36   ui_listing_t *list;
   37   search_q_t *q;
   38   char *hubname;
   39   time_t age;
   40   int order;
   41   gboolean reverse : 1;
   42   gboolean hide_hub : 1;
   43 } tab_t;
   44 
   45 
   46 // Columns to sort on
   47 #define SORT_USER  0
   48 #define SORT_SIZE  1
   49 #define SORT_SLOTS 2
   50 #define SORT_FILE  3
   51 
   52 
   53 // Note: The ordering of the results partly depends on whether the user is
   54 // online or not (i.e. whether we know its name and hub). However, we do not
   55 // get notified when a user or hub changes state and can therefore not keep the
   56 // ordering of the list correct. This isn't a huge problem, though.
   57 
   58 
   59 // Compares users, uses a hub comparison as fallback
   60 static int cmp_user(guint64 ua, guint64 ub) {
   61   hub_user_t *a = g_hash_table_lookup(hub_uids, &ua);
   62   hub_user_t *b = g_hash_table_lookup(hub_uids, &ub);
   63   int o =
   64     !a && !b ? (ua > ub ? 1 : ua < ub ? -1 : 0) :
   65      a && !b ? 1 : !a && b ? -1 : g_utf8_collate(a->name, b->name);
   66   if(!o && a && b)
   67     return g_utf8_collate(a->hub->tab->name, b->hub->tab->name);
   68   return o;
   69 }
   70 
   71 
   72 static int cmp_file(const char *fa, const char *fb) {
   73   const char *a = strrchr(fa, '/');
   74   const char *b = strrchr(fb, '/');
   75   return g_utf8_collate(a?a+1:fa, b?b+1:fb);
   76 }
   77 
   78 
   79 static gint sort_func(gconstpointer da, gconstpointer db, gpointer dat) {
   80   const search_r_t *a = da;
   81   const search_r_t *b = db;
   82   tab_t *t = dat;
   83   int p = t->order;
   84 
   85   /* Sort columns and their alternatives:
   86    * USER:  user/hub  -> file name -> file size
   87    * SIZE:  size      -> TTH       -> file name
   88    * SLOTS: slots     -> user/hub  -> file name
   89    * FILE:  file name -> size      -> TTH
   90    */
   91 #define CMP_USER  cmp_user(a->uid, b->uid)
   92 #define CMP_SIZE  (a->size == b->size ? 0 : (a->size == G_MAXUINT64 ? 0 : a->size) > (b->size == G_MAXUINT64 ? 0 : b->size) ? 1 : -1)
   93 #define CMP_SLOTS (a->slots > b->slots ? 1 : a->slots < b->slots ? -1 : 0)
   94 #define CMP_FILE  cmp_file(a->file, b->file)
   95 #define CMP_TTH   memcmp(a->tth, b->tth, 24)
   96 
   97   // Try 1
   98   int o = p == SORT_USER ? CMP_USER : p == SORT_SIZE ? CMP_SIZE : p == SORT_SLOTS ? CMP_SLOTS : CMP_FILE;
   99   // Try 2
  100   if(!o)
  101     o = p == SORT_USER ? CMP_FILE : p == SORT_SIZE ? CMP_TTH : p == SORT_SLOTS ? CMP_USER : CMP_SIZE;
  102   // Try 3
  103   if(!o)
  104     o = p == SORT_USER ? CMP_SIZE : p == SORT_SIZE ? CMP_FILE : p == SORT_SLOTS ? CMP_FILE : CMP_TTH;
  105 
  106 #undef CMP_USER
  107 #undef CMP_SIZE
  108 #undef CMP_SLOTS
  109 #undef CMP_FILE
  110 #undef CMP_TTH
  111 
  112   return t->reverse ? -o : o;
  113 }
  114 
  115 
  116 // Callback from search.c when we have a new result.
  117 static void result(search_r_t *r, void *dat) {
  118   tab_t *t = dat;
  119   g_sequence_insert_sorted(t->list->list, search_r_copy(r), sort_func, t);
  120   ui_listing_inserted(t->list);
  121   ui_tab_incprio((ui_tab_t *)t, UIP_LOW);
  122 }
  123 
  124 
  125 static const char *search_r_get_file(GSequenceIter *iter) {
  126   search_r_t *r = g_sequence_get(iter);
  127   return r->file;
  128 }
  129 
  130 
  131 // Performs a search and opens a new tab for the results.
  132 // May return NULL on error, behaves similarly to search_add() w.r.t *err.
  133 // Ownership of q is passed to the tab, and will be freed on error or close.
  134 ui_tab_t *uit_search_create(hub_t *hub, search_q_t *q, GError **err) {
  135   tab_t *t = g_new0(tab_t, 1);
  136   t->tab.type = uit_search;
  137   t->q = q;
  138   t->hubname = hub ? g_strdup(hub->tab->name) : NULL;
  139   t->hide_hub = hub ? TRUE : FALSE;
  140   t->order = SORT_FILE;
  141   time(&t->age);
  142 
  143   // Do the search
  144   q->cb_dat = t;
  145   q->cb = result;
  146   if(!search_add(hub, q, err)) {
  147     g_free(t);
  148     return NULL;
  149   }
  150 
  151   // figure out a suitable tab name
  152   if(q->type == 9) {
  153     t->tab.name = g_new0(char, 41);
  154     t->tab.name[0] = '?';
  155     base32_encode(q->tth, t->tab.name+1);
  156   } else {
  157     char *s = g_strjoinv(" ", q->query);
  158     t->tab.name = g_strdup_printf("?%s", s);
  159     g_free(s);
  160   }
  161   if(strlen(t->tab.name) > 15)
  162     t->tab.name[15] = 0;
  163   while(t->tab.name[strlen(t->tab.name)-1] == ' ')
  164     t->tab.name[strlen(t->tab.name)-1] = 0;
  165 
  166   t->list = ui_listing_create(g_sequence_new(search_r_free), NULL, t, search_r_get_file);
  167   return (ui_tab_t *)t;
  168 }
  169 
  170 
  171 static void t_close(ui_tab_t *tab) {
  172   tab_t *t = (tab_t *)tab;
  173   search_remove(t->q);
  174   g_sequence_free(t->list->list);
  175   ui_listing_free(t->list);
  176   ui_tab_remove(tab);
  177   g_free(t->hubname);
  178   g_free(t->tab.name);
  179   g_free(t);
  180 }
  181 
  182 
  183 static char *t_title(ui_tab_t *tab) {
  184   tab_t *t = (tab_t *)tab;
  185   char *sq = search_command(t->q, !!t->hubname);
  186   char *r = t->hubname
  187     ? g_strdup_printf("Results on %s for: %s", t->hubname, sq)
  188     : g_strdup_printf("Results for: %s", sq);
  189   g_free(sq);
  190   return r;
  191 }
  192 
  193 
  194 static void draw_row(ui_listing_t *list, GSequenceIter *iter, int row, void *dat) {
  195   search_r_t *r = g_sequence_get(iter);
  196   tab_t *t = dat;
  197 
  198   attron(iter == list->sel ? UIC(list_select) : UIC(list_default));
  199   mvhline(row, 0, ' ', wincols);
  200   if(iter == list->sel)
  201     mvaddstr(row, 0, ">");
  202 
  203   hub_user_t *u = g_hash_table_lookup(hub_uids, &r->uid);
  204   if(u) {
  205     mvaddnstr(row, 2, u->name, str_offset_from_columns(u->name, 19));
  206     if(!t->hide_hub)
  207       mvaddnstr(row, 22, u->hub->tab->name, str_offset_from_columns(u->hub->tab->name, 13));
  208   } else
  209     mvprintw(row, 2, "ID:%016"G_GINT64_MODIFIER"x%s", r->uid, !t->hide_hub ? " (offline)" : "");
  210 
  211   int i = t->hide_hub ? 22 : 36;
  212   if(r->size == G_MAXUINT64)
  213     mvaddstr(row, i, "   DIR");
  214   else
  215     mvaddstr(row, i, str_formatsize(r->size));
  216 
  217   mvprintw(row, i+12, "%3d/", r->slots);
  218   if(u)
  219     mvprintw(row, i+16, "%3d", u->slots);
  220   else
  221     mvaddstr(row, i+16, "  -");
  222 
  223   mvaddch(row, i+20, ui_file_flag(r->tth));
  224 
  225   char *fn = strrchr(r->file, '/');
  226   if(fn)
  227     fn++;
  228   else
  229     fn = r->file;
  230   mvaddnstr(row, i+22, fn, str_offset_from_columns(fn, wincols-i-22));
  231 
  232   attroff(iter == list->sel ? UIC(list_select) : UIC(list_default));
  233 }
  234 
  235 
  236 static void t_draw(ui_tab_t *tab) {
  237   tab_t *t = (tab_t *)tab;
  238 
  239   attron(UIC(list_header));
  240   mvhline(1, 0, ' ', wincols);
  241   mvaddstr(1,    2, "User");
  242   if(!t->hide_hub)
  243     mvaddstr(1, 22, "Hub");
  244   int i = t->hide_hub ? 22 : 36;
  245   mvaddstr(1, i,    "Size");
  246   mvaddstr(1, i+12, "Slots");
  247   mvaddstr(1, i+20, "F"); // short for "Flags"
  248   mvaddstr(1, i+22, "File");
  249   attroff(UIC(list_header));
  250 
  251   int bottom = winrows-4;
  252   ui_cursor_t cursor;
  253   int pos = ui_listing_draw(t->list, 2, bottom-1, &cursor, draw_row);
  254 
  255   search_r_t *sel = g_sequence_iter_is_end(t->list->sel) ? NULL : g_sequence_get(t->list->sel);
  256 
  257   // footer
  258   attron(UIC(separator));
  259   mvhline(bottom,   0, ' ', wincols);
  260   if(!sel)
  261     mvaddstr(bottom, 0, "Nothing selected.");
  262   else if(sel->size == G_MAXUINT64)
  263     mvaddstr(bottom, 0, "Directory.");
  264   else {
  265     char tth[40] = {};
  266     base32_encode(sel->tth, tth);
  267     mvprintw(bottom, 0, "%s (%s bytes)", tth, str_fullsize(sel->size));
  268   }
  269   const char *age = str_formatinterval(time(NULL)-t->age);
  270   mvprintw(bottom, wincols-25-strlen(age), "%5d results in %s - %3d%%",
  271     g_sequence_get_length(t->list->list), age, pos);
  272   attroff(UIC(separator));
  273   if(sel)
  274     mvaddnstr(bottom+1, 3, sel->file, str_offset_from_columns(sel->file, wincols-3));
  275   move(cursor.y, cursor.x);
  276 }
  277 
  278 
  279 static void t_key(ui_tab_t *tab, guint64 key) {
  280   tab_t *t = (tab_t *)tab;
  281 
  282   if(ui_listing_key(t->list, key, (winrows-4)/2))
  283     return;
  284 
  285   search_r_t *sel = g_sequence_iter_is_end(t->list->sel) ? NULL : g_sequence_get(t->list->sel);
  286   gboolean sort = FALSE;
  287 
  288   switch(key) {
  289   case INPT_CHAR('?'):
  290     uit_main_keys("search");
  291     break;
  292 
  293   case INPT_CHAR('f'): // f - find user
  294     if(!sel)
  295       ui_m(NULL, 0, "Nothing selected.");
  296     else {
  297       hub_user_t *u = g_hash_table_lookup(hub_uids, &sel->uid);
  298       if(!u)
  299         ui_m(NULL, 0, "User is not online.");
  300       else
  301         uit_userlist_open(u->hub, u->uid, NULL, FALSE);
  302     }
  303     break;
  304   case INPT_CHAR('b'): // b - /browse userlist
  305   case INPT_CHAR('B'): // B - /browse -f userlist
  306     if(!sel)
  307       ui_m(NULL, 0, "Nothing selected.");
  308     else {
  309       hub_user_t *u = g_hash_table_lookup(hub_uids, &sel->uid);
  310       if(!u)
  311         ui_m(NULL, 0, "User is not online.");
  312       else
  313         uit_fl_queue(u->uid, key == INPT_CHAR('B'), sel->file, tab, TRUE, FALSE);
  314     }
  315     break;
  316   case INPT_CHAR('d'): // d - download file
  317     if(!sel)
  318       ui_m(NULL, 0, "Nothing selected.");
  319     else if(sel->size == G_MAXUINT64)
  320       ui_m(NULL, 0, "Can't download directories from the search. Use 'b' to browse the file list instead.");
  321     else
  322       dl_queue_add_res(sel);
  323     break;
  324 
  325   case INPT_CHAR('m'): // m - match selected item with queue
  326     if(!sel)
  327       ui_m(NULL, 0, "Nothing selected.");
  328     else if(sel->size == G_MAXUINT64)
  329       ui_m(NULL, 0, "Can't download directories from the search. Use 'b' to browse the file list instead.");
  330     else {
  331       int r = dl_queue_matchfile(sel->uid, sel->tth);
  332       ui_m(NULL, 0, r < 0 ? "File not in the queue." :
  333                    r == 0 ? "User already in the queue."
  334                           : "Added user to queue for the selected file.");
  335     }
  336     break;
  337 
  338   case INPT_CHAR('M'):;// M - match all results with queue
  339     int n = 0, a = 0;
  340     GSequenceIter *i = g_sequence_get_begin_iter(t->list->list);
  341     for(; !g_sequence_iter_is_end(i); i=g_sequence_iter_next(i)) {
  342       search_r_t *r = g_sequence_get(i);
  343       int v = dl_queue_matchfile(r->uid, r->tth);
  344       if(v >= 0)
  345         n++;
  346       if(v == 1)
  347         a++;
  348     }
  349     ui_mf(NULL, 0, "Matched %d files, %d new.", n, a);
  350     break;
  351 
  352   case INPT_CHAR('q'): // q - download filelist and match queue for selected user
  353     if(!sel)
  354       ui_m(NULL, 0, "Nothing selected.");
  355     else
  356       uit_fl_queue(sel->uid, FALSE, NULL, NULL, FALSE, TRUE);
  357     break;
  358 
  359   case INPT_CHAR('Q'):{// Q - download filelist and match queue for all results
  360     GSequenceIter *i = g_sequence_get_begin_iter(t->list->list);
  361     // Use a hash table to avoid checking the same filelist more than once
  362     GHashTable *uids = g_hash_table_new(g_int64_hash, g_int64_equal);
  363     for(; !g_sequence_iter_is_end(i); i=g_sequence_iter_next(i)) {
  364       search_r_t *r = g_sequence_get(i);
  365       // In the case that this wasn't a TTH search, check whether this search
  366       // result matches the queue before checking the file list.
  367       if(t->q->type == 9 || dl_queue_matchfile(r->uid, r->tth) >= 0)
  368         g_hash_table_insert(uids, &r->uid, (void *)1);
  369     }
  370     GHashTableIter iter;
  371     g_hash_table_iter_init(&iter, uids);
  372     guint64 *uid;
  373     while(g_hash_table_iter_next(&iter, (gpointer *)&uid, NULL))
  374       uit_fl_queue(*uid, FALSE, NULL, NULL, FALSE, TRUE);
  375     ui_mf(NULL, 0, "Matching %d file lists...", g_hash_table_size(uids));
  376     g_hash_table_unref(uids);
  377   } break;
  378 
  379   case INPT_CHAR('a'): // a - search for alternative sources
  380     if(!sel)
  381       ui_m(NULL, 0, "Nothing selected.");
  382     else if(sel->size == G_MAXUINT64)
  383       ui_m(NULL, 0, "Can't look for alternative sources for directories.");
  384     else
  385       uit_search_open_tth(sel->tth, tab);
  386     break;
  387   case INPT_CHAR('h'): // h - show/hide hub column
  388     t->hide_hub = !t->hide_hub;
  389     break;
  390   case INPT_CHAR('u'): // u - sort on username
  391     t->reverse = t->order == SORT_USER ? !t->reverse : FALSE;
  392     t->order = SORT_USER;
  393     sort = TRUE;
  394     break;
  395   case INPT_CHAR('s'): // s - sort on size
  396     t->reverse = t->order == SORT_SIZE ? !t->reverse : FALSE;
  397     t->order = SORT_SIZE;
  398     sort = TRUE;
  399     break;
  400   case INPT_CHAR('l'): // l - sort on slots
  401     t->reverse = t->order == SORT_SLOTS ? !t->reverse : FALSE;
  402     t->order = SORT_SLOTS;
  403     sort = TRUE;
  404     break;
  405   case INPT_CHAR('n'): // n - sort on filename
  406     t->reverse = t->order == SORT_FILE ? !t->reverse : FALSE;
  407     t->order = SORT_FILE;
  408     sort = TRUE;
  409     break;
  410   }
  411 
  412   if(sort) {
  413     g_sequence_sort(t->list->list, sort_func, t);
  414     ui_listing_sorted(t->list);
  415     ui_mf(NULL, 0, "Ordering by %s (%s)",
  416       t->order == SORT_USER  ? "user name" :
  417       t->order == SORT_SIZE  ? "file size" :
  418       t->order == SORT_SLOTS ? "free slots" : "filename",
  419       t->reverse ? "descending" : "ascending");
  420   }
  421 }
  422 
  423 
  424 // Open a new tab for a TTH search on all hubs, or write a message to ui_m() on
  425 // error.
  426 void uit_search_open_tth(const char *tth, ui_tab_t *parent) {
  427   GError *err = NULL;
  428   ui_tab_t *rtab = uit_search_create(NULL, search_q_new_tth(tth), &err);
  429   if(err) {
  430     ui_mf(NULL, 0, "%s%s", rtab ? "Warning: " : "", err->message);
  431     g_error_free(err);
  432   }
  433   if(rtab)
  434     ui_tab_open(rtab, TRUE, parent);
  435 }
  436 
  437 
  438 ui_tab_type_t uit_search[1] = { { t_draw, t_title, t_key, t_close } };
  439