1 /* pk-cmd-misc.c - Miscellaneous commands.  */
2 
3 /* Copyright (C) 2019, 2020, 2021 Jose E. Marchesi */
4 
5 /* This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <config.h>
20 #include <assert.h>
21 #include <time.h>
22 #include <stdlib.h> /* For system.  */
23 #include "xalloc.h"
24 
25 #include "findprog.h"
26 #include "readline.h"
27 
28 #include "poke.h"
29 #include "pk-cmd.h"
30 #include "pk-utils.h"
31 
32 static int
pk_cmd_exit(int argc,struct pk_cmd_arg argv[],uint64_t uflags)33 pk_cmd_exit (int argc, struct pk_cmd_arg argv[], uint64_t uflags)
34 {
35   /* exit CODE */
36 
37   int code;
38   assert (argc == 1);
39   if (PK_CMD_ARG_TYPE (argv[0]) == PK_CMD_ARG_NULL)
40     code = 0;
41   else
42     code = (int) PK_CMD_ARG_INT (argv[0]);
43 
44   poke_exit_p = 1;
45   poke_exit_code = code;
46   return 1;
47 }
48 
49 static int
pk_cmd_version(int argc,struct pk_cmd_arg argv[],uint64_t uflags)50 pk_cmd_version (int argc, struct pk_cmd_arg argv[], uint64_t uflags)
51 {
52   /* version */
53   pk_print_version (1 /* hand_p */);
54   return 1;
55 }
56 
57 /* Call the info command for the poke documentation, using
58    the requested node.  */
59 static int
pk_cmd_doc(int argc,struct pk_cmd_arg argv[],uint64_t uflags)60 pk_cmd_doc (int argc, struct pk_cmd_arg argv[], uint64_t uflags)
61 {
62   int ret = 1;
63 
64   /* This command is inherently interactive.  So if we're not
65      supposed to be in interactive mode, then do nothing.  */
66   if (poke_interactive_p)
67   {
68     char *cmd = NULL;
69 
70     /* Unless the doc viewer is set to `less', try first to use
71        `info'.  */
72 
73     if (STRNEQ (poke_doc_viewer, "less"))
74       {
75         const char info_prog_name[] = "info";
76         const char *ip = find_in_path (info_prog_name);
77         if (STRNEQ (ip, info_prog_name))
78           {
79             int size = 0;
80             int bytes = 64;
81             do
82               {
83                 size = bytes + 1;
84                 cmd = xrealloc (cmd, size);
85                 bytes = snprintf (cmd, size, "info -f \"%s/poke.info\"",
86                                   poke_infodir);
87               }
88             while (bytes >= size);
89 
90             if (argv[0].type == PK_CMD_ARG_STR)
91               {
92                 const char *node = argv[0].val.str;
93                 cmd = xrealloc (cmd, bytes + 7 + strlen (node));
94                 strcat (cmd, " -n \"");
95                 strcat (cmd, node);
96                 strcat (cmd, "\"");
97               }
98           }
99       }
100 
101     /* Fallback to `less' if no `info' was found.  */
102     if (cmd == NULL)
103       {
104         const char info_prog_name[] = "less";
105         const char *ip = find_in_path (info_prog_name);
106         if (STREQ (ip, info_prog_name))
107           {
108             pk_term_class ("error");
109             pk_puts ("error: ");
110             pk_term_end_class ("error");
111             pk_puts ("a suitable documentation viewer is not installed.\n");
112             return 0;
113           }
114 
115         asprintf (&cmd, "less -p '%s' %s/poke.text",
116                   argv[0].val.str, poke_docdir);
117         assert (cmd != NULL);
118       }
119 
120     /* Open the documentation at the requested page.  */
121     ret = (0 == system (cmd));
122     free (cmd);
123   }
124 
125   return ret;
126 }
127 
128 static int
pk_cmd_jmd(int argc,struct pk_cmd_arg argv[],uint64_t uflags)129 pk_cmd_jmd (int argc, struct pk_cmd_arg argv[], uint64_t uflags)
130 {
131   assert (argc == 0);
132 
133   static const char *strings[] =
134     {
135      "<jmd> I never win on the pokies.",
136      "<jmd> \"poke\" is an anagram of \"peok\" which is the "
137      "Indonesian word for \"dent\".",
138      "<jmd> Good morning poke(wo)men!",
139      "<jmd> jemarch: I though it was a dismissal for a golden duck.",
140      "<jmd> Just have a .do-what-i-want command and be done "
141      "with it.",
142      "<jmd> It looks as if Jose was poking late into the night!",
143      "<jmd> I inadvertently pushed some experimental crap.",
144      "<jmd> Whey are they called \"pickles\"?  They ought to be "
145      "called \"pokles\".",
146      "<jmd> I thought I'd just poke my nose in here and see what's "
147      "going on.",
148      "[jmd wonders if jemarch has \"export EDITOR=poke\" in his .bashrc]",
149      "<jmd> everytime I type \"killall -11 poke\", poke segfaults.",
150      "<jemarch> a bugfix a day keeps jmd away",
151      "<jmd> Did you know that \"Poke\" is a Hawaiian salad?",
152      "<jmd> I never place periods after my strncpy.",
153      "<jmd> pokie pokie!",
154      "<jmd> Hokus Pokus",
155      "<mnabipoor> I wanted making a zero-length array for a long (!) time",
156      NULL
157     };
158   static int num_strings = 0;
159 
160   if (num_strings == 0)
161     {
162       srand (time (NULL));
163       const char **p = strings;
164       while (*p++ != NULL)
165         num_strings++;
166     }
167 
168   pk_printf ("%s\n", strings[rand () % num_strings]);
169   return 1;
170 }
171 
172 /* A completer to provide the node names of the info
173    documentation.  */
174 char *
doc_completion_function(const char * x,int state)175 doc_completion_function (const char *x, int state)
176 {
177   static char **nodelist = NULL;
178   if (nodelist == NULL)
179     {
180       int n_nodes = 0;
181       char nlfile[256];
182       snprintf (nlfile, 256, "%s/nodelist", poke_docdir);
183       FILE *fp = fopen (nlfile, "r");
184       if (fp == NULL)
185         return NULL;
186       char *lineptr = NULL;
187       size_t size = 0;
188       while (!feof (fp))
189         {
190           int x = getline (&lineptr, &size, fp);
191           if (x != -1)
192             {
193               nodelist = xrealloc (nodelist, ++n_nodes * sizeof (*nodelist));
194               lineptr [strlen (lineptr) - 1] = '\0';
195               nodelist[n_nodes - 1] = xstrdup (lineptr);
196             }
197         }
198       fclose (fp);
199       free (lineptr);
200       nodelist = xrealloc (nodelist, ++n_nodes * sizeof (*nodelist));
201       nodelist[n_nodes - 1] = NULL;
202     }
203 
204   static int idx = 0;
205   if (state == 0)
206     idx = 0;
207   else
208     ++idx;
209 
210   int len = strlen (x);
211   while (1)
212     {
213       const char *name = nodelist[idx];
214       if (name == NULL)
215         break;
216 
217       if (strncmp (name, x, len) == 0)
218         return xstrdup (name);
219 
220       idx++;
221     }
222 
223   return NULL;
224 }
225 
226 
227 const struct pk_cmd quit_cmd =
228   {"quit", "?i", "", 0, NULL, pk_cmd_exit, "quit [CODE]", NULL};
229 
230 const struct pk_cmd exit_cmd =
231   {"exit", "?i", "", 0, NULL, pk_cmd_exit, "exit [CODE]", NULL};
232 
233 const struct pk_cmd version_cmd =
234   {"version", "", "", 0, NULL, pk_cmd_version, "version", NULL};
235 
236 const struct pk_cmd jmd_cmd =
237   {"jmd", "", "", 0, NULL, pk_cmd_jmd, "jmd", NULL};
238 
239 const struct pk_cmd doc_cmd =
240   {"doc", "?s", "", 0, NULL, pk_cmd_doc, "doc [section]", doc_completion_function};
241