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