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