* New version 2.21.999
[alpine.git] / alpine / alpine.c
blobd762059b04c6985cf0f65ac8eccc36274fcb2823
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-2018 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"
27 #include "../pith/body.h"
29 #include "osdep/debuging.h"
30 #include "osdep/termout.gen.h"
31 #include "osdep/chnge_pw.h"
33 #include "alpine.h"
34 #include "mailindx.h"
35 #include "mailcmd.h"
36 #include "addrbook.h"
37 #include "reply.h"
38 #include "arg.h"
39 #include "keymenu.h"
40 #include "status.h"
41 #include "context.h"
42 #include "mailview.h"
43 #include "imap.h"
44 #include "radio.h"
45 #include "folder.h"
46 #include "send.h"
47 #include "help.h"
48 #include "titlebar.h"
49 #include "takeaddr.h"
50 #include "dispfilt.h"
51 #include "init.h"
52 #include "remote.h"
53 #include "pattern.h"
54 #include "setup.h"
55 #include "newuser.h"
56 #include "adrbkcmd.h"
57 #include "signal.h"
58 #include "kblock.h"
59 #include "ldapconf.h"
60 #include "roleconf.h"
61 #include "colorconf.h"
62 #include "print.h"
63 #include "after.h"
64 #include "smime.h"
65 #include "newmail.h"
66 #ifndef _WINDOWS
67 #include "../pico/osdep/raw.h" /* for STD*_FD */
68 #endif
71 #define PIPED_FD 5 /* Some innocuous desc */
74 /* look for my_timer_period in pico directory for an explanation */
75 int my_timer_period = ((IDLE_TIMEOUT + 1)*1000);
77 /* byte count used by our gets routine to keep track */
78 static unsigned long gets_bytes;
82 * Internal prototypes
84 void convert_args_to_utf8(struct pine *, ARGDATA_S *);
85 void preopen_stayopen_folders(void);
86 int read_stdin_char(char *);
87 void main_redrawer(void);
88 void show_main_screen(struct pine *, int, OtherMenu, struct key_menu *, int, Pos *);
89 void do_menu(int, Pos *, struct key_menu *);
90 int choose_setup_cmd(int, MSGNO_S *, SCROLL_S *);
91 int setup_menu(struct pine *);
92 void do_setup_task(int);
93 void queue_init_errors(struct pine *);
94 void process_init_cmds(struct pine *, char **);
95 void goodnight_gracey(struct pine *, int);
96 void pine_read_progress(GETS_DATA *, unsigned long);
97 int remote_pinerc_failure(void);
98 void dump_supported_options(void);
99 int prune_folders_ok(void);
100 #ifdef WIN32
101 char *pine_user_callback(void);
102 #endif
103 #ifdef _WINDOWS
104 int fkey_mode_callback(int, long);
105 void imap_telemetry_on(void);
106 void imap_telemetry_off(void);
107 char *pcpine_help_main(char *);
108 int pcpine_main_cursor(int, long);
109 #define main app_main
110 #endif
113 typedef struct setup_return_val {
114 int cmd;
115 int exc;
116 }SRV_S;
120 * strlen of longest label from keymenu, of labels corresponding to
121 * commands in the middle of the screen. 9 is length of ListFldrs
123 #define LONGEST_LABEL 9 /* length of longest label from keymenu */
125 #define EDIT_EXCEPTION (0x100)
128 static int in_panic = 0;
131 /*----------------------------------------------------------------------
132 main routine -- entry point
134 Args: argv, argc -- The command line arguments
137 Initialize pine, parse arguments and so on
139 If there is a user address on the command line go into send mode and exit,
140 otherwise loop executing the various screens in Alpine.
142 NOTE: The Windows port def's this to "app_main"
143 ----*/
146 main(int argc, char **argv)
148 ARGDATA_S args;
149 int rv;
150 long rvl;
151 struct pine *pine_state;
152 gf_io_t stdin_getc = NULL;
153 char *args_for_debug = NULL, *init_pinerc_debugging = NULL;
155 /*----------------------------------------------------------------------
156 Set up buffering and some data structures
157 ----------------------------------------------------------------------*/
159 pine_state = new_pine_struct();
160 pine_state->id = fs_get(sizeof(IDLIST));
161 pine_state->id->name = cpystr("name");
162 pine_state->id->value = cpystr(PACKAGE_NAME);
163 pine_state->id->next = fs_get(sizeof(IDLIST));
164 pine_state->id->next->name = cpystr("version");
165 pine_state->id->next->value = cpystr(PACKAGE_VERSION);
166 pine_state->id->next->next = NULL;
167 mail_parameters(NULL, SET_IDPARAMS, (void *) pine_state->id);
168 ps_global = pine_state;
171 * fill in optional pith-offered behavior hooks
173 pith_opt_read_msg_prompt = read_msg_prompt;
174 pith_opt_paint_index_hline = paint_index_hline;
175 pith_opt_rfc2369_editorial = rfc2369_editorial;
176 pith_opt_condense_thread_cue = condensed_thread_cue;
177 pith_opt_truncate_sfstr = truncate_subj_and_from_strings;
178 pith_opt_save_and_restore = save_and_restore;
179 pith_opt_newmail_announce = newmail_status_message;
180 pith_opt_newmail_check_cue = newmail_check_cue;
181 pith_opt_checkpoint_cue = newmail_check_point_cue;
182 pith_opt_icon_text = icon_text;
183 pith_opt_rd_metadata_name = rd_metadata_name;
184 pith_opt_remote_pinerc_failure = remote_pinerc_failure;
185 pith_opt_reopen_folder = ask_mailbox_reopen;
186 pith_opt_expunge_prompt = expunge_prompt;
187 pith_opt_begin_closing = expunge_and_close_begins;
188 pith_opt_replyto_prompt = reply_using_replyto_query;
189 pith_opt_reply_to_all_prompt = reply_to_all_query;
190 pith_opt_save_create_prompt = create_for_save_prompt;
191 pith_opt_daemon_confirm = confirm_daemon_send;
192 pith_opt_save_size_changed_prompt = save_size_changed_prompt;
193 pith_opt_save_index_state = setup_index_state;
194 pith_opt_filter_pattern_cmd = pattern_filter_command;
195 pith_opt_get_signature_file = get_signature_file;
196 pith_opt_pretty_var_name = pretty_var_name;
197 pith_opt_pretty_feature_name = pretty_feature_name;
198 pith_opt_closing_stream = titlebar_stream_closing;
199 pith_opt_current_expunged = mm_expunged_current;
200 #ifdef SMIME
201 pith_opt_smime_get_passphrase = smime_get_passphrase;
202 pith_smime_import_certificate = smime_import_certificate;
203 pith_smime_enter_password = alpine_get_password;
204 pith_smime_confirm_save = alpine_smime_confirm_save;
205 #endif
206 #ifdef ENABLE_LDAP
207 pith_opt_save_ldap_entry = save_ldap_entry;
208 #endif
210 status_message_lock_init();
211 inverse_itokens();
213 #if HAVE_SRANDOM
215 * Seed the random number generator with the date & pid. Random
216 * numbers are used for new mail notification and bug report id's
218 srandom(getpid() + time(0));
219 #endif
221 /* need home directory early */
222 get_user_info(&ps_global->ui);
224 if(!(pine_state->home_dir = our_getenv("HOME")))
225 pine_state->home_dir = cpystr(ps_global->ui.homedir);
227 #ifdef _WINDOWS
229 char *p;
231 /* normalize path delimiters */
232 for(p = pine_state->home_dir; p = strchr(p, '/'); p++)
233 *p='\\';
235 #endif /* _WINDOWS */
237 #ifdef DEBUG
238 { size_t len = 0;
239 int i;
240 char *p;
241 char *no_args = " <no args>";
243 for(i = 0; i < argc; i++)
244 len += (strlen(argv[i] ? argv[i] : "")+3);
246 if(argc == 1)
247 len += strlen(no_args);
249 p = args_for_debug = (char *)fs_get((len+2) * sizeof(char));
250 *p++ = '\n';
251 *p = '\0';
253 for(i = 0; i < argc; i++){
254 snprintf(p, len+2-(p-args_for_debug), "%s\"%s\"", i ? " " : "", argv[i] ? argv[i] : "");
255 args_for_debug[len+2-1] = '\0';
256 p += strlen(p);
259 if(argc == 1){
260 strncat(args_for_debug, no_args, len+2-strlen(args_for_debug)-1);
261 args_for_debug[len+2-1] = '\0';
264 #endif
266 /*----------------------------------------------------------------------
267 Parse arguments and initialize debugging
268 ----------------------------------------------------------------------*/
269 pine_args(pine_state, argc, argv, &args);
271 #ifndef _WINDOWS
272 if(!isatty(0)){
274 * monkey with descriptors so our normal tty i/o routines don't
275 * choke...
277 dup2(STDIN_FD, PIPED_FD); /* redirected stdin to new desc */
278 dup2(STDERR_FD, STDIN_FD); /* rebind stdin to the tty */
279 stdin_getc = read_stdin_char;
280 if(stdin_getc && args.action == aaURL){
281 display_args_err(
282 "Cannot read stdin when using -url\nFor mailto URLs, use \'body=\' instead",
283 NULL, 1);
284 args_help();
285 exit(-1);
289 #else /* _WINDOWS */
291 * We now have enough information to do some of the basic registry settings.
293 if(ps_global->update_registry != UREG_NEVER_SET){
294 mswin_reg(MSWR_OP_SET
295 | ((ps_global->update_registry == UREG_ALWAYS_SET)
296 ? MSWR_OP_FORCE : 0),
297 MSWR_PINE_DIR, ps_global->pine_dir, (size_t)NULL);
298 mswin_reg(MSWR_OP_SET
299 | ((ps_global->update_registry == UREG_ALWAYS_SET)
300 ? MSWR_OP_FORCE : 0),
301 MSWR_PINE_EXE, ps_global->pine_name, (size_t)NULL);
304 #endif /* _WINDOWS */
306 if(ps_global->convert_sigs &&
307 (!ps_global->pinerc || !ps_global->pinerc[0])){
308 fprintf(stderr, "Use -p <pinerc> with -convert_sigs\n");
309 exit(-1);
312 /* Windows has its own functions to determine width of a character
313 * in the screen, so this is not necessary to do in Window, and
314 * using pith_ucs4width does not produce the correct result
316 #if !defined(_WINDOWS) && defined(LC_CTYPE)
317 { char *s;
318 if((s = setlocale(LC_CTYPE, "")) != NULL
319 && strlen(s) >= 5
320 && !strucmp(s+strlen(s)-5, "UTF-8"))
321 mail_parameters(NULL, SET_UCS4WIDTH, (void *) pith_ucs4width);
323 #endif /* !_WINDOWS && LC_CTYPE */
324 mail_parameters(NULL, SET_QUOTA, (void *) pine_parse_quota);
325 /* set some default timeouts in case pinerc is remote */
326 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)30);
327 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)15);
328 mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
329 /* could be TO_BAIL_THRESHOLD, 15 seems more appropriate for now */
330 pine_state->tcp_query_timeout = 15;
332 mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened);
333 mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
334 mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt);
335 mail_parameters(NULL, SET_FREEBODYSPAREP, (void *) free_body_sparep);
337 init_pinerc(pine_state, &init_pinerc_debugging);
339 #ifdef DEBUG
340 /* Since this is specific debugging we don't mind if the
341 ifdef is the type of system.
343 #if defined(HAVE_SMALLOC) || defined(NXT)
344 if(ps_global->debug_malloc)
345 malloc_debug(ps_global->debug_malloc);
346 #endif
347 #ifdef CSRIMALLOC
348 if(ps_global->debug_malloc)
349 mal_debug(ps_global->debug_malloc);
350 #endif
352 if(!ps_global->convert_sigs
353 #ifdef _WINDOWS
354 && !ps_global->install_flag
355 #endif /* _WINDOWS */
357 init_debug();
359 if(args_for_debug){
360 dprint((0, " %s (PID=%ld)\n\n", args_for_debug,
361 (long) getpid()));
362 fs_give((void **)&args_for_debug);
366 char *env_to_free;
367 if((env_to_free = our_getenv("HOME")) != NULL){
368 dprint((2, "Setting home dir from $HOME: \"%s\"\n",
369 env_to_free));
370 fs_give((void **)&env_to_free);
372 else{
373 dprint((2, "Setting home dir: \"%s\"\n",
374 pine_state->home_dir ? pine_state->home_dir : "<?>"));
378 /* Watch out. Sensitive information in debug file. */
379 if(ps_global->debug_imap > 4)
380 mail_parameters(NULL, SET_DEBUGSENSITIVE, (void *) TRUE);
382 #ifndef DEBUGJOURNAL
383 if(ps_global->debug_tcp)
384 #endif
385 mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
387 #ifdef _WINDOWS
388 mswin_setdebug(debug, debugfile);
389 mswin_setdebugoncallback (imap_telemetry_on);
390 mswin_setdebugoffcallback (imap_telemetry_off);
391 mswin_enableimaptelemetry(ps_global->debug_imap != 0);
392 #endif
393 #endif /* DEBUG */
395 #ifdef _WINDOWS
396 mswin_setsortcallback(index_sort_callback);
397 mswin_setflagcallback(flag_callback);
398 mswin_sethdrmodecallback(header_mode_callback);
399 mswin_setselectedcallback(any_selected_callback);
400 mswin_setzoomodecallback(zoom_mode_callback);
401 mswin_setfkeymodecallback(fkey_mode_callback);
402 #endif
404 /*------- Set up c-client drivers -------*/
405 #include "../c-client/linkage.c"
407 /*------- ... then tune the drivers just installed -------*/
408 #ifdef _WINDOWS
409 if(_tgetenv(TEXT("HOME")))
410 mail_parameters(NULL, SET_HOMEDIR, (void *) pine_state->home_dir);
412 mail_parameters(NULL, SET_USERPROMPT, (void *) pine_user_callback);
415 * Sniff the environment for timezone offset. We need to do this
416 * here since Windows needs help figuring out UTC, and will adjust
417 * what time() returns based on TZ. THIS WILL SCREW US because
418 * we use time() differences to manage status messages. So, if
419 * rfc822_date, which calls localtime() and thus needs tzset(),
420 * is called while a status message is displayed, it's possible
421 * for time() to return a time *before* what we remember as the
422 * time we put the status message on the display. Sheesh.
424 tzset();
425 #else /* !_WINDOWS */
427 * We used to let c-client do this for us automatically, but it declines
428 * to do so for root. This forces c-client to establish an environment,
429 * even if the uid is 0.
431 env_init(ps_global->ui.login, ps_global->ui.homedir);
434 * Install callback to let us know the progress of network reads...
436 (void) mail_parameters(NULL, SET_READPROGRESS, (void *)pine_read_progress);
437 #endif /* !_WINDOWS */
440 * Install callback to handle certificate validation failures,
441 * allowing the user to continue if they wish.
443 mail_parameters(NULL, SET_SSLCERTIFICATEQUERY, (void *) pine_sslcertquery);
444 mail_parameters(NULL, SET_SSLFAILURE, (void *) pine_sslfailure);
446 if(init_pinerc_debugging){
447 dprint((2, init_pinerc_debugging));
448 fs_give((void **)&init_pinerc_debugging);
452 * Initial allocation of array of stream pool pointers.
453 * We do this before init_vars so that we can re-use streams used for
454 * remote config files. These sizes may get changed later.
456 ps_global->s_pool.max_remstream = 2;
457 dprint((9,
458 "Setting initial max_remstream to %d for remote config re-use\n",
459 ps_global->s_pool.max_remstream));
461 init_vars(pine_state, process_init_cmds);
463 #ifdef SMIME
464 if(F_ON(F_DONT_DO_SMIME, ps_global))
465 smime_deinit();
466 #endif /* SMIME */
468 #ifdef ENABLE_NLS
470 * LC_CTYPE is already set from the set_collation call above.
472 * We can't use gettext calls before we do this stuff so it doesn't
473 * help to translate strings that come before this in the program.
474 * Maybe we could rearrange things to accomodate that.
476 setlocale(LC_MESSAGES, "");
477 bindtextdomain(PACKAGE, LOCALEDIR);
478 bind_textdomain_codeset(PACKAGE, "UTF-8");
479 textdomain(PACKAGE);
480 #endif /* ENABLE_NLS */
482 convert_args_to_utf8(pine_state, &args);
484 if(args.action == aaFolder){
485 pine_state->beginning_of_month = first_run_of_month();
486 pine_state->beginning_of_year = first_run_of_year();
489 /* Set up optional for user-defined display filtering */
490 pine_state->tools.display_filter = dfilter;
491 pine_state->tools.display_filter_trigger = dfilter_trigger;
493 #ifdef _WINDOWS
494 if(ps_global->install_flag){
495 init_install_get_vars();
497 if(ps_global->prc)
498 free_pinerc_s(&ps_global->prc);
500 exit(0);
502 #endif
504 if(ps_global->convert_sigs){
505 if(convert_sigs_to_literal(ps_global, 0) == -1){
506 /* TRANSLATORS: sigs refers to signatures, which the user was trying to convert */
507 fprintf(stderr, _("trouble converting sigs\n"));
508 exit(-1);
511 if(ps_global->prc){
512 if(ps_global->prc->outstanding_pinerc_changes)
513 write_pinerc(ps_global, Main, WRP_NONE);
515 free_pinerc_s(&pine_state->prc);
518 exit(0);
522 * Set up a c-client read timeout and timeout handler. In general,
523 * it shouldn't happen, but a server crash or dead link can cause
524 * pine to appear wedged if we don't set this up...
526 rv = 30;
527 if(pine_state->VAR_TCPOPENTIMEO)
528 (void)SVAR_TCP_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF);
529 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)rv);
531 rv = 15;
532 if(pine_state->VAR_TCPREADWARNTIMEO)
533 (void)SVAR_TCP_READWARN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF);
534 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)rv);
536 rv = 0;
537 if(pine_state->VAR_TCPWRITEWARNTIMEO){
538 if(!SVAR_TCP_WRITEWARN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
539 if(rv == 0 || rv > 4) /* making sure */
540 mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long)rv);
543 mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
545 rv = 15;
546 if(pine_state->VAR_RSHOPENTIMEO){
547 if(!SVAR_RSH_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
548 if(rv == 0 || rv > 4) /* making sure */
549 mail_parameters(NULL, SET_RSHTIMEOUT, (void *)(long)rv);
552 rv = 15;
553 if(pine_state->VAR_SSHOPENTIMEO){
554 if(!SVAR_SSH_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
555 if(rv == 0 || rv > 4) /* making sure */
556 mail_parameters(NULL, SET_SSHTIMEOUT, (void *)(long)rv);
559 rvl = 60L;
560 if(pine_state->VAR_MAILDROPCHECK){
561 if(!SVAR_MAILDCHK(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF)){
562 if(rvl == 0L)
563 rvl = (60L * 60L * 24L * 100L); /* 100 days */
565 if(rvl >= 60L) /* making sure */
566 mail_parameters(NULL, SET_SNARFINTERVAL, (void *) rvl);
571 * Lookups of long login names which don't exist are very slow in aix.
572 * This would normally get set in system-wide config if not needed.
574 if(F_ON(F_DISABLE_SHARED_NAMESPACES, ps_global))
575 mail_parameters(NULL, SET_DISABLEAUTOSHAREDNS, (void *) TRUE);
577 if(F_ON(F_HIDE_NNTP_PATH, ps_global))
578 mail_parameters(NULL, SET_NNTPHIDEPATH, (void *) TRUE);
580 if(F_ON(F_MAILDROPS_PRESERVE_STATE, ps_global))
581 mail_parameters(NULL, SET_SNARFPRESERVE, (void *) TRUE);
583 rvl = 0L;
584 if(pine_state->VAR_NNTPRANGE){
585 if(!SVAR_NNTPRANGE(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF))
586 if(rvl > 0L)
587 mail_parameters(NULL, SET_NNTPRANGE, (void *) rvl);
591 * Tell c-client not to be so aggressive about uid mappings
593 mail_parameters(NULL, SET_UIDLOOKAHEAD, (void *) 20);
596 * Setup referral handling
598 mail_parameters(NULL, SET_IMAPREFERRAL, (void *) imap_referral);
599 mail_parameters(NULL, SET_MAILPROXYCOPY, (void *) imap_proxycopy);
602 * Setup multiple newsrc transition
604 mail_parameters(NULL, SET_NEWSRCQUERY, (void *) pine_newsrcquery);
607 * Disable some drivers if requested.
609 if(ps_global->VAR_DISABLE_DRIVERS &&
610 ps_global->VAR_DISABLE_DRIVERS[0] &&
611 ps_global->VAR_DISABLE_DRIVERS[0][0]){
612 char **t;
614 for(t = ps_global->VAR_DISABLE_DRIVERS; t[0] && t[0][0]; t++)
615 if(mail_parameters(NULL, DISABLE_DRIVER, (void *)(*t))){
616 dprint((2, "Disabled mail driver \"%s\"\n", *t));
618 else{
619 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
620 _("Failed to disable mail driver \"%s\": name not found"),
621 *t);
622 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
623 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
628 * Disable some authenticators if requested.
630 if(ps_global->VAR_DISABLE_AUTHS &&
631 ps_global->VAR_DISABLE_AUTHS[0] &&
632 ps_global->VAR_DISABLE_AUTHS[0][0]){
633 char **t;
635 for(t = ps_global->VAR_DISABLE_AUTHS; t[0] && t[0][0]; t++)
636 if(mail_parameters(NULL, DISABLE_AUTHENTICATOR, (void *)(*t))){
637 dprint((2,"Disabled SASL authenticator \"%s\"\n", *t));
639 else{
640 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
641 _("Failed to disable SASL authenticator \"%s\": name not found"),
642 *t);
643 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
644 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
649 * setup alternative authentication driver preference for IMAP opens
651 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
652 mail_parameters(NULL, SET_IMAPTRYALT, (void *) TRUE);
655 * Install handler to let us know about potential delays
657 (void) mail_parameters(NULL, SET_BLOCKNOTIFY, (void *) pine_block_notify);
659 if(ps_global->dump_supported_options){
660 dump_supported_options();
661 exit(0);
665 * Install extra headers to fetch along with all the other stuff
666 * mail_fetch_structure and mail_fetch_overview requests.
668 calc_extra_hdrs();
669 if(get_extra_hdrs())
670 (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS,
671 (void *) get_extra_hdrs());
673 if(init_username(pine_state) < 0){
674 fprintf(stderr, _("Who are you? (Unable to look up login name)\n"));
675 exit(-1);
678 if(init_userdir(pine_state) < 0)
679 exit(-1);
681 if(init_hostname(pine_state) < 0)
682 exit(-1);
685 * Verify mail dir if we're not in send only mode...
687 if(args.action == aaFolder && init_mail_dir(pine_state) < 0)
688 exit(-1);
690 init_signals();
692 /*--- input side ---*/
693 if(init_tty_driver(pine_state)){
694 #ifndef _WINDOWS /* always succeeds under _WINDOWS */
695 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);
696 exit(-1);
697 #endif /* !_WINDOWS */
701 /*--- output side ---*/
702 rv = config_screen(&(pine_state->ttyo));
703 #ifndef _WINDOWS /* always succeeds under _WINDOWS */
704 if(rv){
705 switch(rv){
706 case -1:
707 printf(_("Terminal type (environment variable TERM) not set.\n"));
708 break;
709 case -2:
710 printf(_("Terminal type \"%s\" is unknown.\n"), getenv("TERM"));
711 break;
712 case -3:
713 printf(_("Can't open terminal capabilities database.\n"));
714 break;
715 case -4:
716 printf(_("Your terminal, of type \"%s\", is lacking functions needed to run alpine.\n"), getenv("TERM"));
717 break;
720 printf("\r");
721 end_tty_driver(pine_state);
722 exit(-1);
724 #endif /* !_WINDOWS */
726 if(F_ON(F_BLANK_KEYMENU,ps_global))
727 FOOTER_ROWS(ps_global) = 1;
729 init_screen();
730 init_keyboard(pine_state->orig_use_fkeys);
731 strncpy(pine_state->inbox_name, INBOX_NAME,
732 sizeof(pine_state->inbox_name)-1);
733 init_folders(pine_state); /* digest folder spec's */
735 pine_state->in_init_seq = 0; /* so output (& ClearScreen) show up */
736 pine_state->dont_use_init_cmds = 1; /* don't use up initial_commands yet */
737 ClearScreen();
739 /* initialize titlebar in case we use it */
740 set_titlebar("", NULL, NULL, NULL, NULL, 0, FolderName, 0, 0, NULL);
743 * Prep storage object driver for PicoText
745 so_register_external_driver(pine_pico_get, pine_pico_give, pine_pico_writec, pine_pico_readc,
746 pine_pico_puts, pine_pico_seek, NULL, NULL);
748 #ifdef DEBUG
749 if(ps_global->debug_imap > 4 || debug > 9){
750 q_status_message(SM_ORDER | SM_DING, 5, 9,
751 _("Warning: sensitive authentication data included in debug file"));
752 flush_status_messages(0);
754 #endif
756 if(args.action == aaPrcCopy || args.action == aaAbookCopy){
757 int exit_val = -1;
758 char *err_msg = NULL;
761 * Don't translate these into UTF-8 because we'll be using them
762 * before we translate next time. User should use ascii.
764 if(args.data.copy.local && args.data.copy.remote){
765 switch(args.action){
766 case aaPrcCopy:
767 exit_val = copy_pinerc(args.data.copy.local,
768 args.data.copy.remote, &err_msg);
769 break;
771 case aaAbookCopy:
772 exit_val = copy_abook(args.data.copy.local,
773 args.data.copy.remote, &err_msg);
774 break;
776 default:
777 break;
780 if(err_msg){
781 q_status_message(SM_ORDER | SM_DING, 3, 4, err_msg);
782 fs_give((void **)&err_msg);
784 goodnight_gracey(pine_state, exit_val);
787 if(args.action == aaFolder
788 && (pine_state->first_time_user || pine_state->show_new_version)){
789 pine_state->mangled_header = 1;
790 show_main_screen(pine_state, 0, FirstMenu, &main_keymenu, 0,
791 (Pos *) NULL);
792 new_user_or_version(pine_state);
793 ClearScreen();
796 /* put back in case we need to suppress output */
797 pine_state->in_init_seq = pine_state->save_in_init_seq;
799 /* queue any init errors so they get displayed in a screen below */
800 queue_init_errors(ps_global);
802 /* "Page" the given file? */
803 if(args.action == aaMore){
804 int dice = 1, redir = 0;
806 if(pine_state->in_init_seq){
807 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
808 clear_cursor_pos();
809 if(pine_state->free_initial_cmds)
810 fs_give((void **)&(pine_state->free_initial_cmds));
812 pine_state->initial_cmds = NULL;
815 /*======= Requested that we simply page the given file =======*/
816 if(args.data.file){ /* Open the requested file... */
817 SourceType src;
818 STORE_S *store = NULL;
819 char *decode_error = NULL;
820 char filename[MAILTMPLEN];
822 if(args.data.file[0] == '\0'){
823 HelpType help = NO_HELP;
825 pine_state->mangled_footer = 1;
826 filename[0] = '\0';
827 while(1){
828 int flags = OE_APPEND_CURRENT;
830 rv = optionally_enter(filename, -FOOTER_ROWS(pine_state),
831 0, sizeof(filename),
832 /* TRANSLATORS: file is computer data */
833 _("File to open : "),
834 NULL, help, &flags);
835 if(rv == 3){
836 help = (help == NO_HELP) ? h_no_F_arg : NO_HELP;
837 continue;
840 if(rv != 4)
841 break;
844 if(rv == 1){
845 q_status_message(SM_ORDER, 0, 2, _("Cancelled"));
846 goodnight_gracey(pine_state, -1);
849 if(*filename){
850 removing_trailing_white_space(filename);
851 removing_leading_white_space(filename);
852 if(is_absolute_path(filename))
853 fnexpand(filename, sizeof(filename));
855 args.data.file = filename;
858 if(!*filename){
859 /* TRANSLATORS: file is computer data */
860 q_status_message(SM_ORDER, 0, 2 ,_("No file to open"));
861 goodnight_gracey(pine_state, -1);
865 if(stdin_getc){
866 redir++;
867 src = CharStar;
868 if(isatty(0) && (store = so_get(src, NULL, EDIT_ACCESS))){
869 gf_io_t pc;
871 gf_set_so_writec(&pc, store);
872 gf_filter_init();
873 if((decode_error = gf_pipe(stdin_getc, pc)) != NULL){
874 dice = 0;
875 q_status_message1(SM_ORDER, 3, 4,
876 _("Problem reading standard input: %s"),
877 decode_error);
880 gf_clear_so_writec(store);
882 else
883 dice = 0;
885 else{
886 src = FileStar;
887 strncpy(ps_global->cur_folder, args.data.file,
888 sizeof(ps_global->cur_folder)-1);
889 ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
890 if(!(store = so_get(src, args.data.file, READ_ACCESS|READ_FROM_LOCALE)))
891 dice = 0;
894 if(dice){
895 SCROLL_S sargs;
897 memset(&sargs, 0, sizeof(SCROLL_S));
898 sargs.text.text = so_text(store);
899 sargs.text.src = src;
900 /* TRANSLATORS: file is computer file being read by user */
901 sargs.text.desc = _("file");
902 /* TRANSLATORS: this is in the title bar at top of screen */
903 sargs.bar.title = _("FILE VIEW");
904 sargs.bar.style = FileTextPercent;
905 sargs.keys.menu = &simple_file_keymenu;
906 setbitmap(sargs.keys.bitmap);
907 scrolltool(&sargs);
909 printf("\n\n");
910 so_give(&store);
914 if(!dice){
915 q_status_message2(SM_ORDER, 3, 4,
916 _("Can't display \"%s\": %s"),
917 (redir) ? _("Standard Input")
918 : args.data.file ? args.data.file : "NULL",
919 error_description(errno));
922 goodnight_gracey(pine_state, 0);
924 else if(args.action == aaMail || (stdin_getc && (args.action != aaURL))){
925 /*======= address on command line/send one message mode ============*/
926 char *to = NULL, *error = NULL, *addr = NULL;
927 int len, good_addr = 1;
928 int exit_val = 0;
929 BUILDER_ARG fcc;
931 if(pine_state->in_init_seq){
932 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
933 clear_cursor_pos();
934 if(pine_state->free_initial_cmds)
935 fs_give((void **) &(pine_state->free_initial_cmds));
937 pine_state->initial_cmds = NULL;
940 /*----- Format the To: line with commas for the composer ---*/
941 if(args.data.mail.addrlist){
942 STRLIST_S *p;
944 for(p = args.data.mail.addrlist, len = 0; p; p = p->next)
945 len += strlen(p->name) + 2;
947 to = (char *) fs_get((len + 5) * sizeof(char));
948 for(p = args.data.mail.addrlist, *to = '\0'; p; p = p->next){
949 if(*to){
950 strncat(to, ", ", len+5-strlen(to)-1);
951 to[len+5-1] = '\0';
954 strncat(to, p->name, len+5-strlen(to)-1);
955 to[len+5-1] = '\0';
958 memset((void *)&fcc, 0, sizeof(BUILDER_ARG));
959 dprint((2, "building addr: -->%s<--\n", to ? to : "?"));
960 good_addr = (build_address(to, &addr, &error, &fcc, NULL) >= 0);
961 dprint((2, "mailing to: -->%s<--\n", addr ? addr : "?"));
962 free_strlist(&args.data.mail.addrlist);
964 else
965 memset(&fcc, 0, sizeof(fcc));
967 if(good_addr){
968 compose_mail(addr, fcc.tptr, NULL,
969 args.data.mail.attachlist, stdin_getc);
971 else{
972 /* TRANSLATORS: refers to bad email address */
973 q_status_message1(SM_ORDER, 3, 4, _("Bad address: %s"), error);
974 exit_val = -1;
977 if(addr)
978 fs_give((void **) &addr);
980 if(fcc.tptr)
981 fs_give((void **) &fcc.tptr);
983 if(args.data.mail.attachlist)
984 free_attachment_list(&args.data.mail.attachlist);
986 if(to)
987 fs_give((void **) &to);
989 if(error)
990 fs_give((void **) &error);
992 goodnight_gracey(pine_state, exit_val);
994 else{
995 char int_mail[MAXPATH+1];
996 struct key_menu *km = &main_keymenu;
998 /*========== Normal pine mail reading mode ==========*/
1000 pine_state->mail_stream = NULL;
1001 pine_state->mangled_screen = 1;
1003 if(args.action == aaURL){
1004 url_tool_t f;
1006 if(pine_state->in_init_seq){
1007 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
1008 clear_cursor_pos();
1009 if(pine_state->free_initial_cmds)
1010 fs_give((void **) &(pine_state->free_initial_cmds));
1011 pine_state->initial_cmds = NULL;
1013 if((f = url_local_handler(args.url)) != NULL){
1014 if(args.data.mail.attachlist){
1015 if(f == url_local_mailto){
1016 if(!(url_local_mailto_and_atts(args.url,
1017 args.data.mail.attachlist)
1018 && pine_state->next_screen))
1019 free_attachment_list(&args.data.mail.attachlist);
1020 goodnight_gracey(pine_state, 0);
1022 else {
1023 q_status_message(SM_ORDER | SM_DING, 3, 4,
1024 _("Only mailto URLs are allowed with file attachments"));
1025 goodnight_gracey(pine_state, -1); /* no return */
1028 else if(!((*f)(args.url) && pine_state->next_screen))
1029 goodnight_gracey(pine_state, 0); /* no return */
1031 else{
1032 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1033 _("Unrecognized URL \"%s\""), args.url);
1034 goodnight_gracey(pine_state, -1); /* no return */
1037 else if(!pine_state->start_in_index){
1038 /* flash message about executing initial commands */
1039 if(pine_state->in_init_seq){
1040 pine_state->in_init_seq = 0;
1041 clear_cursor_pos();
1042 pine_state->mangled_header = 1;
1043 pine_state->mangled_footer = 1;
1044 pine_state->mangled_screen = 0;
1045 /* show that this is Alpine */
1046 show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
1047 pine_state->mangled_screen = 1;
1048 pine_state->painted_footer_on_startup = 1;
1049 if(MIN(4, pine_state->ttyo->screen_rows - 4) > 1){
1050 char buf1[6*MAX_SCREEN_COLS+1];
1051 char buf2[6*MAX_SCREEN_COLS+1];
1052 int wid;
1054 /* TRANSLATORS: Initial Keystroke List is the literal name of an option */
1055 strncpy(buf1, _("Executing Initial Keystroke List......"), sizeof(buf1));
1056 buf1[sizeof(buf1)-1] = '\0';
1057 wid = utf8_width(buf1);
1058 if(wid > ps_global->ttyo->screen_cols){
1059 utf8_pad_to_width(buf2, buf1, sizeof(buf2), ps_global->ttyo->screen_cols, 1);
1060 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4), 0, buf2);
1062 else{
1063 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4),
1064 MAX(MIN(11, pine_state->ttyo->screen_cols - wid), 0), buf1);
1068 pine_state->in_init_seq = 1;
1070 else{
1071 show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
1072 pine_state->painted_body_on_startup = 1;
1073 pine_state->painted_footer_on_startup = 1;
1076 else{
1077 /* cancel any initial commands, overridden by cmd line */
1078 if(pine_state->in_init_seq){
1079 pine_state->in_init_seq = 0;
1080 pine_state->save_in_init_seq = 0;
1081 clear_cursor_pos();
1082 if(pine_state->initial_cmds){
1083 if(pine_state->free_initial_cmds)
1084 fs_give((void **)&(pine_state->free_initial_cmds));
1086 pine_state->initial_cmds = NULL;
1089 F_SET(F_USE_FK,pine_state, pine_state->orig_use_fkeys);
1092 (void) do_index_border(pine_state->context_current,
1093 pine_state->cur_folder,
1094 pine_state->mail_stream,
1095 pine_state->msgmap, MsgIndex, NULL,
1096 INDX_CLEAR|INDX_HEADER|INDX_FOOTER);
1097 pine_state->painted_footer_on_startup = 1;
1098 if(MIN(4, pine_state->ttyo->screen_rows - 4) > 1){
1099 char buf1[6*MAX_SCREEN_COLS+1];
1100 char buf2[6*MAX_SCREEN_COLS+1];
1101 int wid;
1103 strncpy(buf1, _("Please wait, opening mail folder......"), sizeof(buf1));
1104 buf1[sizeof(buf1)-1] = '\0';
1105 wid = utf8_width(buf1);
1106 if(wid > ps_global->ttyo->screen_cols){
1107 utf8_pad_to_width(buf2, buf1, sizeof(buf2), ps_global->ttyo->screen_cols, 1);
1108 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4), 0, buf2);
1110 else{
1111 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4),
1112 MAX(MIN(11, pine_state->ttyo->screen_cols - wid), 0), buf1);
1117 fflush(stdout);
1119 #if !defined(_WINDOWS) && !defined(LEAVEOUTFIFO)
1120 if(ps_global->VAR_FIFOPATH && ps_global->VAR_FIFOPATH[0])
1121 init_newmailfifo(ps_global->VAR_FIFOPATH);
1122 #endif
1124 if(pine_state->in_init_seq){
1125 pine_state->in_init_seq = 0;
1126 clear_cursor_pos();
1129 if(args.action == aaFolder && args.data.folder){
1130 CONTEXT_S *cntxt = NULL, *tc = NULL;
1131 char foldername[MAILTMPLEN];
1132 int notrealinbox = 0;
1134 if(args.data.folder[0] == '\0'){
1135 char *fldr;
1136 unsigned save_def_goto_rule;
1138 foldername[0] = '\0';
1139 save_def_goto_rule = pine_state->goto_default_rule;
1140 pine_state->goto_default_rule = GOTO_FIRST_CLCTN;
1141 tc = default_save_context(pine_state->context_list);
1142 fldr = broach_folder(-FOOTER_ROWS(pine_state), 1, &notrealinbox, &tc);
1143 pine_state->goto_default_rule = save_def_goto_rule;
1144 if(fldr){
1145 strncpy(foldername, fldr, sizeof(foldername)-1);
1146 foldername[sizeof(foldername)-1] = '\0';
1149 if(*foldername){
1150 removing_trailing_white_space(foldername);
1151 removing_leading_white_space(foldername);
1152 args.data.folder = cpystr(foldername);
1155 if(!*foldername){
1156 q_status_message(SM_ORDER, 0, 2 ,_("No folder to open"));
1157 goodnight_gracey(pine_state, -1);
1161 if(tc)
1162 cntxt = tc;
1163 else if((rv = pine_state->init_context) < 0)
1165 * As with almost all the folder vars in the pinerc,
1166 * we subvert the collection "breakout" here if the
1167 * folder name given looks like an asolute path on
1168 * this system...
1170 cntxt = (is_absolute_path(args.data.folder))
1171 ? NULL : pine_state->context_current;
1172 else if(rv == 0)
1173 cntxt = NULL;
1174 else
1175 for(cntxt = pine_state->context_list;
1176 rv > 1 && cntxt->next;
1177 rv--, cntxt = cntxt->next)
1180 if(pine_state && pine_state->ttyo){
1181 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1182 pine_state->painted_footer_on_startup = 0;
1183 pine_state->mangled_footer = 1;
1186 if(args.data.folder && *args.data.folder
1187 && !strucmp(args.data.folder, ps_global->inbox_name)
1188 && cntxt != ps_global->context_list)
1189 notrealinbox = 1;
1191 if(do_broach_folder(args.data.folder, cntxt, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT) <= 0){
1192 q_status_message1(SM_ORDER, 3, 4,
1193 _("Unable to open folder \"%s\""), args.data.folder);
1195 goodnight_gracey(pine_state, -1);
1198 else if(args.action == aaFolder){
1199 #ifdef _WINDOWS
1201 * need to ask for the inbox name if no default under DOS
1202 * since there is no "inbox"
1205 if(!pine_state->VAR_INBOX_PATH || !pine_state->VAR_INBOX_PATH[0]
1206 || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0){
1207 HelpType help = NO_HELP;
1208 static ESCKEY_S ekey[] = {{ctrl(T), 2, "^T", "To Fldrs"},
1209 {-1, 0, NULL, NULL}};
1211 pine_state->mangled_footer = 1;
1212 int_mail[0] = '\0';
1213 while(1){
1214 int flags = OE_APPEND_CURRENT;
1216 rv = optionally_enter(int_mail, -FOOTER_ROWS(pine_state),
1217 0, sizeof(int_mail),
1218 _("No inbox! Folder to open as inbox : "),
1219 /* ekey */ NULL, help, &flags);
1220 if(rv == 3){
1221 help = (help == NO_HELP) ? h_sticky_inbox : NO_HELP;
1222 continue;
1225 if(rv != 4)
1226 break;
1229 if(rv == 1){
1230 q_status_message(SM_ORDER, 0, 2 ,_("Folder open cancelled"));
1231 rv = 0; /* reset rv */
1233 else if(rv == 2){
1234 show_main_screen(pine_state,0,FirstMenu,km,0,(Pos *)NULL);
1237 if(*int_mail){
1238 removing_trailing_white_space(int_mail);
1239 removing_leading_white_space(int_mail);
1240 if((!pine_state->VAR_INBOX_PATH
1241 || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0)
1242 /* TRANSLATORS: Inbox-Path and PINERC are literal, not to be translated */
1243 && want_to(_("Preserve folder as \"Inbox-Path\" in PINERC"),
1244 'y', 'n', NO_HELP, WT_NORM) == 'y'){
1245 set_variable(V_INBOX_PATH, int_mail, 1, 1, Main);
1247 else{
1248 if(pine_state->VAR_INBOX_PATH)
1249 fs_give((void **)&pine_state->VAR_INBOX_PATH);
1251 pine_state->VAR_INBOX_PATH = cpystr(int_mail);
1254 if(pine_state && pine_state->ttyo){
1255 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1256 pine_state->painted_footer_on_startup = 0;
1257 pine_state->mangled_footer = 1;
1260 do_broach_folder(pine_state->inbox_name,
1261 pine_state->context_list, NULL, DB_INBOXWOCNTXT);
1263 else
1264 q_status_message(SM_ORDER, 0, 2 ,_("No folder opened"));
1267 else
1269 #endif /* _WINDOWS */
1270 if(F_ON(F_PREOPEN_STAYOPENS, ps_global))
1271 preopen_stayopen_folders();
1273 if(pine_state && pine_state->ttyo){
1274 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1275 pine_state->painted_footer_on_startup = 0;
1276 pine_state->mangled_footer = 1;
1279 /* open inbox */
1280 do_broach_folder(pine_state->inbox_name,
1281 pine_state->context_list, NULL, DB_INBOXWOCNTXT);
1284 if(pine_state->mangled_footer)
1285 pine_state->painted_footer_on_startup = 0;
1287 if(args.action == aaFolder
1288 && pine_state->mail_stream
1289 && expire_sent_mail())
1290 pine_state->painted_footer_on_startup = 0;
1293 * Initialize the defaults. Initializing here means that
1294 * if they're remote, the user isn't prompted for an imap login
1295 * before the display's drawn, AND there's the chance that
1296 * we can climb onto the already opened folder's stream...
1298 if(ps_global->first_time_user)
1299 init_save_defaults(); /* initialize default save folders */
1301 build_path(int_mail,
1302 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
1303 : pine_state->home_dir,
1304 INTERRUPTED_MAIL, sizeof(int_mail));
1305 if(args.action == aaFolder
1306 && (folder_exists(NULL, int_mail) & FEX_ISFILE))
1307 q_status_message(SM_ORDER | SM_DING, 4, 5,
1308 _("Use Compose command to continue interrupted message."));
1310 #if defined(USE_QUOTAS)
1312 long q;
1313 int over;
1314 q = disk_quota(pine_state->home_dir, &over);
1315 if(q > 0 && over){
1316 q_status_message2(SM_ASYNC | SM_DING, 4, 5,
1317 _("WARNING! Over your disk quota by %s bytes (%s)"),
1318 comatose(q),byte_string(q));
1321 #endif
1323 pine_state->in_init_seq = pine_state->save_in_init_seq;
1324 pine_state->dont_use_init_cmds = 0;
1325 clear_cursor_pos();
1327 if(pine_state->give_fixed_warning)
1328 q_status_message(SM_ASYNC, 0, 10,
1329 /* TRANSLATORS: config is an abbreviation for configuration */
1330 _("Note: some of your config options conflict with site policy and are ignored"));
1332 if(!prune_folders_ok())
1333 q_status_message(SM_ASYNC, 0, 10,
1334 /* TRANSLATORS: Pruned-Folders is literal */
1335 _("Note: ignoring Pruned-Folders outside of default collection for saves"));
1337 if(get_input_timeout() == 0 &&
1338 ps_global->VAR_INBOX_PATH &&
1339 ps_global->VAR_INBOX_PATH[0] == '{')
1340 q_status_message(SM_ASYNC, 0, 10,
1341 _("Note: Mail-Check-Interval=0 may cause IMAP server connection to time out"));
1343 #ifdef _WINDOWS
1344 mswin_setnewmailwidth(ps_global->nmw_width);
1345 #endif
1348 /*-------------------------------------------------------------------
1349 Loop executing the commands
1351 This is done like this so that one command screen can cause
1352 another one to execute it with out going through the main menu.
1353 ------------------------------------------------------------------*/
1354 if(!pine_state->next_screen)
1355 pine_state->next_screen = pine_state->start_in_index
1356 ? mail_index_screen : main_menu_screen;
1357 while(1){
1358 if(pine_state->next_screen == SCREEN_FUN_NULL)
1359 pine_state->next_screen = main_menu_screen;
1361 (*(pine_state->next_screen))(pine_state);
1365 exit(0);
1370 * The arguments need to be converted to UTF-8 for our internal use.
1371 * Not all arguments are converted because some are used before we
1372 * are able to do the conversion, like the pinerc name.
1374 void
1375 convert_args_to_utf8(struct pine *ps, ARGDATA_S *args)
1377 char *fromcharset = NULL;
1378 char *conv;
1380 if(args){
1381 if(ps->keyboard_charmap && strucmp(ps->keyboard_charmap, "UTF-8")
1382 && strucmp(ps->keyboard_charmap, "US-ASCII"))
1383 fromcharset = ps->keyboard_charmap;
1384 else if(ps->display_charmap && strucmp(ps->display_charmap, "UTF-8")
1385 && strucmp(ps->display_charmap, "US-ASCII"))
1386 fromcharset = ps->display_charmap;
1387 #ifndef _WINDOWS
1388 else if(ps->VAR_OLD_CHAR_SET && strucmp(ps->VAR_OLD_CHAR_SET, "UTF-8")
1389 && strucmp(ps->VAR_OLD_CHAR_SET, "US-ASCII"))
1390 fromcharset = ps->VAR_OLD_CHAR_SET;
1391 #endif /* ! _WINDOWS */
1393 if(args->action == aaURL && args->url){
1394 conv = convert_to_utf8(args->url, fromcharset, 0);
1395 if(conv){
1396 fs_give((void **) &args->url);
1397 args->url = conv;
1401 if(args->action == aaFolder && args->data.folder){
1402 conv = convert_to_utf8(args->data.folder, fromcharset, 0);
1403 if(conv){
1404 fs_give((void **) &args->data.folder);
1405 args->data.folder = conv;
1409 if(args->action == aaMore && args->data.file){
1410 conv = convert_to_utf8(args->data.file, fromcharset, 0);
1411 if(conv){
1412 fs_give((void **) &args->data.file);
1413 args->data.file = conv;
1417 if(args->action == aaURL || args->action == aaMail){
1418 if(args->data.mail.addrlist){
1419 STRLIST_S *p;
1421 for(p = args->data.mail.addrlist; p; p=p->next){
1422 if(p->name){
1423 conv = convert_to_utf8(p->name, fromcharset, 0);
1424 if(conv){
1425 fs_give((void **) &p->name);
1426 p->name = conv;
1432 if(args->data.mail.attachlist){
1433 PATMT *p;
1435 for(p = args->data.mail.attachlist; p; p=p->next){
1436 if(p->filename){
1437 conv = convert_to_utf8(p->filename, fromcharset, 0);
1438 if(conv){
1439 fs_give((void **) &p->filename);
1440 p->filename = conv;
1450 void
1451 preopen_stayopen_folders(void)
1453 char **open_these;
1455 for(open_these = ps_global->VAR_PERMLOCKED;
1456 open_these && *open_these; open_these++)
1457 (void) do_broach_folder(*open_these, ps_global->context_list,
1458 NULL, DB_NOVISIT);
1463 * read_stdin_char - simple function to return a character from
1464 * redirected stdin
1467 read_stdin_char(char *c)
1469 int rv;
1471 /* it'd probably be a good idea to fix this to pre-read blocks */
1472 while(1){
1473 rv = read(PIPED_FD, c, 1);
1474 if(rv < 0){
1475 if(errno == EINTR){
1476 dprint((2, "read_stdin_char: read interrupted, restarting\n"));
1477 continue;
1479 else
1480 dprint((1, "read_stdin_char: read FAILED: %s\n",
1481 error_description(errno)));
1483 break;
1485 return(rv);
1489 /* this default is from the array of structs below */
1490 #define DEFAULT_MENU_ITEM ((unsigned) 3) /* LIST FOLDERS */
1491 #define ABOOK_MENU_ITEM ((unsigned) 4) /* ADDRESS BOOK */
1492 #define MAX_MENU_ITEM ((unsigned) 6)
1494 * Skip this many spaces between rows of main menu screen.
1495 * We have MAX_MENU_ITEM+1 = # of commands in menu
1496 * 1 = copyright line
1497 * MAX_MENU_ITEM = rows between commands
1498 * 1 = extra row above commands
1499 * 1 = row between commands and copyright
1501 * To make it simple, if there is enough room for all of that include all the
1502 * extra space, if not, cut it all out.
1504 #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)
1506 static unsigned menu_index = DEFAULT_MENU_ITEM;
1509 * One of these for each line that gets printed in the middle of the
1510 * screen in the main menu.
1512 static struct menu_key {
1513 char *key_and_name,
1514 *news_addition;
1515 int key_index; /* index into keymenu array for this cmd */
1516 } mkeys[] = {
1518 * TRANSLATORS: These next few are headings on the Main alpine menu.
1519 * It's nice if the dashes can be made to line up vertically.
1521 {N_(" %s HELP - Get help using Alpine"),
1522 NULL, MAIN_HELP_KEY},
1523 {N_(" %s COMPOSE MESSAGE - Compose and send%s a message"),
1524 /* TRANSLATORS: We think of sending an email message or posting a news message.
1525 The message is shown as Compose and send/post a message */
1526 N_("/post"), MAIN_COMPOSE_KEY},
1527 {N_(" %s MESSAGE INDEX - View messages in current folder"),
1528 NULL, MAIN_INDEX_KEY},
1529 {N_(" %s FOLDER LIST - Select a folder%s to view"),
1530 /* TRANSLATORS: When news is supported the message above becomes
1531 Select a folder OR news group to view */
1532 N_(" OR news group"), MAIN_FOLDER_KEY},
1533 {N_(" %s ADDRESS BOOK - Update address book"),
1534 NULL, MAIN_ADDRESS_KEY},
1535 {N_(" %s SETUP - Configure Alpine Options"),
1536 NULL, MAIN_SETUP_KEY},
1537 /* TRANSLATORS: final Main menu line */
1538 {N_(" %s QUIT - Leave the Alpine program"),
1539 NULL, MAIN_QUIT_KEY}
1544 /*----------------------------------------------------------------------
1545 display main menu and execute main menu commands
1547 Args: The usual pine structure
1549 Result: main menu commands are executed
1552 M A I N M E N U S C R E E N
1554 Paint the main menu on the screen, get the commands and either execute
1555 the function or pass back the name of the function to execute for the menu
1556 selection. Only simple functions that always return here can be executed
1557 here.
1559 This functions handling of new mail, redrawing, errors and such can
1560 serve as a template for the other screen that do much the same thing.
1562 There is a loop that fetchs and executes commands until a command to leave
1563 this screen is given. Then the name of the next screen to display is
1564 stored in next_screen member of the structure and this function is exited
1565 with a return.
1567 First a check for new mail is performed. This might involve reading the new
1568 mail into the inbox which might then cause the screen to be repainted.
1570 Then the general screen painting is done. This is usually controlled
1571 by a few flags and some other position variables. If they change they
1572 tell this part of the code what to repaint. This will include cursor
1573 motion and so on.
1574 ----*/
1575 void
1576 main_menu_screen(struct pine *pine_state)
1578 UCS ch;
1579 int cmd, just_a_navigate_cmd, setup_command, km_popped;
1580 int notrealinbox;
1581 char *new_folder, *utf8str;
1582 CONTEXT_S *tc;
1583 struct key_menu *km;
1584 OtherMenu what;
1585 Pos curs_pos;
1587 ps_global = pine_state;
1588 just_a_navigate_cmd = 0;
1589 km_popped = 0;
1590 menu_index = DEFAULT_MENU_ITEM;
1591 what = FirstMenu; /* which keymenu to display */
1592 ch = 'x'; /* For display_message 1st time through */
1593 pine_state->next_screen = SCREEN_FUN_NULL;
1594 pine_state->prev_screen = main_menu_screen;
1595 curs_pos.row = pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
1596 curs_pos.col = 0;
1597 km = &main_keymenu;
1599 mailcap_free(); /* free resources we won't be using for a while */
1601 if(!pine_state->painted_body_on_startup
1602 && !pine_state->painted_footer_on_startup){
1603 pine_state->mangled_screen = 1;
1606 dprint((1, "\n\n ---- MAIN_MENU_SCREEN ----\n"));
1608 while(1){
1609 if(km_popped){
1610 km_popped--;
1611 if(km_popped == 0){
1612 clearfooter(pine_state);
1613 pine_state->mangled_body = 1;
1618 * fix up redrawer just in case some submenu caused it to get
1619 * reassigned...
1621 pine_state->redrawer = main_redrawer;
1623 /*----------- Check for new mail -----------*/
1624 if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
1625 pine_state->mangled_header = 1;
1627 if(streams_died())
1628 pine_state->mangled_header = 1;
1630 show_main_screen(pine_state, just_a_navigate_cmd, what, km,
1631 km_popped, &curs_pos);
1632 just_a_navigate_cmd = 0;
1633 what = SameMenu;
1635 /*---- This displays new mail notification, or errors ---*/
1636 if(km_popped){
1637 FOOTER_ROWS(pine_state) = 3;
1638 mark_status_dirty();
1641 display_message(ch);
1642 if(km_popped){
1643 FOOTER_ROWS(pine_state) = 1;
1644 mark_status_dirty();
1647 if(F_OFF(F_SHOW_CURSOR, ps_global)){
1648 curs_pos.row =pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
1649 curs_pos.col =0;
1652 MoveCursor(curs_pos.row, curs_pos.col);
1654 /*------ Read the command from the keyboard ----*/
1655 #ifdef MOUSE
1656 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
1657 register_mfunc(mouse_in_content, HEADER_ROWS(pine_state), 0,
1658 pine_state->ttyo->screen_rows-(FOOTER_ROWS(pine_state)+1),
1659 pine_state->ttyo->screen_cols);
1660 #endif
1661 #if defined(DOS) || defined(OS2)
1663 * AND pre-build header lines. This works just fine under
1664 * DOS since we wait for characters in a loop. Something will
1665 * will have to change under UNIX if we want to do the same.
1667 /* while_waiting = build_header_cache; */
1668 #ifdef _WINDOWS
1669 mswin_sethelptextcallback(pcpine_help_main);
1670 mswin_mousetrackcallback(pcpine_main_cursor);
1671 #endif
1672 #endif
1673 ch = READ_COMMAND(&utf8str);
1674 #ifdef MOUSE
1675 clear_mfunc(mouse_in_content);
1676 #endif
1677 #if defined(DOS) || defined(OS2)
1678 /* while_waiting = NULL; */
1679 #ifdef _WINDOWS
1680 mswin_sethelptextcallback(NULL);
1681 mswin_mousetrackcallback(NULL);
1682 #endif
1683 #endif
1685 /* No matter what, Quit here always works */
1686 if(ch == 'q' || ch == 'Q'){
1687 cmd = MC_QUIT;
1689 #ifdef DEBUG
1690 else if(debug && ch && ch < 0x80 && strchr("123456789", ch)){
1691 int olddebug;
1693 olddebug = debug;
1694 debug = ch - '0';
1695 if(debug > 7)
1696 ps_global->debug_timestamp = 1;
1697 else
1698 ps_global->debug_timestamp = 0;
1700 if(debug > 7)
1701 ps_global->debug_imap = 4;
1702 else if(debug > 6)
1703 ps_global->debug_imap = 3;
1704 else if(debug > 4)
1705 ps_global->debug_imap = 2;
1706 else if(debug > 2)
1707 ps_global->debug_imap = 1;
1708 else
1709 ps_global->debug_imap = 0;
1711 if(ps_global->mail_stream){
1712 if(ps_global->debug_imap > 0){
1713 mail_debug(ps_global->mail_stream);
1714 #ifdef _WINDOWS
1715 mswin_enableimaptelemetry(TRUE);
1716 #endif
1718 else{
1719 mail_nodebug(ps_global->mail_stream);
1720 #ifdef _WINDOWS
1721 mswin_enableimaptelemetry(FALSE);
1722 #endif
1726 if(debug > 7 && olddebug <= 7)
1727 mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
1728 else if(debug <= 7 && olddebug > 7 && !ps_global->debugmem)
1729 mail_parameters(NULL, SET_TCPDEBUG, (void *) FALSE);
1731 dprint((1, "*** Debug level set to %d ***\n", debug));
1732 if(debugfile)
1733 fflush(debugfile);
1735 q_status_message1(SM_ORDER, 0, 1, _("Debug level set to %s"),
1736 int2string(debug));
1737 continue;
1739 #endif /* DEBUG */
1740 else{
1741 cmd = menu_command(ch, km);
1743 if(km_popped)
1744 switch(cmd){
1745 case MC_NONE :
1746 case MC_OTHER :
1747 case MC_RESIZE :
1748 case MC_REPAINT :
1749 km_popped++;
1750 break;
1752 default:
1753 clearfooter(pine_state);
1754 break;
1758 /*------ Execute the command ------*/
1759 switch (cmd){
1760 help_case :
1761 /*------ HELP ------*/
1762 case MC_HELP :
1764 if(FOOTER_ROWS(pine_state) == 1 && km_popped == 0){
1765 km_popped = 2;
1766 pine_state->mangled_footer = 1;
1768 else{
1769 /* TRANSLATORS: This is a screen title */
1770 helper(main_menu_tx, _("HELP FOR MAIN MENU"), 0);
1771 pine_state->mangled_screen = 1;
1774 break;
1777 /*---------- display other key bindings ------*/
1778 case MC_OTHER :
1779 if(ch == 'o')
1780 warn_other_cmds();
1782 what = NextMenu;
1783 pine_state->mangled_footer = 1;
1784 break;
1787 /*---------- Previous item in menu ----------*/
1788 case MC_PREVITEM :
1789 if(menu_index > 0) {
1790 menu_index--;
1791 pine_state->mangled_body = 1;
1792 if(km->which == 0)
1793 pine_state->mangled_footer = 1;
1795 just_a_navigate_cmd++;
1797 else
1798 /* TRANSLATORS: list refers to list of commands in main menu */
1799 q_status_message(SM_ORDER, 0, 2, _("Already at top of list"));
1801 break;
1804 /*---------- Next item in menu ----------*/
1805 case MC_NEXTITEM :
1806 if(menu_index < MAX_MENU_ITEM){
1807 menu_index++;
1808 pine_state->mangled_body = 1;
1809 if(km->which == 0)
1810 pine_state->mangled_footer = 1;
1812 just_a_navigate_cmd++;
1814 else
1815 q_status_message(SM_ORDER, 0, 2, _("Already at bottom of list"));
1817 break;
1820 /*---------- Release Notes ----------*/
1821 case MC_RELNOTES :
1822 /* TRANSLATORS: This is a screen title */
1823 helper(h_news, _("ALPINE RELEASE NOTES"), 0);
1824 pine_state->mangled_screen = 1;
1825 break;
1828 #ifdef KEYBOARD_LOCK
1829 /*---------- Keyboard lock ----------*/
1830 case MC_KBLOCK :
1831 (void) lock_keyboard();
1832 pine_state->mangled_screen = 1;
1833 break;
1834 #endif /* KEYBOARD_LOCK */
1837 /*---------- Quit pine ----------*/
1838 case MC_QUIT :
1839 pine_state->next_screen = quit_screen;
1840 return;
1843 /*---------- Go to composer ----------*/
1844 case MC_COMPOSE :
1845 pine_state->next_screen = compose_screen;
1846 return;
1849 /*---- Go to alternate composer ------*/
1850 case MC_ROLE :
1851 pine_state->next_screen = alt_compose_screen;
1852 return;
1855 /*---------- Top of Folder list ----------*/
1856 case MC_COLLECTIONS :
1857 pine_state->next_screen = folder_screen;
1858 return;
1861 /*---------- Goto new folder ----------*/
1862 case MC_GOTO :
1863 tc = ps_global->context_current;
1864 new_folder = broach_folder(-FOOTER_ROWS(pine_state), 1, &notrealinbox, &tc);
1865 if(new_folder)
1866 visit_folder(ps_global, new_folder, tc, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT);
1868 return;
1871 /*---------- Go to index ----------*/
1872 case MC_INDEX :
1873 if(THREADING()
1874 && sp_viewing_a_thread(pine_state->mail_stream)
1875 && unview_thread(pine_state, pine_state->mail_stream,
1876 pine_state->msgmap)){
1877 pine_state->view_skipped_index = 0;
1878 pine_state->mangled_screen = 1;
1881 pine_state->next_screen = mail_index_screen;
1882 return;
1885 /*---------- Review Status Messages ----------*/
1886 case MC_JOURNAL :
1887 review_messages();
1888 pine_state->mangled_screen = 1;
1889 break;
1892 /*---------- Setup mini menu ----------*/
1893 case MC_SETUP :
1894 setup_case :
1895 setup_command = setup_menu(pine_state);
1896 pine_state->mangled_footer = 1;
1897 do_setup_task(setup_command);
1898 if(ps_global->next_screen != main_menu_screen)
1899 return;
1901 break;
1904 /*---------- Go to address book ----------*/
1905 case MC_ADDRBOOK :
1906 pine_state->next_screen = addr_book_screen;
1907 return;
1910 /*------ Repaint the works -------*/
1911 case MC_RESIZE :
1912 case MC_REPAINT :
1913 ClearScreen();
1914 pine_state->mangled_screen = 1;
1915 break;
1918 #ifdef MOUSE
1919 /*------- Mouse event ------*/
1920 case MC_MOUSE :
1922 MOUSEPRESS mp;
1923 unsigned ndmi;
1924 struct pine *ps = pine_state;
1926 mouse_get_last (NULL, &mp);
1928 #ifdef _WINDOWS
1929 if(mp.button == M_BUTTON_RIGHT){
1930 if(!mp.doubleclick){
1931 static MPopup main_popup[] = {
1932 {tQueue, {"Folder List", lNormal}, {'L'}},
1933 {tQueue, {"Message Index", lNormal}, {'I'}},
1934 {tSeparator},
1935 {tQueue, {"Address Book", lNormal}, {'A'}},
1936 {tQueue, {"Setup Options", lNormal}, {'S'}},
1937 {tTail}
1940 mswin_popup(main_popup);
1943 else {
1944 #endif
1945 if (mp.row >= (HEADER_ROWS(ps) + MNSKIP(ps)))
1946 ndmi = (mp.row+1 - HEADER_ROWS(ps) - (MNSKIP(ps)+1))/(MNSKIP(ps)+1);
1948 if (mp.row >= (HEADER_ROWS(ps) + MNSKIP(ps))
1949 && !(MNSKIP(ps) && (mp.row+1) & 0x01)
1950 && ndmi <= MAX_MENU_ITEM
1951 && FOOTER_ROWS(ps) + (ndmi+1)*(MNSKIP(ps)+1)
1952 + MNSKIP(ps) + FOOTER_ROWS(ps) <= ps->ttyo->screen_rows){
1953 if(mp.doubleclick){
1954 switch(ndmi){ /* fake main_screen request */
1955 case 0 :
1956 goto help_case;
1958 case 1 :
1959 pine_state->next_screen = compose_screen;
1960 return;
1962 case 2 :
1963 pine_state->next_screen = mail_index_screen;
1964 return;
1966 case 3 :
1967 pine_state->next_screen = folder_screen;
1968 return;
1970 case 4 :
1971 pine_state->next_screen = addr_book_screen;
1972 return;
1974 case 5 :
1975 goto setup_case;
1977 case 6 :
1978 pine_state->next_screen = quit_screen;
1979 return;
1981 default: /* no op */
1982 break;
1985 else{
1986 menu_index = ndmi;
1987 pine_state->mangled_body = 1;
1988 if(km->which == 0)
1989 pine_state->mangled_footer = 1;
1991 just_a_navigate_cmd++;
1994 #ifdef _WINDOWS
1996 #endif
1999 break;
2000 #endif
2003 /*------ Input timeout ------*/
2004 case MC_NONE :
2005 break; /* noop for timeout loop mail check */
2008 /*------ Bogus Input ------*/
2009 case MC_UNKNOWN :
2010 if(ch == 'm' || ch == 'M'){
2011 q_status_message(SM_ORDER, 0, 1, "Already in Main Menu");
2012 break;
2015 default:
2016 bogus_command(ch, F_ON(F_USE_FK,pine_state) ? "F1" : "?");
2017 break;
2019 case MC_UTF8:
2020 bogus_utf8_command(utf8str, F_ON(F_USE_FK, pine_state) ? "F1" : "?");
2021 break;
2022 } /* the switch */
2023 } /* the BIG while loop! */
2027 /*----------------------------------------------------------------------
2028 Re-Draw the main menu
2030 Args: none.
2032 Result: main menu is re-displayed
2033 ----*/
2034 void
2035 main_redrawer(void)
2037 struct key_menu *km = &main_keymenu;
2039 ps_global->mangled_screen = 1;
2040 show_main_screen(ps_global, 0, FirstMenu, km, 0, (Pos *)NULL);
2044 /*----------------------------------------------------------------------
2045 Draw the main menu
2047 Args: pine_state - the usual struct
2048 quick_draw - tells do_menu() it can skip some drawing
2049 what - tells which section of keymenu to draw
2050 km - the keymenu
2051 cursor_pos - returns a good position for the cursor to be located
2053 Result: main menu is displayed
2054 ----*/
2055 void
2056 show_main_screen(struct pine *ps, int quick_draw, OtherMenu what,
2057 struct key_menu *km, int km_popped, Pos *cursor_pos)
2059 if(ps->painted_body_on_startup || ps->painted_footer_on_startup){
2060 ps->mangled_screen = 0; /* only worry about it here */
2061 ps->mangled_header = 1; /* we have to redo header */
2062 if(!ps->painted_body_on_startup)
2063 ps->mangled_body = 1; /* make sure to paint body*/
2065 if(!ps->painted_footer_on_startup)
2066 ps->mangled_footer = 1; /* make sure to paint footer*/
2068 ps->painted_body_on_startup = 0;
2069 ps->painted_footer_on_startup = 0;
2072 if(ps->mangled_screen){
2073 ps->mangled_header = 1;
2074 ps->mangled_body = 1;
2075 ps->mangled_footer = 1;
2076 ps->mangled_screen = 0;
2079 #ifdef _WINDOWS
2080 /* Reset the scroll range. Main screen never scrolls. */
2081 scroll_setrange (0L, 0L);
2082 mswin_beginupdate();
2083 #endif
2085 /* paint the titlebar if needed */
2086 if(ps->mangled_header){
2087 /* TRANSLATORS: screen title */
2088 set_titlebar(_("MAIN MENU"), ps->mail_stream, ps->context_current,
2089 ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0, NULL);
2090 ps->mangled_header = 0;
2093 /* paint the body if needed */
2094 if(ps->mangled_body){
2095 if(!quick_draw)
2096 ClearBody();
2098 do_menu(quick_draw, cursor_pos, km);
2099 ps->mangled_body = 0;
2102 /* paint the keymenu if needed */
2103 if(km && ps->mangled_footer){
2104 static char label[LONGEST_LABEL + 2 + 1], /* label + brackets + \0 */
2105 name[8];
2106 bitmap_t bitmap;
2108 setbitmap(bitmap);
2110 #ifdef KEYBOARD_LOCK
2111 if(ps_global->restricted || F_ON(F_DISABLE_KBLOCK_CMD,ps_global))
2112 #endif
2113 clrbitn(MAIN_KBLOCK_KEY, bitmap);
2115 menu_clear_binding(km, '>');
2116 menu_clear_binding(km, '.');
2117 menu_clear_binding(km, KEY_RIGHT);
2118 menu_clear_binding(km, ctrl('M'));
2119 menu_clear_binding(km, ctrl('J'));
2120 km->keys[MAIN_DEFAULT_KEY].bind
2121 = km->keys[mkeys[menu_index].key_index].bind;
2122 km->keys[MAIN_DEFAULT_KEY].label
2123 = km->keys[mkeys[menu_index].key_index].label;
2125 /* put brackets around the default action */
2126 snprintf(label, sizeof(label), "[%s]", km->keys[mkeys[menu_index].key_index].label);
2127 label[sizeof(label)-1] = '\0';
2128 strncpy(name, ">", sizeof(name));
2129 name[sizeof(name)-1] = '\0';
2130 km->keys[MAIN_DEFAULT_KEY].label = label;
2131 km->keys[MAIN_DEFAULT_KEY].name = name;
2132 menu_add_binding(km, '>', km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2133 menu_add_binding(km, '.', km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2134 menu_add_binding(km, ctrl('M'), km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2135 menu_add_binding(km, ctrl('J'), km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2137 if(F_ON(F_ARROW_NAV,ps_global))
2138 menu_add_binding(km, KEY_RIGHT, km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2140 if(km_popped){
2141 FOOTER_ROWS(ps) = 3;
2142 clearfooter(ps);
2145 draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
2146 1-FOOTER_ROWS(ps_global), 0, what);
2147 ps->mangled_footer = 0;
2148 if(km_popped){
2149 FOOTER_ROWS(ps) = 1;
2150 mark_keymenu_dirty();
2154 #ifdef _WINDOWS
2155 mswin_endupdate();
2156 #endif
2160 /*----------------------------------------------------------------------
2161 Actually display the main menu
2163 Args: quick_draw - just a next or prev command was typed so we only have
2164 to redraw the highlighting
2165 cursor_pos - a place to return a good value for cursor location
2167 Result: Main menu is displayed
2168 ---*/
2169 void
2170 do_menu(int quick_draw, Pos *cursor_pos, struct key_menu *km)
2172 struct pine *ps = ps_global;
2173 int dline, indent, longest = 0, cmd;
2174 char buf[4*MAX_SCREEN_COLS+1];
2175 char buf2[4*MAX_SCREEN_COLS+1];
2176 static int last_inverse = -1;
2177 Pos pos;
2179 /* find the longest command */
2180 for(cmd = 0; cmd < sizeof(mkeys)/(sizeof(mkeys[1])); cmd++){
2181 memset((void *) buf, ' ', sizeof(buf));
2182 snprintf(buf, sizeof(buf), mkeys[cmd].key_and_name[0] ? _(mkeys[cmd].key_and_name) : "",
2183 (F_OFF(F_USE_FK,ps)
2184 && km->keys[mkeys[cmd].key_index].name)
2185 ? km->keys[mkeys[cmd].key_index].name : "",
2186 (ps->VAR_NEWS_SPEC && mkeys[cmd].news_addition && mkeys[cmd].news_addition[0])
2187 ? _(mkeys[cmd].news_addition) : "");
2188 buf[sizeof(buf)-1] = '\0';
2190 if(longest < (indent = utf8_width(buf)))
2191 longest = indent;
2194 indent = MAX(((ps->ttyo->screen_cols - longest)/2) - 1, 0);
2196 dline = HEADER_ROWS(ps) + MNSKIP(ps);
2197 for(cmd = 0; cmd < sizeof(mkeys)/(sizeof(mkeys[1])); cmd++){
2198 /* leave room for copyright and footer */
2199 if(dline + MNSKIP(ps) + 1 + FOOTER_ROWS(ps) >= ps->ttyo->screen_rows)
2200 break;
2202 if(quick_draw && !(cmd == last_inverse || cmd == menu_index)){
2203 dline += (1 + MNSKIP(ps));
2204 continue;
2207 if(cmd == menu_index)
2208 StartInverse();
2210 memset((void *) buf, ' ', sizeof(buf));
2211 snprintf(buf, sizeof(buf), mkeys[cmd].key_and_name[0] ? _(mkeys[cmd].key_and_name) : "",
2212 (F_OFF(F_USE_FK,ps)
2213 && km->keys[mkeys[cmd].key_index].name)
2214 ? km->keys[mkeys[cmd].key_index].name : "",
2215 (ps->VAR_NEWS_SPEC && mkeys[cmd].news_addition && mkeys[cmd].news_addition[0])
2216 ? _(mkeys[cmd].news_addition) : "");
2217 buf[sizeof(buf)-1] = '\0';
2219 utf8_pad_to_width(buf2, buf, sizeof(buf2),
2220 MIN(ps->ttyo->screen_cols-indent,longest+1), 1);
2221 pos.row = dline++;
2222 pos.col = indent;
2223 PutLine0(pos.row, pos.col, buf2);
2225 if(MNSKIP(ps))
2226 dline++;
2228 if(cmd == menu_index){
2229 if(cursor_pos){
2230 cursor_pos->row = pos.row;
2231 /* 6 is 1 for the letter plus 5 spaces */
2232 cursor_pos->col = pos.col + 6;
2233 if(F_OFF(F_USE_FK,ps))
2234 cursor_pos->col++;
2236 cursor_pos->col = MIN(cursor_pos->col, ps->ttyo->screen_cols);
2239 EndInverse();
2244 last_inverse = menu_index;
2246 if(!quick_draw && FOOTER_ROWS(ps)+1 < ps->ttyo->screen_rows){
2247 utf8_to_width(buf2, LEGAL_NOTICE, sizeof(buf2),
2248 ps->ttyo->screen_cols-3, NULL);
2249 PutLine0(ps->ttyo->screen_rows - (FOOTER_ROWS(ps)+1),
2250 MAX(0, ((ps->ttyo->screen_cols-utf8_width(buf2))/2)),
2251 buf2);
2254 fflush(stdout);
2259 choose_setup_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2261 int rv = 1;
2262 SRV_S *srv;
2264 if(!(srv = (SRV_S *)sparms->proc.data.p)){
2265 sparms->proc.data.p = (SRV_S *)fs_get(sizeof(*srv));
2266 srv = (SRV_S *)sparms->proc.data.p;
2267 memset(srv, 0, sizeof(*srv));
2270 ps_global->next_screen = SCREEN_FUN_NULL;
2272 switch(cmd){
2273 case MC_PRINTER :
2274 srv->cmd = 'p';
2275 break;
2277 case MC_PASSWD :
2278 srv->cmd = 'n';
2279 break;
2281 case MC_CONFIG :
2282 srv->cmd = 'c';
2283 break;
2285 case MC_SIG :
2286 srv->cmd = 's';
2287 break;
2289 case MC_ABOOKS :
2290 srv->cmd = 'a';
2291 break;
2293 case MC_CLISTS :
2294 srv->cmd = 'l';
2295 break;
2297 case MC_RULES :
2298 srv->cmd = 'r';
2299 break;
2301 case MC_DIRECTORY :
2302 srv->cmd = 'd';
2303 break;
2305 case MC_KOLOR :
2306 srv->cmd = 'k';
2307 break;
2309 case MC_REMOTE :
2310 srv->cmd = 'z';
2311 break;
2313 case MC_SECURITY : /* S/MIME setup screen */
2314 srv->cmd = 'm';
2315 break;
2317 case MC_EXCEPT :
2318 srv->exc = !srv->exc;
2319 menu_clear_binding(sparms->keys.menu, 'x');
2320 if(srv->exc){
2321 if(sparms->bar.title) fs_give((void **)&sparms->bar.title);
2322 /* TRANSLATORS: screen title */
2323 sparms->bar.title = cpystr(_("SETUP EXCEPTIONS"));
2324 ps_global->mangled_header = 1;
2325 /* TRANSLATORS: The reason the X is upper case in eXceptions
2326 is because the command key is X. It isn't necessary, just
2327 nice if it works. */
2328 menu_init_binding(sparms->keys.menu, 'x', MC_EXCEPT, "X",
2329 N_("not eXceptions"), SETUP_EXCEPT);
2331 else{
2332 if(sparms->bar.title) fs_give((void **)&sparms->bar.title);
2333 /* TRANSLATORS: screen title */
2334 sparms->bar.title = cpystr(_("SETUP"));
2335 ps_global->mangled_header = 1;
2336 menu_init_binding(sparms->keys.menu, 'x', MC_EXCEPT, "X",
2337 N_("eXceptions"), SETUP_EXCEPT);
2340 if(sparms->keys.menu->which == 1)
2341 ps_global->mangled_footer = 1;
2343 rv = 0;
2344 break;
2346 case MC_NO_EXCEPT :
2347 #if defined(DOS) || defined(OS2)
2348 q_status_message(SM_ORDER, 0, 2, _("Need argument \"-x <except_config>\" or \"PINERCEX\" file to use eXceptions"));
2349 #else
2350 q_status_message(SM_ORDER, 0, 2, _("Need argument \"-x <except_config>\" or \".pinercex\" file to use eXceptions"));
2351 #endif
2352 rv = 0;
2353 break;
2355 default:
2356 alpine_panic("Unexpected command in choose_setup_cmd");
2357 break;
2360 return(rv);
2365 setup_menu(struct pine *ps)
2367 int ret = 0, exceptions = 0;
2368 int printer = 0, passwd = 0, config = 0, sig = 0, dir = 0, smime = 0, exc = 0;
2369 SCROLL_S sargs;
2370 SRV_S *srv;
2371 STORE_S *store;
2373 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2374 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
2375 return(ret);
2378 #if !defined(DOS)
2379 if(!ps_global->vars[V_PRINTER].is_fixed) /* printer can be changed */
2380 printer++;
2381 #endif
2383 #ifdef PASSWD_PROG
2384 if(F_OFF(F_DISABLE_PASSWORD_CMD,ps_global)) /* password is allowed */
2385 passwd++;
2386 #endif
2388 if(F_OFF(F_DISABLE_CONFIG_SCREEN,ps_global)) /* config allowed */
2389 config++;
2391 if(F_OFF(F_DISABLE_SIGEDIT_CMD,ps_global)) /* .sig editing is allowed */
2392 sig++;
2394 #ifdef ENABLE_LDAP
2395 dir++;
2396 #endif
2398 #ifdef SMIME
2399 smime++;
2400 #endif
2402 if(ps_global->post_prc)
2403 exc++;
2405 /* TRANSLATORS: starting here we have a whole screen of help text */
2406 so_puts(store, _("This is the Setup screen for Alpine. Choose from the following commands:\n"));
2408 so_puts(store, "\n");
2409 so_puts(store, _("(E) Exit Setup:\n"));
2410 so_puts(store, _(" This puts you back at the Main Menu.\n"));
2412 if(exc){
2413 so_puts(store, "\n");
2414 so_puts(store, _("(X) eXceptions:\n"));
2415 so_puts(store, _(" This command is different from the rest. It is not actually a command\n"));
2416 so_puts(store, _(" itself. Instead, it is a toggle which modifies the behavior of the\n"));
2417 so_puts(store, _(" other commands. You toggle Exceptions editing on and off with this\n"));
2418 so_puts(store, _(" command. When it is off you will be editing (changing) your regular\n"));
2419 so_puts(store, _(" configuration file. When it is on you will be editing your exceptions\n"));
2420 so_puts(store, _(" configuration file. For example, you might want to type the command \n"));
2421 so_puts(store, _(" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"));
2422 so_puts(store, _(" on a particular platform.\n"));
2423 so_puts(store, _(" (Note: this command does not show up on the keymenu at the bottom of\n"));
2424 so_puts(store, _(" the screen unless you press \"O\" for \"Other Commands\" --but you don't\n"));
2425 so_puts(store, _(" need to press the \"O\" in order to invoke the command.)\n"));
2428 if(printer){
2429 so_puts(store, "\n");
2430 so_puts(store, _("(P) Printer:\n"));
2431 so_puts(store, _(" Allows you to set a default printer and to define custom\n"));
2432 so_puts(store, _(" print commands.\n"));
2435 if(passwd){
2436 so_puts(store, "\n");
2437 so_puts(store, _("(N) Newpassword:\n"));
2438 so_puts(store, _(" Change your password.\n"));
2441 if(config){
2442 so_puts(store, "\n");
2443 so_puts(store, _("(C) Config:\n"));
2444 so_puts(store, _(" Allows you to set or unset many features of Alpine.\n"));
2445 so_puts(store, _(" You may also set the values of many options with this command.\n"));
2448 if(sig){
2449 so_puts(store, "\n");
2450 so_puts(store, _("(S) Signature:\n"));
2451 so_puts(store, _(" Enter or edit a custom signature which will\n"));
2452 so_puts(store, _(" be included with each new message you send.\n"));
2455 so_puts(store, "\n");
2456 so_puts(store, _("(A) AddressBooks:\n"));
2457 so_puts(store, _(" Define a non-default address book.\n"));
2459 so_puts(store, "\n");
2460 so_puts(store, _("(L) collectionLists:\n"));
2461 so_puts(store, _(" You may define groups of folders to help you better organize your mail.\n"));
2463 so_puts(store, "\n");
2464 so_puts(store, _("(R) Rules:\n"));
2465 so_puts(store, _(" This has up to six sub-categories: Roles, Index Colors, Filters,\n"));
2466 so_puts(store, _(" SetScores, Search, and Other. If the Index Colors option is\n"));
2467 so_puts(store, _(" missing you may turn it on (if possible) with Setup/Kolor.\n"));
2468 so_puts(store, _(" If Roles is missing it has probably been administratively disabled.\n"));
2470 if(dir){
2471 so_puts(store, "\n");
2472 so_puts(store, _("(D) Directory:\n"));
2473 so_puts(store, _(" Define an LDAP Directory server for Alpine's use. A directory server is\n"));
2474 so_puts(store, _(" similar to an address book, but it is usually maintained by an\n"));
2475 so_puts(store, _(" organization. It is similar to a telephone directory.\n"));
2478 so_puts(store, "\n");
2479 so_puts(store, _("(K) Kolor:\n"));
2480 so_puts(store, _(" Set custom colors for various parts of the Alpine screens. For example, the\n"));
2481 so_puts(store, _(" command key labels, the titlebar at the top of each page, and quoted\n"));
2482 so_puts(store, _(" sections of messages you are viewing.\n"));
2484 if(smime){
2485 so_puts(store, "\n");
2486 so_puts(store, _("(M) S/MIME:\n"));
2487 so_puts(store, _(" Setup for using S/MIME to verify signed messages, decrypt\n"));
2488 so_puts(store, _(" encrypted messages, and to sign or encrypt outgoing messages.\n"));
2491 so_puts(store, "\n");
2492 so_puts(store, _("(Z) RemoteConfigSetup:\n"));
2493 so_puts(store, _(" This is a command you will probably only want to use once, if at all.\n"));
2494 so_puts(store, _(" It helps you transfer your Alpine configuration data to an IMAP server,\n"));
2495 so_puts(store, _(" where it will be accessible from any of the computers you read mail\n"));
2496 so_puts(store, _(" from (using Alpine). The idea behind a remote configuration is that you\n"));
2497 so_puts(store, _(" can change your configuration in one place and have that change show\n"));
2498 so_puts(store, _(" up on all of the computers you use.\n"));
2499 so_puts(store, _(" (Note: this command does not show up on the keymenu at the bottom of\n"));
2500 so_puts(store, _(" the screen unless you press \"O\" for \"Other Commands\" --but you don't\n"));
2501 so_puts(store, _(" need to press the \"O\" in order to invoke the command.)\n"));
2503 /* put this down here for people who don't have exceptions */
2504 if(!exc){
2505 so_puts(store, "\n");
2506 so_puts(store, _("(X) eXceptions:\n"));
2507 so_puts(store, _(" This command is different from the rest. It is not actually a command\n"));
2508 so_puts(store, _(" itself. Instead, it is a toggle which modifies the behavior of the\n"));
2509 so_puts(store, _(" other commands. You toggle Exceptions editing on and off with this\n"));
2510 so_puts(store, _(" command. When it is off you will be editing (changing) your regular\n"));
2511 so_puts(store, _(" configuration file. When it is on you will be editing your exceptions\n"));
2512 so_puts(store, _(" configuration file. For example, you might want to type the command \n"));
2513 so_puts(store, _(" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"));
2514 so_puts(store, _(" on a particular platform.\n"));
2515 so_puts(store, _(" (Note: this command does not do anything unless you have a configuration\n"));
2516 so_puts(store, _(" with exceptions enabled (you don't have that). Common ways to enable an\n"));
2517 so_puts(store, _(" exceptions config are the command line argument \"-x <exception_config>\";\n"));
2518 so_puts(store, _(" or the existence of the file \".pinercex\" for Unix Alpine, or \"PINERCEX\")\n"));
2519 so_puts(store, _(" for PC-Alpine.)\n"));
2520 so_puts(store, _(" (Another note: this command does not show up on the keymenu at the bottom\n"));
2521 so_puts(store, _(" of the screen unless you press \"O\" for \"Other Commands\" --but you\n"));
2522 so_puts(store, _(" don't need to press the \"O\" in order to invoke the command.)\n"));
2525 memset(&sargs, 0, sizeof(SCROLL_S));
2526 sargs.text.text = so_text(store);
2527 sargs.text.src = CharStar;
2528 sargs.text.desc = _("Information About Setup Command");
2529 sargs.bar.title = cpystr(_("SETUP"));
2530 sargs.proc.tool = choose_setup_cmd;
2531 sargs.help.text = NO_HELP;
2532 sargs.help.title = NULL;
2533 sargs.keys.menu = &choose_setup_keymenu;
2534 sargs.keys.menu->how_many = 2;
2536 setbitmap(sargs.keys.bitmap);
2537 if(!printer)
2538 clrbitn(SETUP_PRINTER, sargs.keys.bitmap);
2540 if(!passwd)
2541 clrbitn(SETUP_PASSWD, sargs.keys.bitmap);
2543 if(!config)
2544 clrbitn(SETUP_CONFIG, sargs.keys.bitmap);
2546 if(!sig)
2547 clrbitn(SETUP_SIG, sargs.keys.bitmap);
2549 if(!dir)
2550 clrbitn(SETUP_DIRECTORY, sargs.keys.bitmap);
2552 if(!smime)
2553 clrbitn(SETUP_SMIME, sargs.keys.bitmap);
2555 if(exc)
2556 menu_init_binding(sargs.keys.menu, 'x', MC_EXCEPT, "X",
2557 N_("eXceptions"), SETUP_EXCEPT);
2558 else
2559 menu_init_binding(sargs.keys.menu, 'x', MC_NO_EXCEPT, "X",
2560 N_("eXceptions"), SETUP_EXCEPT);
2563 scrolltool(&sargs);
2565 ps->mangled_screen = 1;
2567 srv = (SRV_S *)sargs.proc.data.p;
2569 exceptions = srv ? srv->exc : 0;
2571 so_give(&store);
2573 if(sargs.bar.title) fs_give((void**)&sargs.bar.title);
2574 if(srv){
2575 ret = srv->cmd;
2576 fs_give((void **)&sargs.proc.data.p);
2578 else
2579 ret = 'e';
2581 return(ret | (exceptions ? EDIT_EXCEPTION : 0));
2585 /*----------------------------------------------------------------------
2587 Args: command -- command char to perform
2589 ----*/
2590 void
2591 do_setup_task(int command)
2593 char *err = NULL;
2594 int rtype;
2595 int edit_exceptions = 0;
2596 int do_lit_sig = 0;
2598 if(command & EDIT_EXCEPTION){
2599 edit_exceptions = 1;
2600 command &= ~EDIT_EXCEPTION;
2603 switch(command) {
2604 /*----- EDIT SIGNATURE -----*/
2605 case 's':
2606 if(ps_global->VAR_LITERAL_SIG)
2607 do_lit_sig = 1;
2608 else {
2609 char sig_path[MAXPATH+1];
2611 if(!signature_path(ps_global->VAR_SIGNATURE_FILE, sig_path, MAXPATH))
2612 do_lit_sig = 1;
2613 else if((!IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)
2614 && can_access(sig_path, READ_ACCESS) == 0)
2615 ||(IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)
2616 && (folder_exists(NULL, sig_path) & FEX_ISFILE)))
2617 do_lit_sig = 0;
2618 else if(!ps_global->vars[V_SIGNATURE_FILE].main_user_val.p
2619 && !ps_global->vars[V_SIGNATURE_FILE].cmdline_val.p
2620 && !ps_global->vars[V_SIGNATURE_FILE].fixed_val.p)
2621 do_lit_sig = 1;
2622 else
2623 do_lit_sig = 0;
2626 if(do_lit_sig){
2627 char *result = NULL;
2628 char **apval;
2629 EditWhich ew;
2630 int readonly = 0;
2632 ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
2634 if(ps_global->restricted)
2635 readonly = 1;
2636 else switch(ew){
2637 case Main:
2638 readonly = ps_global->prc->readonly;
2639 break;
2640 case Post:
2641 readonly = ps_global->post_prc->readonly;
2642 break;
2643 default:
2644 break;
2647 if(readonly)
2648 err = cpystr(ps_global->restricted
2649 ? "Alpine demo can't change config file"
2650 : _("Config file not changeable"));
2652 if(!err){
2653 apval = APVAL(&ps_global->vars[V_LITERAL_SIG], ew);
2654 if(!apval)
2655 err = cpystr(_("Problem accessing configuration"));
2656 else{
2657 char *input;
2659 input = (char *)fs_get((strlen(*apval ? *apval : "")+1) *
2660 sizeof(char));
2661 input[0] = '\0';
2662 cstring_to_string(*apval, input);
2663 err = signature_edit_lit(input, &result,
2664 _("SIGNATURE EDITOR"),
2665 h_composer_sigedit);
2666 fs_give((void **)&input);
2670 if(!err){
2671 char *cstring_version;
2673 cstring_version = string_to_cstring(result);
2675 set_variable(V_LITERAL_SIG, cstring_version, 0, 0, ew);
2677 if(cstring_version)
2678 fs_give((void **)&cstring_version);
2681 if(result)
2682 fs_give((void **)&result);
2684 else
2685 err = signature_edit(ps_global->VAR_SIGNATURE_FILE,
2686 _("SIGNATURE EDITOR"));
2688 if(err){
2689 q_status_message(SM_ORDER, 3, 4, err);
2690 fs_give((void **)&err);
2693 ps_global->mangled_screen = 1;
2694 break;
2696 /*----- ADD ADDRESSBOOK ----*/
2697 case 'a':
2698 addr_book_config(ps_global, edit_exceptions);
2699 menu_index = ABOOK_MENU_ITEM;
2700 ps_global->mangled_screen = 1;
2701 break;
2703 #ifdef ENABLE_LDAP
2704 /*--- ADD DIRECTORY SERVER --*/
2705 case 'd':
2706 directory_config(ps_global, edit_exceptions);
2707 ps_global->mangled_screen = 1;
2708 break;
2709 #endif
2711 #ifdef SMIME
2712 /*--- S/MIME --*/
2713 case 'm':
2714 smime_config_screen(ps_global, edit_exceptions);
2715 ps_global->mangled_screen = 1;
2716 break;
2717 #endif
2719 /*----- CONFIGURE OPTIONS -----*/
2720 case 'c':
2721 option_screen(ps_global, edit_exceptions);
2722 ps_global->mangled_screen = 1;
2723 break;
2725 /*----- COLLECTION LIST -----*/
2726 case 'l':
2727 folder_config_screen(ps_global, edit_exceptions);
2728 ps_global->mangled_screen = 1;
2729 break;
2731 /*----- RULES -----*/
2732 case 'r':
2733 rtype = rule_setup_type(ps_global, RS_RULES | RS_INCFILTNOW,
2734 _("Type of rule setup : "));
2735 switch(rtype){
2736 case 'r':
2737 case 's':
2738 case 'i':
2739 case 'f':
2740 case 'o':
2741 case 'c':
2742 role_config_screen(ps_global, (rtype == 'r') ? ROLE_DO_ROLES :
2743 (rtype == 's') ? ROLE_DO_SCORES :
2744 (rtype == 'o') ? ROLE_DO_OTHER :
2745 (rtype == 'f') ? ROLE_DO_FILTER :
2746 (rtype == 'c') ? ROLE_DO_SRCH :
2747 ROLE_DO_INCOLS,
2748 edit_exceptions);
2749 break;
2751 case 'Z':
2752 q_status_message(SM_ORDER | SM_DING, 3, 5,
2753 _("Try turning on color with the Setup/Kolor command."));
2754 break;
2756 case 'n':
2757 role_process_filters();
2758 break;
2760 default:
2761 cmd_cancelled(NULL);
2762 break;
2765 ps_global->mangled_screen = 1;
2766 break;
2768 /*----- COLOR -----*/
2769 case 'k':
2770 color_config_screen(ps_global, edit_exceptions);
2771 ps_global->mangled_screen = 1;
2772 break;
2774 case 'z':
2775 convert_to_remote_config(ps_global, edit_exceptions);
2776 ps_global->mangled_screen = 1;
2777 break;
2779 /*----- EXIT -----*/
2780 case 'e':
2781 break;
2783 /*----- NEW PASSWORD -----*/
2784 case 'n':
2785 #ifdef PASSWD_PROG
2786 if(ps_global->restricted){
2787 q_status_message(SM_ORDER, 3, 5,
2788 "Password change unavailable in restricted demo version of Alpine.");
2789 }else {
2790 change_passwd();
2791 ClearScreen();
2792 ps_global->mangled_screen = 1;
2794 #else
2795 q_status_message(SM_ORDER, 0, 5,
2796 _("Password changing not configured for this version of Alpine."));
2797 display_message('x');
2798 #endif /* DOS */
2799 break;
2801 #if !defined(DOS)
2802 /*----- CHOOSE PRINTER ------*/
2803 case 'p':
2804 select_printer(ps_global, edit_exceptions);
2805 ps_global->mangled_screen = 1;
2806 break;
2807 #endif
2813 rule_setup_type(struct pine *ps, int flags, char *prompt)
2815 ESCKEY_S opts[9];
2816 int ekey_num = 0, deefault = 0;
2818 if(flags & RS_INCADDR){
2819 deefault = 'a';
2820 opts[ekey_num].ch = 'a';
2821 opts[ekey_num].rval = 'a';
2822 opts[ekey_num].name = "A";
2823 opts[ekey_num++].label = "Addrbook";
2826 if(flags & RS_RULES){
2828 if(F_OFF(F_DISABLE_ROLES_SETUP,ps)){ /* roles are allowed */
2829 if(deefault != 'a')
2830 deefault = 'r';
2832 opts[ekey_num].ch = 'r';
2833 opts[ekey_num].rval = 'r';
2834 opts[ekey_num].name = "R";
2835 opts[ekey_num++].label = "Roles";
2837 else if(deefault != 'a')
2838 deefault = 's';
2840 opts[ekey_num].ch = 's';
2841 opts[ekey_num].rval = 's';
2842 opts[ekey_num].name = "S";
2843 opts[ekey_num++].label = "SetScores";
2845 #ifndef _WINDOWS
2846 if(ps->color_style != COL_NONE && pico_hascolor()){
2847 #endif
2848 if(deefault != 'a')
2849 deefault = 'i';
2851 opts[ekey_num].ch = 'i';
2852 opts[ekey_num].rval = 'i';
2853 opts[ekey_num].name = "I";
2854 opts[ekey_num++].label = "Indexcolor";
2855 #ifndef _WINDOWS
2857 else{
2858 opts[ekey_num].ch = 'i';
2859 opts[ekey_num].rval = 'Z'; /* notice this rval! */
2860 opts[ekey_num].name = "I";
2861 opts[ekey_num++].label = "Indexcolor";
2863 #endif
2865 opts[ekey_num].ch = 'f';
2866 opts[ekey_num].rval = 'f';
2867 opts[ekey_num].name = "F";
2868 opts[ekey_num++].label = "Filters";
2870 opts[ekey_num].ch = 'o';
2871 opts[ekey_num].rval = 'o';
2872 opts[ekey_num].name = "O";
2873 opts[ekey_num++].label = "Other";
2875 opts[ekey_num].ch = 'c';
2876 opts[ekey_num].rval = 'c';
2877 opts[ekey_num].name = "C";
2878 opts[ekey_num++].label = "searCh";
2882 if(flags & RS_INCEXP){
2883 opts[ekey_num].ch = 'e';
2884 opts[ekey_num].rval = 'e';
2885 opts[ekey_num].name = "E";
2886 opts[ekey_num++].label = "Export";
2889 if(flags & RS_INCFILTNOW){
2890 opts[ekey_num].ch = 'n';
2891 opts[ekey_num].rval = 'n';
2892 opts[ekey_num].name = "N";
2893 opts[ekey_num++].label = "filterNow";
2896 opts[ekey_num].ch = -1;
2898 return(radio_buttons(prompt, -FOOTER_ROWS(ps), opts,
2899 deefault, 'x', NO_HELP, RB_NORM));
2905 * Process the command list, changing function key notation into
2906 * lexical equivalents.
2908 void
2909 process_init_cmds(struct pine *ps, char **list)
2911 char **p;
2912 int i = 0;
2913 int j;
2914 int lpm1;
2915 #define MAX_INIT_CMDS 500
2916 /* this is just a temporary stack array, the real one is allocated below */
2917 int i_cmds[MAX_INIT_CMDS];
2918 int fkeys = 0;
2919 int not_fkeys = 0;
2921 if(list){
2922 for(p = list; *p; p++){
2923 if(i >= MAX_INIT_CMDS){
2924 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
2925 "Initial keystroke list too long at \"%s\"", *p);
2926 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
2927 break;
2931 /* regular character commands */
2932 if(strlen(*p) == 1){
2933 i_cmds[i++] = **p;
2934 not_fkeys++;
2937 /* special commands */
2938 else if(strucmp(*p, "SPACE") == 0)
2939 i_cmds[i++] = ' ';
2940 else if(strucmp(*p, "CR") == 0)
2941 i_cmds[i++] = '\n';
2942 else if(strucmp(*p, "TAB") == 0)
2943 i_cmds[i++] = '\t';
2944 else if(strucmp(*p, "UP") == 0)
2945 i_cmds[i++] = KEY_UP;
2946 else if(strucmp(*p, "DOWN") == 0)
2947 i_cmds[i++] = KEY_DOWN;
2948 else if(strucmp(*p, "LEFT") == 0)
2949 i_cmds[i++] = KEY_LEFT;
2950 else if(strucmp(*p, "RIGHT") == 0)
2951 i_cmds[i++] = KEY_RIGHT;
2953 /* control chars */
2954 else if(strlen(*p) == 2 && **p == '^')
2955 i_cmds[i++] = ctrl(*((*p)+1));
2957 /* function keys */
2958 else if(**p == 'F' || **p == 'f'){
2959 int v;
2961 fkeys++;
2962 v = atoi((*p)+1);
2963 if(v >= 1 && v <= 12)
2964 i_cmds[i++] = PF1 + v - 1;
2965 else
2966 i_cmds[i++] = KEY_JUNK;
2969 /* literal string */
2970 else if(**p == '"' && (*p)[lpm1 = strlen(*p) - 1] == '"'){
2971 if(lpm1 + i - 1 > MAX_INIT_CMDS){
2972 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
2973 "Initial keystroke list too long, truncated at %s\n", *p);
2974 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
2975 break; /* Bail out of this loop! */
2976 } else
2977 for(j = 1; j < lpm1; j++)
2978 i_cmds[i++] = (*p)[j];
2980 else {
2981 snprintf(tmp_20k_buf,SIZEOF_20KBUF,
2982 "Bad initial keystroke \"%.500s\" (missing comma?)", *p);
2983 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
2984 break;
2990 * We don't handle the case where function keys are used to specify the
2991 * commands but some non-function key input is also required. For example,
2992 * you might want to jump to a specific message number and view it
2993 * on start up. To do that, you need to use character commands instead
2994 * of function key commands in the initial-keystroke-list.
2996 if(fkeys && not_fkeys){
2997 init_error(ps, SM_ORDER | SM_DING, 3, 5,
2998 "Mixed characters and function keys in \"initial-keystroke-list\", skipping.");
2999 i = 0;
3002 if(fkeys && !not_fkeys)
3003 F_TURN_ON(F_USE_FK,ps);
3004 if(!fkeys && not_fkeys)
3005 F_TURN_OFF(F_USE_FK,ps);
3007 if(i > 0){
3008 ps->initial_cmds = (int *)fs_get((i+1) * sizeof(int));
3009 ps->free_initial_cmds = ps->initial_cmds;
3010 for(j = 0; j < i; j++)
3011 ps->initial_cmds[j] = i_cmds[j];
3013 ps->initial_cmds[i] = 0;
3014 ps->in_init_seq = ps->save_in_init_seq = 1;
3019 UCS *
3020 user_wordseps(char **list)
3022 char **p;
3023 int i = 0;
3024 int j;
3025 #define MAX_SEPARATORS 500
3027 * This is just a temporary stack array, the real one is allocated below.
3028 * This is supposed to be way large enough.
3030 UCS seps[MAX_SEPARATORS+1];
3031 UCS *u;
3032 UCS *return_array = NULL;
3033 size_t l;
3035 seps[0] = '\0';
3037 if(list){
3038 for(p = list; *p; p++){
3039 if(i >= MAX_SEPARATORS){
3040 q_status_message(SM_ORDER | SM_DING, 3, 3,
3041 "Warning: composer-word-separators list is too long");
3042 break;
3045 u = utf8_to_ucs4_cpystr(*p);
3047 if(u){
3048 if(ucs4_strlen(u) == 1)
3049 seps[i++] = *u;
3050 else if(*u == '"' && u[l = ucs4_strlen(u) - 1] == '"'){
3051 if(l + i - 1 > MAX_SEPARATORS){
3052 q_status_message(SM_ORDER | SM_DING, 3, 3,
3053 "Warning: composer-word-separators list is too long");
3054 break; /* Bail out of this loop! */
3056 else{
3057 for(j = 1; j < l; j++)
3058 seps[i++] = u[j];
3061 else{
3062 l = ucs4_strlen(u);
3063 if(l + i > MAX_SEPARATORS){
3064 q_status_message(SM_ORDER | SM_DING, 3, 3,
3065 "Warning: composer-word-separators list is too long");
3066 break; /* Bail out of this loop! */
3068 else{
3069 for(j = 0; j < l; j++)
3070 seps[i++] = u[j];
3074 fs_give((void **) &u);
3079 seps[i] = '\0';
3081 if(i > 0)
3082 return_array = ucs4_cpystr(seps);
3084 return(return_array);
3089 * Make sure any errors during initialization get queued for display
3091 void
3092 queue_init_errors(struct pine *ps)
3094 int i;
3096 if(ps->init_errs){
3097 for(i = 0; (ps->init_errs)[i].message; i++){
3098 q_status_message((ps->init_errs)[i].flags,
3099 (ps->init_errs)[i].min_time,
3100 (ps->init_errs)[i].max_time,
3101 (ps->init_errs)[i].message);
3102 fs_give((void **)&(ps->init_errs)[i].message);
3105 fs_give((void **)&ps->init_errs);
3110 /*----------------------------------------------------------------------
3111 Quit pine if the user wants to
3113 Args: The usual pine structure
3115 Result: User is asked if she wants to quit, if yes then execute quit.
3117 Q U I T S C R E E N
3119 Not really a full screen. Just count up deletions and ask if we really
3120 want to quit.
3121 ----*/
3122 void
3123 quit_screen(struct pine *pine_state)
3125 int quit = 0;
3127 dprint((1, "\n\n ---- QUIT SCREEN ----\n"));
3129 if(F_ON(F_CHECK_MAIL_ONQUIT,ps_global)
3130 && new_mail(1, VeryBadTime, NM_STATUS_MSG | NM_DEFER_SORT) > 0
3131 && (quit = want_to(_("Quit even though new mail just arrived"), 'y', 0,
3132 NO_HELP, WT_NORM)) != 'y'){
3133 refresh_sort(pine_state->mail_stream, pine_state->msgmap, SRT_VRB);
3134 pine_state->next_screen = pine_state->prev_screen;
3135 return;
3138 if(quit != 'y'
3139 && F_OFF(F_QUIT_WO_CONFIRM,pine_state)
3140 && want_to(_("Really quit Alpine"), 'y', 0, NO_HELP, WT_NORM) != 'y'){
3141 pine_state->next_screen = pine_state->prev_screen;
3142 return;
3145 goodnight_gracey(pine_state, 0);
3149 /*----------------------------------------------------------------------
3150 The nuts and bolts of actually cleaning up and exitting pine
3152 Args: ps -- the usual pine structure,
3153 exit_val -- what to tell our parent
3155 Result: This never returns
3157 ----*/
3158 void
3159 goodnight_gracey(struct pine *pine_state, int exit_val)
3161 int i, cnt_user_streams = 0;
3162 char *final_msg = NULL;
3163 char msg[MAX_SCREEN_COLS+1];
3164 char *pf = _("Alpine finished");
3165 MAILSTREAM *m;
3166 extern KBESC_T *kbesc;
3168 dprint((2, "goodnight_gracey:\n"));
3170 /* We want to do this here before we close up the streams */
3171 trim_remote_adrbks();
3173 for(i = 0; i < ps_global->s_pool.nstream; i++){
3174 m = ps_global->s_pool.streams[i];
3175 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR))
3176 cnt_user_streams++;
3179 /* clean up open streams */
3181 if(pine_state->mail_stream
3182 && sp_flagged(pine_state->mail_stream, SP_LOCKED)
3183 && sp_flagged(pine_state->mail_stream, SP_USERFLDR)){
3184 dprint((5, "goodnight_gracey: close current stream\n"));
3185 expunge_and_close(pine_state->mail_stream,
3186 (cnt_user_streams <= 1) ? &final_msg : NULL, EC_NONE);
3187 cnt_user_streams--;
3190 pine_state->mail_stream = NULL;
3191 pine_state->redrawer = (void (*)(void))NULL;
3193 dprint((5,
3194 "goodnight_gracey: close other stream pool streams\n"));
3195 for(i = 0; i < ps_global->s_pool.nstream; i++){
3196 m = ps_global->s_pool.streams[i];
3198 * fix global for functions that depend(ed) on it sort_folder.
3199 * Hopefully those will get phased out.
3201 ps_global->mail_stream = m;
3202 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
3203 && !sp_flagged(m, SP_INBOX)){
3204 sp_set_expunge_count(m, 0L);
3205 expunge_and_close(m, (cnt_user_streams <= 1) ? &final_msg : NULL,
3206 EC_NONE);
3207 cnt_user_streams--;
3211 for(i = 0; i < ps_global->s_pool.nstream; i++){
3212 m = ps_global->s_pool.streams[i];
3214 * fix global for functions that depend(ed) on it (sort_folder).
3215 * Hopefully those will get phased out.
3217 ps_global->mail_stream = m;
3218 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
3219 && sp_flagged(m, SP_INBOX)){
3220 dprint((5,
3221 "goodnight_gracey: close inbox stream stream\n"));
3222 sp_set_expunge_count(m, 0L);
3223 expunge_and_close(m, (cnt_user_streams <= 1) ? &final_msg : NULL,
3224 EC_NONE);
3225 cnt_user_streams--;
3229 #ifdef _WINDOWS
3230 if(ps_global->ttyo)
3231 (void)get_windsize(ps_global->ttyo);
3232 #endif
3234 dprint((7, "goodnight_gracey: close config files\n"));
3236 #ifdef SMIME
3237 smime_deinit();
3238 #endif
3240 free_pinerc_strings(&pine_state);
3242 strncpy(msg, pf, sizeof(msg));
3243 msg[sizeof(msg)-1] = '\0';
3244 if(final_msg){
3245 strncat(msg, " -- ", sizeof(msg)-strlen(msg)-1);
3246 msg[sizeof(msg)-1] = '\0';
3247 strncat(msg, final_msg, sizeof(msg)-strlen(msg)-1);
3248 msg[sizeof(msg)-1] = '\0';
3249 fs_give((void **)&final_msg);
3252 dprint((7, "goodnight_gracey: sp_end\n"));
3253 ps_global->noshow_error = 1;
3254 sp_end();
3256 /* after sp_end, which might call a filter */
3257 completely_done_with_adrbks();
3259 dprint((7, "goodnight_gracey: end_screen\n"));
3260 end_screen(msg, exit_val);
3261 dprint((7, "goodnight_gracey: end_titlebar\n"));
3262 end_titlebar();
3263 dprint((7, "goodnight_gracey: end_keymenu\n"));
3264 end_keymenu();
3266 dprint((7, "goodnight_gracey: end_keyboard\n"));
3267 end_keyboard(F_ON(F_USE_FK,pine_state));
3268 dprint((7, "goodnight_gracey: end_ttydriver\n"));
3269 end_tty_driver(pine_state);
3270 #if !defined(DOS) && !defined(OS2)
3271 kbdestroy(kbesc);
3272 #if !defined(LEAVEOUTFIFO)
3273 close_newmailfifo();
3274 #endif
3275 #endif
3276 end_signals(0);
3277 if(filter_data_file(0))
3278 our_unlink(filter_data_file(0));
3280 imap_flush_passwd_cache(TRUE);
3281 free_newsgrp_cache();
3282 mailcap_free();
3283 close_every_pattern();
3284 free_extra_hdrs();
3285 free_contexts(&ps_global->context_list);
3286 free_charsetchecker();
3287 dprint((7, "goodnight_gracey: free more memory\n"));
3288 #ifdef ENABLE_LDAP
3289 free_saved_query_parameters();
3290 #endif
3292 free_pine_struct(&pine_state);
3294 free_histlist();
3296 #ifdef DEBUG
3297 if(debugfile){
3298 if(debug >= 2)
3299 fputs("goodnight_gracey finished\n", debugfile);
3301 fclose(debugfile);
3303 #endif
3305 exit(exit_val);
3309 /*----------------------------------------------------------------------
3310 Call back for c-client to feed us back the progress of network reads
3312 Input:
3314 Result:
3315 ----*/
3316 void
3317 pine_read_progress(GETS_DATA *md, long unsigned int count)
3319 gets_bytes += count; /* update counter */
3323 /*----------------------------------------------------------------------
3324 Function to fish the current byte count from a c-client fetch.
3326 Input: reset -- flag telling us to reset the count
3328 Result: Returns the number of bytes read by the c-client so far
3329 ----*/
3330 unsigned long
3331 pine_gets_bytes(int reset)
3333 if(reset)
3334 gets_bytes = 0L;
3336 return(gets_bytes);
3340 /*----------------------------------------------------------------------
3341 Panic pine - call on detected programmatic errors to exit pine
3343 Args: message -- message to record in debug file and to be printed for user
3345 Result: The various tty modes are restored
3346 If debugging is active a core dump will be generated
3347 Exits Alpine
3349 This is also called from imap routines and fs_get and fs_resize.
3350 ----*/
3351 void
3352 alpine_panic(char *message)
3354 char buf[256];
3356 /* global variable in .../pico/edef.h */
3357 in_panic = 1;
3359 if(ps_global->ttyo){
3360 end_screen(NULL, -1);
3361 end_keyboard(ps_global != NULL ? F_ON(F_USE_FK,ps_global) : 0);
3362 end_tty_driver(ps_global);
3363 end_signals(1);
3365 if(filter_data_file(0))
3366 our_unlink(filter_data_file(0));
3368 dprint((1, "\n===========================================\n\n"));
3369 dprint((1, " Alpine Panic: %s\n\n", message ? message : "?"));
3370 dprint((1, "===========================================\n\n"));
3372 /* intercept c-client "free storage" errors */
3373 if(strstr(message, "free storage"))
3374 snprintf(buf, sizeof(buf), _("No more available memory.\nAlpine Exiting"));
3375 else
3376 snprintf(buf, sizeof(buf), _("Problem detected: \"%s\".\nAlpine Exiting."), message);
3378 buf[sizeof(buf)-1] = '\0';
3380 #ifdef _WINDOWS
3381 /* Put up a message box. */
3382 mswin_messagebox (buf, 1);
3383 #else
3384 fprintf(stderr, "\n\n%s\n", buf);
3385 #endif
3387 #ifdef DEBUG
3388 if(debugfile){
3389 save_debug_on_crash(debugfile, recent_keystroke);
3392 coredump(); /*--- If we're debugging get a core dump --*/
3393 #endif
3395 exit(-1);
3396 fatal("ffo"); /* BUG -- hack to get fatal out of library in right order*/
3401 * panicking - function to test whether or not we're exiting under stress.
3405 panicking(void)
3407 return(in_panic);
3411 /*----------------------------------------------------------------------
3412 exceptional_exit - called to exit under unusual conditions (with no core)
3414 Args: message -- message to record in debug file and to be printed for user
3415 ev -- exit value
3417 ----*/
3418 void
3419 exceptional_exit(char *message, int ev)
3421 fprintf(stderr, "%s\n", message);
3422 exit(ev);
3427 * PicoText Storage Object Support Routines
3430 STORE_S *
3431 pine_pico_get(void)
3433 return((STORE_S *)pico_get());
3437 pine_pico_give(STORE_S **sop)
3439 pico_give((void *)sop);
3440 return(1);
3444 pine_pico_writec(int c, STORE_S *so)
3446 unsigned char ch = (unsigned char) c;
3448 return(pico_writec(so->txt, ch, PICOREADC_NONE));
3452 pine_pico_writec_noucs(int c, STORE_S *so)
3454 unsigned char ch = (unsigned char) c;
3456 return(pico_writec(so->txt, ch, PICOREADC_NOUCS));
3460 pine_pico_readc(unsigned char *c, STORE_S *so)
3462 return(pico_readc(so->txt, c, PICOREADC_NONE));
3466 pine_pico_readc_noucs(unsigned char *c, STORE_S *so)
3468 return(pico_readc(so->txt, c, PICOREADC_NOUCS));
3472 pine_pico_puts(STORE_S *so, char *s)
3474 return(pico_puts(so->txt, s, PICOREADC_NONE));
3478 pine_pico_puts_noucs(STORE_S *so, char *s)
3480 return(pico_puts(so->txt, s, PICOREADC_NOUCS));
3484 pine_pico_seek(STORE_S *so, long pos, int orig)
3486 return(pico_seek((void *)so, pos, orig));
3491 remote_pinerc_failure(void)
3493 #ifdef _WINDOWS
3494 if(ps_global->install_flag) /* just exit silently */
3495 exit(0);
3496 #endif /* _WINDOWS */
3498 if(ps_global->exit_if_no_pinerc){
3499 exceptional_exit("Exiting because -bail option is set and config file not readable.", -1);
3502 if(want_to("Trouble reading remote configuration! Continue anyway ",
3503 'n', 'n', NO_HELP, WT_FLUSH_IN) != 'y'){
3504 return(0);
3507 return(1);
3511 void
3512 dump_supported_options(void)
3514 char **config;
3516 config = get_supported_options();
3517 if(config){
3518 display_args_err(NULL, config, 0);
3519 free_list_array(&config);
3524 /*----------------------------------------------------------------------
3525 Check pruned-folders for validity, making sure they are in the
3526 same context as sent-mail.
3528 ----*/
3530 prune_folders_ok(void)
3532 char **p;
3534 for(p = ps_global->VAR_PRUNED_FOLDERS; p && *p && **p; p++)
3535 if(!context_isambig(*p))
3536 return(0);
3538 return(1);
3542 #ifdef WIN32
3543 char *
3544 pine_user_callback()
3546 if(ps_global->VAR_USER_ID && ps_global->VAR_USER_ID[0]){
3547 return(ps_global->VAR_USER_ID);
3549 else{
3550 /* SHOULD PROMPT HERE! */
3551 return(NULL);
3554 #endif
3556 #ifdef _WINDOWS
3558 * windows callback to get/set function keys mode state
3561 fkey_mode_callback(set, args)
3562 int set;
3563 long args;
3565 return(F_ON(F_USE_FK, ps_global) != 0);
3569 void
3570 imap_telemetry_on()
3572 if(ps_global->mail_stream)
3573 mail_debug(ps_global->mail_stream);
3577 void
3578 imap_telemetry_off()
3580 if(ps_global->mail_stream)
3581 mail_nodebug(ps_global->mail_stream);
3585 char *
3586 pcpine_help_main(title)
3587 char *title;
3589 if(title)
3590 strncpy(title, _("PC-Alpine MAIN MENU Help"), 256);
3592 return(pcpine_help(main_menu_tx));
3597 pcpine_main_cursor(col, row)
3598 int col;
3599 long row;
3601 unsigned ndmi;
3603 if (row >= (HEADER_ROWS(ps_global) + MNSKIP(ps_global)))
3604 ndmi = (row+1 - HEADER_ROWS(ps_global) - (MNSKIP(ps_global)+1))/(MNSKIP(ps_global)+1);
3606 if (row >= (HEADER_ROWS(ps_global) + MNSKIP(ps_global))
3607 && !(MNSKIP(ps_global) && (row+1) & 0x01)
3608 && ndmi <= MAX_MENU_ITEM
3609 && FOOTER_ROWS(ps_global) + (ndmi+1)*(MNSKIP(ps_global)+1)
3610 + MNSKIP(ps_global) + FOOTER_ROWS(ps_global) <= ps_global->ttyo->screen_rows)
3611 return(MSWIN_CURSOR_HAND);
3612 else
3613 return(MSWIN_CURSOR_ARROW);
3615 #endif /* _WINDOWS */