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