Commit 549f2c21 549f2c21d0e1fcb6837063bd6d83dcc190ff792e by Sergey Poznyakoff

Implement variable expansions in prompt.

1 parent f906eddf
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
2 GNU Mailutils pop3 functions. This application interactively allows users 2 GNU Mailutils pop3 functions. This application interactively allows users
3 to contact a pop3 server. 3 to contact a pop3 server.
4 4
5 Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation 5 Copyright (C) 2003, 2004, 2005, 2007, 2008 Free Software Foundation
6 6
7 GNU Mailutils is free software; you can redistribute it and/or modify 7 GNU Mailutils is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by 8 it under the terms of the GNU General Public License as published by
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
23 # include <config.h> 23 # include <config.h>
24 #endif 24 #endif
25 #include <sys/types.h> 25 #include <sys/types.h>
26 #include <netinet/in.h>
27 #include <netdb.h>
26 #include <unistd.h> 28 #include <unistd.h>
27 #include <stdio.h> 29 #include <stdio.h>
28 #include <errno.h> 30 #include <errno.h>
...@@ -42,6 +44,8 @@ ...@@ -42,6 +44,8 @@
42 #include <mailutils/iterator.h> 44 #include <mailutils/iterator.h>
43 #include <mailutils/error.h> 45 #include <mailutils/error.h>
44 #include <mailutils/errno.h> 46 #include <mailutils/errno.h>
47 #include <mailutils/vartab.h>
48 #include <mailutils/argcv.h>
45 49
46 /* A structure which contains information on the commands this program 50 /* A structure which contains information on the commands this program
47 can understand. */ 51 can understand. */
...@@ -73,6 +77,7 @@ int com_top (char *); ...@@ -73,6 +77,7 @@ int com_top (char *);
73 int com_uidl (char *); 77 int com_uidl (char *);
74 int com_user (char *); 78 int com_user (char *);
75 int com_verbose (char *); 79 int com_verbose (char *);
80 int com_prompt (char *);
76 81
77 void initialize_readline (void); 82 void initialize_readline (void);
78 char *stripwhite (char *); 83 char *stripwhite (char *);
...@@ -84,31 +89,29 @@ int valid_argument (const char *, char *); ...@@ -84,31 +89,29 @@ int valid_argument (const char *, char *);
84 void sig_int (int); 89 void sig_int (int);
85 90
86 COMMAND commands[] = { 91 COMMAND commands[] = {
87 {"apop", com_apop, "Authenticate with APOP: APOP user secret"}, 92 { "apop", com_apop, "Authenticate with APOP: APOP user secret" },
88 {"capa", com_capa, "List capabilities: capa"}, 93 { "capa", com_capa, "List capabilities: capa" },
89 {"disconnect", com_disconnect, "Close connection: disconnect"}, 94 { "disconnect", com_disconnect, "Close connection: disconnect" },
90 {"dele", com_dele, "Mark message: DELE msgno"}, 95 { "dele", com_dele, "Mark message: DELE msgno" },
91 {"exit", com_exit, "exit program"}, 96 { "exit", com_exit, "exit program" },
92 {"help", com_help, "Display this text"}, 97 { "help", com_help, "Display this text" },
93 {"?", com_help, "Synonym for `help'"}, 98 { "?", com_help, "Synonym for `help'" },
94 {"list", com_list, "List messages: LIST [msgno]"}, 99 { "list", com_list, "List messages: LIST [msgno]" },
95 {"noop", com_noop, "Send no operation: NOOP"}, 100 { "noop", com_noop, "Send no operation: NOOP" },
96 {"pass", com_pass, "Send passwd: PASS [passwd]"}, 101 { "pass", com_pass, "Send passwd: PASS [passwd]" },
97 {"connect", com_connect, "Open connection: connect hostname [port]"}, 102 { "prompt", com_prompt, "Set command prompt" },
98 {"quit", com_quit, "Go to Update state : QUIT"}, 103 { "connect", com_connect, "Open connection: connect hostname [port]" },
99 {"retr", com_retr, "Dowload message: RETR msgno"}, 104 { "quit", com_quit, "Go to Update state : QUIT" },
100 {"rset", com_rset, "Unmark all messages: RSET"}, 105 { "retr", com_retr, "Dowload message: RETR msgno" },
101 {"stat", com_stat, "Get the size and count of mailbox : STAT [msgno]"}, 106 { "rset", com_rset, "Unmark all messages: RSET" },
102 {"top", com_top, "Get the header of message: TOP msgno [lines]"}, 107 { "stat", com_stat, "Get the size and count of mailbox : STAT [msgno]" },
103 {"uidl", com_uidl, "Get the unique id of message: UIDL [msgno]"}, 108 { "top", com_top, "Get the header of message: TOP msgno [lines]" },
104 {"user", com_user, "send login: USER user"}, 109 { "uidl", com_uidl, "Get the unique id of message: UIDL [msgno]" },
105 {"verbose", com_verbose, "Enable Protocol tracing: verbose [on|off]"}, 110 { "user", com_user, "send login: USER user" },
106 {NULL, NULL, NULL} 111 { "verbose", com_verbose, "Enable Protocol tracing: verbose [on|off]" },
112 { NULL, NULL, NULL }
107 }; 113 };
108 114
109 /* The name of this program, as taken from argv[0]. */
110 char *progname;
111
112 /* Global handle for pop3. */ 115 /* Global handle for pop3. */
113 mu_pop3_t pop3; 116 mu_pop3_t pop3;
114 117
...@@ -118,6 +121,69 @@ int verbose; ...@@ -118,6 +121,69 @@ int verbose;
118 /* When non-zero, this global means the user is done using this program. */ 121 /* When non-zero, this global means the user is done using this program. */
119 int done; 122 int done;
120 123
124 enum pop_session_status
125 {
126 pop_session_disconnected,
127 pop_session_connected,
128 pop_session_logged_in
129 };
130
131 enum pop_session_status pop_session_status;
132
133 int connect_argc;
134 char **connect_argv;
135
136 /* Host we are connected to. */
137 #define host connect_argv[0]
138 int port = 110;
139 char *username;
140
141 /* Command line prompt */
142 #define DEFAULT_PROMPT "pop3> "
143 char *prompt;
144
145 const char *
146 pop_session_str (enum pop_session_status stat)
147 {
148 switch (stat)
149 {
150 case pop_session_disconnected:
151 return "disconnected";
152
153 case pop_session_connected:
154 return "connected";
155
156 case pop_session_logged_in:
157 return "logged in";
158 }
159 }
160
161 char *
162 expand_prompt ()
163 {
164 mu_vartab_t vtab;
165 char *str;
166
167 if (mu_vartab_create (&vtab))
168 return strdup (prompt);
169 mu_vartab_define (vtab, "user",
170 (pop_session_status == pop_session_logged_in) ?
171 username : "not logged in", 1);
172 mu_vartab_define (vtab, "host",
173 (pop_session_status != pop_session_disconnected) ?
174 host : "not connected", 1);
175 mu_vartab_define (vtab, "program-name", mu_program_name, 1);
176 mu_vartab_define (vtab, "canonical-program-name", "pop3client", 1);
177 mu_vartab_define (vtab, "package", PACKAGE, 1);
178 mu_vartab_define (vtab, "version", PACKAGE_VERSION, 1);
179 mu_vartab_define (vtab, "status", pop_session_str (pop_session_status), 1);
180
181 if (mu_vartab_expand (vtab, prompt, &str))
182 str = strdup (prompt);
183 mu_vartab_destroy (&vtab);
184 return str;
185 }
186
121 char * 187 char *
122 dupstr (const char *s) 188 dupstr (const char *s)
123 { 189 {
...@@ -126,7 +192,7 @@ dupstr (const char *s) ...@@ -126,7 +192,7 @@ dupstr (const char *s)
126 r = malloc (strlen (s) + 1); 192 r = malloc (strlen (s) + 1);
127 if (!r) 193 if (!r)
128 { 194 {
129 fprintf (stderr, "Memory exhausted\n"); 195 mu_error ("Memory exhausted");
130 exit (1); 196 exit (1);
131 } 197 }
132 strcpy (r, s); 198 strcpy (r, s);
...@@ -159,11 +225,10 @@ initialize_readline () ...@@ -159,11 +225,10 @@ initialize_readline ()
159 in case we want to do some simple parsing. Return the array of matches, 225 in case we want to do some simple parsing. Return the array of matches,
160 or NULL if there aren't any. */ 226 or NULL if there aren't any. */
161 char ** 227 char **
162 pop_completion (char *text, int start, int end) 228 pop_completion (char *text, int start, int end MU_ARG_UNUSED)
163 { 229 {
164 char **matches; 230 char **matches;
165 231
166 (void) end;
167 matches = (char **) NULL; 232 matches = (char **) NULL;
168 233
169 /* If this word is at the start of the line, then it is a command 234 /* If this word is at the start of the line, then it is a command
...@@ -203,7 +268,7 @@ command_generator (const char *text, int state) ...@@ -203,7 +268,7 @@ command_generator (const char *text, int state)
203 } 268 }
204 269
205 /* If no names matched, then return NULL. */ 270 /* If no names matched, then return NULL. */
206 return ((char *) NULL); 271 return NULL;
207 } 272 }
208 273
209 #else 274 #else
...@@ -240,19 +305,16 @@ main (int argc MU_ARG_UNUSED, char **argv) ...@@ -240,19 +305,16 @@ main (int argc MU_ARG_UNUSED, char **argv)
240 { 305 {
241 char *line, *s; 306 char *line, *s;
242 307
243 progname = strrchr (argv[0], '/'); 308 mu_set_program_name (argv[0]);
244 if (progname) 309 prompt = strdup (DEFAULT_PROMPT);
245 progname++;
246 else
247 progname = argv[0];
248
249 initialize_readline (); /* Bind our completer. */ 310 initialize_readline (); /* Bind our completer. */
250 311
251 /* Loop reading and executing lines until the user quits. */ 312 /* Loop reading and executing lines until the user quits. */
252 while (!done) 313 while (!done)
253 { 314 {
254 315 char *p = expand_prompt ();
255 line = readline ((char *) "pop3> "); 316 line = readline (p);
317 free (p);
256 318
257 if (!line) 319 if (!line)
258 break; 320 break;
...@@ -268,7 +330,7 @@ main (int argc MU_ARG_UNUSED, char **argv) ...@@ -268,7 +330,7 @@ main (int argc MU_ARG_UNUSED, char **argv)
268 add_history (s); 330 add_history (s);
269 status = execute_line (s); 331 status = execute_line (s);
270 if (status != 0) 332 if (status != 0)
271 fprintf (stderr, "Error: %s\n", mu_strerror (status)); 333 mu_error ("Error: %s", mu_strerror (status));
272 } 334 }
273 335
274 free (line); 336 free (line);
...@@ -300,8 +362,8 @@ execute_line (char *line) ...@@ -300,8 +362,8 @@ execute_line (char *line)
300 362
301 if (!command) 363 if (!command)
302 { 364 {
303 fprintf (stderr, "%s: No such command for %s.\n", word, progname); 365 mu_error ("%s: No such command.", word);
304 return (-1); 366 return 0;
305 } 367 }
306 368
307 /* Get argument to command, if any. */ 369 /* Get argument to command, if any. */
...@@ -378,14 +440,20 @@ com_verbose (char *arg) ...@@ -378,14 +440,20 @@ com_verbose (char *arg)
378 int 440 int
379 com_user (char *arg) 441 com_user (char *arg)
380 { 442 {
443 int status;
444
381 if (!valid_argument ("user", arg)) 445 if (!valid_argument ("user", arg))
382 return EINVAL; 446 return EINVAL;
383 return mu_pop3_user (pop3, arg); 447 status = mu_pop3_user (pop3, arg);
448 if (status == 0)
449 username = strdup (arg);
450 return status;
384 } 451 }
385 452
386 int 453 int
387 com_apop (char *arg) 454 com_apop (char *arg)
388 { 455 {
456 int status;
389 char *user, *digest; 457 char *user, *digest;
390 458
391 if (!valid_argument ("apop", arg)) 459 if (!valid_argument ("apop", arg))
...@@ -394,7 +462,13 @@ com_apop (char *arg) ...@@ -394,7 +462,13 @@ com_apop (char *arg)
394 digest = strtok (NULL, " "); 462 digest = strtok (NULL, " ");
395 if (!valid_argument ("apop", user) || !valid_argument ("apop", digest)) 463 if (!valid_argument ("apop", user) || !valid_argument ("apop", digest))
396 return EINVAL; 464 return EINVAL;
397 return mu_pop3_apop (pop3, user, digest); 465 status = mu_pop3_apop (pop3, user, digest);
466 if (status == 0)
467 {
468 username = strdup (user);
469 pop_session_status = pop_session_logged_in;
470 }
471 return status;
398 } 472 }
399 473
400 int 474 int
...@@ -505,9 +579,32 @@ echo_on (struct termios *stored_settings) ...@@ -505,9 +579,32 @@ echo_on (struct termios *stored_settings)
505 } 579 }
506 580
507 int 581 int
582 com_prompt (char *arg)
583 {
584 int quote;
585 size_t size;
586
587 if (!valid_argument ("prompt", arg))
588 return EINVAL;
589
590 free (prompt);
591 size = mu_argcv_quoted_length (arg, &quote);
592 prompt = malloc (size + 1);
593 if (!prompt)
594 {
595 mu_error ("Memory exhausted");
596 exit (1);
597 }
598 mu_argcv_unquote_copy (prompt, arg, size);
599 return 0;
600 }
601
602 int
508 com_pass (char *arg) 603 com_pass (char *arg)
509 { 604 {
605 int status;
510 char pass[256]; 606 char pass[256];
607
511 if (!arg || *arg == '\0') 608 if (!arg || *arg == '\0')
512 { 609 {
513 struct termios stored_settings; 610 struct termios stored_settings;
...@@ -522,7 +619,10 @@ com_pass (char *arg) ...@@ -522,7 +619,10 @@ com_pass (char *arg)
522 pass[strlen (pass) - 1] = '\0'; /* nuke the trailing line. */ 619 pass[strlen (pass) - 1] = '\0'; /* nuke the trailing line. */
523 arg = pass; 620 arg = pass;
524 } 621 }
525 return mu_pop3_pass (pop3, arg); 622 status = mu_pop3_pass (pop3, arg);
623 if (status == 0)
624 pop_session_status = pop_session_logged_in;
625 return status;
526 } 626 }
527 627
528 int 628 int
...@@ -533,7 +633,7 @@ com_stat (char *arg MU_ARG_UNUSED) ...@@ -533,7 +633,7 @@ com_stat (char *arg MU_ARG_UNUSED)
533 633
534 count = size = 0; 634 count = size = 0;
535 status = mu_pop3_stat (pop3, &count, &size); 635 status = mu_pop3_stat (pop3, &count, &size);
536 fprintf (stdout, "Mesgs: %d Size %d\n", count, size); 636 printf ("Mesgs: %d Size %d\n", count, size);
537 return status; 637 return status;
538 } 638 }
539 639
...@@ -653,19 +753,74 @@ com_retr (char *arg) ...@@ -653,19 +753,74 @@ com_retr (char *arg)
653 } 753 }
654 754
655 int 755 int
756 get_port (const char *port_str, int *pn)
757 {
758 short port_num;
759 long num;
760 char *p;
761
762 num = port_num = strtol (port_str, &p, 0);
763 if (*p == 0)
764 {
765 if (num != port_num)
766 {
767 mu_error ("bad port number: %s", port_str);
768 return 1;
769 }
770 }
771 else
772 {
773 struct servent *sp = getservbyname (port_str, "tcp");
774 if (!sp)
775 {
776 mu_error ("unknown port name");
777 return 1;
778 }
779 port_num = ntohs (sp->s_port);
780 }
781 *pn = port_num;
782 return 0;
783 }
784
785 int
656 com_connect (char *arg) 786 com_connect (char *arg)
657 { 787 {
658 char host[256];
659 int port = 110;
660 int status; 788 int status;
789 int n = 110;
790 int argc;
791 char **argv;
792
661 if (!valid_argument ("connect", arg)) 793 if (!valid_argument ("connect", arg))
662 return 1; 794 return 1;
663 *host = '\0'; 795
664 sscanf (arg, "%256s %d", host, &port); 796 if (mu_argcv_get (arg, NULL, NULL, &argc, &argv))
665 if (!valid_argument ("connect", host)) 797 {
798 mu_error ("Cannot parse arguments");
799 return 0;
800 }
801
802 if (!valid_argument ("connect", argv[0]))
803 {
804 mu_argcv_free (argc, argv);
666 return EINVAL; 805 return EINVAL;
667 if (pop3) 806 }
807
808 if (argc > 2)
809 {
810 mu_error ("Too many arguments");
811 mu_argcv_free (argc, argv);
812 return 0;
813 }
814
815 if (argc == 2 && get_port (argv[1], &n))
816 {
817 mu_argcv_free (argc, argv);
818 return 0;
819 }
820
821 if (pop_session_status != pop_session_disconnected)
668 com_disconnect (NULL); 822 com_disconnect (NULL);
823
669 status = mu_pop3_create (&pop3); 824 status = mu_pop3_create (&pop3);
670 if (status == 0) 825 if (status == 0)
671 { 826 {
...@@ -674,7 +829,7 @@ com_connect (char *arg) ...@@ -674,7 +829,7 @@ com_connect (char *arg)
674 if (verbose) 829 if (verbose)
675 com_verbose ("on"); 830 com_verbose ("on");
676 status = 831 status =
677 mu_tcp_stream_create (&tcp, host, port, 832 mu_tcp_stream_create (&tcp, argv[0], n,
678 MU_STREAM_READ | MU_STREAM_NO_CHECK); 833 MU_STREAM_READ | MU_STREAM_NO_CHECK);
679 if (status == 0) 834 if (status == 0)
680 { 835 {
...@@ -688,20 +843,35 @@ com_connect (char *arg) ...@@ -688,20 +843,35 @@ com_connect (char *arg)
688 } 843 }
689 } 844 }
690 845
691 if (status != 0) 846 if (status)
692 fprintf (stderr, "Failed to create pop3: %s\n", mu_strerror (status)); 847 {
848 mu_error ("Failed to create pop3: %s", mu_strerror (status));
849 mu_argcv_free (argc, argv);
850 }
851 else
852 {
853 connect_argc = argc;
854 connect_argv = argv;
855 port = n;
856 pop_session_status = pop_session_connected;
857 }
858
693 return status; 859 return status;
694 } 860 }
695 861
696 int 862 int
697 com_disconnect (char *arg MU_ARG_UNUSED) 863 com_disconnect (char *arg MU_ARG_UNUSED)
698 { 864 {
699 (void) arg;
700 if (pop3) 865 if (pop3)
701 { 866 {
702 mu_pop3_disconnect (pop3); 867 mu_pop3_disconnect (pop3);
703 mu_pop3_destroy (&pop3); 868 mu_pop3_destroy (&pop3);
704 pop3 = NULL; 869 pop3 = NULL;
870
871 mu_argcv_free (connect_argc, connect_argv);
872 connect_argc = 0;
873 connect_argv = NULL;
874 pop_session_status = pop_session_disconnected;
705 } 875 }
706 return 0; 876 return 0;
707 } 877 }
...@@ -718,11 +888,11 @@ com_quit (char *arg MU_ARG_UNUSED) ...@@ -718,11 +888,11 @@ com_quit (char *arg MU_ARG_UNUSED)
718 } 888 }
719 else 889 else
720 { 890 {
721 fprintf (stdout, "Try 'exit' to leave %s\n", progname); 891 printf ("Try 'exit' to leave %s\n", mu_program_name);
722 } 892 }
723 } 893 }
724 else 894 else
725 fprintf (stdout, "Try 'exit' to leave %s\n", progname); 895 printf ("Try 'exit' to leave %s\n", mu_program_name);
726 return status; 896 return status;
727 } 897 }
728 898
...@@ -745,7 +915,7 @@ valid_argument (const char *caller, char *arg) ...@@ -745,7 +915,7 @@ valid_argument (const char *caller, char *arg)
745 { 915 {
746 if (!arg || !*arg) 916 if (!arg || !*arg)
747 { 917 {
748 fprintf (stderr, "%s: Argument required.\n", caller); 918 mu_error ("%s: Argument required", caller);
749 return 0; 919 return 0;
750 } 920 }
751 921
......