1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2010-2021 Free Software Foundation, Inc.
3 
4    GNU Mailutils is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GNU Mailutils is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #if defined(HAVE_CONFIG_H)
18 # include <config.h>
19 #endif
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/stat.h>
23 #include <mailutils/mailutils.h>
24 #include "mailutils/cli.h"
25 #include <assert.h>
26 #include <glob.h>
27 
28 char *mailutilsdir;
29 
30 struct mu_tool
31 {
32   char *name;
33   char cmd[1];
34 };
35 
36 #define MUTOOL_PREFIX "mailutils-"
37 
38 mu_list_t
find_tools(char * pat)39 find_tools (char *pat)
40 {
41   mu_list_t tool_list;
42   char *fpat, *pattern;
43   glob_t gbuf;
44 
45   mu_list_create (&tool_list);
46   mu_list_set_destroy_item (tool_list, mu_list_free_item);
47 
48   fpat = mu_alloc (sizeof MUTOOL_PREFIX + strlen (pat));
49   strcat (strcpy (fpat, MUTOOL_PREFIX), pat);
50   pattern = mu_make_file_name (mailutilsdir, fpat);
51   free (fpat);
52 
53   if (glob (pattern, 0, NULL, &gbuf) == 0)
54     {
55       int i;
56       for (i = 0; i < gbuf.gl_pathc; i++)
57 	{
58 	  char *p;
59 	  struct mu_tool *tp = mu_alloc (sizeof (*tp) + strlen (gbuf.gl_pathv[i]));
60 	  strcpy (tp->cmd, gbuf.gl_pathv[i]);
61 	  p = strrchr (tp->cmd, '/');
62 	  assert (p != NULL);
63 	  tp->name = p + sizeof MUTOOL_PREFIX;
64 	  mu_list_push (tool_list, tp);
65 	}
66       globfree (&gbuf);
67     }
68 
69   return tool_list;
70 }
71 
72 static int
mutool_comp(const void * a,const void * b)73 mutool_comp (const void *a, const void *b)
74 {
75   struct mu_tool const *pa = a;
76   struct mu_tool const *pb = b;
77   return strcmp (pa->name, pb->name);
78 }
79 
80 static int
show_help(void * item,void * data)81 show_help (void *item, void *data)
82 {
83   struct mu_tool *t = item;
84   mu_stream_t ostr = data;
85   mu_stream_t istr;
86   char *argv[3];
87   int rc;
88 
89   argv[0] = t->cmd;
90   argv[1] = "--describe";
91   argv[2] = NULL;
92   rc = mu_prog_stream_create (&istr, t->cmd,
93 			      2, argv,
94 			      0, NULL, MU_STREAM_READ);
95   if (rc == 0)
96     {
97       unsigned margin;
98 
99       margin = 2;
100       mu_stream_ioctl (ostr, MU_IOCTL_WORDWRAPSTREAM,
101 		       MU_IOCTL_WORDWRAP_SET_MARGIN,
102 		       &margin);
103       mu_stream_printf (ostr, "%s %s",  mu_program_name, t->name);
104 
105       margin = 29;
106       mu_stream_ioctl (ostr, MU_IOCTL_WORDWRAPSTREAM,
107 		       MU_IOCTL_WORDWRAP_SET_MARGIN,
108 		       &margin);
109       rc = mu_stream_copy (ostr, istr, 0, NULL);
110       if (rc)
111 	mu_diag_funcall (MU_DIAG_ERR, "mu_stream_copy", t->cmd, rc);
112 
113       mu_stream_destroy (&istr);
114     }
115   return 0;
116 }
117 
118 void
subcommand_help(mu_stream_t str)119 subcommand_help (mu_stream_t str)
120 {
121   mu_list_t tool_list = find_tools ("*");
122   if (mu_list_is_empty (tool_list))
123     {
124       mu_stream_printf (str, _("No commands found.\n"));
125     }
126   else
127     {
128       mu_list_sort (tool_list, mutool_comp);
129       mu_list_foreach (tool_list, show_help, str);
130       mu_list_destroy (&tool_list);
131     }
132 }
133 
134 struct mu_cli_setup cli = {
135   .prog_doc = N_("GNU Mailutils multi-purpose tool."),
136   .prog_args = N_("COMMAND [CMDOPTS]"),
137   .inorder = 1,
138   .prog_doc_hook = subcommand_help
139 };
140 
141 struct mu_parseopt pohint = {
142   .po_flags = MU_PARSEOPT_PACKAGE_NAME
143             | MU_PARSEOPT_PACKAGE_URL
144             | MU_PARSEOPT_BUG_ADDRESS
145             | MU_PARSEOPT_EXTRA_INFO
146             | MU_PARSEOPT_VERSION_HOOK,
147   .po_package_name = PACKAGE_NAME,
148   .po_package_url = PACKAGE_URL,
149   .po_bug_address = PACKAGE_BUGREPORT,
150   .po_extra_info = mu_general_help_text,
151   .po_version_hook = mu_version_hook,
152 };
153 struct mu_cfg_parse_hints cfhint = { .flags = 0 };
154 
155 int
main(int argc,char ** argv)156 main (int argc, char **argv)
157 {
158   size_t len;
159   mu_list_t tool_list;
160   size_t cnt;
161   struct mu_tool *tp;
162   int rc;
163   char *str;
164 
165 #define DEVSFX "/.libs/lt-mailutils"
166 #define DEVSFX_LEN (sizeof (DEVSFX) - 1)
167 #define SUBDIR "libexec"
168 #define SUBDIR_LEN (sizeof (SUBDIR) - 1)
169   len = strlen (argv[0]);
170   if (len > DEVSFX_LEN
171       && strcmp (argv[0] + len - DEVSFX_LEN, DEVSFX) == 0)
172     {
173       len -= DEVSFX_LEN;
174       mailutilsdir = mu_alloc (len + 1 + SUBDIR_LEN + 1);
175       memcpy (mailutilsdir, argv[0], len);
176       mailutilsdir[len++] = '/';
177       strcpy (mailutilsdir + len, SUBDIR);
178     }
179   else
180     mailutilsdir = MAILUTILSDIR;
181 
182   /* Native Language Support */
183   MU_APP_INIT_NLS ();
184 
185   mu_cli_ext (argc, argv, &cli, &pohint, &cfhint, NULL, NULL, &argc, &argv);
186 
187   if (argc < 1)
188     {
189       mu_error (_("what do you want me to do?"));
190       exit (1);
191     }
192 
193   if (strcmp (argv[0], "help") == 0)
194     {
195       cli.prog_doc_hook = NULL;
196       cli.prog_doc = N_("display help on mailutils subcommands");
197       cli.prog_args = N_("[COMMAND]");
198       pohint.po_flags |= MU_PARSEOPT_PROG_NAME;
199       mu_asprintf (&str, "%s %s", mu_program_name, argv[0]);
200       pohint.po_prog_name = str;
201 
202       mu_cli_ext (argc, argv, &cli, &pohint, &cfhint,
203 		  NULL, NULL, &argc, &argv);
204 
205       if (argc == 0)
206 	{
207 	  mu_stream_t str;
208 	  unsigned margin;
209 
210 	  if (mu_parseopt_help_stream_create (&str, &pohint, mu_strout))
211 	    abort ();
212 	  subcommand_help (str);
213 
214 	  margin = 0;
215 	  mu_stream_ioctl (str, MU_IOCTL_WORDWRAPSTREAM,
216 			   MU_IOCTL_WORDWRAP_SET_MARGIN,
217 			   &margin);
218 
219 	  mu_stream_printf (str, "\n");
220 	  mu_stream_printf (str,
221 			    _("Run `%s help COMMAND' to get help on a particular mailutils command."),
222 			    mu_program_name);
223 	  mu_stream_destroy (&str);
224 	  exit (0);
225 	}
226       else if (argc == 1)
227 	{
228 	  argv--;
229 	  argv[0] = argv[1];
230 	  argv[1] = "--help";
231 	  argv[2] = NULL;
232 	}
233       else
234 	{
235 	  mu_error (_("too many arguments"));
236 	  exit (1);
237 	}
238     }
239 
240   tool_list = find_tools (argv[0]);
241   mu_list_count (tool_list, &cnt);
242   if (cnt != 1)
243     {
244       mu_error (_("don't know what %s is"), argv[0]);
245       exit (1);
246     }
247   rc = mu_list_head (tool_list, (void**) &tp);
248   if (rc)
249     {
250       mu_diag_funcall (MU_DIAG_CRIT, "mu_list_head", NULL, rc);
251       exit (2);
252     }
253 
254   mu_asprintf (&str, "%s %s", mu_program_name, tp->name);
255   setenv ("MAILUTILS_PROGNAME", str, 1);
256   execv (tp->cmd, argv);
257   mu_diag_funcall (MU_DIAG_CRIT, "execv", tp->cmd, errno);
258   return 2;
259 }
260