* Do not define _BSD_SOURCE but define _DEFAULT_SOURCE instead.
[alpine.git] / alpine / alpine.c
blobe96ce2bb2a12f6df3cd47f5dd38f99d5f2997633
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: alpine.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2016 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "headers.h"
21 #include "../pith/newmail.h"
22 #include "../pith/init.h"
23 #include "../pith/sort.h"
24 #include "../pith/options.h"
25 #include "../pith/list.h"
26 #include "../pith/conf.h"
28 #include "osdep/debuging.h"
29 #include "osdep/termout.gen.h"
30 #include "osdep/chnge_pw.h"
32 #include "alpine.h"
33 #include "mailindx.h"
34 #include "mailcmd.h"
35 #include "addrbook.h"
36 #include "reply.h"
37 #include "arg.h"
38 #include "keymenu.h"
39 #include "status.h"
40 #include "context.h"
41 #include "mailview.h"
42 #include "imap.h"
43 #include "radio.h"
44 #include "folder.h"
45 #include "send.h"
46 #include "help.h"
47 #include "titlebar.h"
48 #include "takeaddr.h"
49 #include "dispfilt.h"
50 #include "init.h"
51 #include "remote.h"
52 #include "pattern.h"
53 #include "setup.h"
54 #include "newuser.h"
55 #include "adrbkcmd.h"
56 #include "signal.h"
57 #include "kblock.h"
58 #include "ldapconf.h"
59 #include "roleconf.h"
60 #include "colorconf.h"
61 #include "print.h"
62 #include "after.h"
63 #include "smime.h"
64 #include "newmail.h"
65 #ifndef _WINDOWS
66 #include "../pico/osdep/raw.h" /* for STD*_FD */
67 #endif
70 #define PIPED_FD 5 /* Some innocuous desc */
73 /* look for my_timer_period in pico directory for an explanation */
74 int my_timer_period = ((IDLE_TIMEOUT + 1)*1000);
76 /* byte count used by our gets routine to keep track */
77 static unsigned long gets_bytes;
81 * Internal prototypes
83 void convert_args_to_utf8(struct pine *, ARGDATA_S *);
84 void preopen_stayopen_folders(void);
85 int read_stdin_char(char *);
86 void main_redrawer(void);
87 void show_main_screen(struct pine *, int, OtherMenu, struct key_menu *, int, Pos *);
88 void do_menu(int, Pos *, struct key_menu *);
89 int choose_setup_cmd(int, MSGNO_S *, SCROLL_S *);
90 int setup_menu(struct pine *);
91 void do_setup_task(int);
92 void queue_init_errors(struct pine *);
93 void process_init_cmds(struct pine *, char **);
94 void goodnight_gracey(struct pine *, int);
95 void pine_read_progress(GETS_DATA *, unsigned long);
96 int remote_pinerc_failure(void);
97 void dump_supported_options(void);
98 int prune_folders_ok(void);
99 #ifdef WIN32
100 char *pine_user_callback(void);
101 #endif
102 #ifdef _WINDOWS
103 int fkey_mode_callback(int, long);
104 void imap_telemetry_on(void);
105 void imap_telemetry_off(void);
106 char *pcpine_help_main(char *);
107 int pcpine_main_cursor(int, long);
108 #define main app_main
109 #endif
112 typedef struct setup_return_val {
113 int cmd;
114 int exc;
115 }SRV_S;
119 * strlen of longest label from keymenu, of labels corresponding to
120 * commands in the middle of the screen. 9 is length of ListFldrs
122 #define LONGEST_LABEL 9 /* length of longest label from keymenu */
124 #define EDIT_EXCEPTION (0x100)
127 static int in_panic = 0;
130 /*----------------------------------------------------------------------
131 main routine -- entry point
133 Args: argv, argc -- The command line arguments
136 Initialize pine, parse arguments and so on
138 If there is a user address on the command line go into send mode and exit,
139 otherwise loop executing the various screens in Alpine.
141 NOTE: The Windows port def's this to "app_main"
142 ----*/
145 main(int argc, char **argv)
147 ARGDATA_S args;
148 int rv;
149 long rvl;
150 struct pine *pine_state;
151 gf_io_t stdin_getc = NULL;
152 char *args_for_debug = NULL, *init_pinerc_debugging = NULL;
154 /*----------------------------------------------------------------------
155 Set up buffering and some data structures
156 ----------------------------------------------------------------------*/
158 pine_state = new_pine_struct();
159 pine_state->id = fs_get(sizeof(IDLIST));
160 pine_state->id->name = cpystr("name");
161 pine_state->id->value = cpystr(PACKAGE_NAME);
162 pine_state->id->next = fs_get(sizeof(IDLIST));
163 pine_state->id->next->name = cpystr("version");
164 pine_state->id->next->value = cpystr(PACKAGE_VERSION);
165 pine_state->id->next->next = NULL;
166 mail_parameters(NULL, SET_IDPARAMS, (void *) pine_state->id);
167 ps_global = pine_state;
170 * fill in optional pith-offered behavior hooks
172 pith_opt_read_msg_prompt = read_msg_prompt;
173 pith_opt_paint_index_hline = paint_index_hline;
174 pith_opt_rfc2369_editorial = rfc2369_editorial;
175 pith_opt_condense_thread_cue = condensed_thread_cue;
176 pith_opt_truncate_sfstr = truncate_subj_and_from_strings;
177 pith_opt_save_and_restore = save_and_restore;
178 pith_opt_newmail_announce = newmail_status_message;
179 pith_opt_newmail_check_cue = newmail_check_cue;
180 pith_opt_checkpoint_cue = newmail_check_point_cue;
181 pith_opt_icon_text = icon_text;
182 pith_opt_rd_metadata_name = rd_metadata_name;
183 pith_opt_remote_pinerc_failure = remote_pinerc_failure;
184 pith_opt_reopen_folder = ask_mailbox_reopen;
185 pith_opt_expunge_prompt = expunge_prompt;
186 pith_opt_begin_closing = expunge_and_close_begins;
187 pith_opt_replyto_prompt = reply_using_replyto_query;
188 pith_opt_reply_to_all_prompt = reply_to_all_query;
189 pith_opt_save_create_prompt = create_for_save_prompt;
190 pith_opt_daemon_confirm = confirm_daemon_send;
191 pith_opt_save_size_changed_prompt = save_size_changed_prompt;
192 pith_opt_save_index_state = setup_index_state;
193 pith_opt_filter_pattern_cmd = pattern_filter_command;
194 pith_opt_get_signature_file = get_signature_file;
195 pith_opt_pretty_var_name = pretty_var_name;
196 pith_opt_pretty_feature_name = pretty_feature_name;
197 pith_opt_closing_stream = titlebar_stream_closing;
198 pith_opt_current_expunged = mm_expunged_current;
199 #ifdef SMIME
200 pith_opt_smime_get_passphrase = smime_get_passphrase;
201 pith_smime_import_certificate = smime_import_certificate;
202 pith_smime_enter_password = alpine_get_password;
203 pith_smime_confirm_save = alpine_smime_confirm_save;
204 #endif
205 #ifdef ENABLE_LDAP
206 pith_opt_save_ldap_entry = save_ldap_entry;
207 #endif
209 status_message_lock_init();
210 inverse_itokens();
212 #if HAVE_SRANDOM
214 * Seed the random number generator with the date & pid. Random
215 * numbers are used for new mail notification and bug report id's
217 srandom(getpid() + time(0));
218 #endif
220 /* need home directory early */
221 get_user_info(&ps_global->ui);
223 if(!(pine_state->home_dir = our_getenv("HOME")))
224 pine_state->home_dir = cpystr(ps_global->ui.homedir);
226 #ifdef _WINDOWS
228 char *p;
230 /* normalize path delimiters */
231 for(p = pine_state->home_dir; p = strchr(p, '/'); p++)
232 *p='\\';
234 #endif /* _WINDOWS */
236 #ifdef DEBUG
237 { size_t len = 0;
238 int i;
239 char *p;
240 char *no_args = " <no args>";
242 for(i = 0; i < argc; i++)
243 len += (strlen(argv[i] ? argv[i] : "")+3);
245 if(argc == 1)
246 len += strlen(no_args);
248 p = args_for_debug = (char *)fs_get((len+2) * sizeof(char));
249 *p++ = '\n';
250 *p = '\0';
252 for(i = 0; i < argc; i++){
253 snprintf(p, len+2-(p-args_for_debug), "%s\"%s\"", i ? " " : "", argv[i] ? argv[i] : "");
254 args_for_debug[len+2-1] = '\0';
255 p += strlen(p);
258 if(argc == 1){
259 strncat(args_for_debug, no_args, len+2-strlen(args_for_debug)-1);
260 args_for_debug[len+2-1] = '\0';
263 #endif
265 /*----------------------------------------------------------------------
266 Parse arguments and initialize debugging
267 ----------------------------------------------------------------------*/
268 pine_args(pine_state, argc, argv, &args);
270 #ifndef _WINDOWS
271 if(!isatty(0)){
273 * monkey with descriptors so our normal tty i/o routines don't
274 * choke...
276 dup2(STDIN_FD, PIPED_FD); /* redirected stdin to new desc */
277 dup2(STDERR_FD, STDIN_FD); /* rebind stdin to the tty */
278 stdin_getc = read_stdin_char;
279 if(stdin_getc && args.action == aaURL){
280 display_args_err(
281 "Cannot read stdin when using -url\nFor mailto URLs, use \'body=\' instead",
282 NULL, 1);
283 args_help();
284 exit(-1);
288 #else /* _WINDOWS */
290 * We now have enough information to do some of the basic registry settings.
292 if(ps_global->update_registry != UREG_NEVER_SET){
293 mswin_reg(MSWR_OP_SET
294 | ((ps_global->update_registry == UREG_ALWAYS_SET)
295 ? MSWR_OP_FORCE : 0),
296 MSWR_PINE_DIR, ps_global->pine_dir, (size_t)NULL);
297 mswin_reg(MSWR_OP_SET
298 | ((ps_global->update_registry == UREG_ALWAYS_SET)
299 ? MSWR_OP_FORCE : 0),
300 MSWR_PINE_EXE, ps_global->pine_name, (size_t)NULL);
303 #endif /* _WINDOWS */
305 if(ps_global->convert_sigs &&
306 (!ps_global->pinerc || !ps_global->pinerc[0])){
307 fprintf(stderr, "Use -p <pinerc> with -convert_sigs\n");
308 exit(-1);
311 /* Windows has its own functions to determine width of a character
312 * in the screen, so this is not necessary to do in Window, and
313 * using pith_ucs4width does not produce the correct result
315 #ifndef _WINDOWS
316 mail_parameters(NULL, SET_UCS4WIDTH, (void *) pith_ucs4width);
317 #endif /* _WINDOWS */
318 mail_parameters(NULL, SET_QUOTA, (void *) pine_parse_quota);
319 /* set some default timeouts in case pinerc is remote */
320 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)30);
321 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)15);
322 mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
323 /* could be TO_BAIL_THRESHOLD, 15 seems more appropriate for now */
324 pine_state->tcp_query_timeout = 15;
326 mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened);
327 mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
328 mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt);
329 #ifdef SMIME
330 mail_parameters(NULL, SET_FREEBODYSPAREP, (void *) free_smime_body_sparep);
331 #endif
333 init_pinerc(pine_state, &init_pinerc_debugging);
335 #ifdef DEBUG
336 /* Since this is specific debugging we don't mind if the
337 ifdef is the type of system.
339 #if defined(HAVE_SMALLOC) || defined(NXT)
340 if(ps_global->debug_malloc)
341 malloc_debug(ps_global->debug_malloc);
342 #endif
343 #ifdef CSRIMALLOC
344 if(ps_global->debug_malloc)
345 mal_debug(ps_global->debug_malloc);
346 #endif
348 if(!ps_global->convert_sigs
349 #ifdef _WINDOWS
350 && !ps_global->install_flag
351 #endif /* _WINDOWS */
353 init_debug();
355 if(args_for_debug){
356 dprint((0, " %s (PID=%ld)\n\n", args_for_debug,
357 (long) getpid()));
358 fs_give((void **)&args_for_debug);
362 char *env_to_free;
363 if((env_to_free = our_getenv("HOME")) != NULL){
364 dprint((2, "Setting home dir from $HOME: \"%s\"\n",
365 env_to_free));
366 fs_give((void **)&env_to_free);
368 else{
369 dprint((2, "Setting home dir: \"%s\"\n",
370 pine_state->home_dir ? pine_state->home_dir : "<?>"));
374 /* Watch out. Sensitive information in debug file. */
375 if(ps_global->debug_imap > 4)
376 mail_parameters(NULL, SET_DEBUGSENSITIVE, (void *) TRUE);
378 #ifndef DEBUGJOURNAL
379 if(ps_global->debug_tcp)
380 #endif
381 mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
383 #ifdef _WINDOWS
384 mswin_setdebug(debug, debugfile);
385 mswin_setdebugoncallback (imap_telemetry_on);
386 mswin_setdebugoffcallback (imap_telemetry_off);
387 mswin_enableimaptelemetry(ps_global->debug_imap != 0);
388 #endif
389 #endif /* DEBUG */
391 #ifdef _WINDOWS
392 mswin_setsortcallback(index_sort_callback);
393 mswin_setflagcallback(flag_callback);
394 mswin_sethdrmodecallback(header_mode_callback);
395 mswin_setselectedcallback(any_selected_callback);
396 mswin_setzoomodecallback(zoom_mode_callback);
397 mswin_setfkeymodecallback(fkey_mode_callback);
398 #endif
400 /*------- Set up c-client drivers -------*/
401 #include "../c-client/linkage.c"
403 /*------- ... then tune the drivers just installed -------*/
404 #ifdef _WINDOWS
405 if(_tgetenv(TEXT("HOME")))
406 mail_parameters(NULL, SET_HOMEDIR, (void *) pine_state->home_dir);
408 mail_parameters(NULL, SET_USERPROMPT, (void *) pine_user_callback);
411 * Sniff the environment for timezone offset. We need to do this
412 * here since Windows needs help figuring out UTC, and will adjust
413 * what time() returns based on TZ. THIS WILL SCREW US because
414 * we use time() differences to manage status messages. So, if
415 * rfc822_date, which calls localtime() and thus needs tzset(),
416 * is called while a status message is displayed, it's possible
417 * for time() to return a time *before* what we remember as the
418 * time we put the status message on the display. Sheesh.
420 tzset();
421 #else /* !_WINDOWS */
423 * We used to let c-client do this for us automatically, but it declines
424 * to do so for root. This forces c-client to establish an environment,
425 * even if the uid is 0.
427 env_init(ps_global->ui.login, ps_global->ui.homedir);
430 * Install callback to let us know the progress of network reads...
432 (void) mail_parameters(NULL, SET_READPROGRESS, (void *)pine_read_progress);
433 #endif /* !_WINDOWS */
436 * Install callback to handle certificate validation failures,
437 * allowing the user to continue if they wish.
439 mail_parameters(NULL, SET_SSLCERTIFICATEQUERY, (void *) pine_sslcertquery);
440 mail_parameters(NULL, SET_SSLFAILURE, (void *) pine_sslfailure);
442 if(init_pinerc_debugging){
443 dprint((2, init_pinerc_debugging));
444 fs_give((void **)&init_pinerc_debugging);
448 * Initial allocation of array of stream pool pointers.
449 * We do this before init_vars so that we can re-use streams used for
450 * remote config files. These sizes may get changed later.
452 ps_global->s_pool.max_remstream = 2;
453 dprint((9,
454 "Setting initial max_remstream to %d for remote config re-use\n",
455 ps_global->s_pool.max_remstream));
457 init_vars(pine_state, process_init_cmds);
459 #ifdef SMIME
460 if(F_ON(F_DONT_DO_SMIME, ps_global))
461 smime_deinit();
462 #endif /* SMIME */
464 #ifdef ENABLE_NLS
466 * LC_CTYPE is already set from the set_collation call above.
468 * We can't use gettext calls before we do this stuff so it doesn't
469 * help to translate strings that come before this in the program.
470 * Maybe we could rearrange things to accomodate that.
472 setlocale(LC_MESSAGES, "");
473 bindtextdomain(PACKAGE, LOCALEDIR);
474 bind_textdomain_codeset(PACKAGE, "UTF-8");
475 textdomain(PACKAGE);
476 #endif /* ENABLE_NLS */
478 convert_args_to_utf8(pine_state, &args);
480 if(args.action == aaFolder){
481 pine_state->beginning_of_month = first_run_of_month();
482 pine_state->beginning_of_year = first_run_of_year();
485 /* Set up optional for user-defined display filtering */
486 pine_state->tools.display_filter = dfilter;
487 pine_state->tools.display_filter_trigger = dfilter_trigger;
489 #ifdef _WINDOWS
490 if(ps_global->install_flag){
491 init_install_get_vars();
493 if(ps_global->prc)
494 free_pinerc_s(&ps_global->prc);
496 exit(0);
498 #endif
500 if(ps_global->convert_sigs){
501 if(convert_sigs_to_literal(ps_global, 0) == -1){
502 /* TRANSLATORS: sigs refers to signatures, which the user was trying to convert */
503 fprintf(stderr, _("trouble converting sigs\n"));
504 exit(-1);
507 if(ps_global->prc){
508 if(ps_global->prc->outstanding_pinerc_changes)
509 write_pinerc(ps_global, Main, WRP_NONE);
511 free_pinerc_s(&pine_state->prc);
514 exit(0);
518 * Set up a c-client read timeout and timeout handler. In general,
519 * it shouldn't happen, but a server crash or dead link can cause
520 * pine to appear wedged if we don't set this up...
522 rv = 30;
523 if(pine_state->VAR_TCPOPENTIMEO)
524 (void)SVAR_TCP_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF);
525 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)rv);
527 rv = 15;
528 if(pine_state->VAR_TCPREADWARNTIMEO)
529 (void)SVAR_TCP_READWARN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF);
530 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)rv);
532 rv = 0;
533 if(pine_state->VAR_TCPWRITEWARNTIMEO){
534 if(!SVAR_TCP_WRITEWARN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
535 if(rv == 0 || rv > 4) /* making sure */
536 mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long)rv);
539 mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
541 rv = 15;
542 if(pine_state->VAR_RSHOPENTIMEO){
543 if(!SVAR_RSH_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
544 if(rv == 0 || rv > 4) /* making sure */
545 mail_parameters(NULL, SET_RSHTIMEOUT, (void *)(long)rv);
548 rv = 15;
549 if(pine_state->VAR_SSHOPENTIMEO){
550 if(!SVAR_SSH_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
551 if(rv == 0 || rv > 4) /* making sure */
552 mail_parameters(NULL, SET_SSHTIMEOUT, (void *)(long)rv);
555 rvl = 60L;
556 if(pine_state->VAR_MAILDROPCHECK){
557 if(!SVAR_MAILDCHK(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF)){
558 if(rvl == 0L)
559 rvl = (60L * 60L * 24L * 100L); /* 100 days */
561 if(rvl >= 60L) /* making sure */
562 mail_parameters(NULL, SET_SNARFINTERVAL, (void *) rvl);
567 * Lookups of long login names which don't exist are very slow in aix.
568 * This would normally get set in system-wide config if not needed.
570 if(F_ON(F_DISABLE_SHARED_NAMESPACES, ps_global))
571 mail_parameters(NULL, SET_DISABLEAUTOSHAREDNS, (void *) TRUE);
573 if(F_ON(F_HIDE_NNTP_PATH, ps_global))
574 mail_parameters(NULL, SET_NNTPHIDEPATH, (void *) TRUE);
576 if(F_ON(F_MAILDROPS_PRESERVE_STATE, ps_global))
577 mail_parameters(NULL, SET_SNARFPRESERVE, (void *) TRUE);
579 rvl = 0L;
580 if(pine_state->VAR_NNTPRANGE){
581 if(!SVAR_NNTPRANGE(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF))
582 if(rvl > 0L)
583 mail_parameters(NULL, SET_NNTPRANGE, (void *) rvl);
587 * Tell c-client not to be so aggressive about uid mappings
589 mail_parameters(NULL, SET_UIDLOOKAHEAD, (void *) 20);
592 * Setup referral handling
594 mail_parameters(NULL, SET_IMAPREFERRAL, (void *) imap_referral);
595 mail_parameters(NULL, SET_MAILPROXYCOPY, (void *) imap_proxycopy);
598 * Setup multiple newsrc transition
600 mail_parameters(NULL, SET_NEWSRCQUERY, (void *) pine_newsrcquery);
603 * Disable some drivers if requested.
605 if(ps_global->VAR_DISABLE_DRIVERS &&
606 ps_global->VAR_DISABLE_DRIVERS[0] &&
607 ps_global->VAR_DISABLE_DRIVERS[0][0]){
608 char **t;
610 for(t = ps_global->VAR_DISABLE_DRIVERS; t[0] && t[0][0]; t++)
611 if(mail_parameters(NULL, DISABLE_DRIVER, (void *)(*t))){
612 dprint((2, "Disabled mail driver \"%s\"\n", *t));
614 else{
615 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
616 _("Failed to disable mail driver \"%s\": name not found"),
617 *t);
618 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
619 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
624 * Disable some authenticators if requested.
626 if(ps_global->VAR_DISABLE_AUTHS &&
627 ps_global->VAR_DISABLE_AUTHS[0] &&
628 ps_global->VAR_DISABLE_AUTHS[0][0]){
629 char **t;
631 for(t = ps_global->VAR_DISABLE_AUTHS; t[0] && t[0][0]; t++)
632 if(mail_parameters(NULL, DISABLE_AUTHENTICATOR, (void *)(*t))){
633 dprint((2,"Disabled SASL authenticator \"%s\"\n", *t));
635 else{
636 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
637 _("Failed to disable SASL authenticator \"%s\": name not found"),
638 *t);
639 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
640 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
645 * setup alternative authentication driver preference for IMAP opens
647 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
648 mail_parameters(NULL, SET_IMAPTRYALT, (void *) TRUE);
651 * Install handler to let us know about potential delays
653 (void) mail_parameters(NULL, SET_BLOCKNOTIFY, (void *) pine_block_notify);
655 if(ps_global->dump_supported_options){
656 dump_supported_options();
657 exit(0);
661 * Install extra headers to fetch along with all the other stuff
662 * mail_fetch_structure and mail_fetch_overview requests.
664 calc_extra_hdrs();
665 if(get_extra_hdrs())
666 (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS,
667 (void *) get_extra_hdrs());
669 if(init_username(pine_state) < 0){
670 fprintf(stderr, _("Who are you? (Unable to look up login name)\n"));
671 exit(-1);
674 if(init_userdir(pine_state) < 0)
675 exit(-1);
677 if(init_hostname(pine_state) < 0)
678 exit(-1);
681 * Verify mail dir if we're not in send only mode...
683 if(args.action == aaFolder && init_mail_dir(pine_state) < 0)
684 exit(-1);
686 init_signals();
688 /*--- input side ---*/
689 if(init_tty_driver(pine_state)){
690 #ifndef _WINDOWS /* always succeeds under _WINDOWS */
691 fprintf(stderr, _("Can't access terminal or input is not a terminal. Redirection of\nstandard input is not allowed. For example \"pine < file\" doesn't work.\n%c"), BELL);
692 exit(-1);
693 #endif /* !_WINDOWS */
697 /*--- output side ---*/
698 rv = config_screen(&(pine_state->ttyo));
699 #ifndef _WINDOWS /* always succeeds under _WINDOWS */
700 if(rv){
701 switch(rv){
702 case -1:
703 printf(_("Terminal type (environment variable TERM) not set.\n"));
704 break;
705 case -2:
706 printf(_("Terminal type \"%s\" is unknown.\n"), getenv("TERM"));
707 break;
708 case -3:
709 printf(_("Can't open terminal capabilities database.\n"));
710 break;
711 case -4:
712 printf(_("Your terminal, of type \"%s\", is lacking functions needed to run alpine.\n"), getenv("TERM"));
713 break;
716 printf("\r");
717 end_tty_driver(pine_state);
718 exit(-1);
720 #endif /* !_WINDOWS */
722 if(F_ON(F_BLANK_KEYMENU,ps_global))
723 FOOTER_ROWS(ps_global) = 1;
725 init_screen();
726 init_keyboard(pine_state->orig_use_fkeys);
727 strncpy(pine_state->inbox_name, INBOX_NAME,
728 sizeof(pine_state->inbox_name)-1);
729 init_folders(pine_state); /* digest folder spec's */
731 pine_state->in_init_seq = 0; /* so output (& ClearScreen) show up */
732 pine_state->dont_use_init_cmds = 1; /* don't use up initial_commands yet */
733 ClearScreen();
735 /* initialize titlebar in case we use it */
736 set_titlebar("", NULL, NULL, NULL, NULL, 0, FolderName, 0, 0, NULL);
739 * Prep storage object driver for PicoText
741 so_register_external_driver(pine_pico_get, pine_pico_give, pine_pico_writec, pine_pico_readc,
742 pine_pico_puts, pine_pico_seek, NULL, NULL);
744 #ifdef DEBUG
745 if(ps_global->debug_imap > 4 || debug > 9){
746 q_status_message(SM_ORDER | SM_DING, 5, 9,
747 _("Warning: sensitive authentication data included in debug file"));
748 flush_status_messages(0);
750 #endif
752 if(args.action == aaPrcCopy || args.action == aaAbookCopy){
753 int exit_val = -1;
754 char *err_msg = NULL;
757 * Don't translate these into UTF-8 because we'll be using them
758 * before we translate next time. User should use ascii.
760 if(args.data.copy.local && args.data.copy.remote){
761 switch(args.action){
762 case aaPrcCopy:
763 exit_val = copy_pinerc(args.data.copy.local,
764 args.data.copy.remote, &err_msg);
765 break;
767 case aaAbookCopy:
768 exit_val = copy_abook(args.data.copy.local,
769 args.data.copy.remote, &err_msg);
770 break;
772 default:
773 break;
776 if(err_msg){
777 q_status_message(SM_ORDER | SM_DING, 3, 4, err_msg);
778 fs_give((void **)&err_msg);
780 goodnight_gracey(pine_state, exit_val);
783 if(args.action == aaFolder
784 && (pine_state->first_time_user || pine_state->show_new_version)){
785 pine_state->mangled_header = 1;
786 show_main_screen(pine_state, 0, FirstMenu, &main_keymenu, 0,
787 (Pos *) NULL);
788 new_user_or_version(pine_state);
789 ClearScreen();
792 /* put back in case we need to suppress output */
793 pine_state->in_init_seq = pine_state->save_in_init_seq;
795 /* queue any init errors so they get displayed in a screen below */
796 queue_init_errors(ps_global);
798 /* "Page" the given file? */
799 if(args.action == aaMore){
800 int dice = 1, redir = 0;
802 if(pine_state->in_init_seq){
803 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
804 clear_cursor_pos();
805 if(pine_state->free_initial_cmds)
806 fs_give((void **)&(pine_state->free_initial_cmds));
808 pine_state->initial_cmds = NULL;
811 /*======= Requested that we simply page the given file =======*/
812 if(args.data.file){ /* Open the requested file... */
813 SourceType src;
814 STORE_S *store = NULL;
815 char *decode_error = NULL;
816 char filename[MAILTMPLEN];
818 if(args.data.file[0] == '\0'){
819 HelpType help = NO_HELP;
821 pine_state->mangled_footer = 1;
822 filename[0] = '\0';
823 while(1){
824 int flags = OE_APPEND_CURRENT;
826 rv = optionally_enter(filename, -FOOTER_ROWS(pine_state),
827 0, sizeof(filename),
828 /* TRANSLATORS: file is computer data */
829 _("File to open : "),
830 NULL, help, &flags);
831 if(rv == 3){
832 help = (help == NO_HELP) ? h_no_F_arg : NO_HELP;
833 continue;
836 if(rv != 4)
837 break;
840 if(rv == 1){
841 q_status_message(SM_ORDER, 0, 2, _("Cancelled"));
842 goodnight_gracey(pine_state, -1);
845 if(*filename){
846 removing_trailing_white_space(filename);
847 removing_leading_white_space(filename);
848 if(is_absolute_path(filename))
849 fnexpand(filename, sizeof(filename));
851 args.data.file = filename;
854 if(!*filename){
855 /* TRANSLATORS: file is computer data */
856 q_status_message(SM_ORDER, 0, 2 ,_("No file to open"));
857 goodnight_gracey(pine_state, -1);
861 if(stdin_getc){
862 redir++;
863 src = CharStar;
864 if(isatty(0) && (store = so_get(src, NULL, EDIT_ACCESS))){
865 gf_io_t pc;
867 gf_set_so_writec(&pc, store);
868 gf_filter_init();
869 if((decode_error = gf_pipe(stdin_getc, pc)) != NULL){
870 dice = 0;
871 q_status_message1(SM_ORDER, 3, 4,
872 _("Problem reading standard input: %s"),
873 decode_error);
876 gf_clear_so_writec(store);
878 else
879 dice = 0;
881 else{
882 src = FileStar;
883 strncpy(ps_global->cur_folder, args.data.file,
884 sizeof(ps_global->cur_folder)-1);
885 ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
886 if(!(store = so_get(src, args.data.file, READ_ACCESS|READ_FROM_LOCALE)))
887 dice = 0;
890 if(dice){
891 SCROLL_S sargs;
893 memset(&sargs, 0, sizeof(SCROLL_S));
894 sargs.text.text = so_text(store);
895 sargs.text.src = src;
896 /* TRANSLATORS: file is computer file being read by user */
897 sargs.text.desc = _("file");
898 /* TRANSLATORS: this is in the title bar at top of screen */
899 sargs.bar.title = _("FILE VIEW");
900 sargs.bar.style = FileTextPercent;
901 sargs.keys.menu = &simple_file_keymenu;
902 setbitmap(sargs.keys.bitmap);
903 scrolltool(&sargs);
905 printf("\n\n");
906 so_give(&store);
910 if(!dice){
911 q_status_message2(SM_ORDER, 3, 4,
912 _("Can't display \"%s\": %s"),
913 (redir) ? _("Standard Input")
914 : args.data.file ? args.data.file : "NULL",
915 error_description(errno));
918 goodnight_gracey(pine_state, 0);
920 else if(args.action == aaMail || (stdin_getc && (args.action != aaURL))){
921 /*======= address on command line/send one message mode ============*/
922 char *to = NULL, *error = NULL, *addr = NULL;
923 int len, good_addr = 1;
924 int exit_val = 0;
925 BUILDER_ARG fcc;
927 if(pine_state->in_init_seq){
928 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
929 clear_cursor_pos();
930 if(pine_state->free_initial_cmds)
931 fs_give((void **) &(pine_state->free_initial_cmds));
933 pine_state->initial_cmds = NULL;
936 /*----- Format the To: line with commas for the composer ---*/
937 if(args.data.mail.addrlist){
938 STRLIST_S *p;
940 for(p = args.data.mail.addrlist, len = 0; p; p = p->next)
941 len += strlen(p->name) + 2;
943 to = (char *) fs_get((len + 5) * sizeof(char));
944 for(p = args.data.mail.addrlist, *to = '\0'; p; p = p->next){
945 if(*to){
946 strncat(to, ", ", len+5-strlen(to)-1);
947 to[len+5-1] = '\0';
950 strncat(to, p->name, len+5-strlen(to)-1);
951 to[len+5-1] = '\0';
954 memset((void *)&fcc, 0, sizeof(BUILDER_ARG));
955 dprint((2, "building addr: -->%s<--\n", to ? to : "?"));
956 good_addr = (build_address(to, &addr, &error, &fcc, NULL) >= 0);
957 dprint((2, "mailing to: -->%s<--\n", addr ? addr : "?"));
958 free_strlist(&args.data.mail.addrlist);
960 else
961 memset(&fcc, 0, sizeof(fcc));
963 if(good_addr){
964 compose_mail(addr, fcc.tptr, NULL,
965 args.data.mail.attachlist, stdin_getc);
967 else{
968 /* TRANSLATORS: refers to bad email address */
969 q_status_message1(SM_ORDER, 3, 4, _("Bad address: %s"), error);
970 exit_val = -1;
973 if(addr)
974 fs_give((void **) &addr);
976 if(fcc.tptr)
977 fs_give((void **) &fcc.tptr);
979 if(args.data.mail.attachlist)
980 free_attachment_list(&args.data.mail.attachlist);
982 if(to)
983 fs_give((void **) &to);
985 if(error)
986 fs_give((void **) &error);
988 goodnight_gracey(pine_state, exit_val);
990 else{
991 char int_mail[MAXPATH+1];
992 struct key_menu *km = &main_keymenu;
994 /*========== Normal pine mail reading mode ==========*/
996 pine_state->mail_stream = NULL;
997 pine_state->mangled_screen = 1;
999 if(args.action == aaURL){
1000 url_tool_t f;
1002 if(pine_state->in_init_seq){
1003 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
1004 clear_cursor_pos();
1005 if(pine_state->free_initial_cmds)
1006 fs_give((void **) &(pine_state->free_initial_cmds));
1007 pine_state->initial_cmds = NULL;
1009 if((f = url_local_handler(args.url)) != NULL){
1010 if(args.data.mail.attachlist){
1011 if(f == url_local_mailto){
1012 if(!(url_local_mailto_and_atts(args.url,
1013 args.data.mail.attachlist)
1014 && pine_state->next_screen))
1015 free_attachment_list(&args.data.mail.attachlist);
1016 goodnight_gracey(pine_state, 0);
1018 else {
1019 q_status_message(SM_ORDER | SM_DING, 3, 4,
1020 _("Only mailto URLs are allowed with file attachments"));
1021 goodnight_gracey(pine_state, -1); /* no return */
1024 else if(!((*f)(args.url) && pine_state->next_screen))
1025 goodnight_gracey(pine_state, 0); /* no return */
1027 else{
1028 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1029 _("Unrecognized URL \"%s\""), args.url);
1030 goodnight_gracey(pine_state, -1); /* no return */
1033 else if(!pine_state->start_in_index){
1034 /* flash message about executing initial commands */
1035 if(pine_state->in_init_seq){
1036 pine_state->in_init_seq = 0;
1037 clear_cursor_pos();
1038 pine_state->mangled_header = 1;
1039 pine_state->mangled_footer = 1;
1040 pine_state->mangled_screen = 0;
1041 /* show that this is Alpine */
1042 show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
1043 pine_state->mangled_screen = 1;
1044 pine_state->painted_footer_on_startup = 1;
1045 if(MIN(4, pine_state->ttyo->screen_rows - 4) > 1){
1046 char buf1[6*MAX_SCREEN_COLS+1];
1047 char buf2[6*MAX_SCREEN_COLS+1];
1048 int wid;
1050 /* TRANSLATORS: Initial Keystroke List is the literal name of an option */
1051 strncpy(buf1, _("Executing Initial Keystroke List......"), sizeof(buf1));
1052 buf1[sizeof(buf1)-1] = '\0';
1053 wid = utf8_width(buf1);
1054 if(wid > ps_global->ttyo->screen_cols){
1055 utf8_pad_to_width(buf2, buf1, sizeof(buf2), ps_global->ttyo->screen_cols, 1);
1056 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4), 0, buf2);
1058 else{
1059 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4),
1060 MAX(MIN(11, pine_state->ttyo->screen_cols - wid), 0), buf1);
1064 pine_state->in_init_seq = 1;
1066 else{
1067 show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
1068 pine_state->painted_body_on_startup = 1;
1069 pine_state->painted_footer_on_startup = 1;
1072 else{
1073 /* cancel any initial commands, overridden by cmd line */
1074 if(pine_state->in_init_seq){
1075 pine_state->in_init_seq = 0;
1076 pine_state->save_in_init_seq = 0;
1077 clear_cursor_pos();
1078 if(pine_state->initial_cmds){
1079 if(pine_state->free_initial_cmds)
1080 fs_give((void **)&(pine_state->free_initial_cmds));
1082 pine_state->initial_cmds = NULL;
1085 F_SET(F_USE_FK,pine_state, pine_state->orig_use_fkeys);
1088 (void) do_index_border(pine_state->context_current,
1089 pine_state->cur_folder,
1090 pine_state->mail_stream,
1091 pine_state->msgmap, MsgIndex, NULL,
1092 INDX_CLEAR|INDX_HEADER|INDX_FOOTER);
1093 pine_state->painted_footer_on_startup = 1;
1094 if(MIN(4, pine_state->ttyo->screen_rows - 4) > 1){
1095 char buf1[6*MAX_SCREEN_COLS+1];
1096 char buf2[6*MAX_SCREEN_COLS+1];
1097 int wid;
1099 strncpy(buf1, _("Please wait, opening mail folder......"), sizeof(buf1));
1100 buf1[sizeof(buf1)-1] = '\0';
1101 wid = utf8_width(buf1);
1102 if(wid > ps_global->ttyo->screen_cols){
1103 utf8_pad_to_width(buf2, buf1, sizeof(buf2), ps_global->ttyo->screen_cols, 1);
1104 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4), 0, buf2);
1106 else{
1107 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4),
1108 MAX(MIN(11, pine_state->ttyo->screen_cols - wid), 0), buf1);
1113 fflush(stdout);
1115 #if !defined(_WINDOWS) && !defined(LEAVEOUTFIFO)
1116 if(ps_global->VAR_FIFOPATH && ps_global->VAR_FIFOPATH[0])
1117 init_newmailfifo(ps_global->VAR_FIFOPATH);
1118 #endif
1120 if(pine_state->in_init_seq){
1121 pine_state->in_init_seq = 0;
1122 clear_cursor_pos();
1125 if(args.action == aaFolder && args.data.folder){
1126 CONTEXT_S *cntxt = NULL, *tc = NULL;
1127 char foldername[MAILTMPLEN];
1128 int notrealinbox = 0;
1130 if(args.data.folder[0] == '\0'){
1131 char *fldr;
1132 unsigned save_def_goto_rule;
1134 foldername[0] = '\0';
1135 save_def_goto_rule = pine_state->goto_default_rule;
1136 pine_state->goto_default_rule = GOTO_FIRST_CLCTN;
1137 tc = default_save_context(pine_state->context_list);
1138 fldr = broach_folder(-FOOTER_ROWS(pine_state), 1, &notrealinbox, &tc);
1139 pine_state->goto_default_rule = save_def_goto_rule;
1140 if(fldr){
1141 strncpy(foldername, fldr, sizeof(foldername)-1);
1142 foldername[sizeof(foldername)-1] = '\0';
1145 if(*foldername){
1146 removing_trailing_white_space(foldername);
1147 removing_leading_white_space(foldername);
1148 args.data.folder = cpystr(foldername);
1151 if(!*foldername){
1152 q_status_message(SM_ORDER, 0, 2 ,_("No folder to open"));
1153 goodnight_gracey(pine_state, -1);
1157 if(tc)
1158 cntxt = tc;
1159 else if((rv = pine_state->init_context) < 0)
1161 * As with almost all the folder vars in the pinerc,
1162 * we subvert the collection "breakout" here if the
1163 * folder name given looks like an asolute path on
1164 * this system...
1166 cntxt = (is_absolute_path(args.data.folder))
1167 ? NULL : pine_state->context_current;
1168 else if(rv == 0)
1169 cntxt = NULL;
1170 else
1171 for(cntxt = pine_state->context_list;
1172 rv > 1 && cntxt->next;
1173 rv--, cntxt = cntxt->next)
1176 if(pine_state && pine_state->ttyo){
1177 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1178 pine_state->painted_footer_on_startup = 0;
1179 pine_state->mangled_footer = 1;
1182 if(do_broach_folder(args.data.folder, cntxt, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT) <= 0){
1183 q_status_message1(SM_ORDER, 3, 4,
1184 _("Unable to open folder \"%s\""), args.data.folder);
1186 goodnight_gracey(pine_state, -1);
1189 else if(args.action == aaFolder){
1190 #ifdef _WINDOWS
1192 * need to ask for the inbox name if no default under DOS
1193 * since there is no "inbox"
1196 if(!pine_state->VAR_INBOX_PATH || !pine_state->VAR_INBOX_PATH[0]
1197 || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0){
1198 HelpType help = NO_HELP;
1199 static ESCKEY_S ekey[] = {{ctrl(T), 2, "^T", "To Fldrs"},
1200 {-1, 0, NULL, NULL}};
1202 pine_state->mangled_footer = 1;
1203 int_mail[0] = '\0';
1204 while(1){
1205 int flags = OE_APPEND_CURRENT;
1207 rv = optionally_enter(int_mail, -FOOTER_ROWS(pine_state),
1208 0, sizeof(int_mail),
1209 _("No inbox! Folder to open as inbox : "),
1210 /* ekey */ NULL, help, &flags);
1211 if(rv == 3){
1212 help = (help == NO_HELP) ? h_sticky_inbox : NO_HELP;
1213 continue;
1216 if(rv != 4)
1217 break;
1220 if(rv == 1){
1221 q_status_message(SM_ORDER, 0, 2 ,_("Folder open cancelled"));
1222 rv = 0; /* reset rv */
1224 else if(rv == 2){
1225 show_main_screen(pine_state,0,FirstMenu,km,0,(Pos *)NULL);
1228 if(*int_mail){
1229 removing_trailing_white_space(int_mail);
1230 removing_leading_white_space(int_mail);
1231 if((!pine_state->VAR_INBOX_PATH
1232 || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0)
1233 /* TRANSLATORS: Inbox-Path and PINERC are literal, not to be translated */
1234 && want_to(_("Preserve folder as \"Inbox-Path\" in PINERC"),
1235 'y', 'n', NO_HELP, WT_NORM) == 'y'){
1236 set_variable(V_INBOX_PATH, int_mail, 1, 1, Main);
1238 else{
1239 if(pine_state->VAR_INBOX_PATH)
1240 fs_give((void **)&pine_state->VAR_INBOX_PATH);
1242 pine_state->VAR_INBOX_PATH = cpystr(int_mail);
1245 if(pine_state && pine_state->ttyo){
1246 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1247 pine_state->painted_footer_on_startup = 0;
1248 pine_state->mangled_footer = 1;
1251 do_broach_folder(pine_state->inbox_name,
1252 pine_state->context_list, NULL, DB_INBOXWOCNTXT);
1254 else
1255 q_status_message(SM_ORDER, 0, 2 ,_("No folder opened"));
1258 else
1260 #endif /* _WINDOWS */
1261 if(F_ON(F_PREOPEN_STAYOPENS, ps_global))
1262 preopen_stayopen_folders();
1264 if(pine_state && pine_state->ttyo){
1265 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1266 pine_state->painted_footer_on_startup = 0;
1267 pine_state->mangled_footer = 1;
1270 /* open inbox */
1271 do_broach_folder(pine_state->inbox_name,
1272 pine_state->context_list, NULL, DB_INBOXWOCNTXT);
1275 if(pine_state->mangled_footer)
1276 pine_state->painted_footer_on_startup = 0;
1278 if(args.action == aaFolder
1279 && pine_state->mail_stream
1280 && expire_sent_mail())
1281 pine_state->painted_footer_on_startup = 0;
1284 * Initialize the defaults. Initializing here means that
1285 * if they're remote, the user isn't prompted for an imap login
1286 * before the display's drawn, AND there's the chance that
1287 * we can climb onto the already opened folder's stream...
1289 if(ps_global->first_time_user)
1290 init_save_defaults(); /* initialize default save folders */
1292 build_path(int_mail,
1293 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
1294 : pine_state->home_dir,
1295 INTERRUPTED_MAIL, sizeof(int_mail));
1296 if(args.action == aaFolder
1297 && (folder_exists(NULL, int_mail) & FEX_ISFILE))
1298 q_status_message(SM_ORDER | SM_DING, 4, 5,
1299 _("Use Compose command to continue interrupted message."));
1301 #if defined(USE_QUOTAS)
1303 long q;
1304 int over;
1305 q = disk_quota(pine_state->home_dir, &over);
1306 if(q > 0 && over){
1307 q_status_message2(SM_ASYNC | SM_DING, 4, 5,
1308 _("WARNING! Over your disk quota by %s bytes (%s)"),
1309 comatose(q),byte_string(q));
1312 #endif
1314 pine_state->in_init_seq = pine_state->save_in_init_seq;
1315 pine_state->dont_use_init_cmds = 0;
1316 clear_cursor_pos();
1318 if(pine_state->give_fixed_warning)
1319 q_status_message(SM_ASYNC, 0, 10,
1320 /* TRANSLATORS: config is an abbreviation for configuration */
1321 _("Note: some of your config options conflict with site policy and are ignored"));
1323 if(!prune_folders_ok())
1324 q_status_message(SM_ASYNC, 0, 10,
1325 /* TRANSLATORS: Pruned-Folders is literal */
1326 _("Note: ignoring Pruned-Folders outside of default collection for saves"));
1328 if(get_input_timeout() == 0 &&
1329 ps_global->VAR_INBOX_PATH &&
1330 ps_global->VAR_INBOX_PATH[0] == '{')
1331 q_status_message(SM_ASYNC, 0, 10,
1332 _("Note: Mail-Check-Interval=0 may cause IMAP server connection to time out"));
1334 #ifdef _WINDOWS
1335 mswin_setnewmailwidth(ps_global->nmw_width);
1336 #endif
1339 /*-------------------------------------------------------------------
1340 Loop executing the commands
1342 This is done like this so that one command screen can cause
1343 another one to execute it with out going through the main menu.
1344 ------------------------------------------------------------------*/
1345 if(!pine_state->next_screen)
1346 pine_state->next_screen = pine_state->start_in_index
1347 ? mail_index_screen : main_menu_screen;
1348 while(1){
1349 if(pine_state->next_screen == SCREEN_FUN_NULL)
1350 pine_state->next_screen = main_menu_screen;
1352 (*(pine_state->next_screen))(pine_state);
1356 exit(0);
1361 * The arguments need to be converted to UTF-8 for our internal use.
1362 * Not all arguments are converted because some are used before we
1363 * are able to do the conversion, like the pinerc name.
1365 void
1366 convert_args_to_utf8(struct pine *ps, ARGDATA_S *args)
1368 char *fromcharset = NULL;
1369 char *conv;
1371 if(args){
1372 if(ps->keyboard_charmap && strucmp(ps->keyboard_charmap, "UTF-8")
1373 && strucmp(ps->keyboard_charmap, "US-ASCII"))
1374 fromcharset = ps->keyboard_charmap;
1375 else if(ps->display_charmap && strucmp(ps->display_charmap, "UTF-8")
1376 && strucmp(ps->display_charmap, "US-ASCII"))
1377 fromcharset = ps->display_charmap;
1378 #ifndef _WINDOWS
1379 else if(ps->VAR_OLD_CHAR_SET && strucmp(ps->VAR_OLD_CHAR_SET, "UTF-8")
1380 && strucmp(ps->VAR_OLD_CHAR_SET, "US-ASCII"))
1381 fromcharset = ps->VAR_OLD_CHAR_SET;
1382 #endif /* ! _WINDOWS */
1384 if(args->action == aaURL && args->url){
1385 conv = convert_to_utf8(args->url, fromcharset, 0);
1386 if(conv){
1387 fs_give((void **) &args->url);
1388 args->url = conv;
1392 if(args->action == aaFolder && args->data.folder){
1393 conv = convert_to_utf8(args->data.folder, fromcharset, 0);
1394 if(conv){
1395 fs_give((void **) &args->data.folder);
1396 args->data.folder = conv;
1400 if(args->action == aaMore && args->data.file){
1401 conv = convert_to_utf8(args->data.file, fromcharset, 0);
1402 if(conv){
1403 fs_give((void **) &args->data.file);
1404 args->data.file = conv;
1408 if(args->action == aaURL || args->action == aaMail){
1409 if(args->data.mail.addrlist){
1410 STRLIST_S *p;
1412 for(p = args->data.mail.addrlist; p; p=p->next){
1413 if(p->name){
1414 conv = convert_to_utf8(p->name, fromcharset, 0);
1415 if(conv){
1416 fs_give((void **) &p->name);
1417 p->name = conv;
1423 if(args->data.mail.attachlist){
1424 PATMT *p;
1426 for(p = args->data.mail.attachlist; p; p=p->next){
1427 if(p->filename){
1428 conv = convert_to_utf8(p->filename, fromcharset, 0);
1429 if(conv){
1430 fs_give((void **) &p->filename);
1431 p->filename = conv;
1441 void
1442 preopen_stayopen_folders(void)
1444 char **open_these;
1446 for(open_these = ps_global->VAR_PERMLOCKED;
1447 open_these && *open_these; open_these++)
1448 (void) do_broach_folder(*open_these, ps_global->context_list,
1449 NULL, DB_NOVISIT);
1454 * read_stdin_char - simple function to return a character from
1455 * redirected stdin
1458 read_stdin_char(char *c)
1460 int rv;
1462 /* it'd probably be a good idea to fix this to pre-read blocks */
1463 while(1){
1464 rv = read(PIPED_FD, c, 1);
1465 if(rv < 0){
1466 if(errno == EINTR){
1467 dprint((2, "read_stdin_char: read interrupted, restarting\n"));
1468 continue;
1470 else
1471 dprint((1, "read_stdin_char: read FAILED: %s\n",
1472 error_description(errno)));
1474 break;
1476 return(rv);
1480 /* this default is from the array of structs below */
1481 #define DEFAULT_MENU_ITEM ((unsigned) 3) /* LIST FOLDERS */
1482 #define ABOOK_MENU_ITEM ((unsigned) 4) /* ADDRESS BOOK */
1483 #define MAX_MENU_ITEM ((unsigned) 6)
1485 * Skip this many spaces between rows of main menu screen.
1486 * We have MAX_MENU_ITEM+1 = # of commands in menu
1487 * 1 = copyright line
1488 * MAX_MENU_ITEM = rows between commands
1489 * 1 = extra row above commands
1490 * 1 = row between commands and copyright
1492 * To make it simple, if there is enough room for all of that include all the
1493 * extra space, if not, cut it all out.
1495 #define MNSKIP(X) (((HEADER_ROWS(X)+FOOTER_ROWS(X)+(MAX_MENU_ITEM+1)+1+MAX_MENU_ITEM+1+1) <= (X)->ttyo->screen_rows) ? 1 : 0)
1497 static unsigned menu_index = DEFAULT_MENU_ITEM;
1500 * One of these for each line that gets printed in the middle of the
1501 * screen in the main menu.
1503 static struct menu_key {
1504 char *key_and_name,
1505 *news_addition;
1506 int key_index; /* index into keymenu array for this cmd */
1507 } mkeys[] = {
1509 * TRANSLATORS: These next few are headings on the Main alpine menu.
1510 * It's nice if the dashes can be made to line up vertically.
1512 {N_(" %s HELP - Get help using Alpine"),
1513 NULL, MAIN_HELP_KEY},
1514 {N_(" %s COMPOSE MESSAGE - Compose and send%s a message"),
1515 /* TRANSLATORS: We think of sending an email message or posting a news message.
1516 The message is shown as Compose and send/post a message */
1517 N_("/post"), MAIN_COMPOSE_KEY},
1518 {N_(" %s MESSAGE INDEX - View messages in current folder"),
1519 NULL, MAIN_INDEX_KEY},
1520 {N_(" %s FOLDER LIST - Select a folder%s to view"),
1521 /* TRANSLATORS: When news is supported the message above becomes
1522 Select a folder OR news group to view */
1523 N_(" OR news group"), MAIN_FOLDER_KEY},
1524 {N_(" %s ADDRESS BOOK - Update address book"),
1525 NULL, MAIN_ADDRESS_KEY},
1526 {N_(" %s SETUP - Configure Alpine Options"),
1527 NULL, MAIN_SETUP_KEY},
1528 /* TRANSLATORS: final Main menu line */
1529 {N_(" %s QUIT - Leave the Alpine program"),
1530 NULL, MAIN_QUIT_KEY}
1535 /*----------------------------------------------------------------------
1536 display main menu and execute main menu commands
1538 Args: The usual pine structure
1540 Result: main menu commands are executed
1543 M A I N M E N U S C R E E N
1545 Paint the main menu on the screen, get the commands and either execute
1546 the function or pass back the name of the function to execute for the menu
1547 selection. Only simple functions that always return here can be executed
1548 here.
1550 This functions handling of new mail, redrawing, errors and such can
1551 serve as a template for the other screen that do much the same thing.
1553 There is a loop that fetchs and executes commands until a command to leave
1554 this screen is given. Then the name of the next screen to display is
1555 stored in next_screen member of the structure and this function is exited
1556 with a return.
1558 First a check for new mail is performed. This might involve reading the new
1559 mail into the inbox which might then cause the screen to be repainted.
1561 Then the general screen painting is done. This is usually controlled
1562 by a few flags and some other position variables. If they change they
1563 tell this part of the code what to repaint. This will include cursor
1564 motion and so on.
1565 ----*/
1566 void
1567 main_menu_screen(struct pine *pine_state)
1569 UCS ch;
1570 int cmd, just_a_navigate_cmd, setup_command, km_popped;
1571 int notrealinbox;
1572 char *new_folder, *utf8str;
1573 CONTEXT_S *tc;
1574 struct key_menu *km;
1575 OtherMenu what;
1576 Pos curs_pos;
1578 ps_global = pine_state;
1579 just_a_navigate_cmd = 0;
1580 km_popped = 0;
1581 menu_index = DEFAULT_MENU_ITEM;
1582 what = FirstMenu; /* which keymenu to display */
1583 ch = 'x'; /* For display_message 1st time through */
1584 pine_state->next_screen = SCREEN_FUN_NULL;
1585 pine_state->prev_screen = main_menu_screen;
1586 curs_pos.row = pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
1587 curs_pos.col = 0;
1588 km = &main_keymenu;
1590 mailcap_free(); /* free resources we won't be using for a while */
1592 if(!pine_state->painted_body_on_startup
1593 && !pine_state->painted_footer_on_startup){
1594 pine_state->mangled_screen = 1;
1597 dprint((1, "\n\n ---- MAIN_MENU_SCREEN ----\n"));
1599 while(1){
1600 if(km_popped){
1601 km_popped--;
1602 if(km_popped == 0){
1603 clearfooter(pine_state);
1604 pine_state->mangled_body = 1;
1609 * fix up redrawer just in case some submenu caused it to get
1610 * reassigned...
1612 pine_state->redrawer = main_redrawer;
1614 /*----------- Check for new mail -----------*/
1615 if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
1616 pine_state->mangled_header = 1;
1618 if(streams_died())
1619 pine_state->mangled_header = 1;
1621 show_main_screen(pine_state, just_a_navigate_cmd, what, km,
1622 km_popped, &curs_pos);
1623 just_a_navigate_cmd = 0;
1624 what = SameMenu;
1626 /*---- This displays new mail notification, or errors ---*/
1627 if(km_popped){
1628 FOOTER_ROWS(pine_state) = 3;
1629 mark_status_dirty();
1632 display_message(ch);
1633 if(km_popped){
1634 FOOTER_ROWS(pine_state) = 1;
1635 mark_status_dirty();
1638 if(F_OFF(F_SHOW_CURSOR, ps_global)){
1639 curs_pos.row =pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
1640 curs_pos.col =0;
1643 MoveCursor(curs_pos.row, curs_pos.col);
1645 /*------ Read the command from the keyboard ----*/
1646 #ifdef MOUSE
1647 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
1648 register_mfunc(mouse_in_content, HEADER_ROWS(pine_state), 0,
1649 pine_state->ttyo->screen_rows-(FOOTER_ROWS(pine_state)+1),
1650 pine_state->ttyo->screen_cols);
1651 #endif
1652 #if defined(DOS) || defined(OS2)
1654 * AND pre-build header lines. This works just fine under
1655 * DOS since we wait for characters in a loop. Something will
1656 * will have to change under UNIX if we want to do the same.
1658 /* while_waiting = build_header_cache; */
1659 #ifdef _WINDOWS
1660 mswin_sethelptextcallback(pcpine_help_main);
1661 mswin_mousetrackcallback(pcpine_main_cursor);
1662 #endif
1663 #endif
1664 ch = READ_COMMAND(&utf8str);
1665 #ifdef MOUSE
1666 clear_mfunc(mouse_in_content);
1667 #endif
1668 #if defined(DOS) || defined(OS2)
1669 /* while_waiting = NULL; */
1670 #ifdef _WINDOWS
1671 mswin_sethelptextcallback(NULL);
1672 mswin_mousetrackcallback(NULL);
1673 #endif
1674 #endif
1676 /* No matter what, Quit here always works */
1677 if(ch == 'q' || ch == 'Q'){
1678 cmd = MC_QUIT;
1680 #ifdef DEBUG
1681 else if(debug && ch && ch < 0x80 && strchr("123456789", ch)){
1682 int olddebug;
1684 olddebug = debug;
1685 debug = ch - '0';
1686 if(debug > 7)
1687 ps_global->debug_timestamp = 1;
1688 else
1689 ps_global->debug_timestamp = 0;
1691 if(debug > 7)
1692 ps_global->debug_imap = 4;
1693 else if(debug > 6)
1694 ps_global->debug_imap = 3;
1695 else if(debug > 4)
1696 ps_global->debug_imap = 2;
1697 else if(debug > 2)
1698 ps_global->debug_imap = 1;
1699 else
1700 ps_global->debug_imap = 0;
1702 if(ps_global->mail_stream){
1703 if(ps_global->debug_imap > 0){
1704 mail_debug(ps_global->mail_stream);
1705 #ifdef _WINDOWS
1706 mswin_enableimaptelemetry(TRUE);
1707 #endif
1709 else{
1710 mail_nodebug(ps_global->mail_stream);
1711 #ifdef _WINDOWS
1712 mswin_enableimaptelemetry(FALSE);
1713 #endif
1717 if(debug > 7 && olddebug <= 7)
1718 mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
1719 else if(debug <= 7 && olddebug > 7 && !ps_global->debugmem)
1720 mail_parameters(NULL, SET_TCPDEBUG, (void *) FALSE);
1722 dprint((1, "*** Debug level set to %d ***\n", debug));
1723 if(debugfile)
1724 fflush(debugfile);
1726 q_status_message1(SM_ORDER, 0, 1, _("Debug level set to %s"),
1727 int2string(debug));
1728 continue;
1730 #endif /* DEBUG */
1731 else{
1732 cmd = menu_command(ch, km);
1734 if(km_popped)
1735 switch(cmd){
1736 case MC_NONE :
1737 case MC_OTHER :
1738 case MC_RESIZE :
1739 case MC_REPAINT :
1740 km_popped++;
1741 break;
1743 default:
1744 clearfooter(pine_state);
1745 break;
1749 /*------ Execute the command ------*/
1750 switch (cmd){
1751 help_case :
1752 /*------ HELP ------*/
1753 case MC_HELP :
1755 if(FOOTER_ROWS(pine_state) == 1 && km_popped == 0){
1756 km_popped = 2;
1757 pine_state->mangled_footer = 1;
1759 else{
1760 /* TRANSLATORS: This is a screen title */
1761 helper(main_menu_tx, _("HELP FOR MAIN MENU"), 0);
1762 pine_state->mangled_screen = 1;
1765 break;
1768 /*---------- display other key bindings ------*/
1769 case MC_OTHER :
1770 if(ch == 'o')
1771 warn_other_cmds();
1773 what = NextMenu;
1774 pine_state->mangled_footer = 1;
1775 break;
1778 /*---------- Previous item in menu ----------*/
1779 case MC_PREVITEM :
1780 if(menu_index > 0) {
1781 menu_index--;
1782 pine_state->mangled_body = 1;
1783 if(km->which == 0)
1784 pine_state->mangled_footer = 1;
1786 just_a_navigate_cmd++;
1788 else
1789 /* TRANSLATORS: list refers to list of commands in main menu */
1790 q_status_message(SM_ORDER, 0, 2, _("Already at top of list"));
1792 break;
1795 /*---------- Next item in menu ----------*/
1796 case MC_NEXTITEM :
1797 if(menu_index < MAX_MENU_ITEM){
1798 menu_index++;
1799 pine_state->mangled_body = 1;
1800 if(km->which == 0)
1801 pine_state->mangled_footer = 1;
1803 just_a_navigate_cmd++;
1805 else
1806 q_status_message(SM_ORDER, 0, 2, _("Already at bottom of list"));
1808 break;
1811 /*---------- Release Notes ----------*/
1812 case MC_RELNOTES :
1813 /* TRANSLATORS: This is a screen title */
1814 helper(h_news, _("ALPINE RELEASE NOTES"), 0);
1815 pine_state->mangled_screen = 1;
1816 break;
1819 #ifdef KEYBOARD_LOCK
1820 /*---------- Keyboard lock ----------*/
1821 case MC_KBLOCK :
1822 (void) lock_keyboard();
1823 pine_state->mangled_screen = 1;
1824 break;
1825 #endif /* KEYBOARD_LOCK */
1828 /*---------- Quit pine ----------*/
1829 case MC_QUIT :
1830 pine_state->next_screen = quit_screen;
1831 return;
1834 /*---------- Go to composer ----------*/
1835 case MC_COMPOSE :
1836 pine_state->next_screen = compose_screen;
1837 return;
1840 /*---- Go to alternate composer ------*/
1841 case MC_ROLE :
1842 pine_state->next_screen = alt_compose_screen;
1843 return;
1846 /*---------- Top of Folder list ----------*/
1847 case MC_COLLECTIONS :
1848 pine_state->next_screen = folder_screen;
1849 return;
1852 /*---------- Goto new folder ----------*/
1853 case MC_GOTO :
1854 tc = ps_global->context_current;
1855 new_folder = broach_folder(-FOOTER_ROWS(pine_state), 1, &notrealinbox, &tc);
1856 if(new_folder)
1857 visit_folder(ps_global, new_folder, tc, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT);
1859 return;
1862 /*---------- Go to index ----------*/
1863 case MC_INDEX :
1864 if(THREADING()
1865 && sp_viewing_a_thread(pine_state->mail_stream)
1866 && unview_thread(pine_state, pine_state->mail_stream,
1867 pine_state->msgmap)){
1868 pine_state->view_skipped_index = 0;
1869 pine_state->mangled_screen = 1;
1872 pine_state->next_screen = mail_index_screen;
1873 return;
1876 /*---------- Review Status Messages ----------*/
1877 case MC_JOURNAL :
1878 review_messages();
1879 pine_state->mangled_screen = 1;
1880 break;
1883 /*---------- Setup mini menu ----------*/
1884 case MC_SETUP :
1885 setup_case :
1886 setup_command = setup_menu(pine_state);
1887 pine_state->mangled_footer = 1;
1888 do_setup_task(setup_command);
1889 if(ps_global->next_screen != main_menu_screen)
1890 return;
1892 break;
1895 /*---------- Go to address book ----------*/
1896 case MC_ADDRBOOK :
1897 pine_state->next_screen = addr_book_screen;
1898 return;
1901 /*------ Repaint the works -------*/
1902 case MC_RESIZE :
1903 case MC_REPAINT :
1904 ClearScreen();
1905 pine_state->mangled_screen = 1;
1906 break;
1909 #ifdef MOUSE
1910 /*------- Mouse event ------*/
1911 case MC_MOUSE :
1913 MOUSEPRESS mp;
1914 unsigned ndmi;
1915 struct pine *ps = pine_state;
1917 mouse_get_last (NULL, &mp);
1919 #ifdef _WINDOWS
1920 if(mp.button == M_BUTTON_RIGHT){
1921 if(!mp.doubleclick){
1922 static MPopup main_popup[] = {
1923 {tQueue, {"Folder List", lNormal}, {'L'}},
1924 {tQueue, {"Message Index", lNormal}, {'I'}},
1925 {tSeparator},
1926 {tQueue, {"Address Book", lNormal}, {'A'}},
1927 {tQueue, {"Setup Options", lNormal}, {'S'}},
1928 {tTail}
1931 mswin_popup(main_popup);
1934 else {
1935 #endif
1936 if (mp.row >= (HEADER_ROWS(ps) + MNSKIP(ps)))
1937 ndmi = (mp.row+1 - HEADER_ROWS(ps) - (MNSKIP(ps)+1))/(MNSKIP(ps)+1);
1939 if (mp.row >= (HEADER_ROWS(ps) + MNSKIP(ps))
1940 && !(MNSKIP(ps) && (mp.row+1) & 0x01)
1941 && ndmi <= MAX_MENU_ITEM
1942 && FOOTER_ROWS(ps) + (ndmi+1)*(MNSKIP(ps)+1)
1943 + MNSKIP(ps) + FOOTER_ROWS(ps) <= ps->ttyo->screen_rows){
1944 if(mp.doubleclick){
1945 switch(ndmi){ /* fake main_screen request */
1946 case 0 :
1947 goto help_case;
1949 case 1 :
1950 pine_state->next_screen = compose_screen;
1951 return;
1953 case 2 :
1954 pine_state->next_screen = mail_index_screen;
1955 return;
1957 case 3 :
1958 pine_state->next_screen = folder_screen;
1959 return;
1961 case 4 :
1962 pine_state->next_screen = addr_book_screen;
1963 return;
1965 case 5 :
1966 goto setup_case;
1968 case 6 :
1969 pine_state->next_screen = quit_screen;
1970 return;
1972 default: /* no op */
1973 break;
1976 else{
1977 menu_index = ndmi;
1978 pine_state->mangled_body = 1;
1979 if(km->which == 0)
1980 pine_state->mangled_footer = 1;
1982 just_a_navigate_cmd++;
1985 #ifdef _WINDOWS
1987 #endif
1990 break;
1991 #endif
1994 /*------ Input timeout ------*/
1995 case MC_NONE :
1996 break; /* noop for timeout loop mail check */
1999 /*------ Bogus Input ------*/
2000 case MC_UNKNOWN :
2001 if(ch == 'm' || ch == 'M'){
2002 q_status_message(SM_ORDER, 0, 1, "Already in Main Menu");
2003 break;
2006 default:
2007 bogus_command(ch, F_ON(F_USE_FK,pine_state) ? "F1" : "?");
2008 break;
2010 case MC_UTF8:
2011 bogus_utf8_command(utf8str, F_ON(F_USE_FK, pine_state) ? "F1" : "?");
2012 break;
2013 } /* the switch */
2014 } /* the BIG while loop! */
2018 /*----------------------------------------------------------------------
2019 Re-Draw the main menu
2021 Args: none.
2023 Result: main menu is re-displayed
2024 ----*/
2025 void
2026 main_redrawer(void)
2028 struct key_menu *km = &main_keymenu;
2030 ps_global->mangled_screen = 1;
2031 show_main_screen(ps_global, 0, FirstMenu, km, 0, (Pos *)NULL);
2035 /*----------------------------------------------------------------------
2036 Draw the main menu
2038 Args: pine_state - the usual struct
2039 quick_draw - tells do_menu() it can skip some drawing
2040 what - tells which section of keymenu to draw
2041 km - the keymenu
2042 cursor_pos - returns a good position for the cursor to be located
2044 Result: main menu is displayed
2045 ----*/
2046 void
2047 show_main_screen(struct pine *ps, int quick_draw, OtherMenu what,
2048 struct key_menu *km, int km_popped, Pos *cursor_pos)
2050 if(ps->painted_body_on_startup || ps->painted_footer_on_startup){
2051 ps->mangled_screen = 0; /* only worry about it here */
2052 ps->mangled_header = 1; /* we have to redo header */
2053 if(!ps->painted_body_on_startup)
2054 ps->mangled_body = 1; /* make sure to paint body*/
2056 if(!ps->painted_footer_on_startup)
2057 ps->mangled_footer = 1; /* make sure to paint footer*/
2059 ps->painted_body_on_startup = 0;
2060 ps->painted_footer_on_startup = 0;
2063 if(ps->mangled_screen){
2064 ps->mangled_header = 1;
2065 ps->mangled_body = 1;
2066 ps->mangled_footer = 1;
2067 ps->mangled_screen = 0;
2070 #ifdef _WINDOWS
2071 /* Reset the scroll range. Main screen never scrolls. */
2072 scroll_setrange (0L, 0L);
2073 mswin_beginupdate();
2074 #endif
2076 /* paint the titlebar if needed */
2077 if(ps->mangled_header){
2078 /* TRANSLATORS: screen title */
2079 set_titlebar(_("MAIN MENU"), ps->mail_stream, ps->context_current,
2080 ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0, NULL);
2081 ps->mangled_header = 0;
2084 /* paint the body if needed */
2085 if(ps->mangled_body){
2086 if(!quick_draw)
2087 ClearBody();
2089 do_menu(quick_draw, cursor_pos, km);
2090 ps->mangled_body = 0;
2093 /* paint the keymenu if needed */
2094 if(km && ps->mangled_footer){
2095 static char label[LONGEST_LABEL + 2 + 1], /* label + brackets + \0 */
2096 name[8];
2097 bitmap_t bitmap;
2099 setbitmap(bitmap);
2101 #ifdef KEYBOARD_LOCK
2102 if(ps_global->restricted || F_ON(F_DISABLE_KBLOCK_CMD,ps_global))
2103 #endif
2104 clrbitn(MAIN_KBLOCK_KEY, bitmap);
2106 menu_clear_binding(km, '>');
2107 menu_clear_binding(km, '.');
2108 menu_clear_binding(km, KEY_RIGHT);
2109 menu_clear_binding(km, ctrl('M'));
2110 menu_clear_binding(km, ctrl('J'));
2111 km->keys[MAIN_DEFAULT_KEY].bind
2112 = km->keys[mkeys[menu_index].key_index].bind;
2113 km->keys[MAIN_DEFAULT_KEY].label
2114 = km->keys[mkeys[menu_index].key_index].label;
2116 /* put brackets around the default action */
2117 snprintf(label, sizeof(label), "[%s]", km->keys[mkeys[menu_index].key_index].label);
2118 label[sizeof(label)-1] = '\0';
2119 strncpy(name, ">", sizeof(name));
2120 name[sizeof(name)-1] = '\0';
2121 km->keys[MAIN_DEFAULT_KEY].label = label;
2122 km->keys[MAIN_DEFAULT_KEY].name = name;
2123 menu_add_binding(km, '>', km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2124 menu_add_binding(km, '.', km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2125 menu_add_binding(km, ctrl('M'), km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2126 menu_add_binding(km, ctrl('J'), km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2128 if(F_ON(F_ARROW_NAV,ps_global))
2129 menu_add_binding(km, KEY_RIGHT, km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2131 if(km_popped){
2132 FOOTER_ROWS(ps) = 3;
2133 clearfooter(ps);
2136 draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
2137 1-FOOTER_ROWS(ps_global), 0, what);
2138 ps->mangled_footer = 0;
2139 if(km_popped){
2140 FOOTER_ROWS(ps) = 1;
2141 mark_keymenu_dirty();
2145 #ifdef _WINDOWS
2146 mswin_endupdate();
2147 #endif
2151 /*----------------------------------------------------------------------
2152 Actually display the main menu
2154 Args: quick_draw - just a next or prev command was typed so we only have
2155 to redraw the highlighting
2156 cursor_pos - a place to return a good value for cursor location
2158 Result: Main menu is displayed
2159 ---*/
2160 void
2161 do_menu(int quick_draw, Pos *cursor_pos, struct key_menu *km)
2163 struct pine *ps = ps_global;
2164 int dline, indent, longest = 0, cmd;
2165 char buf[4*MAX_SCREEN_COLS+1];
2166 char buf2[4*MAX_SCREEN_COLS+1];
2167 static int last_inverse = -1;
2168 Pos pos;
2170 /* find the longest command */
2171 for(cmd = 0; cmd < sizeof(mkeys)/(sizeof(mkeys[1])); cmd++){
2172 memset((void *) buf, ' ', sizeof(buf));
2173 snprintf(buf, sizeof(buf), mkeys[cmd].key_and_name[0] ? _(mkeys[cmd].key_and_name) : "",
2174 (F_OFF(F_USE_FK,ps)
2175 && km->keys[mkeys[cmd].key_index].name)
2176 ? km->keys[mkeys[cmd].key_index].name : "",
2177 (ps->VAR_NEWS_SPEC && mkeys[cmd].news_addition && mkeys[cmd].news_addition[0])
2178 ? _(mkeys[cmd].news_addition) : "");
2179 buf[sizeof(buf)-1] = '\0';
2181 if(longest < (indent = utf8_width(buf)))
2182 longest = indent;
2185 indent = MAX(((ps->ttyo->screen_cols - longest)/2) - 1, 0);
2187 dline = HEADER_ROWS(ps) + MNSKIP(ps);
2188 for(cmd = 0; cmd < sizeof(mkeys)/(sizeof(mkeys[1])); cmd++){
2189 /* leave room for copyright and footer */
2190 if(dline + MNSKIP(ps) + 1 + FOOTER_ROWS(ps) >= ps->ttyo->screen_rows)
2191 break;
2193 if(quick_draw && !(cmd == last_inverse || cmd == menu_index)){
2194 dline += (1 + MNSKIP(ps));
2195 continue;
2198 if(cmd == menu_index)
2199 StartInverse();
2201 memset((void *) buf, ' ', sizeof(buf));
2202 snprintf(buf, sizeof(buf), mkeys[cmd].key_and_name[0] ? _(mkeys[cmd].key_and_name) : "",
2203 (F_OFF(F_USE_FK,ps)
2204 && km->keys[mkeys[cmd].key_index].name)
2205 ? km->keys[mkeys[cmd].key_index].name : "",
2206 (ps->VAR_NEWS_SPEC && mkeys[cmd].news_addition && mkeys[cmd].news_addition[0])
2207 ? _(mkeys[cmd].news_addition) : "");
2208 buf[sizeof(buf)-1] = '\0';
2210 utf8_pad_to_width(buf2, buf, sizeof(buf2),
2211 MIN(ps->ttyo->screen_cols-indent,longest+1), 1);
2212 pos.row = dline++;
2213 pos.col = indent;
2214 PutLine0(pos.row, pos.col, buf2);
2216 if(MNSKIP(ps))
2217 dline++;
2219 if(cmd == menu_index){
2220 if(cursor_pos){
2221 cursor_pos->row = pos.row;
2222 /* 6 is 1 for the letter plus 5 spaces */
2223 cursor_pos->col = pos.col + 6;
2224 if(F_OFF(F_USE_FK,ps))
2225 cursor_pos->col++;
2227 cursor_pos->col = MIN(cursor_pos->col, ps->ttyo->screen_cols);
2230 EndInverse();
2235 last_inverse = menu_index;
2237 if(!quick_draw && FOOTER_ROWS(ps)+1 < ps->ttyo->screen_rows){
2238 utf8_to_width(buf2, LEGAL_NOTICE, sizeof(buf2),
2239 ps->ttyo->screen_cols-3, NULL);
2240 PutLine0(ps->ttyo->screen_rows - (FOOTER_ROWS(ps)+1),
2241 MAX(0, ((ps->ttyo->screen_cols-utf8_width(buf2))/2)),
2242 buf2);
2245 fflush(stdout);
2250 choose_setup_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2252 int rv = 1;
2253 SRV_S *srv;
2255 if(!(srv = (SRV_S *)sparms->proc.data.p)){
2256 sparms->proc.data.p = (SRV_S *)fs_get(sizeof(*srv));
2257 srv = (SRV_S *)sparms->proc.data.p;
2258 memset(srv, 0, sizeof(*srv));
2261 ps_global->next_screen = SCREEN_FUN_NULL;
2263 switch(cmd){
2264 case MC_PRINTER :
2265 srv->cmd = 'p';
2266 break;
2268 case MC_PASSWD :
2269 srv->cmd = 'n';
2270 break;
2272 case MC_CONFIG :
2273 srv->cmd = 'c';
2274 break;
2276 case MC_SIG :
2277 srv->cmd = 's';
2278 break;
2280 case MC_ABOOKS :
2281 srv->cmd = 'a';
2282 break;
2284 case MC_CLISTS :
2285 srv->cmd = 'l';
2286 break;
2288 case MC_RULES :
2289 srv->cmd = 'r';
2290 break;
2292 case MC_DIRECTORY :
2293 srv->cmd = 'd';
2294 break;
2296 case MC_KOLOR :
2297 srv->cmd = 'k';
2298 break;
2300 case MC_REMOTE :
2301 srv->cmd = 'z';
2302 break;
2304 case MC_SECURITY : /* S/MIME setup screen */
2305 srv->cmd = 'm';
2306 break;
2308 case MC_EXCEPT :
2309 srv->exc = !srv->exc;
2310 menu_clear_binding(sparms->keys.menu, 'x');
2311 if(srv->exc){
2312 if(sparms->bar.title) fs_give((void **)&sparms->bar.title);
2313 /* TRANSLATORS: screen title */
2314 sparms->bar.title = cpystr(_("SETUP EXCEPTIONS"));
2315 ps_global->mangled_header = 1;
2316 /* TRANSLATORS: The reason the X is upper case in eXceptions
2317 is because the command key is X. It isn't necessary, just
2318 nice if it works. */
2319 menu_init_binding(sparms->keys.menu, 'x', MC_EXCEPT, "X",
2320 N_("not eXceptions"), SETUP_EXCEPT);
2322 else{
2323 if(sparms->bar.title) fs_give((void **)&sparms->bar.title);
2324 /* TRANSLATORS: screen title */
2325 sparms->bar.title = cpystr(_("SETUP"));
2326 ps_global->mangled_header = 1;
2327 menu_init_binding(sparms->keys.menu, 'x', MC_EXCEPT, "X",
2328 N_("eXceptions"), SETUP_EXCEPT);
2331 if(sparms->keys.menu->which == 1)
2332 ps_global->mangled_footer = 1;
2334 rv = 0;
2335 break;
2337 case MC_NO_EXCEPT :
2338 #if defined(DOS) || defined(OS2)
2339 q_status_message(SM_ORDER, 0, 2, _("Need argument \"-x <except_config>\" or \"PINERCEX\" file to use eXceptions"));
2340 #else
2341 q_status_message(SM_ORDER, 0, 2, _("Need argument \"-x <except_config>\" or \".pinercex\" file to use eXceptions"));
2342 #endif
2343 rv = 0;
2344 break;
2346 default:
2347 alpine_panic("Unexpected command in choose_setup_cmd");
2348 break;
2351 return(rv);
2356 setup_menu(struct pine *ps)
2358 int ret = 0, exceptions = 0;
2359 int printer = 0, passwd = 0, config = 0, sig = 0, dir = 0, smime = 0, exc = 0;
2360 SCROLL_S sargs;
2361 SRV_S *srv;
2362 STORE_S *store;
2364 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2365 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
2366 return(ret);
2369 #if !defined(DOS)
2370 if(!ps_global->vars[V_PRINTER].is_fixed) /* printer can be changed */
2371 printer++;
2372 #endif
2374 #ifdef PASSWD_PROG
2375 if(F_OFF(F_DISABLE_PASSWORD_CMD,ps_global)) /* password is allowed */
2376 passwd++;
2377 #endif
2379 if(F_OFF(F_DISABLE_CONFIG_SCREEN,ps_global)) /* config allowed */
2380 config++;
2382 if(F_OFF(F_DISABLE_SIGEDIT_CMD,ps_global)) /* .sig editing is allowed */
2383 sig++;
2385 #ifdef ENABLE_LDAP
2386 dir++;
2387 #endif
2389 #ifdef SMIME
2390 smime++;
2391 #endif
2393 if(ps_global->post_prc)
2394 exc++;
2396 /* TRANSLATORS: starting here we have a whole screen of help text */
2397 so_puts(store, _("This is the Setup screen for Alpine. Choose from the following commands:\n"));
2399 so_puts(store, "\n");
2400 so_puts(store, _("(E) Exit Setup:\n"));
2401 so_puts(store, _(" This puts you back at the Main Menu.\n"));
2403 if(exc){
2404 so_puts(store, "\n");
2405 so_puts(store, _("(X) eXceptions:\n"));
2406 so_puts(store, _(" This command is different from the rest. It is not actually a command\n"));
2407 so_puts(store, _(" itself. Instead, it is a toggle which modifies the behavior of the\n"));
2408 so_puts(store, _(" other commands. You toggle Exceptions editing on and off with this\n"));
2409 so_puts(store, _(" command. When it is off you will be editing (changing) your regular\n"));
2410 so_puts(store, _(" configuration file. When it is on you will be editing your exceptions\n"));
2411 so_puts(store, _(" configuration file. For example, you might want to type the command \n"));
2412 so_puts(store, _(" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"));
2413 so_puts(store, _(" on a particular platform.\n"));
2414 so_puts(store, _(" (Note: this command does not show up on the keymenu at the bottom of\n"));
2415 so_puts(store, _(" the screen unless you press \"O\" for \"Other Commands\" --but you don't\n"));
2416 so_puts(store, _(" need to press the \"O\" in order to invoke the command.)\n"));
2419 if(printer){
2420 so_puts(store, "\n");
2421 so_puts(store, _("(P) Printer:\n"));
2422 so_puts(store, _(" Allows you to set a default printer and to define custom\n"));
2423 so_puts(store, _(" print commands.\n"));
2426 if(passwd){
2427 so_puts(store, "\n");
2428 so_puts(store, _("(N) Newpassword:\n"));
2429 so_puts(store, _(" Change your password.\n"));
2432 if(config){
2433 so_puts(store, "\n");
2434 so_puts(store, _("(C) Config:\n"));
2435 so_puts(store, _(" Allows you to set or unset many features of Alpine.\n"));
2436 so_puts(store, _(" You may also set the values of many options with this command.\n"));
2439 if(sig){
2440 so_puts(store, "\n");
2441 so_puts(store, _("(S) Signature:\n"));
2442 so_puts(store, _(" Enter or edit a custom signature which will\n"));
2443 so_puts(store, _(" be included with each new message you send.\n"));
2446 so_puts(store, "\n");
2447 so_puts(store, _("(A) AddressBooks:\n"));
2448 so_puts(store, _(" Define a non-default address book.\n"));
2450 so_puts(store, "\n");
2451 so_puts(store, _("(L) collectionLists:\n"));
2452 so_puts(store, _(" You may define groups of folders to help you better organize your mail.\n"));
2454 so_puts(store, "\n");
2455 so_puts(store, _("(R) Rules:\n"));
2456 so_puts(store, _(" This has up to six sub-categories: Roles, Index Colors, Filters,\n"));
2457 so_puts(store, _(" SetScores, Search, and Other. If the Index Colors option is\n"));
2458 so_puts(store, _(" missing you may turn it on (if possible) with Setup/Kolor.\n"));
2459 so_puts(store, _(" If Roles is missing it has probably been administratively disabled.\n"));
2461 if(dir){
2462 so_puts(store, "\n");
2463 so_puts(store, _("(D) Directory:\n"));
2464 so_puts(store, _(" Define an LDAP Directory server for Alpine's use. A directory server is\n"));
2465 so_puts(store, _(" similar to an address book, but it is usually maintained by an\n"));
2466 so_puts(store, _(" organization. It is similar to a telephone directory.\n"));
2469 so_puts(store, "\n");
2470 so_puts(store, _("(K) Kolor:\n"));
2471 so_puts(store, _(" Set custom colors for various parts of the Alpine screens. For example, the\n"));
2472 so_puts(store, _(" command key labels, the titlebar at the top of each page, and quoted\n"));
2473 so_puts(store, _(" sections of messages you are viewing.\n"));
2475 if(smime){
2476 so_puts(store, "\n");
2477 so_puts(store, _("(M) S/MIME:\n"));
2478 so_puts(store, _(" Setup for using S/MIME to verify signed messages, decrypt\n"));
2479 so_puts(store, _(" encrypted messages, and to sign or encrypt outgoing messages.\n"));
2482 so_puts(store, "\n");
2483 so_puts(store, _("(Z) RemoteConfigSetup:\n"));
2484 so_puts(store, _(" This is a command you will probably only want to use once, if at all.\n"));
2485 so_puts(store, _(" It helps you transfer your Alpine configuration data to an IMAP server,\n"));
2486 so_puts(store, _(" where it will be accessible from any of the computers you read mail\n"));
2487 so_puts(store, _(" from (using Alpine). The idea behind a remote configuration is that you\n"));
2488 so_puts(store, _(" can change your configuration in one place and have that change show\n"));
2489 so_puts(store, _(" up on all of the computers you use.\n"));
2490 so_puts(store, _(" (Note: this command does not show up on the keymenu at the bottom of\n"));
2491 so_puts(store, _(" the screen unless you press \"O\" for \"Other Commands\" --but you don't\n"));
2492 so_puts(store, _(" need to press the \"O\" in order to invoke the command.)\n"));
2494 /* put this down here for people who don't have exceptions */
2495 if(!exc){
2496 so_puts(store, "\n");
2497 so_puts(store, _("(X) eXceptions:\n"));
2498 so_puts(store, _(" This command is different from the rest. It is not actually a command\n"));
2499 so_puts(store, _(" itself. Instead, it is a toggle which modifies the behavior of the\n"));
2500 so_puts(store, _(" other commands. You toggle Exceptions editing on and off with this\n"));
2501 so_puts(store, _(" command. When it is off you will be editing (changing) your regular\n"));
2502 so_puts(store, _(" configuration file. When it is on you will be editing your exceptions\n"));
2503 so_puts(store, _(" configuration file. For example, you might want to type the command \n"));
2504 so_puts(store, _(" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"));
2505 so_puts(store, _(" on a particular platform.\n"));
2506 so_puts(store, _(" (Note: this command does not do anything unless you have a configuration\n"));
2507 so_puts(store, _(" with exceptions enabled (you don't have that). Common ways to enable an\n"));
2508 so_puts(store, _(" exceptions config are the command line argument \"-x <exception_config>\";\n"));
2509 so_puts(store, _(" or the existence of the file \".pinercex\" for Unix Alpine, or \"PINERCEX\")\n"));
2510 so_puts(store, _(" for PC-Alpine.)\n"));
2511 so_puts(store, _(" (Another note: this command does not show up on the keymenu at the bottom\n"));
2512 so_puts(store, _(" of the screen unless you press \"O\" for \"Other Commands\" --but you\n"));
2513 so_puts(store, _(" don't need to press the \"O\" in order to invoke the command.)\n"));
2516 memset(&sargs, 0, sizeof(SCROLL_S));
2517 sargs.text.text = so_text(store);
2518 sargs.text.src = CharStar;
2519 sargs.text.desc = _("Information About Setup Command");
2520 sargs.bar.title = cpystr(_("SETUP"));
2521 sargs.proc.tool = choose_setup_cmd;
2522 sargs.help.text = NO_HELP;
2523 sargs.help.title = NULL;
2524 sargs.keys.menu = &choose_setup_keymenu;
2525 sargs.keys.menu->how_many = 2;
2527 setbitmap(sargs.keys.bitmap);
2528 if(!printer)
2529 clrbitn(SETUP_PRINTER, sargs.keys.bitmap);
2531 if(!passwd)
2532 clrbitn(SETUP_PASSWD, sargs.keys.bitmap);
2534 if(!config)
2535 clrbitn(SETUP_CONFIG, sargs.keys.bitmap);
2537 if(!sig)
2538 clrbitn(SETUP_SIG, sargs.keys.bitmap);
2540 if(!dir)
2541 clrbitn(SETUP_DIRECTORY, sargs.keys.bitmap);
2543 if(!smime)
2544 clrbitn(SETUP_SMIME, sargs.keys.bitmap);
2546 if(exc)
2547 menu_init_binding(sargs.keys.menu, 'x', MC_EXCEPT, "X",
2548 N_("eXceptions"), SETUP_EXCEPT);
2549 else
2550 menu_init_binding(sargs.keys.menu, 'x', MC_NO_EXCEPT, "X",
2551 N_("eXceptions"), SETUP_EXCEPT);
2554 scrolltool(&sargs);
2556 ps->mangled_screen = 1;
2558 srv = (SRV_S *)sargs.proc.data.p;
2560 exceptions = srv ? srv->exc : 0;
2562 so_give(&store);
2564 if(sargs.bar.title) fs_give((void**)&sargs.bar.title);
2565 if(srv){
2566 ret = srv->cmd;
2567 fs_give((void **)&sargs.proc.data.p);
2569 else
2570 ret = 'e';
2572 return(ret | (exceptions ? EDIT_EXCEPTION : 0));
2576 /*----------------------------------------------------------------------
2578 Args: command -- command char to perform
2580 ----*/
2581 void
2582 do_setup_task(int command)
2584 char *err = NULL;
2585 int rtype;
2586 int edit_exceptions = 0;
2587 int do_lit_sig = 0;
2589 if(command & EDIT_EXCEPTION){
2590 edit_exceptions = 1;
2591 command &= ~EDIT_EXCEPTION;
2594 switch(command) {
2595 /*----- EDIT SIGNATURE -----*/
2596 case 's':
2597 if(ps_global->VAR_LITERAL_SIG)
2598 do_lit_sig = 1;
2599 else {
2600 char sig_path[MAXPATH+1];
2602 if(!signature_path(ps_global->VAR_SIGNATURE_FILE, sig_path, MAXPATH))
2603 do_lit_sig = 1;
2604 else if((!IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)
2605 && can_access(sig_path, READ_ACCESS) == 0)
2606 ||(IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)
2607 && (folder_exists(NULL, sig_path) & FEX_ISFILE)))
2608 do_lit_sig = 0;
2609 else if(!ps_global->vars[V_SIGNATURE_FILE].main_user_val.p
2610 && !ps_global->vars[V_SIGNATURE_FILE].cmdline_val.p
2611 && !ps_global->vars[V_SIGNATURE_FILE].fixed_val.p)
2612 do_lit_sig = 1;
2613 else
2614 do_lit_sig = 0;
2617 if(do_lit_sig){
2618 char *result = NULL;
2619 char **apval;
2620 EditWhich ew;
2621 int readonly = 0;
2623 ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
2625 if(ps_global->restricted)
2626 readonly = 1;
2627 else switch(ew){
2628 case Main:
2629 readonly = ps_global->prc->readonly;
2630 break;
2631 case Post:
2632 readonly = ps_global->post_prc->readonly;
2633 break;
2634 default:
2635 break;
2638 if(readonly)
2639 err = cpystr(ps_global->restricted
2640 ? "Alpine demo can't change config file"
2641 : _("Config file not changeable"));
2643 if(!err){
2644 apval = APVAL(&ps_global->vars[V_LITERAL_SIG], ew);
2645 if(!apval)
2646 err = cpystr(_("Problem accessing configuration"));
2647 else{
2648 char *input;
2650 input = (char *)fs_get((strlen(*apval ? *apval : "")+1) *
2651 sizeof(char));
2652 input[0] = '\0';
2653 cstring_to_string(*apval, input);
2654 err = signature_edit_lit(input, &result,
2655 _("SIGNATURE EDITOR"),
2656 h_composer_sigedit);
2657 fs_give((void **)&input);
2661 if(!err){
2662 char *cstring_version;
2664 cstring_version = string_to_cstring(result);
2666 set_variable(V_LITERAL_SIG, cstring_version, 0, 0, ew);
2668 if(cstring_version)
2669 fs_give((void **)&cstring_version);
2672 if(result)
2673 fs_give((void **)&result);
2675 else
2676 err = signature_edit(ps_global->VAR_SIGNATURE_FILE,
2677 _("SIGNATURE EDITOR"));
2679 if(err){
2680 q_status_message(SM_ORDER, 3, 4, err);
2681 fs_give((void **)&err);
2684 ps_global->mangled_screen = 1;
2685 break;
2687 /*----- ADD ADDRESSBOOK ----*/
2688 case 'a':
2689 addr_book_config(ps_global, edit_exceptions);
2690 menu_index = ABOOK_MENU_ITEM;
2691 ps_global->mangled_screen = 1;
2692 break;
2694 #ifdef ENABLE_LDAP
2695 /*--- ADD DIRECTORY SERVER --*/
2696 case 'd':
2697 directory_config(ps_global, edit_exceptions);
2698 ps_global->mangled_screen = 1;
2699 break;
2700 #endif
2702 #ifdef SMIME
2703 /*--- S/MIME --*/
2704 case 'm':
2705 smime_config_screen(ps_global, edit_exceptions);
2706 ps_global->mangled_screen = 1;
2707 break;
2708 #endif
2710 /*----- CONFIGURE OPTIONS -----*/
2711 case 'c':
2712 option_screen(ps_global, edit_exceptions);
2713 ps_global->mangled_screen = 1;
2714 break;
2716 /*----- COLLECTION LIST -----*/
2717 case 'l':
2718 folder_config_screen(ps_global, edit_exceptions);
2719 ps_global->mangled_screen = 1;
2720 break;
2722 /*----- RULES -----*/
2723 case 'r':
2724 rtype = rule_setup_type(ps_global, RS_RULES | RS_INCFILTNOW,
2725 _("Type of rule setup : "));
2726 switch(rtype){
2727 case 'r':
2728 case 's':
2729 case 'i':
2730 case 'f':
2731 case 'o':
2732 case 'c':
2733 role_config_screen(ps_global, (rtype == 'r') ? ROLE_DO_ROLES :
2734 (rtype == 's') ? ROLE_DO_SCORES :
2735 (rtype == 'o') ? ROLE_DO_OTHER :
2736 (rtype == 'f') ? ROLE_DO_FILTER :
2737 (rtype == 'c') ? ROLE_DO_SRCH :
2738 ROLE_DO_INCOLS,
2739 edit_exceptions);
2740 break;
2742 case 'Z':
2743 q_status_message(SM_ORDER | SM_DING, 3, 5,
2744 _("Try turning on color with the Setup/Kolor command."));
2745 break;
2747 case 'n':
2748 role_process_filters();
2749 break;
2751 default:
2752 cmd_cancelled(NULL);
2753 break;
2756 ps_global->mangled_screen = 1;
2757 break;
2759 /*----- COLOR -----*/
2760 case 'k':
2761 color_config_screen(ps_global, edit_exceptions);
2762 ps_global->mangled_screen = 1;
2763 break;
2765 case 'z':
2766 convert_to_remote_config(ps_global, edit_exceptions);
2767 ps_global->mangled_screen = 1;
2768 break;
2770 /*----- EXIT -----*/
2771 case 'e':
2772 break;
2774 /*----- NEW PASSWORD -----*/
2775 case 'n':
2776 #ifdef PASSWD_PROG
2777 if(ps_global->restricted){
2778 q_status_message(SM_ORDER, 3, 5,
2779 "Password change unavailable in restricted demo version of Alpine.");
2780 }else {
2781 change_passwd();
2782 ClearScreen();
2783 ps_global->mangled_screen = 1;
2785 #else
2786 q_status_message(SM_ORDER, 0, 5,
2787 _("Password changing not configured for this version of Alpine."));
2788 display_message('x');
2789 #endif /* DOS */
2790 break;
2792 #if !defined(DOS)
2793 /*----- CHOOSE PRINTER ------*/
2794 case 'p':
2795 select_printer(ps_global, edit_exceptions);
2796 ps_global->mangled_screen = 1;
2797 break;
2798 #endif
2804 rule_setup_type(struct pine *ps, int flags, char *prompt)
2806 ESCKEY_S opts[9];
2807 int ekey_num = 0, deefault = 0;
2809 if(flags & RS_INCADDR){
2810 deefault = 'a';
2811 opts[ekey_num].ch = 'a';
2812 opts[ekey_num].rval = 'a';
2813 opts[ekey_num].name = "A";
2814 opts[ekey_num++].label = "Addrbook";
2817 if(flags & RS_RULES){
2819 if(F_OFF(F_DISABLE_ROLES_SETUP,ps)){ /* roles are allowed */
2820 if(deefault != 'a')
2821 deefault = 'r';
2823 opts[ekey_num].ch = 'r';
2824 opts[ekey_num].rval = 'r';
2825 opts[ekey_num].name = "R";
2826 opts[ekey_num++].label = "Roles";
2828 else if(deefault != 'a')
2829 deefault = 's';
2831 opts[ekey_num].ch = 's';
2832 opts[ekey_num].rval = 's';
2833 opts[ekey_num].name = "S";
2834 opts[ekey_num++].label = "SetScores";
2836 #ifndef _WINDOWS
2837 if(ps->color_style != COL_NONE && pico_hascolor()){
2838 #endif
2839 if(deefault != 'a')
2840 deefault = 'i';
2842 opts[ekey_num].ch = 'i';
2843 opts[ekey_num].rval = 'i';
2844 opts[ekey_num].name = "I";
2845 opts[ekey_num++].label = "Indexcolor";
2846 #ifndef _WINDOWS
2848 else{
2849 opts[ekey_num].ch = 'i';
2850 opts[ekey_num].rval = 'Z'; /* notice this rval! */
2851 opts[ekey_num].name = "I";
2852 opts[ekey_num++].label = "Indexcolor";
2854 #endif
2856 opts[ekey_num].ch = 'f';
2857 opts[ekey_num].rval = 'f';
2858 opts[ekey_num].name = "F";
2859 opts[ekey_num++].label = "Filters";
2861 opts[ekey_num].ch = 'o';
2862 opts[ekey_num].rval = 'o';
2863 opts[ekey_num].name = "O";
2864 opts[ekey_num++].label = "Other";
2866 opts[ekey_num].ch = 'c';
2867 opts[ekey_num].rval = 'c';
2868 opts[ekey_num].name = "C";
2869 opts[ekey_num++].label = "searCh";
2873 if(flags & RS_INCEXP){
2874 opts[ekey_num].ch = 'e';
2875 opts[ekey_num].rval = 'e';
2876 opts[ekey_num].name = "E";
2877 opts[ekey_num++].label = "Export";
2880 if(flags & RS_INCFILTNOW){
2881 opts[ekey_num].ch = 'n';
2882 opts[ekey_num].rval = 'n';
2883 opts[ekey_num].name = "N";
2884 opts[ekey_num++].label = "filterNow";
2887 opts[ekey_num].ch = -1;
2889 return(radio_buttons(prompt, -FOOTER_ROWS(ps), opts,
2890 deefault, 'x', NO_HELP, RB_NORM));
2896 * Process the command list, changing function key notation into
2897 * lexical equivalents.
2899 void
2900 process_init_cmds(struct pine *ps, char **list)
2902 char **p;
2903 int i = 0;
2904 int j;
2905 int lpm1;
2906 #define MAX_INIT_CMDS 500
2907 /* this is just a temporary stack array, the real one is allocated below */
2908 int i_cmds[MAX_INIT_CMDS];
2909 int fkeys = 0;
2910 int not_fkeys = 0;
2912 if(list){
2913 for(p = list; *p; p++){
2914 if(i >= MAX_INIT_CMDS){
2915 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
2916 "Initial keystroke list too long at \"%s\"", *p);
2917 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
2918 break;
2922 /* regular character commands */
2923 if(strlen(*p) == 1){
2924 i_cmds[i++] = **p;
2925 not_fkeys++;
2928 /* special commands */
2929 else if(strucmp(*p, "SPACE") == 0)
2930 i_cmds[i++] = ' ';
2931 else if(strucmp(*p, "CR") == 0)
2932 i_cmds[i++] = '\n';
2933 else if(strucmp(*p, "TAB") == 0)
2934 i_cmds[i++] = '\t';
2935 else if(strucmp(*p, "UP") == 0)
2936 i_cmds[i++] = KEY_UP;
2937 else if(strucmp(*p, "DOWN") == 0)
2938 i_cmds[i++] = KEY_DOWN;
2939 else if(strucmp(*p, "LEFT") == 0)
2940 i_cmds[i++] = KEY_LEFT;
2941 else if(strucmp(*p, "RIGHT") == 0)
2942 i_cmds[i++] = KEY_RIGHT;
2944 /* control chars */
2945 else if(strlen(*p) == 2 && **p == '^')
2946 i_cmds[i++] = ctrl(*((*p)+1));
2948 /* function keys */
2949 else if(**p == 'F' || **p == 'f'){
2950 int v;
2952 fkeys++;
2953 v = atoi((*p)+1);
2954 if(v >= 1 && v <= 12)
2955 i_cmds[i++] = PF1 + v - 1;
2956 else
2957 i_cmds[i++] = KEY_JUNK;
2960 /* literal string */
2961 else if(**p == '"' && (*p)[lpm1 = strlen(*p) - 1] == '"'){
2962 if(lpm1 + i - 1 > MAX_INIT_CMDS){
2963 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
2964 "Initial keystroke list too long, truncated at %s\n", *p);
2965 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
2966 break; /* Bail out of this loop! */
2967 } else
2968 for(j = 1; j < lpm1; j++)
2969 i_cmds[i++] = (*p)[j];
2971 else {
2972 snprintf(tmp_20k_buf,SIZEOF_20KBUF,
2973 "Bad initial keystroke \"%.500s\" (missing comma?)", *p);
2974 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
2975 break;
2981 * We don't handle the case where function keys are used to specify the
2982 * commands but some non-function key input is also required. For example,
2983 * you might want to jump to a specific message number and view it
2984 * on start up. To do that, you need to use character commands instead
2985 * of function key commands in the initial-keystroke-list.
2987 if(fkeys && not_fkeys){
2988 init_error(ps, SM_ORDER | SM_DING, 3, 5,
2989 "Mixed characters and function keys in \"initial-keystroke-list\", skipping.");
2990 i = 0;
2993 if(fkeys && !not_fkeys)
2994 F_TURN_ON(F_USE_FK,ps);
2995 if(!fkeys && not_fkeys)
2996 F_TURN_OFF(F_USE_FK,ps);
2998 if(i > 0){
2999 ps->initial_cmds = (int *)fs_get((i+1) * sizeof(int));
3000 ps->free_initial_cmds = ps->initial_cmds;
3001 for(j = 0; j < i; j++)
3002 ps->initial_cmds[j] = i_cmds[j];
3004 ps->initial_cmds[i] = 0;
3005 ps->in_init_seq = ps->save_in_init_seq = 1;
3010 UCS *
3011 user_wordseps(char **list)
3013 char **p;
3014 int i = 0;
3015 int j;
3016 #define MAX_SEPARATORS 500
3018 * This is just a temporary stack array, the real one is allocated below.
3019 * This is supposed to be way large enough.
3021 UCS seps[MAX_SEPARATORS+1];
3022 UCS *u;
3023 UCS *return_array = NULL;
3024 size_t l;
3026 seps[0] = '\0';
3028 if(list){
3029 for(p = list; *p; p++){
3030 if(i >= MAX_SEPARATORS){
3031 q_status_message(SM_ORDER | SM_DING, 3, 3,
3032 "Warning: composer-word-separators list is too long");
3033 break;
3036 u = utf8_to_ucs4_cpystr(*p);
3038 if(u){
3039 if(ucs4_strlen(u) == 1)
3040 seps[i++] = *u;
3041 else if(*u == '"' && u[l = ucs4_strlen(u) - 1] == '"'){
3042 if(l + i - 1 > MAX_SEPARATORS){
3043 q_status_message(SM_ORDER | SM_DING, 3, 3,
3044 "Warning: composer-word-separators list is too long");
3045 break; /* Bail out of this loop! */
3047 else{
3048 for(j = 1; j < l; j++)
3049 seps[i++] = u[j];
3052 else{
3053 l = ucs4_strlen(u);
3054 if(l + i > MAX_SEPARATORS){
3055 q_status_message(SM_ORDER | SM_DING, 3, 3,
3056 "Warning: composer-word-separators list is too long");
3057 break; /* Bail out of this loop! */
3059 else{
3060 for(j = 0; j < l; j++)
3061 seps[i++] = u[j];
3065 fs_give((void **) &u);
3070 seps[i] = '\0';
3072 if(i > 0)
3073 return_array = ucs4_cpystr(seps);
3075 return(return_array);
3080 * Make sure any errors during initialization get queued for display
3082 void
3083 queue_init_errors(struct pine *ps)
3085 int i;
3087 if(ps->init_errs){
3088 for(i = 0; (ps->init_errs)[i].message; i++){
3089 q_status_message((ps->init_errs)[i].flags,
3090 (ps->init_errs)[i].min_time,
3091 (ps->init_errs)[i].max_time,
3092 (ps->init_errs)[i].message);
3093 fs_give((void **)&(ps->init_errs)[i].message);
3096 fs_give((void **)&ps->init_errs);
3101 /*----------------------------------------------------------------------
3102 Quit pine if the user wants to
3104 Args: The usual pine structure
3106 Result: User is asked if she wants to quit, if yes then execute quit.
3108 Q U I T S C R E E N
3110 Not really a full screen. Just count up deletions and ask if we really
3111 want to quit.
3112 ----*/
3113 void
3114 quit_screen(struct pine *pine_state)
3116 int quit = 0;
3118 dprint((1, "\n\n ---- QUIT SCREEN ----\n"));
3120 if(F_ON(F_CHECK_MAIL_ONQUIT,ps_global)
3121 && new_mail(1, VeryBadTime, NM_STATUS_MSG | NM_DEFER_SORT) > 0
3122 && (quit = want_to(_("Quit even though new mail just arrived"), 'y', 0,
3123 NO_HELP, WT_NORM)) != 'y'){
3124 refresh_sort(pine_state->mail_stream, pine_state->msgmap, SRT_VRB);
3125 pine_state->next_screen = pine_state->prev_screen;
3126 return;
3129 if(quit != 'y'
3130 && F_OFF(F_QUIT_WO_CONFIRM,pine_state)
3131 && want_to(_("Really quit Alpine"), 'y', 0, NO_HELP, WT_NORM) != 'y'){
3132 pine_state->next_screen = pine_state->prev_screen;
3133 return;
3136 goodnight_gracey(pine_state, 0);
3140 /*----------------------------------------------------------------------
3141 The nuts and bolts of actually cleaning up and exitting pine
3143 Args: ps -- the usual pine structure,
3144 exit_val -- what to tell our parent
3146 Result: This never returns
3148 ----*/
3149 void
3150 goodnight_gracey(struct pine *pine_state, int exit_val)
3152 int i, cnt_user_streams = 0;
3153 char *final_msg = NULL;
3154 char msg[MAX_SCREEN_COLS+1];
3155 char *pf = _("Alpine finished");
3156 MAILSTREAM *m;
3157 extern KBESC_T *kbesc;
3159 dprint((2, "goodnight_gracey:\n"));
3161 /* We want to do this here before we close up the streams */
3162 trim_remote_adrbks();
3164 for(i = 0; i < ps_global->s_pool.nstream; i++){
3165 m = ps_global->s_pool.streams[i];
3166 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR))
3167 cnt_user_streams++;
3170 /* clean up open streams */
3172 if(pine_state->mail_stream
3173 && sp_flagged(pine_state->mail_stream, SP_LOCKED)
3174 && sp_flagged(pine_state->mail_stream, SP_USERFLDR)){
3175 dprint((5, "goodnight_gracey: close current stream\n"));
3176 expunge_and_close(pine_state->mail_stream,
3177 (cnt_user_streams <= 1) ? &final_msg : NULL, EC_NONE);
3178 cnt_user_streams--;
3181 pine_state->mail_stream = NULL;
3182 pine_state->redrawer = (void (*)(void))NULL;
3184 dprint((5,
3185 "goodnight_gracey: close other stream pool streams\n"));
3186 for(i = 0; i < ps_global->s_pool.nstream; i++){
3187 m = ps_global->s_pool.streams[i];
3189 * fix global for functions that depend(ed) on it sort_folder.
3190 * Hopefully those will get phased out.
3192 ps_global->mail_stream = m;
3193 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
3194 && !sp_flagged(m, SP_INBOX)){
3195 sp_set_expunge_count(m, 0L);
3196 expunge_and_close(m, (cnt_user_streams <= 1) ? &final_msg : NULL,
3197 EC_NONE);
3198 cnt_user_streams--;
3202 for(i = 0; i < ps_global->s_pool.nstream; i++){
3203 m = ps_global->s_pool.streams[i];
3205 * fix global for functions that depend(ed) on it (sort_folder).
3206 * Hopefully those will get phased out.
3208 ps_global->mail_stream = m;
3209 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
3210 && sp_flagged(m, SP_INBOX)){
3211 dprint((5,
3212 "goodnight_gracey: close inbox stream stream\n"));
3213 sp_set_expunge_count(m, 0L);
3214 expunge_and_close(m, (cnt_user_streams <= 1) ? &final_msg : NULL,
3215 EC_NONE);
3216 cnt_user_streams--;
3220 #ifdef _WINDOWS
3221 if(ps_global->ttyo)
3222 (void)get_windsize(ps_global->ttyo);
3223 #endif
3225 dprint((7, "goodnight_gracey: close config files\n"));
3227 #ifdef SMIME
3228 smime_deinit();
3229 #endif
3231 free_pinerc_strings(&pine_state);
3233 strncpy(msg, pf, sizeof(msg));
3234 msg[sizeof(msg)-1] = '\0';
3235 if(final_msg){
3236 strncat(msg, " -- ", sizeof(msg)-strlen(msg)-1);
3237 msg[sizeof(msg)-1] = '\0';
3238 strncat(msg, final_msg, sizeof(msg)-strlen(msg)-1);
3239 msg[sizeof(msg)-1] = '\0';
3240 fs_give((void **)&final_msg);
3243 dprint((7, "goodnight_gracey: sp_end\n"));
3244 ps_global->noshow_error = 1;
3245 sp_end();
3247 /* after sp_end, which might call a filter */
3248 completely_done_with_adrbks();
3250 dprint((7, "goodnight_gracey: end_screen\n"));
3251 end_screen(msg, exit_val);
3252 dprint((7, "goodnight_gracey: end_titlebar\n"));
3253 end_titlebar();
3254 dprint((7, "goodnight_gracey: end_keymenu\n"));
3255 end_keymenu();
3257 dprint((7, "goodnight_gracey: end_keyboard\n"));
3258 end_keyboard(F_ON(F_USE_FK,pine_state));
3259 dprint((7, "goodnight_gracey: end_ttydriver\n"));
3260 end_tty_driver(pine_state);
3261 #if !defined(DOS) && !defined(OS2)
3262 kbdestroy(kbesc);
3263 #if !defined(LEAVEOUTFIFO)
3264 close_newmailfifo();
3265 #endif
3266 #endif
3267 end_signals(0);
3268 if(filter_data_file(0))
3269 our_unlink(filter_data_file(0));
3271 imap_flush_passwd_cache(TRUE);
3272 free_newsgrp_cache();
3273 mailcap_free();
3274 close_every_pattern();
3275 free_extra_hdrs();
3276 free_contexts(&ps_global->context_list);
3277 free_charsetchecker();
3278 dprint((7, "goodnight_gracey: free more memory\n"));
3279 #ifdef ENABLE_LDAP
3280 free_saved_query_parameters();
3281 #endif
3283 free_pine_struct(&pine_state);
3285 free_histlist();
3287 #ifdef DEBUG
3288 if(debugfile){
3289 if(debug >= 2)
3290 fputs("goodnight_gracey finished\n", debugfile);
3292 fclose(debugfile);
3294 #endif
3296 exit(exit_val);
3300 /*----------------------------------------------------------------------
3301 Call back for c-client to feed us back the progress of network reads
3303 Input:
3305 Result:
3306 ----*/
3307 void
3308 pine_read_progress(GETS_DATA *md, long unsigned int count)
3310 gets_bytes += count; /* update counter */
3314 /*----------------------------------------------------------------------
3315 Function to fish the current byte count from a c-client fetch.
3317 Input: reset -- flag telling us to reset the count
3319 Result: Returns the number of bytes read by the c-client so far
3320 ----*/
3321 unsigned long
3322 pine_gets_bytes(int reset)
3324 if(reset)
3325 gets_bytes = 0L;
3327 return(gets_bytes);
3331 /*----------------------------------------------------------------------
3332 Panic pine - call on detected programmatic errors to exit pine
3334 Args: message -- message to record in debug file and to be printed for user
3336 Result: The various tty modes are restored
3337 If debugging is active a core dump will be generated
3338 Exits Alpine
3340 This is also called from imap routines and fs_get and fs_resize.
3341 ----*/
3342 void
3343 alpine_panic(char *message)
3345 char buf[256];
3347 /* global variable in .../pico/edef.h */
3348 in_panic = 1;
3350 if(ps_global->ttyo){
3351 end_screen(NULL, -1);
3352 end_keyboard(ps_global != NULL ? F_ON(F_USE_FK,ps_global) : 0);
3353 end_tty_driver(ps_global);
3354 end_signals(1);
3356 if(filter_data_file(0))
3357 our_unlink(filter_data_file(0));
3359 dprint((1, "\n===========================================\n\n"));
3360 dprint((1, " Alpine Panic: %s\n\n", message ? message : "?"));
3361 dprint((1, "===========================================\n\n"));
3363 /* intercept c-client "free storage" errors */
3364 if(strstr(message, "free storage"))
3365 snprintf(buf, sizeof(buf), _("No more available memory.\nAlpine Exiting"));
3366 else
3367 snprintf(buf, sizeof(buf), _("Problem detected: \"%s\".\nAlpine Exiting."), message);
3369 buf[sizeof(buf)-1] = '\0';
3371 #ifdef _WINDOWS
3372 /* Put up a message box. */
3373 mswin_messagebox (buf, 1);
3374 #else
3375 fprintf(stderr, "\n\n%s\n", buf);
3376 #endif
3378 #ifdef DEBUG
3379 if(debugfile){
3380 save_debug_on_crash(debugfile, recent_keystroke);
3383 coredump(); /*--- If we're debugging get a core dump --*/
3384 #endif
3386 exit(-1);
3387 fatal("ffo"); /* BUG -- hack to get fatal out of library in right order*/
3392 * panicking - function to test whether or not we're exiting under stress.
3396 panicking(void)
3398 return(in_panic);
3402 /*----------------------------------------------------------------------
3403 exceptional_exit - called to exit under unusual conditions (with no core)
3405 Args: message -- message to record in debug file and to be printed for user
3406 ev -- exit value
3408 ----*/
3409 void
3410 exceptional_exit(char *message, int ev)
3412 fprintf(stderr, "%s\n", message);
3413 exit(ev);
3418 * PicoText Storage Object Support Routines
3421 STORE_S *
3422 pine_pico_get(void)
3424 return((STORE_S *)pico_get());
3428 pine_pico_give(STORE_S **sop)
3430 pico_give((void *)sop);
3431 return(1);
3435 pine_pico_writec(int c, STORE_S *so)
3437 unsigned char ch = (unsigned char) c;
3439 return(pico_writec(so->txt, ch, PICOREADC_NONE));
3443 pine_pico_writec_noucs(int c, STORE_S *so)
3445 unsigned char ch = (unsigned char) c;
3447 return(pico_writec(so->txt, ch, PICOREADC_NOUCS));
3451 pine_pico_readc(unsigned char *c, STORE_S *so)
3453 return(pico_readc(so->txt, c, PICOREADC_NONE));
3457 pine_pico_readc_noucs(unsigned char *c, STORE_S *so)
3459 return(pico_readc(so->txt, c, PICOREADC_NOUCS));
3463 pine_pico_puts(STORE_S *so, char *s)
3465 return(pico_puts(so->txt, s, PICOREADC_NONE));
3469 pine_pico_puts_noucs(STORE_S *so, char *s)
3471 return(pico_puts(so->txt, s, PICOREADC_NOUCS));
3475 pine_pico_seek(STORE_S *so, long pos, int orig)
3477 return(pico_seek((void *)so, pos, orig));
3482 remote_pinerc_failure(void)
3484 #ifdef _WINDOWS
3485 if(ps_global->install_flag) /* just exit silently */
3486 exit(0);
3487 #endif /* _WINDOWS */
3489 if(ps_global->exit_if_no_pinerc){
3490 exceptional_exit("Exiting because -bail option is set and config file not readable.", -1);
3493 if(want_to("Trouble reading remote configuration! Continue anyway ",
3494 'n', 'n', NO_HELP, WT_FLUSH_IN) != 'y'){
3495 return(0);
3498 return(1);
3502 void
3503 dump_supported_options(void)
3505 char **config;
3507 config = get_supported_options();
3508 if(config){
3509 display_args_err(NULL, config, 0);
3510 free_list_array(&config);
3515 /*----------------------------------------------------------------------
3516 Check pruned-folders for validity, making sure they are in the
3517 same context as sent-mail.
3519 ----*/
3521 prune_folders_ok(void)
3523 char **p;
3525 for(p = ps_global->VAR_PRUNED_FOLDERS; p && *p && **p; p++)
3526 if(!context_isambig(*p))
3527 return(0);
3529 return(1);
3533 #ifdef WIN32
3534 char *
3535 pine_user_callback()
3537 if(ps_global->VAR_USER_ID && ps_global->VAR_USER_ID[0]){
3538 return(ps_global->VAR_USER_ID);
3540 else{
3541 /* SHOULD PROMPT HERE! */
3542 return(NULL);
3545 #endif
3547 #ifdef _WINDOWS
3549 * windows callback to get/set function keys mode state
3552 fkey_mode_callback(set, args)
3553 int set;
3554 long args;
3556 return(F_ON(F_USE_FK, ps_global) != 0);
3560 void
3561 imap_telemetry_on()
3563 if(ps_global->mail_stream)
3564 mail_debug(ps_global->mail_stream);
3568 void
3569 imap_telemetry_off()
3571 if(ps_global->mail_stream)
3572 mail_nodebug(ps_global->mail_stream);
3576 char *
3577 pcpine_help_main(title)
3578 char *title;
3580 if(title)
3581 strncpy(title, _("PC-Alpine MAIN MENU Help"), 256);
3583 return(pcpine_help(main_menu_tx));
3588 pcpine_main_cursor(col, row)
3589 int col;
3590 long row;
3592 unsigned ndmi;
3594 if (row >= (HEADER_ROWS(ps_global) + MNSKIP(ps_global)))
3595 ndmi = (row+1 - HEADER_ROWS(ps_global) - (MNSKIP(ps_global)+1))/(MNSKIP(ps_global)+1);
3597 if (row >= (HEADER_ROWS(ps_global) + MNSKIP(ps_global))
3598 && !(MNSKIP(ps_global) && (row+1) & 0x01)
3599 && ndmi <= MAX_MENU_ITEM
3600 && FOOTER_ROWS(ps_global) + (ndmi+1)*(MNSKIP(ps_global)+1)
3601 + MNSKIP(ps_global) + FOOTER_ROWS(ps_global) <= ps_global->ttyo->screen_rows)
3602 return(MSWIN_CURSOR_HAND);
3603 else
3604 return(MSWIN_CURSOR_ARROW);
3606 #endif /* _WINDOWS */