OSDN Git Service

7cae2988d6145ee854b1385c4db84ef0846301d6
[ntch/develop.git] / src / ui / disp_reslist.c
1 /* Copyright 2013 Akira Ohta (akohta001@gmail.com)
2     This file is part of ntch.
3
4     The ntch is free software: you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation, either version 3 of the License, or
7     (at your option) any later version.
8
9     The ntch is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with ntch.  If not, see <http://www.gnu.org/licenses/>.
16     
17 */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <assert.h>
22 #include <limits.h>
23 #include <wchar.h>
24 #include <regex.h>
25
26 #include        "env.h"
27 #include        "error.h"
28 #include        "utils/nt_std_t.h"
29 #include        "usr/usr_db_t.h"
30 #include        "utils/text.h"
31 #include        "_2ch/_2ch.h"
32 #include        "_2ch/model_2ch.h"
33 #include        "_2ch/parse_2ch.h"
34 #include        "ui/disp.h"
35 #include        "ui/disp_string.h"
36
37 #define NT_CMD_NONE -1
38 #define NT_CMD_WRITE 1
39 #define NT_CMD_JMP_NEW 2
40 #define NT_CMD_TREE 3
41 #define NT_CMD_ID 4
42 #define NT_CMD_SEARCH_THREAD 5
43 #define NT_CMD_FAVORITE 6
44 #define NT_CMD_HISTORY 7
45 #define NT_CMD_AUTO_SCROLL 8
46 #define NT_CMD_ADD_NGWORD 9
47 #define NT_CMD_ADD_NGNAME 10
48 #define NT_CMD_ADD_NGID 11
49 #define NT_CMD_EDT_NGWORD 12
50 #define NT_CMD_EDT_NGNAME 13
51 #define NT_CMD_EDT_NGID 14
52
53 #define AUTO_SCROLL_NONE 0
54 #define AUTO_SCROLL_UP  -1
55 #define AUTO_SCROLL_DOWN 1
56
57 typedef struct tag_ctx_reslist_t *ctx_reslist_tp;
58 typedef struct tag_ctx_reslist_t
59 {
60         int prev_state;
61         int res_num;
62         int cur_res;
63         int cur_res_offset;
64         int max_cur_res;
65         int max_cur_offset;
66         int prev_read_cnt;
67         nt_stack_tp     selected_num_stackp;
68
69         regex_t regex;
70         BOOL regex_init;
71         int sel_res_no;
72         int sel_res_line;
73         
74         int auto_scroll;
75
76         nt_link_tp res_disp_list;
77         
78         nt_stack_tp child_ctx_stackp;
79         
80 } ctx_reslist_t;
81
82 typedef struct tag_res_data_t *res_data_tp;
83 typedef struct tag_res_data_t {
84         nt_res_handle h_res;
85         wchar_t *res_msg;
86         nt_link_tp cite_num_list;
87         nt_link_tp cited_num_list;
88         int msg_line_num;
89         int msg_header_line_num;
90         nt_link_tp msg_line_linkp;
91         int id_num;
92 }res_data_t;
93
94 static ctx_reslist_tp init_context(nt_2ch_selected_item_handle h_select, 
95                         nt_usr_db_handle usr_db_handle);
96 static BOOL reslist_clone(nt_thread_handle h_thread, ctx_reslist_tp ctxp);
97 static void int_ptr_free(void *ptr);
98 static int parse_cmd1(const char *param, const char **end);
99 static BOOL search_line_asc(regex_t *regexp, nt_link_tp reslistp, 
100                         int *sel_res_no, int *sel_res_line, int column, 
101                         nt_ng_word_handle h_ngword);
102 static BOOL search_line_desc(regex_t *regexp, ctx_reslist_tp ctxp,
103                         nt_link_tp reslistp, 
104                         int *sel_res_no, int *sel_res_line, int column,
105                         nt_ng_word_handle h_ngword);
106 static int parse_res_msg(nt_link_tp disp_res_list, 
107                         res_data_tp res_datap, size_t colmns, nt_ng_word_handle h_ngword);
108 static void res_msg_free(void *ptr);
109 static nt_link_tp get_cited_num_list(nt_link_tp res_listp, int seq_no);
110 static BOOL reslist_copy(ctx_reslist_tp ctxp, ctx_reslist_tp copy_ctxp, 
111                         nt_link_tp num_listp);
112 static ctx_reslist_tp init_sub_context(ctx_reslist_tp ctxp, nt_link_tp num_listp);
113 static void free_reslist_sub_ctx(void *ptr);
114 static void free_disp_lines_ctx(void *ptr);
115 static nt_link_tp parse_tree_list(ctx_reslist_tp ctxp, const char *param);
116 static nt_link_tp parse_id_list(ctx_reslist_tp ctxp, const char *param);
117 static void search_up_tree(int seq_no, nt_link_tp disp_list, nt_link_tp *num_linkp);
118 static void search_down_tree(int seq_no, nt_link_tp disp_list, nt_link_tp *num_linkp);
119 static int get_id_num(ctx_reslist_tp ctxp, const wchar_t *misc);
120 static BOOL set_res_header(ctx_reslist_tp ctxp, res_data_tp res_datap,
121                                 wchar_t *buf, size_t buf_len);
122
123 int disp_reslist(nt_window_tp wp, int prev_state, nt_2ch_selected_item_handle h_select,
124         nt_usr_db_handle usr_db_handle, nt_ng_word_handle h_ng_word, nt_cloud_handle h_cloud)
125 {
126         ctx_reslist_tp ctxp, child_ctxp;
127         res_data_tp res_datap;
128         nt_link_tp clistp;
129         nt_link_tp linkp;
130         nt_stack_tp stackp;
131         int i, rows;
132         int wlines;
133         int len, ch;
134         int num;
135         int res_offset;
136         int *nptr;
137         void *ptr;
138         wchar_t buf[1024*3+1];
139         wchar_t *cptr;
140         nt_link_tp link_curp;
141         BOOL search_asc;
142         BOOL adjust;
143         BOOL set_value;
144         int line_num;
145         int  cmd;
146         attr_t new_res_attr;
147         const char *endp;
148         int result_state;
149         int auto_scroll_state;
150         
151         ctxp = (ctx_reslist_tp)wp->data;
152         if(!ctxp){
153                 ctxp = init_context(h_select, usr_db_handle);
154                 if(!ctxp)
155                         return DISP_STATE_ERROR;
156                 wp->data = ctxp;
157         }else if(ctxp->child_ctx_stackp){
158                 ctxp = (ctx_reslist_tp)nt_stack_peek(
159                                         ctxp->child_ctx_stackp);
160                 if(!ctxp){
161                         ctxp->child_ctx_stackp = NULL;
162                         ctxp = (ctx_reslist_tp)wp->data;
163                 }
164         }
165         
166         if(prev_state == DISP_STATE_THREADTITLE ||
167                 prev_state == DISP_STATE_SEARCH_THREAD ||
168                 prev_state == DISP_STATE_FAVORITE ||
169                 prev_state == DISP_STATE_HISTORY){
170                 ctxp->prev_state = prev_state;
171         }
172
173         ch = wp->key;
174         result_state = DISP_STATE_RESLIST;
175
176         if(ctxp->max_cur_res < 0 || ctxp->max_cur_offset < 0){
177                 wlines = wp->lines;
178                 int cur_res = ctxp->res_num - 1;
179                 /* get the bottom scroll position*/
180                 clistp = ctxp->res_disp_list->prev;
181                 while(clistp != ctxp->res_disp_list){
182                         res_datap = (res_data_tp)clistp->data;
183                         if(!res_datap->msg_line_linkp){
184                                 parse_res_msg(ctxp->res_disp_list, res_datap, wp->cols-5, h_ng_word);
185                         }
186
187                         if(res_datap->msg_header_line_num <= 0){
188                                 if(!set_res_header((ctx_reslist_tp)wp->data, 
189                                                 res_datap, buf, sizeof(buf)/sizeof(buf[0]))){
190                                         res_datap->msg_header_line_num = 1;
191                                 }else{
192                                         len = wcslen(buf);
193                                         cptr = buf;
194                                         while(len > 0){
195                                                 num = nt_get_wc_count_within_colmns(
196                                                                         cptr, wp->cols);
197                                                 if(num <= 0)
198                                                         break;
199                                                 res_datap->msg_header_line_num++;
200                                                 len -= num;
201                                                 cptr += num;
202                                         }
203                                 }
204                         }
205                         wlines--;
206                         if(wlines == 0){
207                                 ctxp->max_cur_offset = res_datap->msg_header_line_num;
208                                 ctxp->max_cur_offset += res_datap->msg_line_num;
209                                 ctxp->max_cur_res = cur_res;
210                                 break;
211                         }
212                         if(wlines <= res_datap->msg_line_num){
213                                 ctxp->max_cur_offset = res_datap->msg_header_line_num;
214                                 ctxp->max_cur_offset += res_datap->msg_line_num - wlines;
215                                 ctxp->max_cur_res = cur_res;
216                                 break;
217                         }
218                         wlines -= res_datap->msg_line_num;
219                         if(wlines <= res_datap->msg_header_line_num){
220                                 ctxp->max_cur_offset = 
221                                                 res_datap->msg_header_line_num - wlines;
222                                 ctxp->max_cur_res = cur_res;
223                                 break;
224                         }
225                         wlines -= res_datap->msg_header_line_num;
226                         cur_res--;
227                         if(cur_res < 0){
228                                 ctxp->max_cur_offset = 0;
229                                 ctxp->max_cur_res = 0;
230                                 break;
231                         }
232                         clistp = clistp->prev;
233                 }
234         }
235         
236         auto_scroll_state = ctxp->auto_scroll;
237         if(ch == NT_KEY_CMD_AUTO_SCROLL){
238                 result_state |= DISP_CMD_AUTO_SCROLL;
239                 if(ctxp->auto_scroll == AUTO_SCROLL_UP){
240                         ch = NT_KEY_UP;
241                 }else{
242                         ch = NT_KEY_DOWN;
243                         ctxp->auto_scroll = AUTO_SCROLL_DOWN;
244                 }
245         }else{
246                 ctxp->auto_scroll = AUTO_SCROLL_NONE;
247         }
248
249         switch(ch){
250         case NT_KEY_CMD_BOARD_UPDATE:
251         case NT_KEY_CMD_FAVORITE_UPDATE:
252                 if(auto_scroll_state != AUTO_SCROLL_NONE){
253                         result_state |= DISP_CMD_AUTO_SCROLL;
254                         ctxp->auto_scroll = auto_scroll_state;
255                 }
256                 break;
257         case NT_KEY_REFRESH:
258                 return DISP_CMD_REFRESH; 
259         case NT_KEY_ERASE:
260                 return DISP_STATE_THREADTITLE; 
261         case NT_KEY_CLOSE:
262         case KEY_LEFT:
263                 return ctxp->prev_state; 
264         case NT_KEY_BOTTOM:
265         case KEY_END:
266                 if(ctxp->cur_res == ctxp->res_num - 1)
267                         break;
268                 if(!ctxp->res_disp_list)
269                         break;
270                 clistp = ctxp->res_disp_list->prev;
271                 ctxp->cur_res = ctxp->max_cur_res;
272                 ctxp->cur_res_offset = ctxp->max_cur_offset;
273                 break;
274         case NT_KEY_LEFT:
275                 if(!ctxp->selected_num_stackp)
276                         break;
277                 if(0 >= nt_stack_get_position(ctxp->selected_num_stackp)){
278                         if((ctx_reslist_tp)wp->data != ctxp){
279                                 stackp = ((ctx_reslist_tp)wp->data)->child_ctx_stackp;
280                                 if(stackp){
281                                         if(0 >= nt_stack_get_position(stackp)){
282                                                 ctxp = (ctx_reslist_tp)wp->data;
283                                                 nt_stack_free(stackp, free_reslist_sub_ctx);
284                                                 ((ctx_reslist_tp)wp->data)->child_ctx_stackp = NULL;
285                                                 break;
286                                         }
287                                         child_ctxp = nt_stack_pop(stackp);
288                                         if(!child_ctxp)
289                                                 break;
290                                         child_ctxp = nt_stack_peek(stackp);
291                                         if(!child_ctxp)
292                                                 break;
293                                         ctxp = child_ctxp;
294                                 }
295                         }
296                         break;
297                 }
298                 nptr = malloc(sizeof(int));
299                 *nptr = ctxp->cur_res;
300                 ptr = nt_stack_add_last(ctxp->selected_num_stackp, nptr);
301                 if(!ptr){
302                         free(nptr);
303                 }
304                 nptr = nt_stack_pop(ctxp->selected_num_stackp);
305                 if(!nptr)
306                         break;
307                 num = *nptr;
308                 if(ctxp->cur_res == num)
309                         break;
310                 if(num >= ctxp->res_num)
311                         break;
312                 ctxp->cur_res = num;
313                 ctxp->cur_res_offset = 0;
314                 if(!ctxp->res_disp_list)
315                         break;
316                 clistp = ctxp->res_disp_list;
317                 for(i = 0; i < num; i++)
318                         clistp = clistp->next;
319                 break;
320         case NT_KEY_RIGHT:
321                 if(!ctxp->selected_num_stackp)
322                         break;
323                 nptr = nt_stack_cursor_next(ctxp->selected_num_stackp);
324                 if(!nptr)
325                         break;
326                 num = *nptr;
327                 if(ctxp->cur_res == num)
328                         break;
329                 if(num >= ctxp->res_num)
330                         break;
331                 ctxp->cur_res = num;
332                 ctxp->cur_res_offset = 0;
333                 if(!ctxp->res_disp_list)
334                         break;
335                 clistp = ctxp->res_disp_list;
336                 for(i = 0; i < num; i++)
337                         clistp = clistp->next;
338                 break;
339         case NT_KEY_COMMAND2:
340         case NT_KEY_COMMAND3:
341                 search_asc = (ch == NT_KEY_COMMAND2);
342                 if(wp->cmd_param && wp->cmd_param[0] != '\0'){
343                         if(0 != regcomp(&(ctxp->regex), 
344                                         wp->cmd_param, REG_EXTENDED)){
345                                 if(ctxp->regex_init){
346                                         regfree(&(ctxp->regex));
347                                         ctxp->regex_init = FALSE;
348                                         break;
349                                 }
350                         }
351                         if(!ctxp->regex_init)
352                                 ctxp->regex_init = TRUE;
353                 }
354                 if(!ctxp->regex_init)
355                         break;
356                 adjust = FALSE;
357                 if(search_asc){
358                         if(search_line_asc(&(ctxp->regex),ctxp->res_disp_list,
359                                         &ctxp->sel_res_no, &ctxp->sel_res_line, 
360                                         wp->cols - 5, h_ng_word)){
361                                 adjust = TRUE;
362                         }
363                 }else{
364                         if(search_line_desc(&(ctxp->regex),ctxp,
365                                         ctxp->res_disp_list,
366                                         &ctxp->sel_res_no, &ctxp->sel_res_line, 
367                                         wp->cols - 5, h_ng_word)){
368                                 adjust = TRUE;
369                         }
370                 }
371                 if(adjust){
372                         set_value = FALSE;
373                         rows = ctxp->res_num - 1;
374                         wlines = wp->lines / 2;
375                         wlines -= ctxp->sel_res_line;
376                         clistp = ctxp->res_disp_list->prev;
377                         do{
378                                 if(rows <= ctxp->sel_res_no){
379                                         res_datap = (res_data_tp)clistp->data;
380                                         //resp = (nt_res_tp)clistp->data;
381                                         if(!res_datap->msg_line_linkp){
382                                                 parse_res_msg(ctxp->res_disp_list, res_datap, wp->cols-5, h_ng_word);
383                                         }
384                                         if(res_datap->msg_header_line_num <= 0){
385                                                 if(!set_res_header((ctx_reslist_tp)wp->data, 
386                                                                 res_datap, buf, sizeof(buf)/sizeof(buf[0]))){
387                                                         res_datap->msg_header_line_num = 1;
388                                                 }else{
389                                                         len = wcslen(buf);
390                                                         cptr = buf;
391                                                         while(len > 0){
392                                                                 num = nt_get_wc_count_within_colmns(
393                                                                                         cptr, wp->cols);
394                                                                 if(num <= 0)
395                                                                         break;
396                                                                 res_datap->msg_header_line_num++;
397                                                                 len -= num;
398                                                                 cptr += num;
399                                                         }
400                                                 }
401                                         }
402                                         if(rows == ctxp->sel_res_no){
403                                                 if(wlines <= ctxp->sel_res_line){
404                                                         ctxp->cur_res = rows;
405                                                         ctxp->cur_res_offset = 
406                                                                         res_datap->msg_header_line_num + 
407                                                                         (ctxp->sel_res_line - wlines);
408                                                         set_value = TRUE;
409                                                         break;
410                                                 }
411                                                 wlines -= ctxp->sel_res_line;
412                                         }else if(wlines <= (res_datap->msg_line_num+1)){
413                                                 ctxp->cur_res = rows;
414                                                 ctxp->cur_res_offset = 
415                                                                 res_datap->msg_header_line_num + wlines;
416                                                 set_value = TRUE;
417                                                 break;
418                                         }else{
419                                                 wlines -= res_datap->msg_line_num + 1;
420                                         }
421                                         if(wlines <= res_datap->msg_header_line_num){
422                                                 ctxp->cur_res = rows;
423                                                 ctxp->cur_res_offset = 
424                                                                 res_datap->msg_header_line_num - wlines;
425                                                 set_value = TRUE;
426                                                 break;
427                                         }
428                                         wlines -= res_datap->msg_header_line_num;
429                                 }
430                                 rows--;
431                                 clistp = clistp->prev;
432                         }while(clistp != ctxp->res_disp_list->prev);
433                         if(!set_value){
434                                 ctxp->cur_res = 0;
435                                 ctxp->cur_res_offset = 0;
436                         }
437                 }
438                 break;
439         case NT_KEY_COMMAND1:
440                 assert(wp->cmd_param);
441                 cmd = parse_cmd1(wp->cmd_param, &endp);
442                 switch(cmd){
443                 case NT_CMD_AUTO_SCROLL:
444                         return DISP_CMD_AUTO_SCROLL | DISP_STATE_RESLIST;
445                 case NT_CMD_SEARCH_THREAD:
446                         return DISP_STATE_SEARCH_THREAD;
447                 case NT_CMD_FAVORITE:
448                         return DISP_STATE_FAVORITE;
449                 case NT_CMD_HISTORY:
450                         return DISP_STATE_HISTORY;
451                 case NT_CMD_WRITE:
452                         return DISP_STATE_EDITOR;
453                 case NT_CMD_JMP_NEW:
454                         if(ctxp->prev_read_cnt <= 0)
455                                 ctxp->cur_res = 0;
456                         else if(ctxp->prev_read_cnt >= ctxp->res_num){
457                                 ctxp->cur_res = ctxp->res_num - 1;
458                         }else{
459                                 ctxp->cur_res = ctxp->prev_read_cnt;
460                         }
461                         ctxp->cur_res_offset = 0;
462                         break;
463                 case NT_CMD_ADD_NGWORD:
464                         nt_ng_word_add_ng_word(h_cloud, h_ng_word, endp);
465                         free_disp_lines_ctx(wp->data);
466                         return DISP_CMD_REENTER | DISP_STATE_RESLIST;
467                 case NT_CMD_ADD_NGNAME:
468                         nt_ng_word_add_ng_name(h_cloud, h_ng_word, endp);
469                         free_disp_lines_ctx(wp->data);
470                         return DISP_CMD_REENTER | DISP_STATE_RESLIST;
471                 case NT_CMD_ADD_NGID:
472                         nt_ng_word_add_ng_id(h_cloud, h_ng_word, endp);
473                         free_disp_lines_ctx(wp->data);
474                         return DISP_CMD_REENTER | DISP_STATE_RESLIST;
475                 case NT_CMD_EDT_NGWORD:
476                         free_disp_lines_ctx(wp->data);
477                         return DISP_CMD_EDIT_NGWORD | DISP_STATE_RESLIST;
478                 case NT_CMD_EDT_NGNAME:
479                         free_disp_lines_ctx(wp->data);
480                         return DISP_CMD_EDIT_NGNAME | DISP_STATE_RESLIST;
481                 case NT_CMD_EDT_NGID:
482                         free_disp_lines_ctx(wp->data);
483                         return DISP_CMD_EDIT_NGID | DISP_STATE_RESLIST;
484                 default:
485                         if(cmd == NT_CMD_TREE){
486                                 linkp = parse_tree_list((ctx_reslist_tp)wp->data, endp);
487                         }else if(cmd == NT_CMD_ID){
488                                 linkp = parse_id_list((ctx_reslist_tp)wp->data, endp);
489                         }else{
490                                 linkp = nt_parse_number_list(wp->cmd_param, NULL);
491                         }
492                         if(!linkp)
493                                 break;
494                         num = nt_link_num(linkp);
495                         if(0 >= num){
496                                 break;
497                         }else if(1 == num){
498                                 num = linkp->n_data;
499                                 free(linkp);
500                                 num--;
501                                 if(ctxp->cur_res == num)
502                                         break;
503                                 if(num >= ctxp->res_num)
504                                         break;
505                                 nptr = malloc(sizeof(int));
506                                 *nptr = ctxp->cur_res;
507                                 ctxp->cur_res = num;
508                                 ctxp->cur_res_offset = 0;
509                                 if(!ctxp->res_disp_list){
510                                         free(nptr);
511                                         break;
512                                 }
513                                 clistp = ctxp->res_disp_list;
514                                 for(i = 0; i < num; i++)
515                                         clistp = clistp->next;
516                                 nt_stack_push(ctxp->selected_num_stackp, nptr);
517                         }else{
518                                 child_ctxp = init_sub_context((ctx_reslist_tp)wp->data, linkp);
519                                 if(child_ctxp){
520                                         ctxp = child_ctxp;
521                                 }
522                                 nt_all_link_free(linkp, NULL);
523                         }
524                         break;
525                 }/* end switch */
526                 break;
527         case NT_KEY_UP:
528         case KEY_UP:
529                 if(auto_scroll_state != AUTO_SCROLL_NONE){
530                         result_state |= DISP_CMD_AUTO_SCROLL;
531                         ctxp->auto_scroll = AUTO_SCROLL_UP;
532                 }
533                 ctxp->cur_res_offset--;
534                 if(0 <= ctxp->cur_res_offset){
535                         break;
536                 }
537                 if(0 >= ctxp->cur_res){
538                         ctxp->cur_res_offset = 0;
539                         ctxp->cur_res = 0;
540                         break;
541                 }
542                 ctxp->cur_res--;
543                 clistp = ctxp->res_disp_list;
544                 for(i = 0; i < ctxp->res_num; i++){
545                         if(i == ctxp->cur_res)
546                                 break;
547                         clistp = clistp->next;
548                 }
549                 if(i == ctxp->res_num)
550                         break;
551                 res_datap = (res_data_tp)clistp->data;
552                 if(!res_datap->msg_line_linkp){
553                         parse_res_msg(ctxp->res_disp_list, res_datap, wp->cols-5, h_ng_word);
554                 }
555                 if(res_datap->msg_header_line_num <= 0){
556                         if(!set_res_header((ctx_reslist_tp)wp->data, 
557                                                 res_datap, buf, sizeof(buf)/sizeof(buf[0]))){
558                                 res_datap->msg_header_line_num = 1;
559                         }else{
560                                 len = wcslen(buf);
561                                 cptr = buf;
562                                 while(len > 0){
563                                         num = nt_get_wc_count_within_colmns(
564                                                                 cptr, wp->cols);
565                                         if(num <= 0)
566                                                 break;
567                                         res_datap->msg_header_line_num++;
568                                         len -= num;
569                                         cptr += num;
570                                 }
571                         }
572                 }
573                 num = res_datap->msg_header_line_num;
574                 num += res_datap->msg_line_num;
575                 ctxp->cur_res_offset = num;
576                 break;
577         case NT_KEY_DOWN:
578         case KEY_DOWN:
579                 if(auto_scroll_state != AUTO_SCROLL_NONE){
580                         result_state |= DISP_CMD_AUTO_SCROLL;
581                         ctxp->auto_scroll = AUTO_SCROLL_DOWN;
582                 }
583                 ctxp->cur_res_offset++;
584                 clistp = ctxp->res_disp_list;
585                 for(i = 0; i < ctxp->res_num; i++){
586                         if(i == ctxp->cur_res)
587                                 break;
588                         clistp = clistp->next;
589                 }
590                 if(i == ctxp->res_num)
591                         break;
592                 res_datap = (res_data_tp)clistp->data;
593                 if(!res_datap->msg_line_linkp){
594                         parse_res_msg(ctxp->res_disp_list, res_datap, wp->cols-5, h_ng_word);
595                 }
596                 if(res_datap->msg_header_line_num <= 0){
597                         if(!set_res_header((ctx_reslist_tp)wp->data, 
598                                         res_datap, buf, sizeof(buf)/sizeof(buf[0]))){
599                                 res_datap->msg_header_line_num = 1;
600                         }else{
601                                 len = wcslen(buf);
602                                 cptr = buf;
603                                 while(len > 0){
604                                         num = nt_get_wc_count_within_colmns(
605                                                                 cptr, wp->cols);
606                                         if(num <= 0)
607                                                 break;
608                                         res_datap->msg_header_line_num++;
609                                         len -= num;
610                                         cptr += num;
611                                 }
612                         }
613                 }
614                 num = res_datap->msg_header_line_num;
615                 num += res_datap->msg_line_num + 1;
616                 if(ctxp->cur_res_offset >= num){
617                         ctxp->cur_res++;
618                         ctxp->cur_res_offset = 0;
619                 }
620                 break;
621         case NT_KEY_PAGEUP:
622         case KEY_PPAGE:
623                 if(auto_scroll_state != AUTO_SCROLL_NONE){
624                         result_state |= DISP_CMD_AUTO_SCROLL;
625                         ctxp->auto_scroll = AUTO_SCROLL_UP;
626                 }
627                 ctxp->cur_res_offset -= wp->lines;
628                 if(ctxp->cur_res_offset >= 0){
629                         break;
630                 }
631                 ctxp->cur_res--;
632                 clistp = ctxp->res_disp_list;
633                 for(i = 0; i < ctxp->res_num; i++){
634                         if(i == ctxp->cur_res)
635                                 break;
636                         clistp = clistp->next;
637                 }
638                 if(i == ctxp->res_num){
639                         ctxp->cur_res_offset = 0;
640                         break;
641                 }
642                 for( ; i > 0; i--){
643                         res_datap = (res_data_tp)clistp->data;
644                         if(!res_datap->msg_line_linkp){
645                                 parse_res_msg(ctxp->res_disp_list, res_datap, wp->cols-5, h_ng_word);
646                         }
647                         if(res_datap->msg_header_line_num <= 0){
648                                 if(!set_res_header((ctx_reslist_tp)wp->data, 
649                                                 res_datap, buf, sizeof(buf)/sizeof(buf[0]))){
650                                         res_datap->msg_header_line_num = 1;
651                                 }else{
652                                         len = wcslen(buf);
653                                         cptr = buf;
654                                         while(len > 0){
655                                                 num = nt_get_wc_count_within_colmns(
656                                                                         cptr, wp->cols);
657                                                 if(num <= 0)
658                                                         break;
659                                                 res_datap->msg_header_line_num++;
660                                                 len -= num;
661                                                 cptr += num;
662                                         }
663                                 }
664                         }
665                         num = res_datap->msg_header_line_num;
666                         num += res_datap->msg_line_num;
667                         if(ctxp->cur_res_offset + num < 0){
668                                 ctxp->cur_res_offset += num;
669                                 ctxp->cur_res--;
670                                 clistp = clistp->prev;
671                                 continue;
672                         }
673                         ctxp->cur_res_offset += num;
674                         break;
675                 }
676                 if(i == 0){
677                         ctxp->cur_res = 0;
678                         ctxp->cur_res_offset = 0;
679                 }
680                 break;
681         case NT_KEY_PAGEDOWN:
682         case KEY_NPAGE:
683                 if(auto_scroll_state != AUTO_SCROLL_NONE){
684                         result_state |= DISP_CMD_AUTO_SCROLL;
685                         ctxp->auto_scroll = AUTO_SCROLL_DOWN;
686                 }
687                 clistp = ctxp->res_disp_list;
688                 if(0 > ctxp->cur_res)
689                         ctxp->cur_res = 0;
690                 for(i = 0; i < ctxp->res_num; i++){
691                         if(i == ctxp->cur_res)
692                                 break;
693                         clistp = clistp->next;
694                 }
695                 if(i == ctxp->res_num)
696                         break;
697                                 
698                 ctxp->cur_res_offset += wp->lines;
699                 for( ; i < ctxp->res_num; i++){
700                         res_datap = (res_data_tp)clistp->data;
701                         if(!res_datap->msg_line_linkp){
702                                 parse_res_msg(ctxp->res_disp_list, res_datap, wp->cols-5, h_ng_word);
703                         }
704                         if(res_datap->msg_header_line_num <= 0){
705                                 if(!set_res_header((ctx_reslist_tp)wp->data, 
706                                                 res_datap, buf, sizeof(buf)/sizeof(buf[0]))){
707                                         res_datap->msg_header_line_num = 1;
708                                 }else{
709                                         len = wcslen(buf);
710                                         cptr = buf;
711                                         while(len > 0){
712                                                 num = nt_get_wc_count_within_colmns(
713                                                                         cptr, wp->cols);
714                                                 if(num <= 0)
715                                                         break;
716                                                 res_datap->msg_header_line_num++;
717                                                 len -= num;
718                                                 cptr += num;
719                                         }
720                                 }
721                         }
722                         num = res_datap->msg_header_line_num;
723                         num += res_datap->msg_line_num + 1;
724                         if(num > ctxp->cur_res_offset){
725                                 break;
726                         }
727                         ctxp->cur_res++;
728                         ctxp->cur_res_offset -= num;
729
730                         clistp = clistp->next;
731                 }
732                 break;
733         }/* end switch */
734
735         if(ctxp->cur_res > ctxp->max_cur_res ||
736                 (ctxp->cur_res == ctxp->max_cur_res && 
737                 ctxp->cur_res_offset > ctxp->max_cur_offset)){
738                 ctxp->cur_res = ctxp->max_cur_res;
739                 ctxp->cur_res_offset = ctxp->max_cur_offset;
740         }
741
742         clistp = ctxp->res_disp_list;
743         res_offset = 0;
744         rows = 0;
745         for(i = 0; i < ctxp->res_num; i++){
746                 if(i < ctxp->cur_res){
747                         clistp = clistp->next;
748                         continue;
749                 }
750
751                 if(rows == wp->lines)
752                         break;
753
754                 res_datap = (res_data_tp)clistp->data;
755                 
756                 if(!set_res_header((ctx_reslist_tp)wp->data, 
757                                 res_datap, buf, sizeof(buf)/sizeof(buf[0]))){
758                         continue;
759                 }
760                 len = wcslen(buf);
761                 num = nt_get_wc_count_within_colmns(buf, wp->cols);
762
763                 if(ctxp->res_num == ctxp->prev_read_cnt)
764                         new_res_attr = 0;
765                 else if(i+1 > ctxp->prev_read_cnt)
766                         new_res_attr = WA_BOLD;
767                 else
768                         new_res_attr = 0;
769
770                 if(num < len){
771                         if(!nt_w_str_move(buf, sizeof(buf), num, 1))
772                                 break;
773                         if(res_offset >= ctxp->cur_res_offset){
774                                 wmove(wp->wp, rows, 0);
775                                 nt_add_wstr(wp->wp, buf, new_res_attr);
776                                 rows++;
777                         }else{
778                                 res_offset++;
779                         }
780                         if(rows == wp->lines)
781                                 break;
782                         if(res_offset >= ctxp->cur_res_offset){
783                                 wmove(wp->wp, rows, 0);
784                                 nt_add_wnch(wp->wp, L' ', WA_UNDERLINE, 5);
785                                 nt_add_wnstr(wp->wp, buf + num + 1, 
786                                                         new_res_attr | WA_UNDERLINE,
787                                                         wp->cols - 5);
788                                 rows++;
789                         }else{
790                                 res_offset++;
791                         }
792                 }else{
793                         if(res_offset >= ctxp->cur_res_offset){
794                                 wmove(wp->wp, rows, 0);
795                                 nt_add_wstr(wp->wp, buf, 
796                                                         new_res_attr | WA_UNDERLINE);
797                                 rows++;
798                         }else{
799                                 res_offset++;
800                         }
801                 }
802                 if(rows == wp->lines)
803                         break;
804                 if(!res_datap->msg_line_linkp){
805                         parse_res_msg(ctxp->res_disp_list, res_datap, wp->cols-5, h_ng_word);
806                 }
807                 if(0 == res_datap->msg_line_num)
808                         continue;
809
810                 link_curp = res_datap->msg_line_linkp;
811                 line_num = 0;
812                 do{
813                         cptr = (wchar_t*)link_curp->data;
814                         if(res_offset >= ctxp->cur_res_offset){
815                                 wmove(wp->wp, rows, 5);
816                                 if(i == ctxp->sel_res_no &&
817                                         line_num == ctxp->sel_res_line){
818                                         /*fprintf(stderr, "hilighted no: %d, line: %d\n",
819                                                 i, line_num);*/
820                                         nt_add_wstr(wp->wp, cptr, WA_REVERSE);
821                                 }else{
822                                         nt_add_wstr(wp->wp, cptr, 0);
823                                 }
824                                 rows++;
825                                 if(rows == wp->lines)
826                                         goto END_FOR;
827                         }else{
828                                 res_offset++;
829                         }
830                         line_num++;
831                         link_curp = link_curp->next;
832                 }while(res_datap->msg_line_linkp != link_curp);
833
834                 if(rows == wp->lines)
835                         goto END_FOR;
836                 if(res_offset >= ctxp->cur_res_offset){
837                         wmove(wp->wp, rows, 0);
838                         nt_add_wnch(wp->wp, L' ', 0, wp->cols);
839                         if(res_datap->cited_num_list){
840                                 cptr = nt_w_format_number_list(
841                                                 res_datap->cited_num_list);
842                                 if(cptr){
843                                         wmove(wp->wp, rows, 0);
844                                         nt_add_wnch(wp->wp, L' ', 0, 6);
845                                         nt_add_wstr(wp->wp, L"<<", 0);
846                                         nt_add_wnstr(wp->wp, cptr, 
847                                                         0, wp->cols - 8);
848                                         free(cptr);
849                                 }
850                         }else{
851                         }
852                         rows++;
853                 }else{
854                         res_offset++;
855                 }
856                 clistp = clistp->next;
857         }/* end for */
858 END_FOR:
859         
860         return result_state; 
861 }
862
863 static int get_id_num(ctx_reslist_tp ctxp, const wchar_t *misc)
864 {
865         int num;
866         num = 0;
867         res_data_tp datap;
868         wchar_t *cptr;
869         const wchar_t *misc2;
870         nt_link_tp linkp, disp_list;
871         
872         assert(ctxp);
873         
874         cptr = wcsstr(misc, L"ID:");
875         if(!cptr)
876                 return 0;
877         
878         cptr = nt_w_trim(cptr);
879         if(!cptr)
880                 return 0;
881         
882         disp_list = ctxp->res_disp_list;
883         linkp = disp_list;
884         do{
885                 datap = (res_data_tp)linkp->data;
886                 misc2 = nt_res_get_misc(datap->h_res);
887                 if(wcsstr(misc2, cptr))
888                         num++;
889                 linkp = linkp->next;
890         }while(linkp != disp_list);
891         
892         free(cptr);
893         return num;
894 }
895
896 static BOOL set_res_header(ctx_reslist_tp ctxp, res_data_tp res_datap,
897                                 wchar_t *buf, size_t buf_len)
898 {
899         const wchar_t *name, *mail, *misc;
900         int seq_no, id_num;
901         
902         seq_no = nt_res_get_seq_number(res_datap->h_res);
903         name = nt_res_get_name(res_datap->h_res);
904         mail = nt_res_get_mail(res_datap->h_res);
905         misc = nt_res_get_misc(res_datap->h_res);
906         
907         id_num = res_datap->id_num;
908         if(id_num < 0){
909                 id_num = get_id_num(ctxp, misc);
910                 res_datap->id_num = id_num;
911         }
912         if(id_num <= 1){
913                 if(-1 == swprintf(buf, buf_len-1, 
914                                 L"%5d. %ls %ls %ls", seq_no, name, mail, misc)){
915                         return FALSE;
916                 }
917         }else{
918                 if(-1 == swprintf(buf, buf_len-1, 
919                                 L"%5d. %ls %ls %ls(%d)", seq_no, name, mail, misc, id_num)){
920                         return FALSE;
921                 }
922         }
923         return TRUE;
924 }
925
926 static int parse_cmd1(const char *param, const char **end)
927 {
928         int len;
929         const char *start;
930         
931         assert(param);
932         
933         if(!nt_strtok(param, ' ', &start, end))
934                 return NT_CMD_NONE;
935         
936         len = *end - start;
937         if(len <= 0)
938                 return NT_CMD_NONE;
939         
940         if(0 == strncmp(NT_COMMAND1_WRITE_MSG_1, start,len) ||
941                 0 == strncmp(NT_COMMAND1_WRITE_MSG_2, start,len)){
942                 return NT_CMD_WRITE;
943         }else if(0 == strncmp(NT_COMMAND1_JMP_NEW_1,start,len) ||
944                 0 == strncmp(NT_COMMAND1_JMP_NEW_2, start,len)){
945                 return NT_CMD_JMP_NEW;
946         }else if(0 == strncmp(NT_COMMAND1_TREE_1, start,len) ||
947                 0 == strncmp(NT_COMMAND1_TREE_2, start,len)){
948                 return NT_CMD_TREE;
949         }else if(0 == strncmp(NT_COMMAND1_ID_1, start,len) ||
950                 0 == strncmp(NT_COMMAND1_ID_2, start,len)){
951                 return NT_CMD_ID;
952         }else if(0 == strncmp(NT_COMMAND1_SEARCH_1,param,
953                         strlen(NT_COMMAND1_SEARCH_1)) ||
954                 0 == strncmp(NT_COMMAND1_SEARCH_2,param,
955                         strlen(NT_COMMAND1_SEARCH_2))){
956                 return NT_CMD_SEARCH_THREAD;
957         }else if(0 == strncmp(NT_COMMAND1_FAVORITE_1,param,
958                         strlen(NT_COMMAND1_FAVORITE_1)) ||
959                 0 == strncmp(NT_COMMAND1_FAVORITE_2,param,
960                         strlen(NT_COMMAND1_FAVORITE_2))){
961                 return NT_CMD_FAVORITE;
962         }else if(0 == strncmp(NT_COMMAND1_HISTORY_1,param,
963                         strlen(NT_COMMAND1_HISTORY_1)) ||
964                 0 == strncmp(NT_COMMAND1_HISTORY_2,param,
965                         strlen(NT_COMMAND1_HISTORY_2))){
966                 return NT_CMD_HISTORY;
967         }else if(0 == strncmp(NT_COMMAND1_AUTOSCROLL_1,param,
968                         strlen(NT_COMMAND1_AUTOSCROLL_1)) ||
969                 0 == strncmp(NT_COMMAND1_AUTOSCROLL_2,param,
970                         strlen(NT_COMMAND1_AUTOSCROLL_2))){
971                 return NT_CMD_AUTO_SCROLL;
972         }else if(0 == strncmp(NT_COMMAND1_NG_WORD_1,param,len) ||
973                         0 == strncmp(NT_COMMAND1_NG_WORD_2,param, len)){
974                 if(!nt_strtok(*end, ' ', &start, end))
975                         return NT_CMD_EDT_NGWORD;
976                 
977                 len = *end - start;
978                 if(len <= 0)
979                         return NT_CMD_EDT_NGWORD;
980                 *end = start;
981                 return NT_CMD_ADD_NGWORD;
982         }else if(0 == strncmp(NT_COMMAND1_NG_NAME_1,param,len) ||
983                         0 == strncmp(NT_COMMAND1_NG_NAME_2,param, len)){
984                 if(!nt_strtok(*end, ' ', &start, end))
985                         return NT_CMD_EDT_NGNAME;
986                 
987                 len = *end - start;
988                 if(len <= 0)
989                         return NT_CMD_EDT_NGNAME;
990                 *end = start;
991                 return NT_CMD_ADD_NGNAME;
992         }else if(0 == strncmp(NT_COMMAND1_NG_ID_1,param,len)){
993                 if(!nt_strtok(*end, ' ', &start, end))
994                         return NT_CMD_EDT_NGID;
995                 
996                 len = *end - start;
997                 if(len <= 0)
998                         return NT_CMD_EDT_NGID;
999                 *end = start;
1000                 return NT_CMD_ADD_NGID;
1001         }
1002         return NT_CMD_NONE;
1003 }
1004
1005 static ctx_reslist_tp init_context(nt_2ch_selected_item_handle h_select,
1006         nt_usr_db_handle usr_db_handle)
1007 {
1008         nt_thread_handle h_thread;
1009         ctx_reslist_tp ctxp;
1010         const wchar_t *board_name, *dat_name;
1011         BOOL b_result;
1012         
1013         board_name = nt_2ch_selected_item_get_board_name(h_select);
1014         dat_name = nt_2ch_selected_item_get_thread_dat_name(h_select);
1015         assert(board_name);
1016         assert(dat_name);
1017
1018         ctxp = (ctx_reslist_tp)calloc(1,sizeof(ctx_reslist_t));
1019
1020         if(!ctxp)
1021                 return NULL;
1022         
1023         h_thread = nt_get_selected_thread(h_select);
1024         if(!h_thread){
1025                 free(ctxp);
1026                 return NULL;
1027         }
1028         ctxp->cur_res = 0;
1029         ctxp->cur_res_offset = 0;
1030         ctxp->max_cur_res = -1;
1031         ctxp->max_cur_offset = -1;
1032         ctxp->selected_num_stackp = nt_stack_alloc();
1033         ctxp->regex_init = FALSE;
1034         ctxp->sel_res_no = -1;
1035         ctxp->sel_res_line = -1;
1036         ctxp->child_ctx_stackp = NULL;
1037         ctxp->prev_state = DISP_STATE_THREADTITLE;
1038         ctxp->auto_scroll = AUTO_SCROLL_NONE;
1039
1040         b_result = reslist_clone(h_thread, ctxp);
1041         nt_thread_release_ref(h_thread);
1042         if(!b_result){
1043                 free_reslist_ctx(ctxp);
1044                 return NULL;
1045         }
1046         
1047         ctxp->prev_read_cnt = 
1048                 nt_usr_db_update_read_count(usr_db_handle, 
1049                                 board_name, dat_name,
1050                                 ctxp->res_num,  NULL);
1051         if(ctxp->prev_read_cnt < 0)
1052                 ctxp->prev_read_cnt = 0;
1053         if(ctxp->prev_read_cnt > 0){
1054                 ctxp->cur_res = ctxp->prev_read_cnt;
1055         }
1056         
1057         return ctxp;
1058 }
1059
1060 static ctx_reslist_tp init_sub_context(ctx_reslist_tp ctxp, nt_link_tp num_listp)
1061 {
1062         ctx_reslist_tp child_ctxp;
1063         child_ctxp = (ctx_reslist_tp)calloc(1,sizeof(ctx_reslist_t));
1064         if(!child_ctxp)
1065                 return NULL;
1066         
1067         child_ctxp->cur_res = 0;
1068         child_ctxp->cur_res_offset = 0;
1069         child_ctxp->max_cur_res = -1;
1070         child_ctxp->max_cur_offset = -1;
1071         child_ctxp->selected_num_stackp = nt_stack_alloc();
1072         child_ctxp->regex_init = FALSE;
1073         child_ctxp->sel_res_no = -1;
1074         child_ctxp->sel_res_line = -1;
1075         child_ctxp->child_ctx_stackp = NULL;
1076         child_ctxp->prev_read_cnt = 0;
1077         child_ctxp->prev_state = ctxp->prev_state;
1078
1079         if(!reslist_copy(ctxp, child_ctxp, num_listp)){
1080                 nt_stack_free(child_ctxp->selected_num_stackp, NULL);
1081                 free(child_ctxp);
1082                 return NULL;
1083         }
1084         
1085         if(!ctxp->child_ctx_stackp){
1086                 ctxp->child_ctx_stackp = nt_stack_alloc();
1087                 if(!ctxp->child_ctx_stackp){
1088                         nt_stack_free(child_ctxp->selected_num_stackp, NULL);
1089                         free(child_ctxp);
1090                         return NULL;
1091                 }
1092         }
1093         if(!nt_stack_push(ctxp->child_ctx_stackp, child_ctxp)){
1094                 nt_stack_free(child_ctxp->selected_num_stackp, NULL);
1095                 free(child_ctxp);
1096                 return NULL;
1097         }
1098         return child_ctxp;
1099 }
1100
1101 static const wchar_t *get_id(const wchar_t *source)
1102 {
1103         const wchar_t *cptr;
1104         if(!source)
1105                 return NULL;
1106         cptr = wcsstr(source, L"ID:");
1107         if(!cptr)
1108                 return NULL;
1109         cptr += 3;
1110         if(cptr[0] == L'\0' || cptr[0] == L'?')
1111                 return NULL;
1112         return cptr;
1113 }
1114
1115 static BOOL reslist_clone(nt_thread_handle h_thread, ctx_reslist_tp ctxp)
1116 {
1117         nt_enum_handle h_enum_res;
1118         nt_link_tp linkp;
1119         nt_res_handle h_res;
1120         res_data_tp datap;
1121         int num_res;
1122         
1123         h_enum_res = nt_thread_get_res_enum(h_thread);
1124         if(!h_enum_res)
1125                 return FALSE;
1126         
1127         num_res = 0;
1128         while(NULL != (h_res = (nt_res_handle)nt_enum_fetch(h_enum_res))){
1129                 datap = (res_data_tp)calloc(1, sizeof(res_data_t));
1130                 if(datap){
1131                         datap->id_num = -1;
1132                         datap->h_res = h_res;
1133                         datap->res_msg = nt_parse_res_msg(
1134                                         nt_res_get_msg(h_res),
1135                                         &datap->cite_num_list);
1136                         if(!datap->res_msg){
1137                                 datap->res_msg = nt_w_str_clone(NT_ERR_MSG_DECODE_TEXT);
1138                         }
1139                         linkp = nt_link_add_data(ctxp->res_disp_list, datap);
1140                         if(!linkp){
1141                                 free(datap->res_msg);
1142                                 free(datap);
1143                                 continue;
1144                         }
1145                         if(!ctxp->res_disp_list)
1146                                 ctxp->res_disp_list = linkp;
1147                         nt_res_add_ref(h_res);
1148                         num_res++;
1149                 }
1150         }
1151         nt_enum_unset(h_enum_res);
1152         ctxp->res_num = num_res;
1153         return TRUE;
1154 }
1155
1156 static BOOL reslist_copy(ctx_reslist_tp ctxp, ctx_reslist_tp copy_ctxp, nt_link_tp num_listp)
1157 {
1158         nt_link_tp linkp, r_linkp;
1159         nt_link_tp d_linkp;
1160         nt_res_handle h_res;
1161         res_data_tp datap;
1162         int num_res;
1163         int seq_no;
1164         
1165         assert(ctxp && copy_ctxp && num_listp);
1166         
1167         num_res = 0;
1168         linkp = num_listp;
1169         do{
1170                 seq_no = linkp->n_data;
1171                 d_linkp = ctxp->res_disp_list;
1172                 do{
1173                         datap = (res_data_tp)d_linkp->data;
1174                         h_res = datap->h_res;
1175                         if(seq_no == nt_res_get_seq_number(h_res)){
1176                                 r_linkp = nt_link_add_data(copy_ctxp->res_disp_list, datap);
1177                                 if(r_linkp){
1178                                         if(!copy_ctxp->res_disp_list)
1179                                                 copy_ctxp->res_disp_list = r_linkp;
1180                                         num_res++;
1181                                 }
1182                                 break;
1183                         }
1184                         d_linkp = d_linkp->next;
1185                 }while(d_linkp != ctxp->res_disp_list);
1186                 linkp = linkp->next;
1187         }while(linkp != num_listp);
1188         copy_ctxp->res_num = num_res;
1189         return TRUE;
1190 }
1191
1192 void free_reslist_ctx(void *ptr)
1193 {
1194         ctx_reslist_tp ctxp;
1195         nt_stack_tp stackp;
1196         if(!ptr)
1197                 return;
1198         ctxp = (ctx_reslist_tp)ptr;
1199         nt_stack_free(ctxp->selected_num_stackp, &int_ptr_free);
1200         if(ctxp->regex_init)
1201                 regfree(&(ctxp->regex));
1202         if(ctxp->res_disp_list){
1203                 nt_all_link_free(ctxp->res_disp_list, res_msg_free);
1204         }
1205         stackp = ctxp->child_ctx_stackp;
1206         if(stackp){
1207                 nt_stack_free(stackp, free_reslist_sub_ctx);
1208         }
1209         free(ptr);
1210 }
1211
1212 static void free_disp_lines_ctx(void *ptr)
1213 {
1214         ctx_reslist_tp ctxp;
1215         nt_stack_tp stackp;
1216         nt_link_tp linkp;
1217         res_data_tp res_datap;
1218         if(!ptr)
1219                 return;
1220         ctxp = (ctx_reslist_tp)ptr;
1221         if(ctxp->res_disp_list){
1222                 linkp = ctxp->res_disp_list;
1223                 do{
1224                         res_datap = (res_data_tp)linkp->data;
1225                         if(res_datap->msg_line_linkp){
1226                                 free(res_datap->msg_line_linkp->data);
1227                                 nt_all_link_free(res_datap->msg_line_linkp, NULL);
1228                                 res_datap->msg_line_linkp = NULL;
1229                         }
1230                         res_datap->msg_line_num = 0;
1231                         linkp = linkp->next;
1232                 }while(linkp != ctxp->res_disp_list);
1233         }
1234         stackp = ctxp->child_ctx_stackp;
1235         if(stackp){
1236                 nt_stack_free(stackp, free_reslist_sub_ctx);
1237                 ctxp->child_ctx_stackp = NULL;
1238         }
1239 }
1240
1241 static void free_reslist_sub_ctx(void *ptr)
1242 {
1243         ctx_reslist_tp ctxp;
1244         if(!ptr)
1245                 return;
1246         ctxp = (ctx_reslist_tp)ptr;
1247         nt_stack_free(ctxp->selected_num_stackp, &int_ptr_free);
1248         if(ctxp->regex_init)
1249                 regfree(&(ctxp->regex));
1250         if(ctxp->res_disp_list){
1251                 nt_all_link_free(ctxp->res_disp_list, NULL);
1252         }
1253         free(ptr);
1254 }
1255
1256 static void int_ptr_free(void *ptr)
1257 {
1258         free(ptr);
1259 }
1260
1261 static void res_msg_free(void *ptr)
1262 {
1263         nt_link_tp curp, nextp;
1264         res_data_tp res_datap;
1265         
1266         assert(ptr);
1267         
1268         res_datap = (res_data_tp)ptr;
1269         
1270         free(res_datap->res_msg);
1271         
1272         nt_res_release_ref(res_datap->h_res);
1273         
1274         if(res_datap->cite_num_list)
1275                 nt_all_link_free(res_datap->cite_num_list, NULL);
1276         if(res_datap->cited_num_list)
1277                 nt_all_link_free(res_datap->cited_num_list, NULL);
1278         
1279         if(res_datap->msg_line_linkp){
1280                 curp = res_datap->msg_line_linkp->next;
1281                 while(curp != res_datap->msg_line_linkp){
1282                         nextp = curp->next;
1283                         /* We must not free the data pointer
1284                          * except the first one
1285                          * because they're alocated as a same block.
1286                          * This is wrong --> free(curp->data);
1287                          */
1288                         free(curp);
1289                         curp = nextp;
1290                 }
1291                 free(curp->data);
1292                 free(curp);
1293                 res_datap->msg_line_linkp = NULL;
1294         }
1295         free(res_datap);
1296 }
1297
1298                 
1299
1300 static nt_link_tp get_cited_num_list(nt_link_tp res_listp, int seq_no)
1301 {
1302         nt_link_tp linkp, wrkp, wrk2p;
1303         nt_link_tp num_listp;
1304         res_data_tp res_datap;
1305         
1306         num_listp = NULL;
1307         
1308         linkp = res_listp;
1309         do{
1310                 res_datap = (res_data_tp)linkp->data;
1311                 if(res_datap->cite_num_list){
1312                         wrkp = res_datap->cite_num_list;
1313                         do{
1314                                 if(wrkp->n_data == seq_no){
1315                                         wrk2p = nt_link_add_n_data(
1316                                                         num_listp, 
1317                                                         nt_res_get_seq_number(res_datap->h_res));
1318                                         if(wrk2p && !num_listp)
1319                                                 num_listp = wrk2p;
1320                                         break;
1321                                 }
1322                                 wrkp = wrkp->next;
1323                         }while(wrkp != res_datap->cite_num_list);
1324                 }
1325                 linkp = linkp->next;
1326         }while(linkp != res_listp);
1327         if(num_listp)
1328                 nt_link_n_sort(&num_listp, nt_comp_int);
1329         return num_listp;
1330 }
1331
1332 static int parse_res_msg(nt_link_tp disp_res_list, 
1333                                 res_data_tp res_datap, size_t colmns,
1334                                 nt_ng_word_handle h_ngword)
1335 {
1336         int i, len, start, lines, nwrite;
1337         wchar_t ch;
1338         wchar_t *buf, *cptr, *srcp, *wrk_buf;
1339         const wchar_t *name, *id, *misc;
1340         wchar_t match[256];
1341         int offset;
1342         nt_link_tp linkp;
1343         int consume_colmns;
1344         int buf_idx, buf_size;
1345         const int delta = 64;
1346
1347         assert(res_datap);
1348         assert(res_datap->h_res);
1349         assert(res_datap->res_msg);
1350
1351         //res_msg_free(res_datap);
1352         res_datap->msg_line_num = 0;
1353         
1354         name = nt_res_get_name(res_datap->h_res);
1355         misc = nt_res_get_misc(res_datap->h_res);
1356         if(misc)
1357                 id = get_id(misc);
1358         else
1359                 id = NULL;
1360         cptr = NULL;
1361         if(name && NG_ITEM_NAME == 
1362                         ng_word_match(h_ngword, NG_ITEM_NAME, name, 
1363                         match, sizeof(match)/sizeof(wchar_t))){
1364                 cptr = L"NGNAME:";
1365         }else if(id && NG_ITEM_ID == 
1366                         ng_word_match(h_ngword, NG_ITEM_ID, id, 
1367                         match, sizeof(match)/sizeof(wchar_t))){
1368                 cptr = L"NGID:";
1369         }else if(NG_ITEM_MSG == ng_word_match(h_ngword, NG_ITEM_MSG, 
1370                         res_datap->res_msg, 
1371                         match, sizeof(match)/sizeof(wchar_t))){
1372                 cptr = L"NGWORD:";
1373         }
1374         if(cptr){
1375                 len = wcslen(cptr) + wcslen(match) + 1;
1376                 buf = malloc(len*sizeof(wchar_t));
1377                 if(!buf)
1378                         return 0;
1379                 swprintf(buf, len, L"%ls%ls", cptr, match);
1380                 nwrite = nt_get_wc_count_within_colmns(buf, colmns);
1381                 if(nwrite <= 0){
1382                         free(buf);
1383                         return 0;
1384                 }
1385                 buf[nwrite] = L'\0';
1386                 res_datap->msg_line_num = 1;
1387                 res_datap->msg_line_linkp = nt_link_add_data(NULL, buf);
1388                 return 1;
1389         }
1390         
1391         srcp = res_datap->res_msg;
1392         
1393         len = wcslen(srcp);
1394         if(len == 0)
1395                 return 0;
1396         buf_size = len + delta;
1397
1398         if(len == 0)
1399                 return 0;
1400         buf = malloc(sizeof(wchar_t) * (buf_size));
1401         if(!buf)
1402                 return 0;
1403         buf_idx = 0;
1404
1405
1406         lines = 0;
1407         start = 0;
1408         consume_colmns = 0;
1409         for(i = 0; i < len; i++){
1410                 ch = srcp[i];
1411                 switch(ch){
1412                 case L'\n':
1413                         cptr = buf + start;
1414                         buf[buf_idx] = L'\0';
1415                         buf_idx++;
1416                         linkp = nt_link_add_data(
1417                                         res_datap->msg_line_linkp, cptr);
1418                         if(!linkp)
1419                                 goto END_FOR;
1420                         if(res_datap->msg_line_linkp == NULL)
1421                                 res_datap->msg_line_linkp = linkp;
1422
1423                         consume_colmns = 0;
1424                         lines++;
1425                         start = buf_idx;
1426                         break;
1427                 default:
1428                         if(ch <= 128 || (ch >= 0xff66 && ch <= 0xff9d))
1429                                 offset = 1;
1430                         else
1431                                 offset = 2;
1432
1433                         if(consume_colmns + offset > colmns){
1434                                 cptr = buf + start;
1435                                 buf[buf_idx] = L'\0';
1436                                 linkp = nt_link_add_data(
1437                                                 res_datap->msg_line_linkp, cptr);
1438                                 if(!linkp)
1439                                         goto END_FOR;
1440                                 if(res_datap->msg_line_linkp == NULL)
1441                                         res_datap->msg_line_linkp = linkp;
1442                                         
1443                                 lines++;
1444                                 buf_idx++;
1445                                 start = buf_idx;
1446                                 consume_colmns = 0;
1447                                 i--;
1448                         }else{
1449                                 consume_colmns += offset;
1450                                 buf[buf_idx] = ch;
1451                                 buf_idx++;
1452                         }
1453                         break;
1454                 }/* end switch */
1455                 if(buf_idx >= (buf_size-1)){
1456                         wrk_buf = nt_w_str_resize(buf, buf_size, (buf_size + delta));
1457                         if(!wrk_buf){
1458                                 nt_all_link_free(
1459                                         res_datap->msg_line_linkp, NULL);
1460                                 free(buf);
1461                                 res_datap->msg_line_linkp = NULL;
1462                                 return 0;
1463                         }
1464                         buf = wrk_buf;
1465                         buf_size += delta;
1466                 }
1467         } /* end for */
1468 END_FOR:
1469         if(consume_colmns > 0){
1470                 cptr = buf + start;
1471                 buf[buf_idx] = L'\0';
1472                 linkp = nt_link_add_data(
1473                                 res_datap->msg_line_linkp, cptr);
1474                 if(linkp){
1475                         if(res_datap->msg_line_linkp == NULL)
1476                                 res_datap->msg_line_linkp = linkp;
1477                         lines++;
1478                 }
1479         }
1480         res_datap->msg_line_num = lines;
1481         
1482         if(!res_datap->cited_num_list){
1483                 res_datap->cited_num_list = 
1484                         get_cited_num_list(disp_res_list, 
1485                                 nt_res_get_seq_number(res_datap->h_res));
1486         }
1487         return lines;
1488 }
1489
1490 static BOOL search_line_asc(regex_t *regexp, nt_link_tp reslistp, 
1491                         int *sel_res_no, int *sel_res_line, int column, nt_ng_word_handle h_ngword)
1492 {
1493         nt_link_tp clistp, listp;
1494         //nt_res_tp resp;
1495         res_data_tp res_datap;
1496         int cur, line, res_no, res_line;
1497         wchar_t *cptr;
1498         char buf[256];
1499         size_t nmatch = 5;
1500         regmatch_t pmatch[5];
1501
1502         if(!reslistp)
1503                 return FALSE;
1504         
1505         if(*sel_res_no < 0)
1506                 res_no = 0;
1507         else
1508                 res_no = *sel_res_no;
1509         if(*sel_res_line < 0)
1510                 res_line = 0;
1511         else
1512                 res_line = *sel_res_line + 1;
1513                 
1514         cur = 0;
1515         clistp = reslistp;
1516         do{
1517                 if(cur >= res_no){
1518                         res_datap = (res_data_tp)clistp->data;
1519                         //resp = res_datap->resp;
1520                         if(!res_datap->msg_line_linkp)
1521                                 parse_res_msg(reslistp, res_datap, column, h_ngword);
1522                         listp = res_datap->msg_line_linkp;
1523                         line = 0;
1524                         do{
1525                                 if(line >= res_line){
1526                                         cptr = (wchar_t*)listp->data;
1527                                         if(0 < wcstombs(buf, cptr, sizeof(buf))){
1528                                                 if(0 == regexec(regexp, buf, 
1529                                                                 nmatch, pmatch, 0)){
1530                                                         *sel_res_line = line;
1531                                                         *sel_res_no = cur;
1532                                                         return TRUE;
1533                                                 }
1534                                         }
1535                                 }
1536                                 line++;
1537                                 listp = listp->next;
1538                         }while(listp != res_datap->msg_line_linkp);
1539                         res_line = 0;
1540                 }
1541                 cur++;
1542                 clistp = clistp->next;
1543         }while(clistp != reslistp);
1544
1545         cur = 0;
1546         clistp = reslistp;
1547         do{
1548                 if(cur >= res_no)
1549                         break;
1550
1551                 res_datap = (res_data_tp)clistp->data;
1552                 if(!res_datap->msg_line_linkp)
1553                         parse_res_msg(reslistp, res_datap, column, h_ngword);
1554                 listp = res_datap->msg_line_linkp;
1555                 line = 0;
1556                 do{
1557                         if(line >= res_line){
1558                                 cptr = (wchar_t*)listp->data;
1559                                 if(0 < wcstombs(buf, cptr, sizeof(buf))){
1560                                         if(0 == regexec(regexp, buf, 
1561                                                         nmatch, pmatch, 0)){
1562                                                 *sel_res_line = line;
1563                                                 *sel_res_no = cur;
1564                                                 return TRUE;
1565                                         }
1566                                 }
1567                         }
1568                         line++;
1569                         listp = listp->next;
1570                 }while(listp != res_datap->msg_line_linkp);
1571                 res_line = 0;
1572                 cur++;
1573                 clistp = clistp->next;
1574         }while(clistp != reslistp);
1575         return FALSE;
1576 }
1577
1578 static BOOL search_line_desc(regex_t *regexp, ctx_reslist_tp ctxp,
1579                         nt_link_tp reslistp, 
1580                         int *sel_res_no, int *sel_res_line, int column, 
1581                         nt_ng_word_handle h_ngword)
1582 {
1583         nt_link_tp clistp, listp;
1584         //nt_res_tp resp;
1585         res_data_tp res_datap;
1586         int cur, line, res_no, res_line;
1587         wchar_t *cptr;
1588         char buf[256];
1589         size_t nmatch = 5;
1590         regmatch_t pmatch[5];
1591
1592         if(!reslistp)
1593                 return FALSE;
1594         
1595         if(*sel_res_no < 0){
1596                 res_no = ctxp->res_num - 1;
1597         }else{
1598                 res_no = *sel_res_no;
1599         }
1600         if(*sel_res_line < 0){
1601                 res_line = INT_MAX;
1602         }else{
1603                 res_line = *sel_res_line - 1;
1604         }
1605                 
1606         cur = ctxp->res_num - 1;
1607         clistp = reslistp->prev;
1608         do{
1609                 if(cur <= res_no){
1610                         res_datap = (res_data_tp)clistp->data;
1611                         //resp = res_datap->resp;
1612                         if(!res_datap->msg_line_linkp)
1613                                 parse_res_msg(reslistp, res_datap, column, h_ngword);
1614                         listp = res_datap->msg_line_linkp->prev;
1615                         line = res_datap->msg_line_num - 1;
1616                         do{
1617                                 if(line <= res_line){
1618                                         cptr = (wchar_t*)listp->data;
1619                                         if(0 < wcstombs(buf, cptr, sizeof(buf))){
1620                                                 if(0 == regexec(regexp, buf, 
1621                                                                 nmatch, pmatch, 0)){
1622                                                         *sel_res_line = line;
1623                                                         *sel_res_no = cur;
1624                                                         return TRUE;
1625                                                 }
1626                                         }
1627                                 }
1628                                 line--;
1629                                 listp = listp->prev;
1630                         }while(listp != res_datap->msg_line_linkp->prev);
1631                         res_line = res_datap->msg_line_num - 1;
1632                 }
1633                 cur--;
1634                 clistp = clistp->prev;
1635         }while(clistp != reslistp->prev);
1636
1637         cur = ctxp->res_num - 1;
1638         clistp = reslistp->prev;
1639         do{
1640                 if(cur <= res_no)
1641                         break;
1642
1643                 res_datap = (res_data_tp)clistp->data;
1644                 //resp = (nt_res_tp)clistp->data;
1645                 if(!res_datap->msg_line_linkp)
1646                         parse_res_msg(reslistp, res_datap, column, h_ngword);
1647                 listp = res_datap->msg_line_linkp->prev;
1648                 line = res_datap->msg_line_num - 1;
1649                 do{
1650                         if(line >= res_line){
1651                                 cptr = (wchar_t*)listp->data;
1652                                 if(0 < wcstombs(buf, cptr, sizeof(buf))){
1653                                         if(0 == regexec(regexp, buf, 
1654                                                         nmatch, pmatch, 0)){
1655                                                 *sel_res_line = line;
1656                                                 *sel_res_no = cur;
1657                                                 return TRUE;
1658                                         }
1659                                 }
1660                         }
1661                         line--;
1662                         listp = listp->prev;
1663                 }while(listp != res_datap->msg_line_linkp->prev);
1664                 res_line = res_datap->msg_line_num - 1;
1665                 cur--;
1666                 clistp = clistp->prev;
1667         }while(clistp != reslistp->prev);
1668         return FALSE;
1669 }
1670
1671
1672 static nt_link_tp parse_id_list(ctx_reslist_tp ctxp, const char *param)
1673 {
1674         nt_link_tp  linkp, disp_list;
1675         nt_link_tp wrkp, num_linkp;
1676         char buf[64];
1677         wchar_t wc[64];
1678         wchar_t *cptr;
1679         const wchar_t *misc;
1680         int len;
1681         const char *start, *end;
1682         res_data_tp res_datap;
1683         nt_res_handle h_res;
1684         int seq_no;
1685         
1686         assert(ctxp);
1687         assert(ctxp->res_disp_list);
1688         
1689         if(!nt_strtok(param, ' ', &start, &end))
1690                 return NULL;
1691         len = end - start;
1692         if(len <= 0)
1693                 return NULL;
1694         
1695         strcpy(buf,"ID:");
1696         strncat(buf, start, len);
1697         
1698         if((size_t)-1 == mbstowcs(wc, buf, sizeof(wc)/sizeof(wchar_t)-1))
1699                 return NULL;
1700                 
1701         num_linkp = NULL;
1702         disp_list = ctxp->res_disp_list;
1703         linkp = disp_list;
1704         do{
1705                 res_datap = (res_data_tp)linkp->data;
1706                 h_res = res_datap->h_res;
1707                 misc = nt_res_get_misc(h_res);
1708                 cptr = wcsstr(misc, wc);
1709                 if(cptr){
1710                         seq_no = nt_res_get_seq_number(h_res);
1711                         wrkp = nt_link_add_n_data(num_linkp, seq_no);
1712                         if(!num_linkp)
1713                                 num_linkp = wrkp;
1714                 }
1715                 linkp = linkp->next;
1716         }while(linkp != disp_list);
1717         
1718         return num_linkp;
1719 }
1720
1721
1722 static nt_link_tp parse_tree_list(ctx_reslist_tp ctxp, const char *param)
1723 {
1724         nt_link_tp  linkp, disp_list;
1725         nt_link_tp wrkp, num_linkp;
1726         int len;
1727         const char *start, *end;
1728         res_data_tp res_datap;
1729         nt_res_handle h_res;
1730         int num;
1731         
1732         assert(ctxp);
1733         assert(ctxp->res_disp_list);
1734         
1735         if(!nt_strtok(param, ' ', &start, &end))
1736                 return NULL;
1737         len = end - start;
1738         if(len <= 0)
1739                 return NULL;
1740         
1741         num = atoi(start);
1742         if(num == 0)
1743                 return NULL;
1744         num_linkp = nt_link_add_n_data(NULL, num);
1745         if(!num_linkp)
1746                 return NULL;
1747         
1748         disp_list = ctxp->res_disp_list;
1749         linkp = disp_list;
1750         do{
1751                 res_datap = (res_data_tp)linkp->data;
1752                 h_res = res_datap->h_res;
1753                 if(num == nt_res_get_seq_number(h_res)){
1754                         if(res_datap->cite_num_list){
1755                                 wrkp = res_datap->cite_num_list;
1756                                 do{
1757                                         if(wrkp->n_data < num)
1758                                                 search_up_tree(wrkp->n_data, disp_list, &num_linkp);
1759                                         wrkp = wrkp->next;
1760                                 }while(wrkp != res_datap->cite_num_list);
1761                         }
1762                         if(res_datap->cited_num_list){
1763                                 wrkp = res_datap->cited_num_list;
1764                                 do{
1765                                         if(wrkp->n_data > num)
1766                                                 search_down_tree(wrkp->n_data, disp_list, &num_linkp);
1767                                         wrkp = wrkp->next;
1768                                 }while(wrkp != res_datap->cited_num_list);
1769                         }
1770                         break;
1771                 }
1772                 linkp = linkp->next;
1773         }while(linkp != disp_list);
1774         if(num_linkp)
1775                 nt_link_n_sort(&num_linkp, nt_comp_int);
1776         return num_linkp;
1777 }
1778 static void search_up_tree(int seq_no, nt_link_tp disp_list, nt_link_tp *num_linkp)
1779 {
1780         nt_link_tp  linkp, wrkp;
1781         nt_link_add_n_data(*num_linkp, seq_no);
1782         res_data_tp res_datap;
1783         nt_res_handle h_res;
1784         
1785         linkp = disp_list;
1786         do{
1787                 res_datap = (res_data_tp)linkp->data;
1788                 h_res = res_datap->h_res;
1789                 if(seq_no == nt_res_get_seq_number(h_res)){
1790                         if(res_datap->cite_num_list){
1791                                 wrkp = res_datap->cite_num_list;
1792                                 do{
1793                                         if(wrkp->n_data < seq_no)
1794                                                 search_up_tree(wrkp->n_data, disp_list, num_linkp);
1795                                         wrkp = wrkp->next;
1796                                 }while(wrkp != res_datap->cite_num_list);
1797                         }
1798                         break;
1799                 }
1800                 linkp = linkp->next;
1801         }while(linkp != disp_list);
1802 }
1803
1804 static void search_down_tree(int seq_no, nt_link_tp disp_list, nt_link_tp *num_linkp)
1805 {
1806         nt_link_tp  linkp, wrkp;
1807         nt_link_add_n_data(*num_linkp, seq_no);
1808         res_data_tp res_datap;
1809         nt_res_handle h_res;
1810         
1811         linkp = disp_list;
1812         do{
1813                 res_datap = (res_data_tp)linkp->data;
1814                 h_res = res_datap->h_res;
1815                 if(seq_no == nt_res_get_seq_number(h_res)){
1816                         if(res_datap->cited_num_list){
1817                                 wrkp = res_datap->cited_num_list;
1818                                 do{
1819                                         if(wrkp->n_data > seq_no)
1820                                                 search_down_tree(wrkp->n_data, disp_list, num_linkp);
1821                                         wrkp = wrkp->next;
1822                                 }while(wrkp != res_datap->cited_num_list);
1823                         }
1824                         break;
1825                 }
1826                 linkp = linkp->next;
1827         }while(linkp != disp_list);
1828 }
1829