1 /* -*- mode: C; mode: fold; -*- */
2 /*
3  This file is part of SLRN.
4 
5  Copyright (c) 1994, 1999, 2007-2016 John E. Davis <jed@jedsoft.org>
6  Copyright (c) 2001-2006 Thomas Schultz <tststs@gmx.de>
7 
8  This program is free software; you can redistribute it and/or modify it
9  under the terms of the GNU General Public License as published by the Free
10  Software Foundation; either version 2 of the License, or (at your option)
11  any later version.
12 
13  This program is distributed in the hope that it will be useful, but WITHOUT
14  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  more details.
17 
18  You should have received a copy of the GNU General Public License along
19  with this program; if not, write to the Free Software Foundation, Inc.,
20  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22 #include "config.h"
23 #include "slrnfeat.h"
24 
25 /*{{{ Include Files */
26 
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <signal.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <time.h>
34 
35 #ifdef HAVE_STDLIB_H
36 # include <stdlib.h>
37 #endif
38 
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42 
43 #if !defined(VMS) && !defined(__WIN32__) && !defined(__NT__)
44 # define HAS_PASSWORD_CODE	1
45 # include <pwd.h>
46 #endif
47 
48 #ifdef VMS
49 # include "vms.h"
50 #else
51 # include <sys/types.h>
52 # include <sys/stat.h>
53 #endif
54 
55 #ifdef HAVE_FCNTL_H
56 # include <fcntl.h>
57 #endif
58 #ifdef HAVE_SYS_FCNTL_H
59 # include <sys/fcntl.h>
60 #endif
61 
62 #if defined(VMS) && defined(MULTINET)
63 # include "multinet_root:[multinet.include]netdb.h"
64 #else
65 # if defined(__NT__)
66 #  include <winsock.h>
67 # else
68 #  if defined(__WIN32__)
69 /* #   define Win32_Winsock */
70 #   define __USE_W32_SOCKETS
71 #   include <windows.h>
72 #   ifdef __MINGW32__
73 #    include <process.h>
74 #   endif
75 #  else
76 #   include <netdb.h>
77 #   if !defined(h_errno) && !defined(__CYGWIN__)
78 extern int h_errno;
79 #   endif
80 #  endif
81 # endif
82 #endif
83 
84 #ifdef HAVE_SYS_WAIT_H
85 # include <sys/wait.h>
86 #endif
87 
88 #ifdef NeXT
89 # undef WIFEXITED
90 # undef WEXITSTATUS
91 #endif
92 
93 #ifndef WEXITSTATUS
94 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
95 #endif
96 #ifndef WIFEXITED
97 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
98 #endif
99 
100 #include <slang.h>
101 #include "jdmacros.h"
102 
103 #include "misc.h"
104 #include "group.h"
105 #include "slrn.h"
106 #include "util.h"
107 #include "server.h"
108 #include "ttymsg.h"
109 #include "art.h"
110 #include "post.h"
111 #include "snprintf.h"
112 #include "slrndir.h"
113 #include "menu.h"
114 #include "hooks.h"
115 #include "startup.h"
116 #include "strutil.h"
117 #include "common.h"
118 
119 #ifdef VMS
120 /* valid filname chars for unix equiv of vms filename */
121 # define VALID_FILENAME_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$_-/"
122 # include "vms.h"
123 #endif
124 /*}}}*/
125 
126 /*{{{ Global Variables */
127 int Slrn_Full_Screen_Update = 1;
128 int Slrn_User_Wants_Confirmation = SLRN_CONFIRM_ALL;
129 int Slrn_Message_Present = 0;
130 int Slrn_Abort_Unmodified = 0;
131 int Slrn_Mail_Editor_Is_Mua = 0;
132 
133 #ifndef VMS
134 char *Slrn_SendMail_Command;
135 #endif
136 
137 char *Slrn_Editor;
138 char *Slrn_Editor_Post;
139 char *Slrn_Editor_Score;
140 char *Slrn_Editor_Mail;
141 int Slrn_Editor_Uses_Mime_Charset = 0;
142 
143 char *Slrn_Top_Status_Line;
144 
145 Slrn_User_Info_Type Slrn_User_Info;
146 SLKeyMap_List_Type *Slrn_RLine_Keymap;
147 SLang_RLine_Info_Type *Slrn_Keymap_RLI;
148 
149 /*}}}*/
150 /*{{{ Static Variables */
151 
152 static int Error_Present;
153 static char *Input_String;
154 static char *Input_String_Ptr;
155 static char *Input_Chars_Ptr;
156 static char *Input_Chars;
157 static int Beep_Pending;
158 /*}}}*/
159 
160 static void redraw_message (void);
161 static void redraw_mini_buffer (void);
162 
163 /*{{{ Screen Update Functions */
164 
slrn_smg_refresh(void)165 void slrn_smg_refresh (void)
166 {
167    if (Slrn_TT_Initialized & SLRN_SMG_INIT)
168      {
169 	slrn_push_suspension (0);
170 	if (Beep_Pending)
171 	  SLtt_beep ();
172 	Beep_Pending = 0;
173 	SLsmg_refresh ();
174 	slrn_pop_suspension ();
175      }
176 }
177 
slrn_set_color(int color)178 void slrn_set_color (int color) /*{{{*/
179 {
180    SLsmg_set_color (color);
181 }
182 
183 /*}}}*/
184 
slrn_redraw(void)185 void slrn_redraw (void) /*{{{*/
186 {
187    if (Slrn_Batch) return;
188 
189    slrn_push_suspension (0);
190 
191    SLsmg_cls ();
192    Slrn_Full_Screen_Update = 1;
193 
194    redraw_message ();
195    slrn_update_screen ();
196    redraw_mini_buffer ();
197 
198    slrn_smg_refresh ();
199    slrn_pop_suspension ();
200 }
201 
202 /*}}}*/
203 
204 /* theoretically, buf needs at least space for 66 characters */
slrn_print_percent(char * buf,SLscroll_Window_Type * w,int lines)205 char *slrn_print_percent (char *buf, SLscroll_Window_Type *w, int lines) /*{{{*/
206 {
207    if (lines)
208      {
209 	sprintf (buf, "%d/%d", w->line_num, w->num_lines);
210      }
211    else
212      {
213 	int bot_showing;
214 	unsigned int bot_number;
215 
216 	bot_number = w->line_num + (w->nrows - w->window_row) - 1;
217 	bot_showing = ((w->bot_window_line == NULL)
218 		       || (w->num_lines == bot_number));
219 
220 	if (w->line_num == w->window_row + 1)
221 	  slrn_strncpy (buf, bot_showing ? _("All") : _("Top"), 66);
222 	else if (bot_showing)
223 	  slrn_strncpy (buf, _("Bot"), 66);
224 	else
225 	  sprintf (buf, "%d%%", (100 * bot_number) / w->num_lines); /* safe */
226     }
227   return buf;
228 }
229 /*}}}*/
230 
top_status_line_cb(char ch,void * data,int * len,int * color)231 static char *top_status_line_cb (char ch, void *data, int *len, int *color) /*{{{*/
232 {
233    static char buf[32];
234    char *retval = NULL;
235    time_t now;
236 
237    (void) data; (void) color; /* we currently don't use these */
238 
239    switch (ch)
240      {
241       case 'd':
242 	time(&now);
243 	if (0 != (*len = strftime (buf, sizeof(buf), "%x", localtime(&now))))
244 	  retval = buf;
245 	break;
246 
247       case 'n':
248 	if (NULL != Slrn_Group_Current_Group)
249 	  retval = Slrn_Group_Current_Group->group_name;
250 	break;
251 
252       case 's':
253 	retval = Slrn_Server_Obj->sv_name;
254 	break;
255 
256       case 't':
257 	time(&now);
258 	if (0 != (*len = strftime (buf, sizeof(buf), "%X", localtime(&now))))
259 	  retval = buf;
260 	break;
261 
262       case 'v':
263 	retval = Slrn_Version_String;
264 	break;
265 
266      }
267    return retval == NULL ? "" : retval;
268 }
269 /*}}}*/
slrn_update_top_status_line(void)270 void slrn_update_top_status_line (void) /*{{{*/
271 {
272    char *fmt = Slrn_Top_Status_Line;
273 
274    if (Slrn_Full_Screen_Update == 0) return;
275 
276    if ((fmt == NULL) || (*fmt == 0))
277      fmt = _("slrn %v ** Press '?' for help, 'q' to quit. ** Server: %s");
278 
279    slrn_custom_printf (fmt, top_status_line_cb, NULL, 0, MENU_COLOR);
280 }
281 /*}}}*/
282 
283 /*}}}*/
284 /*{{{ Message/Error Functions */
285 
skip_char(char * s,char * smax,int ignore_combining)286 static char *skip_char (char *s, char *smax, int ignore_combining)
287 {
288    if (s >= smax)
289      return s;
290 
291    if (Slrn_UTF8_Mode == 0)
292      return s+1;
293 
294    return (char *)SLutf8_skip_chars ((SLuchar_Type *)s, (SLuchar_Type *)smax, 1, NULL, ignore_combining);
295 }
296 
297 /* The first character is the color */
298 static char Message_Buffer[1024];
299 
redraw_message(void)300 static void redraw_message (void)
301 {
302    int color;
303    char *m, *mmax;
304 
305    if (Slrn_Batch) return;
306 
307    if (Slrn_Message_Present == 0)
308      return;
309 
310    slrn_push_suspension (0);
311 
312    SLsmg_gotorc (SLtt_Screen_Rows - 1, 0);
313 
314    color = Message_Buffer [0];
315    m = Message_Buffer + 1;
316    mmax = m + strlen (m);
317 
318    while (1)
319      {
320 	char *m1 = slrn_strbyte (m, 1);
321 	if (m1 == NULL)
322 	  m1 = mmax;
323 
324 	slrn_set_color (color);
325 	SLsmg_write_nchars (m, (unsigned int) (m1 - m));
326 	if (m1 == mmax)
327 	  break;
328 	m1++;
329 	if (m1 == mmax)
330 	  break;
331 
332 	slrn_set_color (RESPONSE_CHAR_COLOR);
333 	m = m1;
334 	m1 = skip_char (m, mmax, 1);
335 	SLsmg_write_nchars (m, m1-m);
336 	m = m1;
337      }
338 
339    SLsmg_erase_eol ();
340    slrn_set_color (0);
341 
342    slrn_pop_suspension ();
343 }
344 
vmessage_1(int color,char * fmt,va_list ap)345 static void vmessage_1 (int color, char *fmt, va_list ap)
346 {
347    slrn_vsnprintf (Message_Buffer + 1, sizeof(Message_Buffer)-1, fmt, ap);
348 
349    Message_Buffer[0] = (char) color;
350    Slrn_Message_Present = 1;
351    redraw_message ();
352 }
353 
vmessage(FILE * fp,char * fmt,va_list ap)354 static void vmessage (FILE *fp, char *fmt, va_list ap)
355 {
356    if (Slrn_TT_Initialized & SLRN_SMG_INIT)
357      vmessage_1 (MESSAGE_COLOR, fmt, ap);
358    else
359      slrn_tty_vmessage (fp, fmt, ap);
360 }
361 
log_error_message(char * fmt,va_list ap)362 static void log_error_message (char *fmt, va_list ap)
363 {
364    if (Slrn_Debug_Fp != NULL)
365      {
366 	(void) vfprintf (Slrn_Debug_Fp, fmt, ap);
367 	(void) fputs ("\n", Slrn_Debug_Fp);
368 	(void) fflush (Slrn_Debug_Fp);
369      }
370 }
371 
slrn_verror(char * fmt,va_list ap)372 void slrn_verror (char *fmt, va_list ap)
373 {
374    va_list ap1;
375 
376    VA_COPY(ap1, ap);
377 
378    if ((Slrn_TT_Initialized & SLRN_SMG_INIT) == 0)
379      {
380 	slrn_tty_vmessage (stderr, fmt, ap);
381      }
382    else if (Error_Present == 0)
383      {
384 	slrn_clear_message ();
385 	Error_Present = 1;
386 	Beep_Pending = 1;
387 	vmessage_1 (ERROR_COLOR, fmt, ap);
388 	SLang_flush_input ();
389      }
390 
391    log_error_message (fmt, ap1);
392    va_end (ap1);
393 
394    if (SLang_get_error () == 0) SLang_set_error (INTRINSIC_ERROR);
395 }
396 
397 /*}}}*/
slrn_clear_message(void)398 void slrn_clear_message (void) /*{{{*/
399 {
400    Slrn_Message_Present = Error_Present = 0;
401    /* SLang_Error = 0; */
402    Beep_Pending = 0;
403    SLKeyBoard_Quit = 0;
404 
405    if ((Slrn_TT_Initialized & SLRN_SMG_INIT) == 0)
406      return;
407 
408    slrn_push_suspension (0);
409    SLsmg_gotorc (SLtt_Screen_Rows - 1, 0);
410    SLsmg_erase_eol ();
411    *Message_Buffer = 0;
412    slrn_pop_suspension ();
413 }
414 
415 /*}}}*/
416 
slrn_clear_error(void)417 void slrn_clear_error (void)
418 {
419    SLang_set_error (0);
420    slrn_clear_message ();
421 }
422 
slrn_va_message(char * fmt,va_list ap)423 void slrn_va_message (char *fmt, va_list ap)
424 {
425    if (Error_Present == 0)
426      vmessage (stdout, fmt, ap);
427 }
428 
slrn_message(char * fmt,...)429 int slrn_message (char *fmt, ...) /*{{{*/
430 {
431    va_list ap;
432 
433    if (Error_Present) return -1;
434    va_start(ap, fmt);
435    vmessage (stdout, fmt, ap);
436    va_end (ap);
437    return 0;
438 }
439 
440 /*}}}*/
441 
slrn_message_now(char * fmt,...)442 int slrn_message_now (char *fmt, ...) /*{{{*/
443 {
444    va_list ap;
445 
446    if (Error_Present) return -1;
447    va_start(ap, fmt);
448    vmessage (stdout, fmt, ap);
449    va_end (ap);
450    slrn_smg_refresh ();
451    Slrn_Message_Present = 0;
452    return 0;
453 }
454 
455 /*}}}*/
456 
slrn_error_now(unsigned secs,char * fmt,...)457 void slrn_error_now (unsigned secs, char *fmt, ...) /*{{{*/
458 {
459    va_list ap;
460 
461    if (fmt != NULL)
462      {
463 	va_start(ap, fmt);
464 	slrn_verror (fmt, ap);
465 	va_end (ap);
466      }
467    slrn_smg_refresh ();
468    Slrn_Message_Present = 0;
469    if (secs) slrn_sleep (secs);
470 }
471 
472 /*}}}*/
473 
474 /* Wrapper around the SLsmg routine.
475  * This writes n bytes (not necessarily screen characters), replacing
476  * unprintable 8bit chars with question marks.
477  */
478 
479 /* Wrapper around the SLsmg routine. */
slrn_write_nbytes(char * s,unsigned int n)480 void slrn_write_nbytes (char *s, unsigned int n) /*{{{*/
481 {
482    unsigned char *s1, *smax;
483    unsigned int eight_bit;
484 
485    if (Slrn_UTF8_Mode)
486      {
487 	SLsmg_write_nchars (s, n);
488 	return;
489      }
490 
491    s1 = (unsigned char *) s;
492    smax = s1 + n;
493    eight_bit = SLsmg_Display_Eight_Bit;
494 
495    while (s1 < smax)
496      {
497 	if ((*s1 & 0x80) && (eight_bit > (unsigned int) *s1))
498 	  {
499 	     if (s != (char *) s1)
500 	       SLsmg_write_nchars (s, (unsigned int) ((char *)s1 - s));
501 	     SLsmg_write_char ('?');
502 	     s1++;
503 	     s = (char *) s1;
504 	  }
505 	else s1++;
506      }
507 
508    if (s != (char *)s1)
509      SLsmg_write_nchars (s, (unsigned int) ((char *)s1 - s));
510 }
511 /*}}}*/
512 
513 /* generic function to display format strings;
514  * cb is called when a descriptor other than '%', '?' or 'g' is encountered */
slrn_custom_printf(char * fmt,PRINTF_CB cb,void * param,int row,int def_color)515 void slrn_custom_printf (char *fmt, PRINTF_CB cb, void *param, /*{{{*/
516 			 int row, int def_color)
517 {
518    char ch, *cond_end = NULL, *elsepart = NULL;
519 
520    SLsmg_gotorc (row, 0);
521    slrn_set_color (def_color);
522 
523    while ((ch = *fmt) != 0)
524      {
525 	char *s, *smax, *p;
526 	int color = def_color;
527 	int len = -1, field_len = -1;
528 	int justify, spaces = 0;
529 
530 	if (fmt == elsepart)
531 	  {
532 	     fmt = cond_end + 1; /* skip the '?' */
533 	     elsepart = cond_end = NULL;
534 	     continue;
535 	  }
536 
537 	if (ch != '%')
538 	  {
539 	     if ((NULL == (s = slrn_strbyte (fmt, '%'))) && (elsepart == NULL))
540 	       {
541 		  SLsmg_write_string (fmt);
542 		  break;
543 	       }
544 	     else
545 	       {
546 		  char *end;
547 		  if ((elsepart != NULL) && (s > elsepart)) end = elsepart;
548 		  else end = s;
549 		  SLsmg_write_nchars (fmt, end - fmt);
550 		  fmt = end;
551 		  continue;
552 	       }
553 	  }
554 
555 	fmt++;
556 	ch = *fmt++;
557 
558 	if (ch == '?')
559 	  {
560 	     int column;
561 	     char *res;
562 
563 	     if (((ch = *fmt++) == '\0') || (*fmt++ != '?') ||
564 		 (NULL == (cond_end = slrn_strbyte (fmt, '?'))))
565 	       break; /* syntax error; stop here */
566 	     if ((NULL == (elsepart = slrn_strbyte (fmt, '&'))) ||
567 		 (elsepart > cond_end))
568 	       elsepart = cond_end;
569 
570 	     column = SLsmg_get_column (); /* callback could print something */
571 	     res = (cb)(ch, param, &len, &color);
572 	     SLsmg_gotorc (row, column);
573 
574 	     if ((res == NULL) || (*res == '\0') ||
575 		 ((*res == '0' || isspace(*res)) && *(res+1) == '\0'))
576 	       {
577 		  fmt = elsepart + 1;
578 		  if (elsepart == cond_end) cond_end = NULL;
579 		  elsepart = cond_end;
580 	       }
581 	     continue;
582 	  }
583 
584 	s = NULL;
585 
586 	if (ch == '-')
587 	  {
588 	     justify = 1;
589 	     ch = *fmt++;
590 	  }
591 	else if (ch == '*')
592 	  {
593 	     justify = 2;
594 	     ch = *fmt++;
595 	  }
596 	else
597 	  justify = 0;
598 
599 	if (isdigit (ch))
600 	  {
601 	     field_len = 0;
602 	     do
603 	       {
604 		  field_len = 10 * field_len + (int) (ch - '0');
605 		  ch = *fmt++;
606 	       }
607 	     while (isdigit (ch));
608 	  }
609 
610 	switch (ch)
611 	  {
612 	   case 0:
613 	     break;
614 
615 	   case '%':
616 	     s = "%";
617 	     len = 1;
618 	     break;
619 
620 	   case 'g':
621 	     SLsmg_erase_eol ();
622 	     if (justify)
623 	       field_len = SLtt_Screen_Cols - field_len;
624 	     SLsmg_gotorc (row, field_len);
625 	     break;
626 
627 	   default:
628 	     s = (cb)(ch, param, &len, &color);
629 	     break;
630 	  }
631 
632 	if (s == NULL)
633 	  continue;
634 
635 	if (color != def_color)
636 	  slrn_set_color (color);
637 
638 	/* In case of UTF-8 characters, it is important to distinguish between
639 	 * the length in bytes and the length in characters. */
640 	smax = s + strlen (s);
641 	len = slrn_screen_strlen (s, smax);
642 	if (NULL != (p = slrn_strbyte (s, '\n')))
643 	  len = slrn_screen_strlen (s, p);
644 
645 	if (field_len != -1)
646 	  {
647 	     if (field_len > len)
648 	       spaces = field_len - len;
649 	     else
650 	       len = field_len;
651 	  }
652 	if (justify)
653 	  {
654 	     int n;
655 	     if (justify == 1) n = 0; /* right justify */
656 	     else n = spaces - (spaces / 2); /* center */
657 	     while (spaces > n)
658 	       {
659 		  SLsmg_write_nchars (" ", 1);
660 		  spaces--;
661 	       }
662 	  }
663 	SLsmg_write_nstring (s, len);
664 	while (spaces)
665 	  {
666 	     SLsmg_write_nchars (" ", 1);
667 	     spaces--;
668 	  }
669 	if (color != def_color)
670 	  slrn_set_color (def_color);
671      }
672 
673    SLsmg_erase_eol ();
674 }
675 /*}}}*/
676 
slrn_set_display_format(char ** formats,unsigned int num,char * entry)677 int slrn_set_display_format (char **formats, unsigned int num, char *entry) /*{{{*/
678 {
679    if (num >= SLRN_MAX_DISPLAY_FORMATS)
680      return -1;
681 
682    if (formats[num] != NULL)
683      SLang_free_slstring (formats[num]);
684 
685    if ((entry == NULL) || (*entry == 0))
686      {
687 	formats[num] = NULL;
688 	return 0;
689      }
690 
691    if (NULL == (formats[num] = SLang_create_slstring (entry)))
692      return -1;
693 
694    return 0;
695 }
696 /*}}}*/
697 
slrn_toggle_format(char ** formats,unsigned int cur)698 unsigned int slrn_toggle_format (char **formats, unsigned int cur) /*{{{*/
699 {
700    unsigned int retval = cur;
701 
702    if (Slrn_Prefix_Arg_Ptr != NULL)
703      {
704 	retval = (unsigned int) *Slrn_Prefix_Arg_Ptr % SLRN_MAX_DISPLAY_FORMATS;
705 	Slrn_Prefix_Arg_Ptr = NULL;
706      }
707    else retval = (retval + 1) % SLRN_MAX_DISPLAY_FORMATS;
708 
709    while ((retval != cur) && (formats[retval] == NULL))
710      retval = (retval + 1) % SLRN_MAX_DISPLAY_FORMATS;
711 
712    Slrn_Full_Screen_Update = 1;
713    return retval;
714 }
715 /*}}}*/
716 
slrn_check_batch(void)717 int slrn_check_batch (void)
718 {
719    if (Slrn_Batch == 0) return 0;
720    slrn_error (_("This function is not available in batch mode."));
721    return -1;
722 }
723 
724 /*}}}*/
725 
726 /*{{{ File Related Functions */
727 
728 #ifdef VMS
729 /*{{{ VMS Filename fixup functions */
730 
vms_fix_name(char * name)731 static void vms_fix_name(char *name)
732 {
733    int idx, pos;
734 
735    pos = strspn(name, VALID_FILENAME_CHARS);
736    if (pos == strlen(name))
737      return;
738    for(idx=pos;idx<strlen(name);idx++)
739      if (!(isdigit(name[idx]) || isalpha(name[idx]) || (name[idx] == '$') || (name[idx] == '_') || (name[idx] == '-')
740 	   || (name[idx] == '/')))
741        name[idx] = '-';
742 }
743 
744 static char Copystr[SLRN_MAX_PATH_LEN];
vms_copyname1(char * name)745 static int vms_copyname1 (char *name)
746 {
747    slrn_strncpy(Copystr, name, sizeof (Copystr));
748    return(1);
749 }
750 
vms_copyname2(char * name,int type)751 static int vms_copyname2 (char *name, int type)
752 {
753    slrn_strncpy(Copystr, name, sizeof (Copystr));
754    return(1);
755 }
756 
757 /*}}}*/
758 #endif
759 
slrn_make_home_filename(char * name,char * file,size_t n)760 void slrn_make_home_filename (char *name, char *file, size_t n) /*{{{*/
761 {
762    char *home;
763 #ifndef VMS
764    if (slrn_is_absolute_path (name)
765        || ((name[0] == '.') &&
766 	   ((name[1] == SLRN_PATH_SLASH_CHAR) ||
767 	    ((name[1] == '.') && (name[2] == SLRN_PATH_SLASH_CHAR))))
768 #if defined(IBMPC_SYSTEM)
769        || ((name[0] == '.') &&
770 	   ((name[1] == '/') ||
771 	    ((name[1] == '.') && name[2] == '/')))
772 #endif
773        )
774      {
775 #ifdef __CYGWIN__
776 	if (slrn_cygwin_convert_path (name, file, n))
777 #endif
778 	  slrn_strncpy (file, name, n);
779 #if defined(IBMPC_SYSTEM) && !defined(__CYGWIN__)
780 	slrn_os2_convert_path (file);
781 #endif
782 	return;
783      }
784 
785    if (NULL == (home = getenv ("SLRNHOME")))
786      home = getenv ("HOME");
787 
788    *file = 0;
789    slrn_dircat (home, name, file, n);
790 #else /* VMS */
791    char *cp, *cp1;
792    static char fname[SLRN_MAX_PATH_LEN];
793    char fn[SLRN_MAX_PATH_LEN], fn1[SLRN_MAX_PATH_LEN];
794    int rc, idx;
795 
796    slrn_strncpy (fn1, name, sizeof (fn1));
797    if (NULL != slrn_strbyte (name, ':'))
798      {
799 	slrn_strncpy (file, name, n);
800 	return;
801      }
802 
803    if (NULL == (home = getenv ("SLRNHOME")))
804      home = getenv ("HOME");
805 
806    *file = 0;
807    if (NULL != (cp = slrn_strbyte (fn1, '/')))
808      {
809 # ifdef __DECC
810 	*cp = '\0'; cp++;
811 	cp1 = decc$translate_vms(home);
812 	if (cp1 == 0 || (int)cp1 == -1)
813 	  { /* error translating */ }
814 	else
815 	  {
816 	     slrn_strncpy(fname, cp1, sizeof (fname));
817 	     strcat(cp1, "/"); /* safe ? */
818  	  }
819 	strcat (cp1, fn1); /* safe ? */
820 
821 	vms_fix_name (cp1);
822 
823 	rc = decc$to_vms(cp1, vms_copyname2, 0, 2);
824  	if (rc > 0)
825  	  {
826  	     slrn_strncpy(fname, Copystr, sizeof (fname));
827  	     rc = mkdir(fname, 0755);
828  	  }
829 	if (strlen (fname) + strlen (cp) < sizeof (fname))
830 	  strcat(fname, cp); /* safe */
831 # else
832 	*cp = '\0'; cp++;
833 	cp1 = shell$translate_vms(home);
834 	if (cp1 == 0 || (int)cp1 == -1)
835 	  { /* error translating */ }
836 	else
837  	  {
838 	     slrn_strncpy(fname, cp1, sizeof (fname));
839 	     strcat(cp1, "/"); /* safe ? */
840 	  }
841 	strcat (cp1, fn1); /* safe ? */
842 
843 	vms_fix_name (cp1);
844 
845 	rc = shell$to_vms(cp1, vms_copyname2, 0, 2);
846 	if (rc > 0)
847 	  {
848 	     slrn_strncpy(fname, Copystr, sizeof (fname));
849 	     rc = mkdir(fname, 0755);
850 	  }
851 	if (strlen (fname) + strlen (cp) < sizeof (fname))
852 	  strcat(fname, cp); /* safe */
853 # endif
854 	slrn_strncpy (file, fname, n);
855      }
856    else
857      {
858 	if (home != NULL) slrn_strncpy(file, home, n);
859 	if (strlen (file) + strlen (name) < n)
860 	  strcat (file, name); /* safe */
861      }
862 #endif /* VMS */
863 }
864 
865 /*}}}*/
866 
slrn_make_home_dirname(char * name,char * dir,size_t n)867 void slrn_make_home_dirname (char *name, char *dir, size_t n) /*{{{*/
868 {
869    /* This needs modified to deal with VMS directory syntax */
870 #ifndef VMS
871    slrn_make_home_filename (name, dir, n);
872 #else
873    char *home, *cp;
874    char fn[SLRN_MAX_PATH_LEN];
875    static char fname[SLRN_MAX_PATH_LEN];
876    int rc, idx, len;
877 
878    if (NULL != slrn_strbyte (name, ':'))
879      {
880 	slrn_strncpy (dir, name, n);
881 	return;
882      }
883    home = getenv ("HOME");
884    *dir = 0;
885    if (cp = strbyte(name,'/'))
886      {
887 #ifdef __DECC
888 	cp = decc$translate_vms(home);
889 	if (cp == 0 || (int)cp == -1)
890 	  { /* error translating */ }
891 	else
892 	  {
893 	     slrn_strncpy(fname, cp, sizeof (fname));
894 	     strcat(cp, "/"); /* safe ? */
895 	  }
896 	strcat (cp, name); /* safe ? */
897 	vms_fix_name (cp);
898 
899 	rc = decc$to_vms(cp, vms_copyname2, 0, 2);
900 	if (rc > 0)
901 	  {
902 	     slrn_strncpy(fname, Copystr, sizeof (fname));
903 	     rc = mkdir(fname, 0755);
904 	  }
905 #else
906 	if (shell$from_vms(home, vms_copyname1, 0))
907 	  {
908 	     if (Copystr != NULL)
909 	       slrn_strncpy (fn, Copystr, sizeof (fn));
910 	     if (strlen (fn) + 1 < sizeof (fn))
911 	       strcat(fn, "/"); /* safe */
912 	  }
913 	if (strlen (fn) + strlen (name) < sizeof (fn))
914 	  strcat (fn, name); /* safe */
915 	vms_fix_name(fn);
916 	if (shell$to_vms(fn, vms_copyname1, 0))
917 	  slrn_strncpy (fname, Copystr, sizeof (fname));
918 #endif
919 	slrn_strncpy (dir, fname, n);
920      }
921    else
922      {
923 	if (home != NULL)
924 	  {
925 	     slrn_strncpy(dir, home, n);
926 	     len = strlen(dir) - 1;
927 	     if (dir[len] == ']')
928 	       {
929 		  if (len + 3 + strlen (name) < n)
930 		    {
931 		       dir[len] = '.';
932 		       strcat(dir, name); /* safe */
933 		       strcat(dir, "]"); /* safe */
934 		    }
935 	       }
936 	     else if (len + 1 + strlen (name) < n)
937 	       strcat(dir, name); /* safe */
938 	 }
939 	else if (strlen (dir) + strlen (name) < n)
940 	  strcat(dir, name); /* safe */
941      }
942 #endif /* VMS */
943 
944    return;
945 }
946 
947 /*}}}*/
948 
make_random(void)949 static unsigned int make_random (void)
950 {
951    static unsigned long s;
952    static int init;
953 
954    if (init == 0)
955      {
956 	s = (unsigned long) time (NULL) + (unsigned long) getpid ();
957 	init = 1;
958      }
959 
960    s = s * 69069UL + 1013904243UL;
961 
962    /* The time has been added to make this number somewhat unpredictable
963     * based on the previous number.  The whole point of this is to foil
964     * any attempt of a hacker to determine the name of the _next_ temp
965     * file that slrn will create.
966     */
967    return (unsigned int) (s + (unsigned long) time (NULL));
968 }
969 
970 /* Note: This function should not create a file that is deleted when it
971  * is closed.
972  */
973 
slrn_open_tmpfile_in_dir(char * dir,char * file,size_t n)974 FILE *slrn_open_tmpfile_in_dir (char *dir, char *file, size_t n)
975 {
976    FILE *fp;
977    unsigned int len;
978    unsigned int i;
979    char buf[80];
980 
981 #ifndef VMS
982    if (2 != slrn_file_exists (dir))
983      return NULL;
984 #endif
985 
986 #if defined(IBMPC_SYSTEM)
987    slrn_snprintf (buf, sizeof (buf), "SLRN%04u", make_random ());
988 #else
989    slrn_snprintf (buf, sizeof (buf), "SLRN%X", make_random ());
990 #endif
991 
992    if (-1 == slrn_dircat (dir, buf, file, n))
993      return NULL;
994 
995 #if defined(IBMPC_SYSTEM)
996 # define MAX_TMP_FILE_NUMBER 999
997 #else
998 # define MAX_TMP_FILE_NUMBER 1024
999 #endif
1000 
1001    len = strlen (file);
1002    for (i = 0; i < MAX_TMP_FILE_NUMBER; i++)
1003      {
1004 	int fd;
1005 
1006 	if (len + 5 < n)
1007 	  sprintf (file + len, ".%u", i); /* safe */
1008 	else if (i)
1009 	  break;
1010 
1011 	fd = open (file, O_WRONLY | O_CREAT | O_EXCL, S_IREAD | S_IWRITE);
1012 	if (fd != -1)
1013 	  {
1014 	     if (NULL == (fp = fdopen (fd, "w")))
1015 	       close (fd);
1016 	     else
1017 	       return fp;
1018 	  }
1019      }
1020    return NULL;
1021 }
1022 
slrn_open_tmpfile(char * file,size_t n)1023 FILE *slrn_open_tmpfile (char *file, size_t n) /*{{{*/
1024 {
1025    char *dir;
1026 
1027    dir = getenv ("TMP");
1028    if ((dir == NULL) || (2 != slrn_file_exists (dir)))
1029      dir = getenv ("TMPDIR");
1030 
1031    if ((dir == NULL) || (2 != slrn_file_exists (dir)))
1032      {
1033 #ifdef VMS
1034 	dir = "SYS$LOGIN:";
1035 #else
1036 # if defined(IBMPC_SYSTEM)
1037 	dir = ".";
1038 # else
1039 	dir = "/tmp";
1040 # endif
1041 #endif
1042      }
1043 
1044    return slrn_open_tmpfile_in_dir (dir, file, n);
1045 }
1046 
1047 /*}}}*/
1048 
slrn_open_home_file(char * name,char * mode,char * file,size_t n,int create_flag)1049 FILE *slrn_open_home_file (char *name, char *mode, char *file, /*{{{*/
1050 			   size_t n, int create_flag)
1051 {
1052    char filebuf[SLRN_MAX_PATH_LEN];
1053 
1054    if (file == NULL)
1055      {
1056 	file = filebuf;
1057 	n = sizeof (filebuf);
1058      }
1059 
1060    slrn_make_home_filename (name, file, n);
1061 
1062 #ifdef VMS
1063    if (create_flag)
1064      {
1065 	FILE *fp = fopen (file, mode, "fop=cif");
1066 	if (fp == NULL) perror ("fopen");
1067 	return fp;
1068      }
1069 #else
1070    (void) create_flag;
1071 #endif
1072 
1073    if (2 == slrn_file_exists (file)) /* don't open directories */
1074      return NULL;
1075    return fopen (file, mode);
1076 }
1077 
1078 /*}}}*/
1079 
slrn_open_home_vfile(char * name,char * file,size_t n)1080 VFILE *slrn_open_home_vfile (char *name, char *file, size_t n)
1081 {
1082    char filebuf[SLRN_MAX_PATH_LEN];
1083 
1084    if (file == NULL)
1085      {
1086 	file = filebuf;
1087 	n = sizeof (filebuf);
1088      }
1089 
1090    slrn_make_home_filename (name, file, n);
1091 
1092    return vopen (file, 4096, 0);
1093 }
1094 
1095 /*}}}*/
1096 
slrn_mail_file(char * file,int edit,unsigned int editline,char * to,char * subject)1097 int slrn_mail_file (char *file, int edit, unsigned int editline, char *to, char *subject) /*{{{*/
1098 {
1099    FILE *pp;
1100    Slrn_Article_Type *a;
1101 #if defined(IBMPC_SYSTEM)
1102    char *buf;
1103    char outfile [SLRN_MAX_PATH_LEN];
1104 #endif
1105 
1106    if (edit && (Slrn_Batch == 0))
1107      {
1108 	if (slrn_edit_file (Slrn_Editor_Mail, file, editline, 1) < 0) return -1;
1109 	if (Slrn_Mail_Editor_Is_Mua) return 0;
1110 
1111 	while (1)
1112 	  {
1113 	     char rsp;
1114 /* Note to translators: "yY" is for "yes", "nN" for "no", "eE" for "edit".
1115  * Do not change the length of this string. You cannot use any of the
1116  * default characters for different fields. */
1117 	     char *responses=_("yYnNeE");
1118 
1119 	     if (strlen (responses) != 6)
1120 	       responses = "";
1121 	     rsp = slrn_get_response ("yYnNeE", responses, _("Mail the message? \001Yes, \001No, \001Edit"));
1122 	     rsp = slrn_map_translated_char ("yYnNeE", responses, rsp) | 0x20;
1123 	     if (rsp == 'n') return -1;
1124 	     if (rsp == 'y') break;
1125 	     if (slrn_edit_file (Slrn_Editor_Mail, file, 1, 0) < 0) return -1;
1126 	  }
1127      }
1128 #ifdef VMS
1129    buf = slrn_strdup_printf ("%s\"%s\"", MAIL_PROTOCOL, to);
1130    vms_send_mail (buf, subject, file);
1131    slrn_free (buf);
1132 #else
1133 
1134    while (1)
1135      {
1136 	int rsp;
1137 
1138 	a = (Slrn_Article_Type*) slrn_malloc (sizeof(Slrn_Article_Type), 1, 1);
1139 	if (a == NULL)
1140 	  return -1;
1141 
1142 	if (0 == (rsp = slrn_prepare_file_for_posting(file, &editline, a, to, 1)))
1143 	  break;
1144 
1145 	if (rsp == -1)
1146 	  {
1147 	     slrn_art_free_article(a);
1148 	     return -1;
1149 	  }
1150 
1151 	SLtt_beep ();
1152 	if (rsp == 1)
1153 	  {
1154 /* Note to translators:
1155 * In the next two strings, "yY" is "yes", "eE" is "edit", "nN" is "no",
1156 * "cC" is "cancel" and "fF" means "force". The usual rules apply.
1157 */
1158 	     char *responses = _("yYeEnNcC");
1159 	     if (strlen (responses) != 8)
1160 	       responses = "";
1161 	     rsp = slrn_get_response ("yYEenNcC\007", responses, _("re-\001Edit,  or \001Cancel"));
1162 	  }
1163 	else
1164 	  {
1165 	     char *responses = _("yYeEnNcCfF");
1166 	     if (strlen (responses) != 10)
1167 	       responses = "";
1168 	     rsp = slrn_get_response ("EeyYnNcC\007Ff", responses, _("re-\001Edit, \001Cancel, or \001Force the mailing (not recommended)"));
1169 	  }
1170 
1171 	rsp = slrn_map_translated_char ("yYeEnNcCfF", _("yYeEnNcCfF"), rsp) | 0x20;
1172 
1173 	if ((rsp == 'c') || (rsp == 'n') ||(rsp == 7) )
1174 	  {
1175 	     slrn_art_free_article(a);
1176 	     return -1;
1177 	  }
1178 	if (rsp == 'f')
1179 	  break;
1180 
1181 	if (slrn_edit_file (Slrn_Editor_Mail, file, editline, 1) < 0)
1182 	  {
1183 	     slrn_art_free_article(a);
1184 	     return -1;
1185 	  }
1186 	/* try again */
1187 	slrn_art_free_article (a);
1188      } /* while (1)*/
1189 
1190    slrn_message_now (_("Sending ..."));
1191 
1192 # if defined(IBMPC_SYSTEM)
1193    pp = slrn_open_tmpfile (outfile, sizeof (outfile));
1194 # else
1195    pp = slrn_popen (Slrn_SendMail_Command, "w");
1196 # endif
1197 
1198    a->cline=a->lines;
1199    while (a->cline != NULL)
1200      {
1201 	fputs (a->cline->buf, pp);
1202 	putc('\n', pp);
1203 	a->cline=a->cline->next;
1204      }
1205 # if defined(IBMPC_SYSTEM)
1206    slrn_fclose (pp);
1207    buf = slrn_strdup_strcat (Slrn_SendMail_Command, " ", outfile, NULL);
1208    /* FIXME */
1209    if (buf != NULL)
1210      {
1211 	slrn_posix_system (buf, 0);
1212 	slrn_free (buf);
1213      }
1214 # else
1215    slrn_pclose (pp);
1216 # endif
1217 
1218 #endif /* NOT VMS */
1219    slrn_message (_("Sending...done"));
1220 
1221    if (-1 == slrn_save_article_to_mail_file (a, Slrn_Save_Replies_File))
1222      {
1223 	slrn_art_free_article(a);
1224 	return -1;
1225      }
1226 
1227    slrn_art_free_article(a);
1228    return 0;
1229 }
1230 
1231 /*}}}*/
1232 
1233 #if SLRN_HAS_PIPING
_slrn_pclose(FILE * fp)1234 int _slrn_pclose (FILE *fp)
1235 {
1236    int ret;
1237 
1238    ret = pclose (fp);
1239    if (ret == 0) return ret;
1240 
1241 #if defined(WIFEXITED) && defined(WEXITSTATUS)
1242    if ((ret != -1) && WIFEXITED(ret))
1243      {
1244 	ret = WEXITSTATUS(ret);
1245      }
1246 #endif
1247    return ret;
1248 }
1249 #endif				       /* SLRN_HAS_PIPING */
1250 
1251 #if SLRN_HAS_PIPING
1252 /* There appears to be no simply way of getting the command assocated with
1253  * a pipe file descriptor.  Therefore, I will simply store them in a table.
1254  */
1255 typedef struct _Pipe_Cmd_Table_Type
1256 {
1257    char *cmd;
1258    FILE *fp;
1259    struct _Pipe_Cmd_Table_Type *next;
1260 }
1261 Pipe_Cmd_Table_Type;
1262 
1263 Pipe_Cmd_Table_Type *Pipe_Cmd_Table;
1264 
store_pipe_cmd(char * cmd,FILE * fp)1265 static int store_pipe_cmd (char *cmd, FILE *fp)
1266 {
1267    Pipe_Cmd_Table_Type *p;
1268 
1269    if (NULL == (cmd = SLang_create_slstring (cmd)))
1270      return -1;
1271 
1272    p = (Pipe_Cmd_Table_Type *)slrn_malloc (sizeof (Pipe_Cmd_Table_Type), 1, 0);
1273    if (p == NULL)
1274      {
1275 	SLang_free_slstring (cmd);
1276 	return -1;
1277      }
1278    p->cmd = cmd;
1279    p->fp = fp;
1280    p->next = Pipe_Cmd_Table;
1281    Pipe_Cmd_Table = p;
1282    return 0;
1283 }
1284 
delete_pipe_cmd(FILE * fp)1285 static void delete_pipe_cmd (FILE *fp)
1286 {
1287    Pipe_Cmd_Table_Type *last, *next;
1288 
1289    last = NULL;
1290    next = Pipe_Cmd_Table;
1291    while (next != NULL)
1292      {
1293 	if (next->fp == fp)
1294 	  {
1295 	     if (last != NULL)
1296 	       last->next = next->next;
1297 	     else
1298 	       Pipe_Cmd_Table = next->next;
1299 
1300 	     SLang_free_slstring (next->cmd);
1301 	     slrn_free ((char *) next);
1302 	     return;
1303 	  }
1304 
1305 	last = next;
1306 	next = next->next;
1307      }
1308 
1309    /* should not be reached */
1310 }
1311 
get_pipe_cmd(FILE * fp)1312 static char *get_pipe_cmd (FILE *fp)
1313 {
1314    Pipe_Cmd_Table_Type *p;
1315 
1316    p = Pipe_Cmd_Table;
1317    while (p != NULL)
1318      {
1319 	if (p->fp == fp)
1320 	  return p->cmd;
1321 
1322 	p = p->next;
1323      }
1324 
1325    return _("**UNKNOWN**");
1326 }
1327 #endif 				       /* SLRN_HAS_PIPING */
slrn_pclose(FILE * fp)1328 int slrn_pclose (FILE *fp) /*{{{*/
1329 {
1330 #if SLRN_HAS_PIPING
1331    int ret;
1332    if (fp == NULL) return -1;
1333 
1334    ret = _slrn_pclose (fp);
1335    if (ret)
1336      {
1337 	char buf[SLRN_MAX_PATH_LEN];
1338 	fprintf (stderr, _("Command %s returned exit status %d.  Press RETURN.\n"),
1339 		 get_pipe_cmd (fp), ret);
1340 	fgets (buf, sizeof(buf), stdin);
1341      }
1342 
1343    delete_pipe_cmd (fp);
1344 
1345    slrn_set_display_state (SLRN_TTY_INIT | SLRN_SMG_INIT);
1346    return 0;
1347 #else
1348    return -1;
1349 #endif
1350 }
1351 
1352 /*}}}*/
1353 
slrn_popen(char * cmd,char * mode)1354 FILE *slrn_popen (char *cmd, char *mode) /*{{{*/
1355 {
1356 #if SLRN_HAS_PIPING
1357    FILE *fp;
1358 
1359    slrn_set_display_state (0);
1360    fp = popen (cmd, mode);
1361 
1362    if (fp == NULL)
1363      {
1364 	char buf[256];
1365 	fprintf (stderr, _("Command %s failed to run.  Press RETURN.\n"), cmd);
1366 	fgets (buf, sizeof(buf), stdin);
1367 	slrn_set_display_state (SLRN_TTY_INIT | SLRN_SMG_INIT);
1368      }
1369    else (void) store_pipe_cmd (cmd, fp);
1370 
1371    return fp;
1372 #else
1373    return NULL;
1374 #endif
1375 }
1376 
1377 /*}}}*/
1378 
1379 /*}}}*/
1380 
1381 /* returns a malloced string */
create_edit_command(char * edit,char * file,unsigned int line)1382 static char *create_edit_command (char *edit, char *file, unsigned int line) /*{{{*/
1383 {
1384    int d, s;
1385    char ch, *p = edit;
1386    /* Look for %d and %s */
1387 
1388    d = s = 0;
1389 
1390    while (0 != (ch = *p++))
1391      {
1392 	if (ch != '%') continue;
1393 	ch = *p;
1394 	if (!d && (ch == 'd'))
1395 	  {
1396 	     *p = 'u';		       /* map %d to %u (unsigned) */
1397 	     if (s == 0) d = 1; else d = 2;
1398 	  }
1399 	else if (!s && (ch == 's'))
1400 	  {
1401 	     if (d == 0) s = 1; else s = 2;
1402 	  }
1403 	else
1404 	  {
1405 	     slrn_error (_("Invalid Editor definition."));
1406 	     return NULL;
1407 	  }
1408 	p++;
1409      }
1410 
1411 #if defined(IBMPC_SYSTEM)
1412    /* Convert editor pathnames from / form to \\ form.  I wonder what
1413     * happens when the pathname contains a space.  Hmmm...
1414     */
1415    p = edit;
1416    while ((*p != ' ') && (*p != '\t') && (*p != 0))
1417      {
1418 	if (*p == '/') *p = SLRN_PATH_SLASH_CHAR;
1419 	p++;
1420      }
1421 #endif
1422 
1423    /* No %d, %s */
1424 
1425    if ((d == 0) && (s == 0))
1426      {
1427 	return slrn_strdup_strcat (edit, " ", file, NULL);
1428      }
1429    else if (d == 0)
1430      {
1431 	return slrn_strdup_printf (edit, file);
1432      }
1433    else if (s == 0)
1434      {
1435 	char *retval, *cmd1;
1436 	cmd1 = slrn_strdup_printf (edit, (int) line);
1437 	retval = slrn_strdup_strcat (cmd1, " ", file, NULL);
1438 	slrn_free (cmd1);
1439 	return retval;
1440      }
1441    else /* d and s */
1442      {
1443 	if (d == 1)
1444 	  return slrn_strdup_printf (edit, line, file);
1445 	else return slrn_strdup_printf (edit, file, line);
1446      }
1447    /* We should never get here */
1448    return NULL;
1449 }
1450 
1451 /*}}}*/
1452 
1453 /* This function returns -1 upon failure, -2 upon unmodified edit */
slrn_edit_file(char * editor,char * file,unsigned int line,int check_mtime)1454 int slrn_edit_file (char *editor, char *file, unsigned int line,
1455 		    int check_mtime) /*{{{*/
1456 {
1457    char *cmd, *editcmd, *msg = NULL;
1458    int ret;
1459    unsigned long mtime = 0;
1460    struct stat st;
1461 
1462    if (Slrn_Abort_Unmodified == 0)
1463      check_mtime = 0;
1464 
1465    if ((editor == NULL) || (*editor == 0))
1466      editor = Slrn_Editor;
1467 
1468    if (((editor == NULL) || (*editor == 0))
1469        && (NULL == (editor = getenv("SLRN_EDITOR")))
1470        && (NULL == (editor = getenv("SLANG_EDITOR")))
1471        && (NULL == (editor = getenv("EDITOR")))
1472        && (NULL == (editor = getenv("VISUAL"))))
1473      {
1474 #if defined(VMS) || defined(__NT__) || defined(__WIN32__)
1475 	editor = "edit";
1476 #else
1477 # if defined(__os2__)
1478 	editor = "e";
1479 # else
1480 #  ifdef __unix__
1481 	editor = "vi";
1482 #  else
1483 	slrn_error (_("No editor command defined."));
1484 	return -1;
1485 #  endif
1486 # endif
1487 #endif
1488      }
1489    editcmd = slrn_safe_strmalloc (editor);
1490 
1491    if (NULL == (cmd = create_edit_command (editcmd, file, line))) return -1;
1492 
1493    if (check_mtime
1494        && (0 == stat (file, &st)))
1495      mtime = (unsigned long) st.st_mtime;
1496 
1497    ret = slrn_posix_system (cmd, 1);
1498 
1499 #if defined(WIFEXITED) && defined(WEXITSTATUS)
1500    if ((ret != -1) && WIFEXITED(ret))
1501      {
1502 	ret = WEXITSTATUS(ret);
1503 	if (ret == 127)
1504 	  {
1505 	     msg = _("The editor could not be found.");
1506 	     ret = -1;
1507 	  }
1508 	else if (ret == 126)
1509 	  {
1510 	     msg = _("The editor was found, but could not be executed.");
1511 	     ret = -1;
1512 	  }
1513 	else if (ret)
1514 	  msg = _("The editor returned a non-zero status.");
1515      }
1516 #endif
1517 
1518    /* Am I the only one who thinks this is a good idea?? */
1519    if (Slrn_TT_Initialized) while (SLang_input_pending (5))
1520      SLang_flush_input ();
1521 
1522    if (check_mtime
1523        && (ret != -1)
1524        && (0 == stat (file, &st))
1525        && (mtime == (unsigned long) st.st_mtime))
1526      {
1527 	if (msg == NULL)
1528 	  msg = _("File was not modified.");
1529 	ret = -2;
1530      }
1531 
1532 #if defined(IBMPC_SYSTEM) && !defined(__CYGWIN__)
1533    if ((strlen (cmd) > 1)
1534        && (cmd[1] == ':')
1535        && (cmd[2] != '\\'))
1536      {
1537 	msg = _("Please use double '\\\\' to separate directories in editor_command");
1538      }
1539 #endif
1540 
1541    if (msg != NULL)
1542      {
1543 	slrn_message ("%s", msg);
1544 	if (ret >= 0)
1545 	  {
1546 	     slrn_update_screen ();
1547 	     slrn_sleep (2);
1548 	  }
1549      }
1550    slrn_free (cmd);
1551    slrn_free (editcmd);
1552    return ret;
1553 }
1554 
1555 /*}}}*/
1556 
1557 /*{{{ Get Input From User Related Functions */
1558 
rline_update(SLrline_Type * rli,char * prompt,char * buf,unsigned int len,unsigned int point,VOID_STAR client_data)1559 static void rline_update (SLrline_Type *rli, char *prompt,
1560 			  char *buf, unsigned int len, unsigned int point,
1561 			  VOID_STAR client_data)
1562 {
1563    int col;
1564    char *ubuf, *u, *umax, *upoint;
1565    unsigned int prompt_len;
1566 
1567    (void) client_data;
1568    (void) rli;
1569 
1570    slrn_push_suspension (0);
1571    if (prompt == NULL)
1572      prompt = "";
1573 
1574    prompt_len = strlen (prompt);
1575    ubuf = slrn_safe_malloc (prompt_len + len + 1);
1576    strcpy (ubuf, prompt);
1577    strncpy (ubuf + prompt_len, buf, len);
1578 
1579    len += prompt_len;
1580    ubuf[len] = 0;
1581 
1582    umax = ubuf + len;
1583    upoint = ubuf + prompt_len + point;
1584 
1585    u = ubuf;
1586    do
1587      {
1588 	SLsmg_gotorc (SLtt_Screen_Rows - 1, 0);
1589 	SLsmg_write_nchars (u, (unsigned int) (upoint - u));
1590 	col = SLsmg_get_column ();
1591 	if (col < SLtt_Screen_Cols)
1592 	  break;
1593 
1594 	u = skip_char (u, upoint, 1);
1595      }
1596    while (u < upoint);
1597 
1598    SLsmg_write_nchars (upoint, (unsigned int) (umax-upoint));
1599    slrn_free (ubuf);
1600 
1601    SLsmg_erase_eol ();
1602    SLsmg_gotorc (SLtt_Screen_Rows - 1, col);
1603    slrn_smg_refresh ();
1604    slrn_pop_suspension ();
1605 }
1606 
1607 /* If 1, redraw read_line.  If 2, redraw message */
1608 static int Reading_Input;
1609 
redraw_mini_buffer(void)1610 static void redraw_mini_buffer (void) /*{{{*/
1611 {
1612    if (Reading_Input == 1)
1613      SLrline_redraw (Slrn_Keymap_RLI);
1614    else if (Reading_Input == 2)
1615      redraw_message ();
1616 }
1617 
1618 /*}}}*/
1619 
init_readline(void)1620 static SLang_RLine_Info_Type *init_readline (void) /*{{{*/
1621 {
1622    SLrline_Type *rli;
1623    unsigned int flags = SL_RLINE_BLINK_MATCH;
1624 
1625    if (Slrn_UTF8_Mode)
1626      flags |= SL_RLINE_UTF8_MODE;
1627 
1628    if (NULL == (rli = SLrline_open (SLtt_Screen_Cols, flags)))
1629      return NULL;
1630 
1631    (void) SLrline_set_update_hook (rli, rline_update, NULL);
1632    return rli;
1633 }
1634 
1635 /*}}}*/
1636 
read_from_input_string(char * str)1637 static int read_from_input_string (char *str)
1638 {
1639    char *s;
1640 
1641    if (Input_String == NULL) return -1;
1642 
1643    s = slrn_strbyte (Input_String_Ptr, '\n');
1644    if (s != NULL)
1645      *s = 0;
1646 
1647    strncpy (str, Input_String_Ptr, 255);
1648    str[255] = 0;
1649 
1650    if (s == NULL)
1651      {
1652 	SLFREE (Input_String);
1653 	Input_String_Ptr = Input_String = NULL;
1654      }
1655    else Input_String_Ptr = s + 1;
1656 
1657    return strlen (str);
1658 }
1659 
1660 /* s could be NULL.  If so, input string is cleared.  The calling routine
1661  * should not free the input pointer.
1662  */
slrn_set_input_string(char * s)1663 void slrn_set_input_string (char *s)
1664 {
1665    slrn_free (Input_String);
1666    Input_String = s;
1667    Input_String_Ptr = s;
1668 }
1669 
read_from_input_char(void)1670 static char read_from_input_char (void)
1671 {
1672    if (Input_Chars_Ptr == NULL)
1673      return 0;
1674 
1675    if (*Input_Chars_Ptr == 0)
1676      {
1677 	slrn_set_input_chars (NULL);
1678 	return 0;
1679      }
1680 
1681    return *Input_Chars_Ptr++;
1682 }
1683 
slrn_set_input_chars(char * s)1684 void slrn_set_input_chars (char *s)
1685 {
1686    slrn_free (Input_Chars);
1687    Input_Chars = s;
1688    Input_Chars_Ptr = s;
1689 }
1690 
1691 /* The char * argument is expected to be SLRN_MAX_PATH_LEN in size */
1692 static int (*Complete_Open) (char *);
1693 static int (*Complete_Next) (char *);
1694 
1695 static char File_Pattern[SLRN_MAX_PATH_LEN], Dir_Name[SLRN_MAX_PATH_LEN];
1696 static Slrn_Dir_Type *Dir;
1697 static int In_Completion;
1698 
1699 /* buf needs room for at least SLRN_MAX_PATH_LEN chars */
dir_findnext(char * buf)1700 static int dir_findnext (char *buf) /*{{{*/
1701 {
1702    Slrn_Dirent_Type *dirent;
1703    unsigned int len = strlen (File_Pattern);
1704 
1705    while (NULL != (dirent = slrn_read_dir (Dir)))
1706      {
1707 	char *name = dirent->name;
1708 	if ((*name == '.') && ((*(name+1) == 0) ||
1709 			       ((*(name+1) == '.') && (*(name+2) == 0))))
1710 	  continue;
1711 	if (!strncmp (dirent->name, File_Pattern, len))
1712 	  {
1713 	     strcpy (buf, Dir_Name); /* safe */
1714 	     len = strlen (buf);
1715 	     slrn_strncpy (buf+len, dirent->name, SLRN_MAX_PATH_LEN - len - 1);
1716 	     return 1;
1717 	  }
1718      }
1719    return 0;
1720 }
1721 /*}}}*/
1722 
dir_findfirst(char * buf)1723 static int dir_findfirst (char *buf) /*{{{*/
1724 {
1725    int pos = strlen (buf);
1726 
1727    if (Dir != NULL)
1728      {
1729 	slrn_close_dir (Dir);
1730 	Dir = NULL;
1731      }
1732 
1733    while ((pos >= 0) && (buf[pos] != SLRN_PATH_SLASH_CHAR))
1734      pos--;
1735 
1736    if (pos == -1)
1737      {
1738 	unsigned int len;
1739 	if (NULL == slrn_getcwd (Dir_Name, sizeof (Dir_Name) - 1))
1740 	  return 0;
1741 	len = strlen (Dir_Name);
1742 	Dir_Name[len] = SLRN_PATH_SLASH_CHAR;
1743 	Dir_Name[len+1] = '\0';
1744      }
1745    else
1746      {
1747 	if (pos + 2 > SLRN_MAX_PATH_LEN)
1748 	  pos = SLRN_MAX_PATH_LEN - 2;
1749 	slrn_strncpy (Dir_Name, buf, pos + 2);
1750      }
1751 
1752    pos++;
1753    slrn_strncpy (File_Pattern, buf + pos, sizeof (File_Pattern));
1754 
1755    if (NULL == (Dir = slrn_open_dir (Dir_Name)))
1756      return 0;
1757 
1758    return dir_findnext (buf);
1759 }
1760 /*}}}*/
1761 
1762 typedef struct
1763 {
1764    char *what;
1765    void *dummy;
1766 }
1767 Generic_Var_Type;
1768 
1769 static Generic_Var_Type *Var;
1770 static int Var_Pos;
1771 
var_findnext(char * buf)1772 static int var_findnext (char *buf) /*{{{*/
1773 {
1774    unsigned int len = strlen (File_Pattern);
1775 
1776    while (Var->what != NULL)
1777      {
1778 	int retval = 0;
1779 	if (!strncmp (Var->what, File_Pattern, len))
1780 	  {
1781 	     strcpy (buf, Dir_Name); /* safe */
1782 	     len = strlen (buf);
1783 	     slrn_strncpy (buf+len, Var->what, SLRN_MAX_PATH_LEN - len - 1);
1784 	     retval = 1;
1785 	  }
1786 	Var++;
1787 	if ((Var->what == NULL) && (Var_Pos == 0))
1788 	  {
1789 	     Var = (Generic_Var_Type*) Slrn_Str_Variables;
1790 	     Var_Pos++;
1791 	  }
1792 	if (retval) return 1;
1793      }
1794    return 0;
1795 }
1796 /*}}}*/
1797 
var_findfirst(char * buf)1798 static int var_findfirst (char *buf) /*{{{*/
1799 {
1800    int pos = strlen (buf);
1801    while ((pos > 0) && (buf[pos-1] != ' '))
1802      pos--;
1803 
1804    slrn_strncpy (Dir_Name, buf, pos + 1);
1805    slrn_strncpy (File_Pattern, buf + pos, SLRN_MAX_PATH_LEN);
1806    Var = (Generic_Var_Type*) Slrn_Int_Variables; Var_Pos = 0;
1807    return var_findnext (buf);
1808 }
1809 /*}}}*/
1810 
strpcmp(char ** a,char ** b)1811 static int strpcmp (char **a, char **b)
1812 {
1813    return strcmp (*a, *b);
1814 }
1815 
rli_self_insert(void)1816 static void rli_self_insert (void) /*{{{*/
1817 {
1818    char last_char [2];
1819    last_char[0] = (char) SLang_Last_Key_Char;
1820    last_char[1] = '\0';
1821    (void) SLrline_ins (Slrn_Keymap_RLI, last_char, 1);
1822    /* SLrline_redraw in slang-2.0.x did not use the redraw-hook.  So
1823     * avoid using this here.  It is not needed anyway.
1824     */
1825    /* SLrline_redraw (Slrn_Keymap_RLI); */
1826 }
1827 /*}}}*/
1828 
generic_mini_complete(int cycle)1829 static void generic_mini_complete (int cycle) /*{{{*/
1830 {
1831    char *pl, *pb;
1832    char last[SLRN_MAX_PATH_LEN], buf[SLRN_MAX_PATH_LEN];
1833    static char prev[SLRN_MAX_PATH_LEN], prevcall[SLRN_MAX_PATH_LEN];
1834    int repeat = 1; /* whether we are called with identical values again */
1835    unsigned int n;
1836    static int flag = 0;  /* when flag goes 0, we call open */
1837    static unsigned int lastpoint = 0;
1838    char **argv = NULL;
1839    unsigned int argc = 0, maxargc = 0;
1840    unsigned int point;
1841    char *rli_buf;
1842 
1843    if (Complete_Open == NULL)
1844      {
1845 	rli_self_insert ();
1846 	return;
1847      }
1848 
1849    n = sizeof (buf);
1850    (void) SLrline_get_point (Slrn_Keymap_RLI, &point);
1851    if (point < n)
1852      n = point + 1;
1853 
1854    rli_buf = SLrline_get_line (Slrn_Keymap_RLI);   /* malloced pointer */
1855    if (rli_buf == NULL)
1856      return;
1857 
1858    slrn_strncpy (buf, rli_buf, n);
1859    n = 0;
1860 
1861    if (strcmp (rli_buf, prevcall) ||
1862        (point != lastpoint) ||
1863        (0 == In_Completion))
1864      {
1865 	slrn_strncpy (prev, buf, sizeof(prev));
1866 	/* strcpy (prev, buf); */ /* safe */ /* save this search context */
1867 	flag = 0;
1868 	repeat = 0;
1869      }
1870 
1871    SLfree (rli_buf); rli_buf = NULL;
1872 
1873    if (In_Completion == 2)
1874      repeat = 0;
1875 
1876    if (cycle)
1877      {
1878 	if (flag)
1879 	  flag = (*Complete_Next)(buf);
1880 	else if (0 == (flag = (*Complete_Open)(buf)))
1881 	  {
1882 	     rli_self_insert ();
1883 	     return;
1884 	  }
1885 	if (flag == 0)
1886 	  slrn_strncpy (buf, prev, sizeof (buf));
1887 	strcpy (last, buf); /* safe */
1888      }
1889    else
1890      {
1891 	flag = (*Complete_Open)(buf);
1892 	strcpy (last, buf); /* safe */
1893 
1894 	/* This loop tests all values from complete_next and returns the
1895 	 * smallest length match of initial characters of buf */
1896 	while (flag)
1897 	  {
1898 	     pl = last;
1899 	     pb = buf;
1900 
1901 	     if (repeat)
1902 	       {
1903 		  if (argc == maxargc)
1904 		    {
1905 		       char **newargv;
1906 		       maxargc += 512;
1907 		       newargv = (char **) slrn_realloc
1908 			 ((char *) argv, maxargc * sizeof (char *), 0);
1909 		       if (newargv == NULL)
1910 			 {
1911 			    slrn_free_argc_argv_list (argc, argv);
1912 			    slrn_free ((char *) argv);
1913 			    argc = 0;
1914 			 }
1915 		       argv = newargv;
1916 		    }
1917 		  if ((NULL != argv) &&
1918 		      (NULL != (argv[argc] = slrn_safe_strmalloc (buf))))
1919 		    argc++;
1920 	       }
1921 
1922 	     while (*pl && (*pl == *pb))
1923 	       {
1924 		  pl++;
1925 		  pb++;
1926 	       }
1927 
1928 	     *pl = 0;
1929 	     n++;
1930 	     flag = (*Complete_Next)(buf);
1931 	  }
1932      }
1933 
1934    if (cycle || n)
1935      {
1936 	unsigned int len;
1937 
1938 	if (repeat && (argc > 1))
1939 	  {
1940 	     int sel;
1941 	     void (*qsort_fun) (char **, unsigned int,
1942 				unsigned int, int (*)(char **, char **));
1943 	     /* This seems to be necessary (see art_sort.c:slrn_sort_headers) */
1944 	     qsort_fun = (void (*)(char **, unsigned int,
1945 				   unsigned int, int (*)(char **, char **)))
1946 	       qsort;
1947 
1948 	     qsort_fun (argv, argc, sizeof (char *), strpcmp);
1949 
1950 	     sel = slrn_select_list_mode (_("Possible completions"), argc,
1951 					  argv, 0, 1, NULL);
1952 	     slrn_update_screen ();
1953 	     if (sel != -1)
1954 	       {
1955 		  slrn_strncpy (last, argv[sel], sizeof (last));
1956 		  n = 1;
1957 	       }
1958 	  }
1959 
1960 	len = strlen (last);
1961 
1962 	if ((n < 2) && ((FVOID_STAR) Complete_Open == (FVOID_STAR) dir_findfirst) &&
1963 	    (flag || !cycle) && (len + 1 < sizeof (last)))
1964 	  {
1965 	     if (2 == slrn_file_exists (last))
1966 	       {
1967 		  last[len] = SLRN_PATH_SLASH_CHAR;
1968 		  last[len+1] = 0;
1969 	       }
1970 	  }
1971 
1972 	if (-1 == SLrline_set_line (Slrn_Keymap_RLI, last))
1973 	  return;
1974 
1975 	SLrline_redraw (Slrn_Keymap_RLI);
1976      }
1977    else SLtt_beep();
1978 
1979    if (!cycle)
1980      strcpy (prev, last); /* safe; make this the new search context */
1981 
1982    slrn_free_argc_argv_list (argc, argv);
1983    slrn_free ((char *) argv);
1984    rli_buf = SLrline_get_line (Slrn_Keymap_RLI);
1985    if (rli_buf == NULL)
1986      {
1987 	In_Completion = 0;
1988 	return;
1989      }
1990    slrn_strncpy (prevcall, rli_buf, sizeof (prevcall));
1991    SLfree (rli_buf);
1992    (void) SLrline_get_point (Slrn_Keymap_RLI, &lastpoint);
1993    In_Completion = cycle ? 2 : 1;
1994 
1995    return;
1996 }
1997 
1998 /*}}}*/
1999 
mini_complete(SLrline_Type * rli)2000 static int mini_complete (SLrline_Type *rli)
2001 {
2002    (void) rli;
2003    generic_mini_complete (0);
2004    return 0;
2005 }
2006 
mini_cycle(SLrline_Type * rli)2007 static int mini_cycle (SLrline_Type *rli)
2008 {
2009    (void) rli;
2010    generic_mini_complete (1);
2011    return 0;
2012 }
2013 
rli_del_bol(SLrline_Type * rli)2014 static int rli_del_bol (SLrline_Type *rli) /*{{{*/
2015 {
2016    unsigned int point;
2017 
2018    (void) SLrline_get_point (rli, &point);
2019    (void) SLrline_set_point (rli, 0);
2020    (void) SLrline_del (rli, point);
2021    return 0;
2022 }
2023 /*}}}*/
2024 
rli_del_bow(SLrline_Type * rli)2025 static int rli_del_bow (SLrline_Type *rli) /*{{{*/
2026 {
2027    char *buf, *b;
2028    unsigned int point;
2029    unsigned int len;
2030 
2031    (void) SLrline_get_point (rli, &point);
2032    if (point == 0)
2033      return 0;
2034 
2035    if (NULL == (buf = SLrline_get_line (rli)))
2036      return -1;
2037 
2038    b = buf + (point - 1);
2039    while ((b > buf) && ((*b == ' ') || (*b == '\t')))
2040      b--;
2041 
2042    while (b > buf)
2043      {
2044 	if ((*b == ' ') || (*b == '\t'))
2045 	  {
2046 	     b++;
2047 	     break;
2048 	  }
2049 	b--;
2050      }
2051 
2052    len = point;
2053    point = b - buf;
2054    (void) SLrline_set_point (rli, point);
2055    (void) SLrline_del (rli, len - point);
2056 
2057    SLfree (buf);
2058    return 0;
2059 }
2060 /*}}}*/
2061 
2062 #define A_KEY(s, f)  {s, (int (*)(void)) f}
2063 
2064 static SLKeymap_Function_Type Slrn_Custom_Readline_Functions [] =
2065 {
2066    A_KEY("complete", mini_complete),
2067    A_KEY("cycle", mini_cycle),
2068    A_KEY("delbol", rli_del_bol),
2069    A_KEY("delbow", rli_del_bow),
2070    A_KEY(NULL, NULL)
2071 };
2072 
slrn_rline_setkey(char * key,char * fun,SLkeymap_Type * kmap)2073 int slrn_rline_setkey (char *key, char *fun, SLkeymap_Type *kmap)
2074 {
2075    SLKeymap_Function_Type *tmp;
2076    int failure;
2077 
2078    if (NULL != SLang_find_key_function(fun, kmap))
2079      return SLang_define_key (key, fun, kmap);
2080 
2081    tmp = kmap->functions;
2082    kmap->functions = Slrn_Custom_Readline_Functions;
2083    failure = SLang_define_key (key, fun, kmap);
2084    kmap->functions = tmp;
2085 
2086    return failure;
2087 }
2088 
2089 /* str needs to have enough space for SLRL_DISPLAY_BUFFER_SIZE characters */
generic_read_input(char * prompt,char * dfl,char * str,int trim_flag,int no_echo,int point)2090 static int generic_read_input (char *prompt, char *dfl, char *str, int trim_flag,
2091 			       int no_echo, int point) /*{{{*/
2092 {
2093    int i;
2094    int tt_init_state;
2095    char prompt_buf[SLRL_DISPLAY_BUFFER_SIZE];
2096    char *buf;
2097    unsigned int len;
2098    int save_slang_error;
2099 
2100    if (prompt == NULL) prompt = "";
2101 
2102    Slrn_Full_Screen_Update = 1;
2103 
2104    slrn_strncpy (prompt_buf, prompt, sizeof (prompt_buf));
2105    len = strlen (prompt);
2106 
2107    if ((dfl != NULL) && *dfl)
2108      {
2109 	if (slrn_snprintf (prompt_buf + len, sizeof (prompt_buf) - len,
2110 	   _("(default: %s) "), dfl) == (int) (sizeof (prompt_buf) - len))
2111 	  {
2112 	     *(prompt_buf + len) = '\0';
2113 	     dfl = NULL;
2114 	  }
2115      }
2116    prompt = prompt_buf;
2117 
2118    if ((str == NULL) && (dfl == NULL)) return -1;
2119 
2120    SLrline_set_display_width (Slrn_Keymap_RLI, SLtt_Screen_Cols);
2121 
2122    /* slrn_set_suspension (1); */
2123 
2124    if ((str != NULL) && *str)
2125      {
2126 	if (-1 == SLrline_set_line (Slrn_Keymap_RLI, str))
2127 	  return -1;
2128 
2129 	if (point < 0)
2130 	  {
2131 	     unsigned int slen = strlen (str);
2132 	     if ((unsigned int)-point > slen) point = 0;
2133 	     else point = slen + (1 + point);
2134 	  }
2135 	SLrline_set_point (Slrn_Keymap_RLI, point);
2136 	*str = 0;
2137      }
2138    if (str == NULL) str = dfl;
2139 
2140    i = read_from_input_string (str);
2141    if (i >= 0) return i;
2142 
2143    tt_init_state = Slrn_TT_Initialized;
2144 
2145    slrn_set_display_state (Slrn_TT_Initialized | SLRN_TTY_INIT);
2146 
2147    if (no_echo)
2148      SLrline_set_echo (Slrn_Keymap_RLI, 0);
2149    else
2150      SLrline_set_echo (Slrn_Keymap_RLI, 1);
2151 
2152    if (tt_init_state & SLRN_SMG_INIT)
2153      (void) SLrline_set_update_hook (Slrn_Keymap_RLI, rline_update, NULL);
2154    else
2155      (void) SLrline_set_update_hook (Slrn_Keymap_RLI, NULL, NULL);
2156 
2157    slrn_enable_mouse (0);
2158 
2159    save_slang_error = SLang_get_error ();
2160    SLang_set_error (0);
2161 
2162    Reading_Input = 1;
2163    slrn_set_color (MESSAGE_COLOR);
2164 
2165    buf = SLrline_read_line (Slrn_Keymap_RLI, prompt, &len);
2166    slrn_set_color (0);
2167    Reading_Input = 0;
2168 
2169    slrn_enable_mouse (1);
2170 
2171    if ((buf != NULL) && (0 == SLang_get_error ()) && !SLKeyBoard_Quit)
2172      {
2173 	char *b = buf;
2174 
2175 	if (*b)
2176 	  {
2177 	     if (no_echo == 0) SLrline_save_line (Slrn_Keymap_RLI);
2178 	     if (trim_flag)
2179 	       {
2180 		  slrn_trim_string (b);
2181 		  b = slrn_skip_whitespace (b);
2182 	       }
2183 	  }
2184 	else if (dfl != NULL) b = dfl;
2185 
2186 	/* b could be equal to dfl and dfl could be equal to str.  If this is
2187 	 * the case, there is no need to perform the strcpy */
2188 	if (b != str)
2189 	  slrn_strncpy (str, b, SLRL_DISPLAY_BUFFER_SIZE); /* safe */
2190 
2191 	i = strlen (str);
2192      }
2193    else i = -1;
2194 
2195    SLfree (buf);
2196 
2197    if (SLKeyBoard_Quit) i = -1;
2198    SLKeyBoard_Quit = 0;
2199    SLang_set_error (save_slang_error);
2200 
2201    slrn_set_display_state (tt_init_state);
2202 
2203    if (tt_init_state & SLRN_SMG_INIT)
2204      {
2205 	/* put cursor at edge of screen to comfort user */
2206 	SLsmg_gotorc (SLtt_Screen_Rows - 1, 0);
2207 	slrn_smg_refresh ();
2208      }
2209    else
2210      {
2211 	putc ('\n', stdout);
2212 	fflush (stdout);
2213      }
2214 
2215    /* slrn_set_suspension (0); */
2216    return i;
2217 }
2218 
2219 /*}}}*/
2220 
slrn_read_input(char * prompt,char * dfl,char * str,int trim_flag,int point)2221 int slrn_read_input (char *prompt, char *dfl, char *str, int trim_flag, int point)
2222 {
2223    return generic_read_input (prompt, dfl, str, trim_flag, 0, point);
2224 }
2225 
slrn_read_input_no_echo(char * prompt,char * dfl,char * str,int trim_flag,int point)2226 int slrn_read_input_no_echo (char *prompt, char *dfl, char *str, int trim_flag, int point)
2227 {
2228    return generic_read_input (prompt, dfl, str, trim_flag, 1, point);
2229 }
2230 
slrn_read_filename(char * prompt,char * dfl,char * str,int trim_flag,int point)2231 int slrn_read_filename (char *prompt, char *dfl, char *str, int trim_flag, int point)
2232 {
2233    int retval;
2234    Complete_Open = dir_findfirst;
2235    Complete_Next = dir_findnext;
2236    retval = generic_read_input (prompt, dfl, str, trim_flag, 0, point);
2237    Complete_Open = NULL;
2238    Complete_Next = NULL;
2239    In_Completion = 0;
2240    return retval;
2241 }
2242 
slrn_read_variable(char * prompt,char * dfl,char * str,int trim_flag,int point)2243 int slrn_read_variable (char *prompt, char *dfl, char *str, int trim_flag, int point)
2244 {
2245    int retval;
2246    Complete_Open = var_findfirst;
2247    Complete_Next = var_findnext;
2248    retval = generic_read_input (prompt, dfl, str, trim_flag, 0, point);
2249    Complete_Open = NULL;
2250    Complete_Next = NULL;
2251    In_Completion = 0;
2252    return retval;
2253 }
2254 
slrn_read_artnum_int(char * prompt,NNTP_Artnum_Type * dflt,NNTP_Artnum_Type * np)2255 int slrn_read_artnum_int (char *prompt, NNTP_Artnum_Type *dflt, NNTP_Artnum_Type *np) /*{{{*/
2256 {
2257    char sdfl_buf[256];
2258    char *sdfl = NULL;
2259    char str[SLRL_DISPLAY_BUFFER_SIZE];
2260    NNTP_Artnum_Type n;
2261 
2262    if (dflt != NULL)
2263      {
2264 	sprintf (sdfl_buf, NNTP_FMT_ARTNUM, *dflt); /* safe */
2265 	sdfl = sdfl_buf;
2266      }
2267 
2268    *str = 0;
2269    if (-1 == slrn_read_input (prompt, sdfl, str, 1, 0))
2270      {
2271 	slrn_error (_("Abort!"));
2272 	return -1;
2273      }
2274 
2275    if (1 != sscanf(str, NNTP_FMT_ARTNUM, &n))
2276      {
2277 	slrn_error (_("Integer expected."));
2278 	return -1;
2279      }
2280    *np = n;
2281    return 0;
2282 }
2283 
2284 /*}}}*/
2285 
slrn_init_readline(void)2286 int slrn_init_readline (void) /*{{{*/
2287 {
2288    if ((Slrn_Keymap_RLI == NULL)
2289        && (NULL == (Slrn_Keymap_RLI = init_readline ())))
2290      return -1;
2291 
2292    Slrn_RLine_Keymap = SLrline_get_keymap (Slrn_Keymap_RLI);
2293    SLkm_define_key ("\t", (FVOID_STAR) mini_complete, Slrn_RLine_Keymap);
2294    SLkm_define_key (" ", (FVOID_STAR) mini_cycle, Slrn_RLine_Keymap);
2295    SLkm_define_key ("^U", (FVOID_STAR) rli_del_bol, Slrn_RLine_Keymap);
2296    SLkm_define_key ("^W", (FVOID_STAR) rli_del_bow, Slrn_RLine_Keymap);
2297 
2298    return 0;
2299 }
2300 
2301 /*}}}*/
2302 
slrn_map_translated_char(char * native_chars,char * translated_chars,char rsp)2303 char slrn_map_translated_char (char *native_chars, /*{{{*/
2304 			       char *translated_chars, char rsp)
2305 {
2306    char *pos;
2307    if ((strlen (native_chars) != strlen (translated_chars)) ||
2308        (NULL != slrn_strbyte (native_chars, rsp)) ||
2309        (NULL == (pos = slrn_strbyte (translated_chars, rsp))))
2310      return rsp;
2311    return native_chars[pos - translated_chars];
2312 }
2313 /*}}}*/
2314 
slrn_get_response(char * valid_chars,char * translated_chars,char * str,...)2315 char slrn_get_response (char *valid_chars, char *translated_chars, /*{{{*/
2316 			char *str, ...)
2317 {
2318    char ch;
2319    va_list ap;
2320    char *v;
2321 
2322    /* if (SLang_Error) return -1; */
2323    if (Error_Present)
2324      slrn_error_now (2, NULL);
2325 
2326    while (1)
2327      {
2328 	Slrn_Full_Screen_Update = 1;
2329 
2330 	ch = read_from_input_char ();
2331 
2332 	if (ch == 0)
2333 	  {
2334 	     if (Slrn_TT_Initialized == 0)
2335 	       {
2336 		  char buf[256];
2337 
2338 		  va_start(ap, str);
2339 		  slrn_tty_vmessage (stdout, str, ap);
2340 		  va_end(ap);
2341 
2342 		  *buf = 0;
2343 		  (void) fgets (buf, sizeof(buf), stdin);
2344 		  ch = *buf;
2345 	       }
2346 	     else
2347 	       {
2348 		  SLang_flush_input ();
2349 		  slrn_clear_message ();
2350 
2351 		  va_start(ap, str);
2352 		  vmessage_1 (MESSAGE_COLOR, str, ap);
2353 		  va_end(ap);
2354 
2355 		  slrn_smg_refresh ();
2356 
2357 		  Reading_Input = 2;
2358 		  ch = SLang_getkey ();
2359 		  Reading_Input = 0;
2360 
2361 		  slrn_clear_message ();
2362 		  SLang_set_error (0);
2363 		  SLKeyBoard_Quit = 0;
2364 	       }
2365 	  }
2366 
2367 	v = valid_chars;
2368 	while (*v)
2369 	  {
2370 	     if (*v == ch) return ch;
2371 	     v++;
2372 	  }
2373 	v = translated_chars;
2374 	while (*v)
2375 	  {
2376 	     if (*v == ch) return ch;
2377 	     v++;
2378 	  }
2379 
2380 	slrn_error_now (0, _("Invalid response! Try again."));
2381 	if (Slrn_TT_Initialized & SLRN_TTY_INIT)
2382 	  {
2383 	     (void) SLang_input_pending (15);
2384 	  }
2385      }
2386 }
2387 
2388 /*}}}*/
2389 
2390 /*{{{ Doubles percent characters. p0 is source, p is destination. */
escape_percent_chars(char * p,char * p0,char * pmax)2391 static void escape_percent_chars (char *p, char *p0, char *pmax)
2392 {
2393    while ((*p0) && (p < pmax - 2))
2394      {
2395 	if (*p0 == '%')
2396 	  *p++ = '%';
2397 	*p++ = *p0++;
2398      }
2399    *p = '\0';
2400 }
2401 /*}}}*/
2402 
slrn_get_yesno(int dflt,char * str,...)2403 int slrn_get_yesno (int dflt, char *str, ...) /*{{{*/
2404 /* For both dflt and the return value, 0 means "no", 1 is "yes". */
2405 {
2406    va_list ap;
2407    char buf0[512], buf[512];
2408    char ch, rsp;
2409    char *prompt, *fmt;
2410    /* Note to translators:
2411     * The translated string needs to have exactly four characters.
2412     * The first two become valid keys for "yes", the last two for "no".
2413     * You *cannot* use "y" for "no" or "n" for "yes".
2414     */
2415    char *responses=_("yYnN");
2416 
2417    /* if (SLang_Error) return -1; */
2418 
2419    va_start(ap, str);
2420    (void) slrn_vsnprintf(buf0, sizeof (buf0), str, ap);
2421    va_end(ap);
2422 
2423    /* As the prompt will be processed through printf again (by
2424     * slrn_get_response), we need to escape percent characters. */
2425    escape_percent_chars (buf, buf0, buf + sizeof (buf));
2426 
2427    if (dflt)
2428      {
2429 	ch = 'y';
2430 	fmt = _("? [\001Y]es, \001No");
2431      }
2432    else
2433      {
2434 	ch = 'n';
2435 	fmt = _("? \001Yes, [\001N]o");
2436      }
2437 
2438    prompt = slrn_strdup_strcat (buf, fmt, NULL);
2439    if (strlen (responses) != 4) /* Translator messed it up */
2440      responses = "";
2441    rsp = slrn_get_response ("yYnN\r\n", responses, "%s", prompt);
2442    slrn_free (prompt);
2443    if ((rsp == '\r') || (rsp == '\n')) rsp = ch;
2444    else rsp = slrn_map_translated_char ("yYnN", responses, rsp) | 0x20;
2445 
2446    if (rsp == 'n')
2447      return 0;
2448    return 1;
2449 }
2450 
2451 /*}}}*/
slrn_get_yesno_cancel(int dflt,char * str,...)2452 int slrn_get_yesno_cancel (int dflt, char *str, ...) /*{{{*/
2453 {
2454    va_list ap;
2455    char buf[512];
2456    char rsp, *prompt;
2457    /* Note to translators:
2458     * The translated string needs to have exactly six characters.
2459     * The first two become valid keys for "yes", the next two for "no",
2460     * the last ones for "cancel". You cannot use the default characters
2461     * for other fields than they originally stood for.
2462     */
2463    char *responses=_("yYnNcC");
2464    if (strlen (responses) != 6)
2465      responses = "";
2466 
2467    if (SLang_get_error ()) return -1;
2468 
2469    va_start(ap, str);
2470    (void) slrn_vsnprintf(buf, sizeof(buf), str, ap);
2471    va_end(ap);
2472 
2473    if (dflt == 1)
2474      prompt = slrn_strdup_strcat (buf, _("? [\001Y]es, \001No, \001Cancel"), NULL);
2475    else if (dflt == 0)
2476      prompt = slrn_strdup_strcat (buf, _("? Yes, \001[N]o, \001Cancel"), NULL);
2477    else
2478      prompt = slrn_strdup_strcat (buf, _("? Yes, \001No, \001[C]ancel"), NULL);
2479 
2480    if (prompt == NULL)
2481      return -1;
2482 
2483    rsp = slrn_get_response ("\007yYnNcC\r", responses, "%s", prompt);
2484    slrn_free (prompt);
2485 
2486    if (rsp == '\r') return dflt;
2487 
2488    if (rsp == 7) return -1;
2489 
2490    rsp = slrn_map_translated_char ("yYnNcC", responses, rsp) | 0x20;
2491 
2492    if (rsp == 'y') return 1;
2493    if (rsp == 'n') return 0;
2494    return -1;
2495 }
2496 
2497 /*}}}*/
2498 
slrn_get_mouse_rc(int * rp,int * cp)2499 void slrn_get_mouse_rc (int *rp, int *cp) /*{{{*/
2500 {
2501    int r, c;
2502 
2503    c = (unsigned char) SLang_getkey () - 32;
2504    r = (unsigned char) SLang_getkey () - 32;
2505    if (cp != NULL) *cp = c;
2506    if (rp != NULL) *rp = r;
2507 }
2508 
2509 /*}}}*/
2510 
slrn_evaluate_cmd(void)2511 void slrn_evaluate_cmd (void) /*{{{*/
2512 {
2513    char buf[SLRL_DISPLAY_BUFFER_SIZE];
2514 
2515    *buf = '\0';
2516    if (slrn_read_input ("S-Lang> ", NULL, buf, 0, 0) > 0)
2517      {
2518 	SLang_load_string (buf);
2519      }
2520 
2521    SLang_set_error (0);
2522 }
2523 /*}}}*/
2524 
2525 /*}}}*/
2526 
2527 /*{{{ Misc Regexp Utility Functions */
2528 
slrn_compile_regexp_pattern(char * pat)2529 SLRegexp_Type *slrn_compile_regexp_pattern (char *pat) /*{{{*/
2530 {
2531    SLRegexp_Type *re;
2532    unsigned int flags = SLREGEXP_CASELESS;
2533 
2534    if (Slrn_UTF8_Mode) flags |= SLREGEXP_UTF8;
2535    if (NULL == (re = SLregexp_compile (pat, flags)))
2536      {
2537 	slrn_error (_("Invalid regular expression or expression too long."));
2538 	return NULL;
2539      }
2540    return re;
2541 }
2542 
2543 /*}}}*/
2544 
slrn_regexp_match(SLRegexp_Type * re,char * str)2545 unsigned char *slrn_regexp_match (SLRegexp_Type *re, char *str) /*{{{*/
2546 {
2547    return (unsigned char *)SLregexp_match (re, str, strlen (str));
2548 }
2549 
2550 /*}}}*/
2551 
2552 /*}}}*/
2553 
slrn_is_fqdn(char * h)2554 int slrn_is_fqdn (char *h) /*{{{*/
2555 {
2556    char *p;
2557 
2558    /* Believe it or not, I have come across one system with a '(' character
2559     * as part of the hostname!!!  I suppose that I should also check for
2560     * other strange characters as well.  This is an issue since a message
2561     * id will be composed from the fqdn.  For that reason, such names will
2562     * be rejected.  Sigh.
2563     */
2564    if (NULL != slrn_strbrk (h, "~`!@#$%^&*()=+|\\[]{}/?;"))
2565      return 0;
2566 
2567    p = slrn_strbyte (h, '.');
2568    if ((p == NULL) || (p == h))
2569      return 0;
2570 
2571    /* Make sure it does not end in a '.' */
2572    if (p [strlen(p)-1] == '.')
2573      return 0;
2574 
2575    return 1;
2576 }
2577 /*}}}*/
2578 
2579 /* Try to get a fully qualified domain name. */
get_hostname(void)2580 static char *get_hostname (void)
2581 {
2582 #ifdef HAVE_GETADDRINFO
2583    struct addrinfo hint, *res;
2584    int r;
2585 #else
2586    struct hostent *host_entry;
2587 #endif
2588    char buf[MAX_HOST_NAME_LEN + 1];
2589 
2590    if ((-1 == gethostname (buf, MAX_HOST_NAME_LEN))
2591        || (*buf == 0))
2592      return NULL;
2593 
2594    /* gethostname may not provide the full name so use gethostbyname
2595     * to get more information.  Why isn't there a simplified interface to
2596     * get the FQDN!!!!
2597     */
2598 #ifdef HAVE_GETADDRINFO
2599    memset(&hint, 0, sizeof (hint));
2600    hint.ai_flags = AI_CANONNAME;
2601 
2602    r = getaddrinfo(buf, NULL, &hint, &res);
2603    if (r == EAI_AGAIN)
2604      {
2605 	slrn_sleep (2);
2606 	r = getaddrinfo(buf, NULL, &hint, &res);
2607      }
2608 
2609    if ((r == 0) && res && res->ai_canonname)
2610      {
2611 	char *ret = slrn_safe_strmalloc (res->ai_canonname);
2612 	freeaddrinfo(res);
2613 	return ret;
2614      }
2615 #else
2616    host_entry = gethostbyname (buf);
2617 
2618 # if defined(TRY_AGAIN) && !defined(MULTINET)
2619    if ((host_entry == NULL) && (h_errno == TRY_AGAIN))
2620      {
2621 	slrn_sleep (2);
2622 	host_entry = gethostbyname (buf);
2623      }
2624 # endif
2625 
2626    if ((host_entry != NULL)
2627        && (host_entry->h_name != NULL)
2628        && (host_entry->h_name[0] != 0))
2629      {
2630 	char **aliases;
2631 
2632 	if ((0 == slrn_is_fqdn ((char *)host_entry->h_name))
2633 	    && (NULL !=	(aliases = host_entry->h_aliases)))
2634 	  {
2635 	     while (*aliases != NULL)
2636 	       {
2637 		  if (slrn_is_fqdn (*aliases))
2638 		    return slrn_safe_strmalloc (*aliases);
2639 		  aliases++;
2640 	       }
2641 	  }
2642 
2643 	return slrn_safe_strmalloc ((char *)host_entry->h_name);
2644      }
2645 #endif
2646 
2647    return slrn_safe_strmalloc (buf);
2648 }
2649 
2650 #ifdef OUR_HOSTNAME
2651 /* Returns a pointer to a statically allocated area */
get_host_from_filename(char * file)2652 static char *get_host_from_filename (char *file)
2653 {
2654    FILE *fp;
2655    char *host;
2656    static char line[MAX_HOST_NAME_LEN + 1];
2657 
2658    if (NULL == (fp = fopen (file, "r")))
2659      return NULL;
2660 
2661    host = NULL;
2662    if (NULL != fgets (line, sizeof (line), fp))
2663      {
2664 	slrn_trim_string (line);
2665 	if (slrn_is_fqdn (line))
2666 	  host = line;
2667      }
2668 
2669    fclose(fp);
2670    return host;
2671 }
2672 #endif				       /* OUR_HOSTNAME */
2673 
slrn_get_user_info(void)2674 void slrn_get_user_info (void) /*{{{*/
2675 {
2676    char *name, *host, *host1, *org;
2677 #ifdef OUR_ORGANIZATION
2678    char *our_org = OUR_ORGANIZATION;
2679 #endif
2680 #ifdef HAS_PASSWORD_CODE
2681    struct passwd *pw;
2682 #endif
2683 
2684    /* Fill in what is assumed to be non-NULL by rest of program. */
2685 
2686    /* no-c-format tells gettext that the following strings do not
2687     * need to be checked as if they were passed to printf. */
2688 
2689    Slrn_User_Info.followup_string =   /* xgettext:no-c-format */
2690      slrn_safe_strmalloc (_("On %D, %r <%f> wrote:"));
2691    Slrn_User_Info.reply_string =      /* xgettext:no-c-format */
2692      slrn_safe_strmalloc (_("In %n, you wrote:"));
2693    Slrn_User_Info.followupto_string = /* xgettext:no-c-format */
2694      slrn_safe_strmalloc (_("[\"Followup-To:\" header set to %n.]"));
2695 
2696    Slrn_CC_Post_Message =         /* xgettext:no-c-format */
2697      slrn_safe_strmalloc (_("[This message has also been posted to %n.]"));
2698 
2699    /* Now get default values for rest. */
2700    host = get_hostname ();
2701    if (host != NULL)
2702      {
2703 	if (slrn_is_fqdn (host))
2704 	  Slrn_User_Info.posting_host = slrn_safe_strmalloc (host);
2705      }
2706 
2707 #if ! SLRN_HAS_STRICT_FROM
2708    /* Allow user chance to specify another hostname.  However, it will not
2709     * affect the posting host.
2710     */
2711    if ((NULL != (host1 = getenv ("HOSTNAME")))
2712        && (0 == slrn_is_fqdn (host1)))
2713 #endif
2714      host1 = NULL;
2715 
2716    /* If a value was compiled in, use it. */
2717 #ifdef OUR_HOSTNAME
2718    if (host1 == NULL)
2719      {
2720 	host1 = OUR_HOSTNAME;
2721 	/* If the value is a file, read the file for the FQDN */
2722 	if (slrn_is_absolute_path (host1))
2723 	  host1 = get_host_from_filename (OUR_HOSTNAME);
2724      }
2725 #endif
2726 
2727    if ((host1 != NULL)
2728        && slrn_is_fqdn (host1))
2729      {
2730 	slrn_free (host);
2731 	host = slrn_safe_strmalloc (host1);
2732      }
2733 
2734    /* Finally!! */
2735    Slrn_User_Info.hostname = host;
2736 
2737 #ifdef VMS
2738    name = slrn_vms_getlogin();
2739 #else
2740    name = NULL;
2741 # ifdef HAS_PASSWORD_CODE
2742    /* I cannot use getlogin under Unix because some implementations
2743     * truncate the username to 8 characters.  Besides, I suspect that
2744     * it is equivalent to the following line.
2745     *
2746     * Also it is not clear if the valued returned by getpwuid is malloced.
2747     * The man page indicates that it _may_ point to a static area.  Valgrind
2748     * reports a leak.  Sigh.
2749     */
2750    pw = getpwuid (getuid ());
2751    if (pw != NULL)
2752      name = pw->pw_name;
2753 # endif
2754 #endif
2755 
2756    if (((name == NULL) || (*name == 0))
2757 #if ! SLRN_HAS_STRICT_FROM
2758        && ((name = getenv("USER")) == NULL)
2759        && ((name = getenv("LOGNAME")) == NULL)
2760 #endif
2761        )
2762      name = "";
2763 
2764    Slrn_User_Info.username = slrn_safe_strmalloc (name);
2765    Slrn_User_Info.login_name = slrn_safe_strmalloc (name);
2766 
2767    if ((Slrn_User_Info.replyto = getenv ("REPLYTO")) == NULL)
2768      Slrn_User_Info.replyto = "";
2769    Slrn_User_Info.replyto = slrn_safe_strmalloc (Slrn_User_Info.replyto);
2770 
2771 #ifdef VMS
2772    Slrn_User_Info.realname = slrn_vms_fix_fullname(slrn_vms_get_uaf_fullname());
2773 #else
2774 # if ! SLRN_HAS_STRICT_FROM
2775    Slrn_User_Info.realname = getenv ("NAME");
2776 # else
2777    Slrn_User_Info.realname = NULL;
2778 # endif
2779    if ((Slrn_User_Info.realname == NULL)
2780 # ifdef HAS_PASSWORD_CODE
2781        && ((pw == NULL) || ((Slrn_User_Info.realname = pw->pw_gecos) == NULL))
2782 # endif
2783        )
2784      {
2785 	Slrn_User_Info.realname = "";
2786      }
2787 #endif
2788 
2789    Slrn_User_Info.realname = slrn_safe_strmalloc (Slrn_User_Info.realname);
2790 
2791    /* truncate at character used to delineate extra gecos fields */
2792    name = Slrn_User_Info.realname;
2793    while (*name && (*name != ',')) name++;
2794    *name = 0;
2795 
2796    org = getenv ("ORGANIZATION");
2797 #ifdef OUR_ORGANIZATION
2798    if (org == NULL) org = our_org;
2799 #endif
2800    if (org != NULL)
2801      {
2802 	/* Check to see if this is an organization file. */
2803 	char orgbuf[512];
2804 	if (slrn_is_absolute_path (org))
2805 	  {
2806 	     FILE *fporg;
2807 	     if (NULL != (fporg = fopen (org, "r")))
2808 	       {
2809 		  if (NULL != fgets (orgbuf, sizeof (orgbuf) - 1, fporg))
2810 		    {
2811 		       unsigned int orglen = strlen (orgbuf);
2812 		       if (orglen && (orgbuf[orglen - 1] == '\n'))
2813 			 orgbuf[orglen - 1] = 0;
2814 		       org = orgbuf;
2815 		    }
2816 		  slrn_fclose (fporg);
2817 	       }
2818 #ifdef OUR_ORGANIZATION
2819 	     else if (our_org == org)
2820 	       org = NULL;
2821 #endif
2822 	  }
2823 	if (org != NULL)
2824 	  Slrn_User_Info.org = slrn_safe_strmalloc (org);
2825      }
2826 
2827    Slrn_User_Info.signature = slrn_safe_strmalloc (SLRN_SIGNATURE_FILE);
2828 
2829 #if SLRN_HAS_CANLOCK
2830    Slrn_User_Info.cancelsecret = slrn_safe_strmalloc ("");
2831 #endif
2832 #ifdef SLRN_SENDMAIL_COMMAND
2833    Slrn_SendMail_Command = slrn_safe_strmalloc (SLRN_SENDMAIL_COMMAND);
2834 #endif
2835 }
2836 
2837 /*}}}*/
2838 
2839 #define IS_ATEXT(x) (((x) > 0x20) && ((x) < 0x7f) && \
2840 			(NULL == slrn_strbyte ("\"(),.:;<>@[\\]", (x))))
is_dot_atom(char * str)2841 static int is_dot_atom (char *str)
2842 {
2843    char ch = *str++;
2844    if (!IS_ATEXT (ch)) return 0;
2845    while ((ch = *str++))
2846      {
2847 	if (ch == '.') ch = *str++;
2848 	if (!IS_ATEXT (ch)) return 0;
2849      }
2850    return 1;
2851 }
2852 
make_escaped_string(char * src,int is_comment)2853 static char *make_escaped_string (char *src, int is_comment)
2854 {
2855    unsigned int len, dlen;
2856    char ch, *p, *pmax;
2857    char *dest;
2858 
2859    if ((src == NULL) || (*src == 0)) return NULL;
2860 
2861    len = strlen (src);
2862    p = src;
2863    pmax = p + len;
2864    dlen = 0;
2865 
2866    while (p < pmax)
2867      {
2868 	switch (*p++)
2869 	  {
2870 	   case '\\':
2871 	     dlen++;
2872 	     break;
2873 
2874 	   case '"':
2875 	     if (is_comment == 0)
2876 	       dlen++;
2877 	     break;
2878 
2879 	   case '(': case ')':
2880 	     if (is_comment)
2881 	       dlen++;
2882 	     break;
2883 	  }
2884      }
2885 
2886    dest = slrn_malloc (len + dlen + 3, 0, 1);
2887    if (dest == NULL)
2888      return NULL;
2889 
2890    p = dest;
2891    if (is_comment)
2892      *p++ = '(';
2893    else
2894      *p++ = '"';
2895 
2896    while (1)
2897      {
2898 	ch = *src++;
2899 	switch (ch)
2900 	  {
2901 	   case 0:
2902 	     if (is_comment)
2903 	       *p++ = ')';
2904 	     else
2905 	       *p++ = '"';
2906 	     *p = 0;
2907 	     return dest;
2908 
2909 	   case '\\':
2910 	     *p++ = '\\';
2911 	     break;
2912 
2913 	   case '"':
2914 	     if (is_comment == 0)
2915 	       *p++ = '\\';
2916 	     break;
2917 
2918 	   case '(': case ')':
2919 	     if (is_comment)
2920 	       *p++ = '\\';
2921 	     break;
2922 	  }
2923 
2924 	*p++ = ch;
2925      }
2926 }
2927 
2928 /* returns NULL upon failure */
make_localpart(char * username)2929 static char *make_localpart (char *username)
2930 {
2931    if ((username == NULL) || (*username == 0) )
2932      return NULL;
2933    if (is_dot_atom (username))
2934      {
2935 	return slrn_safe_strmalloc(username);
2936      }
2937    return make_escaped_string (username, 0);
2938 }
2939 
make_realname(char * realname)2940 static char *make_realname (char *realname)
2941 {
2942    char *p, *pmax;
2943    int flags;
2944 #define REALNAME_HAS_8BIT	0x1
2945 #define REALNAME_HAS_CTRL	0x2
2946 #define REALNAME_HAS_SPEC	0x4
2947 
2948    if ((realname == NULL) || (*realname == 0) )
2949      return NULL;
2950    /*
2951     * If realname consists only of atom-text, or 8-bit, then it may
2952     * be encoded as an atom.  If it contains specials, it must be encoded
2953     * as a quoted-string, or a comment.  It illegal for an 8 bit character
2954     * to be in a quoted string since the text of a quoted string cannot
2955     * be mime-encoded.
2956     */
2957    p = realname;
2958    pmax = p + strlen (realname);
2959    flags = 0;
2960    while (p < pmax)
2961      {
2962 	unsigned char ch = (unsigned char) *p++;
2963 	if (ch & 0x80)
2964 	  {
2965 	     flags |= REALNAME_HAS_8BIT;
2966 	     continue;
2967 	  }
2968 	switch (ch)
2969 	  {
2970 	   case '\r':
2971 	   case '\n':
2972 	   case 127:
2973 	     return NULL;
2974 
2975 	   case '(': case ')': case '<': case '>': case '[':
2976 	   case ']': case ':': case '@': case '\\': case ',':
2977 	   case '.':
2978 	   case '"':		       /* not a special, but treated like one here */
2979 	     flags |= REALNAME_HAS_SPEC;
2980 	     break;
2981 
2982 	   case ' ': case '\t':
2983 	     break;
2984 
2985 	   default:
2986 	     if (ch < 32)
2987 	       {
2988 		  flags |= REALNAME_HAS_CTRL;
2989 		  break;
2990 	       }
2991 	     break;
2992 	  }
2993      }
2994 
2995    if ((flags == 0) || (flags == REALNAME_HAS_8BIT))
2996      return slrn_strmalloc (realname, 1);
2997 
2998    if (flags & REALNAME_HAS_8BIT)
2999      return make_escaped_string (realname, 1);
3000 
3001    return make_escaped_string (realname, 0);
3002 }
3003 
3004 /* This function returns a malloced string of the form "From: value" */
slrn_make_from_header(void)3005 char *slrn_make_from_header (void)
3006 {
3007    static char *buf;
3008    char *localpart, *realname, *msg;
3009 
3010 #if ! SLRN_HAS_STRICT_FROM
3011    if ((1 == slrn_run_hooks (HOOK_MAKE_FROM_STRING, 0))
3012        && (0 == SLang_pop_slstring (&msg)))
3013      {
3014 	if (*msg != 0)
3015 	  {
3016 	     char *prefix = "From: ";
3017 	     if (0 == strncmp (msg, "From: ", 6))
3018 	       prefix = "";
3019 	     buf = slrn_strjoin (prefix, msg, "");
3020 	     SLang_free_slstring (msg);
3021 	     return buf;
3022 	  }
3023 	SLang_free_slstring (msg);
3024 	/* Drop through to default */
3025      }
3026 #endif
3027    msg = NULL;
3028 
3029    if (( localpart = make_localpart (Slrn_User_Info.username)) == NULL)
3030      {
3031 	slrn_error (_("Cannot generate \"From:\" line without a valid username."));
3032 	return NULL;
3033      }
3034    if ((NULL == Slrn_User_Info.hostname) ||
3035 	    (0 == *Slrn_User_Info.hostname))
3036      /* Note: we currently do not check whether hostname is valid */
3037      {
3038 	slrn_error (_("Cannot generate \"From:\" line without a hostname."));
3039 	return NULL;
3040      }
3041 
3042    if (( realname = make_realname (Slrn_User_Info.realname))  != NULL)
3043      {
3044 	buf=slrn_safe_malloc(6 + strlen(realname) +2 + strlen(localpart) + 1
3045 		  + strlen(Slrn_User_Info.hostname)+2);
3046 	sprintf (buf, "From: %s <%s@%s>", realname, localpart, /* safe */
3047 		 Slrn_User_Info.hostname);
3048 	slrn_free(realname);
3049      }
3050    else
3051      {
3052 	buf=slrn_safe_malloc(6 + strlen(localpart) + 1
3053 		  + strlen(Slrn_User_Info.hostname)+2);
3054 	sprintf (buf, "From: %s@%s", localpart, Slrn_User_Info.hostname); /* safe */
3055      }
3056 
3057    slrn_free(localpart);
3058    return buf;
3059 }
3060 
3061