1 /*
2 * lastlog.c: handles the lastlog features of irc.
3 *
4 * Written By Michael Sandrof
5 *
6 * Copyright(c) 1990
7 *
8 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
9 */
10
11
12 #include "irc.h"
13 static char cvsrevision[] = "$Id: lastlog.c 479 2013-11-24 13:15:22Z keaston $";
14 CVS_REVISION(lastlog_c)
15 #include "struct.h"
16
17 #include "lastlog.h"
18 #include "window.h"
19 #include "screen.h"
20 #include "vars.h"
21 #include "ircaux.h"
22 #include "output.h"
23 #include "misc.h"
24 #include "hook.h"
25 #include "status.h"
26 #define MAIN_SOURCE
27 #include "modval.h"
28
29 extern int grab_http (char *, char *, char *);
30 /*
31 * lastlog_level: current bitmap setting of which things should be stored in
32 * the lastlog. The LOG_MSG, LOG_NOTICE, etc., defines tell more about this
33 */
34 static unsigned long lastlog_level;
35 static unsigned long notify_level;
36
37 static unsigned long msglog_level = 0;
38 unsigned long beep_on_level = 0;
39 unsigned long new_server_lastlog_level = 0;
40 unsigned long current_window_level = 0;
41
42
43 FILE *logptr = NULL;
44
45 /*
46 * msg_level: the mask for the current message level. What? Did he really
47 * say that? This is set in the set_lastlog_msg_level() routine as it
48 * compared to the lastlog_level variable to see if what ever is being added
49 * should actually be added
50 */
51 static unsigned long msg_level = LOG_CRAP;
52
53 static char *levels[] =
54 {
55 "CRAP", "PUBLIC", "MSGS", "NOTICES",
56 "WALLS", "WALLOPS", "NOTES", "OPNOTES",
57 "SNOTES", "ACTIONS", "DCC", "CTCP",
58 "USERLOG1", "USERLOG2", "USERLOG3", "USERLOG4",
59 "USERLOG5", "BEEP", "TCL", "SEND_MSG",
60 "KILL", "MODEUSER", "MODECHAN", "KICK",
61 "KICKUSER", "PARTS", "INVITES", "JOIN",
62 "TOPIC", "HELP", "NOTIFY", "DEBUG"
63 };
64
65 #define NUMBER_OF_LEVELS (sizeof(levels) / sizeof(char *))
66
67
reset_hold_mode(Window * win)68 void reset_hold_mode(Window *win)
69 {
70 win->hold_mode = win->save_hold_mode;
71 win->save_hold_mode = win->in_more = 0;
72 }
73
74 /* set_lastlog_msg_level: sets the message level for recording in the lastlog */
BX_set_lastlog_msg_level(unsigned long level)75 unsigned long BX_set_lastlog_msg_level(unsigned long level)
76 {
77 unsigned long old;
78
79 old = msg_level;
80 msg_level = level;
81 return (old);
82 }
83
84 /*
85 * bits_to_lastlog_level: converts the bitmap of lastlog levels into a nice
86 * string format. Note that this uses the global buffer, so watch out
87 */
bits_to_lastlog_level(unsigned long level)88 char * bits_to_lastlog_level(unsigned long level)
89 {
90 int i;
91 unsigned long p;
92 static char buffer[512];
93
94 if (level == LOG_ALL)
95 strcpy(buffer, "ALL");
96 else if (level == 0)
97 strcpy(buffer, "NONE");
98 else
99 {
100 for (*buffer = i = 0, p = 1; i < NUMBER_OF_LEVELS; i++, p <<= 1)
101 {
102 if (level & p)
103 {
104 if (*buffer)
105 strlcat(buffer, space, sizeof buffer);
106 strlcat(buffer, levels[i], sizeof buffer);
107 }
108 }
109 }
110 return (buffer);
111 }
112
parse_lastlog_level(char * str,int display)113 unsigned long parse_lastlog_level(char *str, int display)
114 {
115 char *ptr,
116 *rest;
117 int len,
118 i;
119 unsigned long p,
120 level;
121 int neg;
122
123 level = 0;
124 while ((str = next_arg(str, &rest)) != NULL)
125 {
126 while (str)
127 {
128 if ((ptr = strchr(str, ',')) != NULL)
129 *ptr++ = '\0';
130 if ((len = strlen(str)) != 0)
131 {
132 if (my_strnicmp(str, "ALL", len) == 0)
133 level = LOG_ALL;
134 else if (my_strnicmp(str, "NONE", len) == 0)
135 level = 0;
136 else
137 {
138 if (*str == '-')
139 {
140 str++; len--;
141 neg = 1;
142 }
143 else
144 neg = 0;
145 for (i = 0, p = 1; i < NUMBER_OF_LEVELS; i++, p <<= 1)
146 {
147 if (!my_strnicmp(str, levels[i], len))
148 {
149 if (neg)
150 level &= (LOG_ALL ^ p);
151 else
152 level |= p;
153 break;
154 }
155 }
156 if (i == NUMBER_OF_LEVELS)
157 {
158 if (display)
159 say("Unknown lastlog level: %s", str);
160 return LOG_ALL;
161 }
162 }
163 }
164 str = ptr;
165 }
166 str = rest;
167 }
168 return (level);
169 }
170
171 /*
172 * set_lastlog_level: called whenever a "SET LASTLOG_LEVEL" is done. It
173 * parses the settings and sets the lastlog_level variable appropriately. It
174 * also rewrites the LASTLOG_LEVEL variable to make it look nice
175 */
set_lastlog_level(Window * win,char * str,int unused)176 void set_lastlog_level(Window *win, char *str, int unused)
177 {
178 lastlog_level = parse_lastlog_level(str, 1);
179 set_string_var(LASTLOG_LEVEL_VAR, bits_to_lastlog_level(lastlog_level));
180 current_window->lastlog_level = lastlog_level;
181 }
182
183 /*
184 * set_msglog_level: called whenever a "SET MSGLOG_LEVEL" is done. It
185 * parses the settings and sets the msglog_level variable appropriately. It
186 * also rewrites the MSGLOG_LEVEL variable to make it look nice
187 */
set_msglog_level(Window * win,char * str,int unused)188 void set_msglog_level(Window *win, char *str, int unused)
189 {
190 msglog_level = parse_lastlog_level(str, 1);
191 set_string_var(MSGLOG_LEVEL_VAR, bits_to_lastlog_level(msglog_level));
192 }
193
set_new_server_lastlog_level(Window * win,char * str,int unused)194 void set_new_server_lastlog_level (Window *win, char *str, int unused)
195 {
196 new_server_lastlog_level = parse_lastlog_level(str, 1);
197 set_string_var(NEW_SERVER_LASTLOG_LEVEL_VAR,
198 bits_to_lastlog_level(new_server_lastlog_level));
199 }
200
remove_from_lastlog(Window * window)201 void remove_from_lastlog(Window *window)
202 {
203 Lastlog *tmp, *end_holder;
204
205 if (window->lastlog_tail)
206 {
207 end_holder = window->lastlog_tail;
208 tmp = window->lastlog_tail->prev;
209 window->lastlog_tail = tmp;
210 if (tmp)
211 tmp->next = NULL;
212 else
213 window->lastlog_head = window->lastlog_tail;
214 window->lastlog_size--;
215 new_free(&end_holder->msg);
216 new_free((char **)&end_holder);
217 }
218 else
219 window->lastlog_size = 0;
220 }
221
222 /*
223 * set_lastlog_size: sets up a lastlog buffer of size given. If the lastlog
224 * has gotten larger than it was before, all previous lastlog entry remain.
225 * If it get smaller, some are deleted from the end.
226 */
set_lastlog_size(Window * win_unused,char * unused,int size)227 void set_lastlog_size(Window *win_unused, char *unused, int size)
228 {
229 int i,
230 diff;
231 Window *win = NULL;
232
233 while ((traverse_all_windows(&win)))
234 {
235 if (win->lastlog_size > size)
236 {
237 diff = win->lastlog_size - size;
238 for (i = 0; i < diff; i++)
239 remove_from_lastlog(win);
240 }
241 win->lastlog_max = size;
242 }
243 }
244
free_lastlog(Window * win)245 void free_lastlog(Window *win)
246 {
247 Lastlog *ptr;
248 for (ptr = win->lastlog_head; ptr;)
249 {
250 Lastlog *next = ptr->next;
251 new_free(&ptr->msg);
252 new_free(&ptr);
253 ptr = next;
254 }
255 win->lastlog_head = NULL;
256 win->lastlog_tail = NULL;
257 win->lastlog_size = 0;
258 }
259 /*
260 * lastlog: the /LASTLOG command. Displays the lastlog to the screen. If
261 * args contains a valid integer, only that many lastlog entries are shown
262 * (if the value is less than lastlog_size), otherwise the entire lastlog is
263 * displayed
264 */
BUILT_IN_COMMAND(lastlog)265 BUILT_IN_COMMAND(lastlog)
266 {
267 int cnt,
268 from = 0,
269 p,
270 i,
271 level = 0,
272 msg_level,
273 len,
274 mask = 0,
275 header = 1,
276 lines = 0,
277 reverse = 0,
278 time_log = 1,
279 remove = 0;
280
281 Lastlog *start_pos;
282 char *match = NULL,
283 *arg;
284 const char *file_open_type = "w";
285 const char *filename = NULL;
286 char *blah = NULL;
287 FILE *fp = NULL;
288
289 reset_display_target();
290 cnt = current_window->lastlog_size;
291
292 while ((arg = new_next_arg(args, &args)) != NULL)
293 {
294 if (*arg == '-')
295 {
296 arg++;
297 if (!(len = strlen(arg)))
298 {
299 header = 0;
300 continue;
301 }
302 else if (!my_strnicmp(arg, "MAX", len))
303 {
304 char *ptr = NULL;
305 ptr = new_next_arg(args, &args);
306 if (ptr)
307 lines = atoi(ptr);
308 if (lines < 0)
309 lines = 0;
310 }
311 else if (!my_strnicmp(arg, "LITERAL", len))
312 {
313 if (match)
314 {
315 say("Second -LITERAL argument ignored");
316 (void) new_next_arg(args, &args);
317 continue;
318 }
319 if ((match = new_next_arg(args, &args)) != NULL)
320 continue;
321 say("Need pattern for -LITERAL");
322 return;
323 }
324 else if (!my_strnicmp(arg, "REVERSE", len))
325 reverse = 1;
326 else if (!my_strnicmp(arg, "TIME", len))
327 time_log = 0;
328 else if (!my_strnicmp(arg, "BEEP", len))
329 {
330 if (match)
331 {
332 say("-BEEP is exclusive; ignored");
333 continue;
334 }
335 else
336 match = "\007";
337 }
338 else if (!my_strnicmp(arg, "CLEAR", len))
339 {
340 free_lastlog(current_window);
341 say("Cleared lastlog");
342 return;
343 }
344 else if (!my_strnicmp(arg, "APPEND", len))
345 file_open_type = "a";
346 else if (!my_strnicmp(arg, "FILE", len))
347 {
348 #ifdef PUBLIC_ACCESS
349 bitchsay("This command has been disabled on a public access system");
350 return;
351 #else
352 if (args && *args)
353 {
354 char *filename_arg = next_arg(args, &args);
355 if (filename)
356 say("Additional -FILE argument ignored");
357 else
358 filename = filename_arg;
359 }
360 else
361 {
362 bitchsay("Filename needed for save");
363 return;
364 }
365 #endif
366 }
367 else if (!my_strnicmp(arg, "MORE", len))
368 {
369 current_window->save_hold_mode = current_window->hold_mode;
370 current_window->in_more = 1;
371 reset_line_cnt(current_window, NULL, 1);
372 }
373 else
374 {
375 if (*arg == '-')
376 remove = 1, arg++;
377 else
378 remove = 0;
379
380 /*
381 * Which can be combined with -ALL, which
382 * turns on all levels. Use --MSGS or
383 * whatever to turn off ones you dont want.
384 */
385 if (!my_strnicmp(arg, "ALL", len))
386 {
387 if (remove)
388 mask = 0;
389 else
390 mask = LOG_ALL;
391 continue; /* Go to next arg */
392 }
393
394 /*
395 * Find the lastlog level in our list.
396 */
397 for (i = 0, p = 1; i < NUMBER_OF_LEVELS; i++, p *= 2)
398 {
399 if (!my_strnicmp(levels[i], arg, len))
400 {
401 if (remove)
402 mask &= ~p;
403 else
404 mask |= p;
405 break;
406 }
407 }
408
409 if (i == NUMBER_OF_LEVELS)
410 {
411 bitchsay("Unknown flag: %s", arg);
412 reset_display_target();
413 return;
414 }
415 }
416 }
417 else
418 {
419 if (level == 0)
420 {
421 if (match || isdigit((unsigned char)*arg))
422 {
423 cnt = atoi(arg);
424 level++;
425 }
426 else
427 match = arg;
428 }
429 else if (level == 1)
430 {
431 from = atoi(arg);
432 level++;
433 }
434 }
435 }
436
437 if (filename && !(fp = fopen(filename, file_open_type)))
438 {
439 bitchsay("cannot open file %s", filename);
440 return;
441 }
442
443 start_pos = current_window->lastlog_head;
444 level = current_window->lastlog_level;
445 msg_level = set_lastlog_msg_level(0);
446
447 if (!reverse)
448 {
449 for (i = 0; (i < from) && start_pos; start_pos = start_pos->next)
450 if (!mask || (mask & start_pos->level))
451 i++;
452
453 for (i = 0; (i < cnt) && start_pos; start_pos = start_pos->next)
454 if (!mask || (mask & start_pos->level))
455 i++;
456
457 start_pos = (start_pos) ? start_pos->prev : current_window->lastlog_tail;
458 } else
459 start_pos = current_window->lastlog_head;
460
461 /* Let's not get confused here, display a seperator.. -lynx */
462 strip_ansi_in_echo = 0;
463 if (header && !fp)
464 say("Lastlog:");
465
466 if (match)
467 {
468
469 blah = (char *) alloca(strlen(match)+4);
470 sprintf(blah, "*%s*", match);
471 }
472 for (i = 0; (i < cnt) && start_pos; start_pos = (reverse ? start_pos->next : start_pos->prev))
473 {
474 if (!mask || (mask & start_pos->level))
475 {
476 i++;
477 if (!match || wild_match(blah, start_pos->msg))
478 {
479 char *s = !get_int_var(LASTLOG_ANSI_VAR)?stripansicodes(start_pos->msg):start_pos->msg;
480
481 if (!fp)
482 {
483 put_it("%s", !time_log ? s : convert_output_format(fget_string_var(FORMAT_LASTLOG_FSET), "%l %s", start_pos->time, s));
484 grab_http("*", "*", start_pos->msg);
485 }
486 else
487 {
488 if (time_log)
489 {
490 s = convert_output_format(fget_string_var(FORMAT_LASTLOG_FSET), "%l %s", start_pos->time, s);
491 chop(s, 3);
492 }
493 fprintf(fp, "%s\n", s);
494 }
495 if (lines == 0)
496 continue;
497 else if (lines == 1)
498 break;
499 lines--;
500 }
501 }
502 }
503 if (header && !fp)
504 say("End of Lastlog");
505 if (fp)
506 fclose(fp);
507 strip_ansi_in_echo = 1;
508 current_window->lastlog_level = level;
509 set_lastlog_msg_level(msg_level);
510 }
511
get_lastlog_current_head(Window * win)512 Lastlog *get_lastlog_current_head(Window *win)
513 {
514 return win->lastlog_head;
515 }
516
517 /*
518 * add_to_lastlog: adds the line to the lastlog. If the LASTLOG_CONVERSATION
519 * variable is on, then only those lines that are user messages (private
520 * messages, channel messages, wall's, and any outgoing messages) are
521 * recorded, otherwise, everything is recorded
522 */
add_to_lastlog(Window * window,const char * line)523 void add_to_lastlog(Window *window, const char *line)
524 {
525 Lastlog *new;
526
527 if (window == NULL)
528 window = current_window;
529 if (window->lastlog_level & msg_level)
530 {
531 /* no nulls or empty lines (they contain "> ") */
532 if (line && (strlen(line) > 2))
533 {
534 new = (Lastlog *) new_malloc(sizeof(Lastlog));
535 new->next = window->lastlog_head;
536 new->prev = NULL;
537 new->level = msg_level;
538 new->msg = NULL;
539 new->time = now;
540 new->msg = m_strdup(line);
541
542 if (window->lastlog_head)
543 window->lastlog_head->prev = new;
544 window->lastlog_head = new;
545
546 if (window->lastlog_tail == NULL)
547 window->lastlog_tail = window->lastlog_head;
548
549 if (window->lastlog_size++ >= window->lastlog_max)
550 remove_from_lastlog(window);
551 }
552 }
553 }
554
real_notify_level(void)555 unsigned long real_notify_level(void)
556 {
557 return (notify_level);
558 }
559
real_lastlog_level(void)560 unsigned long real_lastlog_level(void)
561 {
562 return (lastlog_level);
563 }
564
set_notify_level(Window * win,char * str,int unused)565 void set_notify_level(Window *win, char *str, int unused)
566 {
567 notify_level = parse_lastlog_level(str, 1);
568 set_string_var(NOTIFY_LEVEL_VAR, bits_to_lastlog_level(notify_level));
569 current_window->notify_level = notify_level;
570 }
571
logmsg(unsigned long log_type,char * from,int flag,char * format,...)572 int logmsg(unsigned long log_type, char *from, int flag, char *format, ...)
573 {
574 #ifdef PUBLIC_ACCESS
575 return 0;
576 #else
577 char *timestr;
578 time_t t;
579 char *filename = NULL;
580 char *expand = NULL;
581 char *type = NULL;
582 unsigned char **lines = NULL;
583 char msglog_buffer[BIG_BUFFER_SIZE+1];
584
585
586 if (!get_string_var(MSGLOGFILE_VAR) || !get_string_var(CTOOLZ_DIR_VAR))
587 return 0;
588
589 t = now;
590 timestr = update_clock(GET_TIME);
591
592
593 if (format)
594 {
595 va_list ap;
596 va_start(ap, format);
597 vsnprintf(msglog_buffer, BIG_BUFFER_SIZE, format, ap);
598 va_end(ap);
599 }
600
601
602 switch (flag)
603 {
604 case 0:
605 if (!(type = bits_to_lastlog_level(log_type)))
606 type = "Unknown";
607 if (msglog_level & log_type && format)
608 {
609 char *format;
610
611 if (!do_hook(MSGLOG_LIST, "%s %s %s %s", timestr, type, from, msglog_buffer))
612 break;
613 if (!logptr)
614 return 0;
615 if (!(format = fget_string_var(FORMAT_MSGLOG_FSET)))
616 format = "[$[10]0] [$1] - $2-";
617 lines = split_up_line(stripansicodes(convert_output_format(format, "%s %s %s %s", type, timestr, from, msglog_buffer)), 80);
618 for ( ; *lines; lines++)
619 {
620 char *local_copy;
621 int len = strlen(*lines) * 2 + 1;
622 if (!*lines || !**lines) break;
623 local_copy = alloca(len);
624 strcpy(local_copy, *lines);
625
626 if (local_copy[strlen(local_copy)-1] == ALL_OFF)
627 local_copy[strlen(local_copy)-1] = 0;
628 if (logfile_line_mangler)
629 mangle_line(local_copy, logfile_line_mangler, len);
630 if (*local_copy)
631 fprintf(logptr, "%s\n", local_copy);
632 }
633 fflush(logptr);
634 }
635 break;
636 case 1:
637 malloc_sprintf(&filename, "%s/%s", get_string_var(CTOOLZ_DIR_VAR), get_string_var(MSGLOGFILE_VAR));
638 expand = expand_twiddle(filename);
639 new_free(&filename);
640 if (!do_hook(MSGLOG_LIST, "%s %s %s %s", timestr, "On", expand, empty_string))
641 {
642 new_free(&expand);
643 return 1;
644 }
645 if (logptr)
646 {
647 new_free(&expand);
648 return 1;
649 }
650 if (!(logptr = fopen(expand, get_int_var(APPEND_LOG_VAR)?"at":"wt")))
651 {
652 set_int_var(MSGLOG_VAR, 0);
653 new_free(&expand);
654 return 0;
655 }
656
657 fprintf(logptr, "MsgLog started [%s]\n", my_ctime(t));
658 fflush(logptr);
659 if (format)
660 {
661 int i;
662
663 i = logmsg(LOG_CURRENT, from, 0, "%s", msglog_buffer);
664 return i;
665 }
666 bitchsay("Now logging messages to: %s", expand);
667 new_free(&expand);
668 break;
669 case 2:
670 if (!do_hook(MSGLOG_LIST, "%s %s %s %s", timestr, "Off", empty_string, empty_string))
671 return 1;
672 if (!logptr)
673 return 1;
674 fprintf(logptr, "MsgLog ended [%s]\n", my_ctime(t));
675 fclose(logptr);
676 logptr = NULL;
677 break;
678 case 3:
679 return logptr ? 1 : 0;
680 break;
681 case 4:
682 if (!logptr)
683 return 1;
684 fprintf(logptr, "[TimeStamp %s]\n", my_ctime(t));
685 fflush(logptr);
686 break;
687 default:
688 bitchsay("Bad Flag passed to logmsg");
689 return 0;
690 }
691 return 1;
692 #endif
693 }
694
set_beep_on_msg(Window * win,char * str,int unused)695 void set_beep_on_msg (Window *win, char *str, int unused)
696 {
697 beep_on_level = parse_lastlog_level(str, 1);
698 set_string_var(BEEP_ON_MSG_VAR, bits_to_lastlog_level(beep_on_level));
699 }
700
BUILT_IN_COMMAND(awaylog)701 BUILT_IN_COMMAND(awaylog)
702 {
703 if (args && *args)
704 {
705 msglog_level = parse_lastlog_level(args, 1);
706 set_string_var(MSGLOG_LEVEL_VAR, bits_to_lastlog_level(msglog_level));
707 put_it("%s", convert_output_format("$G Away logging set to: $0-", "%s", get_string_var(MSGLOG_LEVEL_VAR)));
708 }
709 else
710 put_it("%s", convert_output_format("$G Away logging currently: $0-", "%s", bits_to_lastlog_level(msglog_level)));
711 }
712
713 #define EMPTY empty_string
714 #define RETURN_EMPTY return m_strdup(EMPTY)
715 #define RETURN_IF_EMPTY(x) if (empty( x )) RETURN_EMPTY
716 #define GET_INT_ARG(x, y) {RETURN_IF_EMPTY(y); x = my_atol(safe_new_next_arg(y, &y));}
717 #define GET_STR_ARG(x, y) {RETURN_IF_EMPTY((y)); x = new_next_arg((y), &(y));RETURN_IF_EMPTY((x));}
718 #define RETURN_STR(x) return m_strdup(x ? x : EMPTY);
719
720 /*
721 * $line(<line number> [window number])
722 * Returns the text of logical line <line number> from the lastlog of
723 * window <window number>. If no window number is supplied, the current
724 * window will be used. If the window number is invalid, the function
725 * will return the false value.
726 *
727 * Lines are numbered from 1, starting at the most recent line in the buffer.
728 * Contributed by Crackbaby (Matt Carothers) on March 19, 1998.
729 */
730
BUILT_IN_FUNCTION(function_line)731 BUILT_IN_FUNCTION(function_line)
732 {
733 int line = 0;
734 char * windesc = zero;
735 Lastlog *start_pos;
736 Window *win;
737 char *extra;
738 int do_level = 0;
739
740
741 GET_INT_ARG(line, input);
742
743 while (input && *input)
744 {
745 GET_STR_ARG(extra, input);
746
747 if (!my_stricmp(extra, "-LEVEL"))
748 do_level = 1;
749 else
750 windesc = extra;
751 }
752
753 /* Get the current window, default to current window */
754 if (!(win = get_window_by_desc(windesc)))
755 RETURN_EMPTY;
756
757 /* Make sure that the line request is within reason */
758 if (line < 1 || line > win->lastlog_size)
759 RETURN_EMPTY;
760
761 /* Get the line from the lastlog */
762 for (start_pos = win->lastlog_head; line; start_pos = start_pos->next)
763 line--;
764
765 if (!start_pos)
766 start_pos = win->lastlog_tail;
767 else
768 start_pos = start_pos->prev;
769
770 if (do_level)
771 return m_sprintf("%s %s", start_pos->msg,
772 levels[start_pos->level]);
773 else
774 RETURN_STR(start_pos->msg);
775 }
776
777 /*
778 * $lastlog(<window description> <lastlog levels>)
779 * Returns all of the lastlog lines (suitable for use with $line()) on the
780 * indicated window (0 for the current window) that have any of the lastlog
781 * levels as represented by the lastlog levels. If the window number is
782 * invalid, the function will return the false value.
783 */
784
BUILT_IN_FUNCTION(function_lastlog)785 BUILT_IN_FUNCTION(function_lastlog)
786 {
787 char * windesc = zero;
788 char * pattern = NULL;
789 char * retval = NULL;
790 Lastlog *iter;
791 Window *win;
792 int levels;
793 int line = 1;
794
795 GET_STR_ARG(windesc, input);
796 GET_STR_ARG(pattern, input);
797 levels = parse_lastlog_level(input, 0);
798
799 /* Get the current window, default to current window */
800 if (!(win = get_window_by_desc(windesc)))
801 RETURN_EMPTY;
802
803 for (iter = win->lastlog_head; iter; iter = iter->next, line++)
804 {
805 if (iter->level & levels)
806 if (wild_match(pattern, iter->msg))
807 m_s3cat(&retval, space, ltoa(line));
808 }
809
810 if (retval)
811 return retval;
812
813 RETURN_EMPTY;
814 }
815
816
817