1 /*************************************************************************
2 * TinyFugue - programmable mud client
3 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2002, 2003, 2004, 2005, 2006-2007 Ken Keys
4 *
5 * TinyFugue (aka "tf") is protected under the terms of the GNU
6 * General Public License. See the file "COPYING" for details.
7 ************************************************************************/
8 static const char RCSid[] = "$Id: world.c,v 35004.77 2007/01/13 23:12:39 kkeys Exp $";
9
10
11 /********************************************************
12 * Fugue world routines. *
13 ********************************************************/
14
15 #include "tfconfig.h"
16 #include "port.h"
17 #include "tf.h"
18 #include "util.h"
19 #include "pattern.h"
20 #include "search.h" /* for tfio.h */
21 #include "tfio.h"
22 #include "history.h"
23 #include "world.h"
24 #include "process.h"
25 #include "macro.h" /* remove_world_macros() */
26 #include "cmdlist.h"
27 #include "socket.h"
28 #include "output.h" /* columns */
29
30 #define LW_TABLE 001
31 #define LW_UNNAMED 002
32 #define LW_SHORT 004
33
34 static int list_worlds(const Pattern *name, const Pattern *type,
35 struct TFILE *file, int flags, int (*cmp)(const void *, const void *));
36 static void free_world(World *w);
37 static World *alloc_world(void);
38
39 static World *hworld = NULL;
40
41 World *defaultworld = NULL;
42
43
free_world(World * w)44 static void free_world(World *w)
45 {
46 if (w->name) FREE(w->name);
47 if (w->character) FREE(w->character);
48 if (w->pass) FREE(w->pass);
49 if (w->host) FREE(w->host);
50 if (w->port) FREE(w->port);
51 if (w->mfile) FREE(w->mfile);
52 if (w->type) FREE(w->type);
53 if (w->myhost) FREE(w->myhost);
54 if (w->screen) free_screen(w->screen);
55 #if !NO_HISTORY
56 if (w->history) {
57 free_history(w->history);
58 FREE(w->history);
59 }
60 #endif
61 FREE(w);
62 }
63
64 #if USE_DMALLOC
free_worlds(void)65 void free_worlds(void)
66 {
67 World *next;
68
69 if (defaultworld)
70 free_world(defaultworld);
71 for ( ; hworld; hworld = next) {
72 next = hworld->next;
73 free_world(hworld);
74 }
75 }
76 #endif
77
alloc_world(void)78 static World *alloc_world(void)
79 {
80 World *result;
81 result = (World *) XMALLOC(sizeof(World));
82 memset(result, 0, sizeof(World));
83 return result;
84 }
85
86 /* A NULL name means unnamed; world will be given a temp name. */
new_world(const char * name,const char * type,const char * host,const char * port,const char * character,const char * pass,const char * mfile,int flags,const char * myhost)87 World *new_world(const char *name, const char *type,
88 const char *host, const char *port,
89 const char *character, const char *pass,
90 const char *mfile, int flags,
91 const char *myhost)
92 {
93 World *result;
94 static int unnamed = 1;
95 smallstr buffer;
96 int is_redef = FALSE;
97
98 /* unnamed worlds can't be defined but can have other fields changed. */
99 if (name && (!*name || strchr(name, ' ') ||
100 (*name == '(' && (*host || *port))))
101 {
102 eprintf("illegal world name: %s", name);
103 return NULL;
104 }
105
106 if (name && cstrcmp(name, "default") == 0) {
107 if (defaultworld) {
108 /* redefine existing default world */
109 result = defaultworld;
110 FREE(defaultworld->name);
111 is_redef = TRUE;
112 } else {
113 /* define default world */
114 result = defaultworld = alloc_world();
115 }
116
117 } else if (name && (result = find_world(name))) {
118 /* redefine existing world */
119 FREE(result->name);
120 is_redef = TRUE;
121
122 } else {
123 /* define new world */
124 World **pp;
125 if ((!*host && *port) || (*host && !*port)) {
126 eprintf("world %s: host and port must be both blank or both "
127 "non-blank.", name);
128 return NULL;
129 }
130 for (pp = &hworld; *pp; pp = &(*pp)->next);
131 *pp = result = alloc_world();
132 init_list(result->triglist);
133 init_list(result->hooklist);
134 #if !NO_HISTORY
135 /* Don't allocate the history's queue until we actually need it. */
136 result->history = init_history(NULL, 0);
137 #endif
138 }
139
140 if (name) {
141 result->name = STRDUP(name);
142 } else {
143 sprintf(buffer, "(unnamed%d)", unnamed++);
144 result->name = STRDUP(buffer);
145 }
146
147 #define setfield(field) \
148 do { \
149 if (field && *field) { \
150 if (result->field) FREE(result->field); \
151 result->field = STRDUP(field); \
152 } \
153 } while (0);
154
155 setfield(character);
156 setfield(pass);
157 setfield(host);
158 setfield(port);
159 setfield(mfile);
160 setfield(type);
161 setfield(myhost);
162 result->flags |= flags;
163
164 #ifdef PLATFORM_UNIX
165 # ifndef __CYGWIN32__
166 if (pass && *pass && loadfile && (loadfile->mode & (S_IROTH | S_IRGRP)) &&
167 !loadfile->warned)
168 {
169 wprintf("file contains passwords and is readable by others.");
170 loadfile->warned++;
171 }
172 # endif /* __CYGWIN32__ */
173 #endif /* PLATFORM_UNIX */
174
175 if (is_redef)
176 do_hook(H_REDEF, "!Redefined %s %s", "%s %s", "world", result->name);
177 return result;
178 }
179
180 /* should not be called for defaultworld */
nuke_world(World * w)181 int nuke_world(World *w)
182 {
183 World **pp;
184
185 if (w->sock) {
186 eprintf("%s is in use.", w->name);
187 return 0;
188 }
189
190 for (pp = &hworld; *pp != w; pp = &(*pp)->next);
191 *pp = w->next;
192 remove_world_macros(w);
193 kill_procs_by_world(w);
194 free_world(w);
195 return 1;
196 }
197
handle_unworld_command(String * args,int offset)198 struct Value *handle_unworld_command(String *args, int offset)
199 {
200 World *w;
201 int result = 0;
202 const char *name;
203 char *ptr = args->data + offset;
204
205 while (*(name = stringarg(&ptr, NULL))) {
206 if (defaultworld && cstrcmp(name, "default") == 0) {
207 free_world(defaultworld);
208 defaultworld = NULL;
209 } else if ((w = find_world(name))) {
210 result += nuke_world(w);
211 } else {
212 eprintf("No world %s", name);
213 }
214 }
215 return newint(result);
216 }
217
worldtypecmp(const void * a,const void * b)218 static int worldtypecmp(const void *a, const void *b) {
219 return nullcstrcmp((*(World**)a)->type, (*(World**)b)->type);
220 }
221
worldhostcmp(const void * a,const void * b)222 static int worldhostcmp(const void *a, const void *b) {
223 return nullcstrcmp((*(World**)a)->host, (*(World**)b)->host);
224 }
225
worldportcmp(const void * a,const void * b)226 static int worldportcmp(const void *a, const void *b) {
227 return nullcstrcmp((*(World**)a)->port, (*(World**)b)->port);
228 }
229
worldcharcmp(const void * a,const void * b)230 static int worldcharcmp(const void *a, const void *b) {
231 return nullcstrcmp((*(World**)a)->character, (*(World**)b)->character);
232 }
233
handle_listworlds_command(String * args,int offset)234 struct Value *handle_listworlds_command(String *args, int offset)
235 {
236 int flags = LW_TABLE, mflag = matching, error = 0, result, n;
237 char c;
238 const char *ptr;
239 int (*cmp)(const void *, const void *) = cstrpppcmp;
240 Pattern type, name, *namep = NULL;
241
242 init_pattern_str(&type, NULL);
243 init_pattern_str(&name, NULL);
244 startopt(CS(args), "T:uscm:S:");
245 while ((c = nextopt(&ptr, NULL, NULL, &offset))) {
246 switch (c) {
247 case 'T': free_pattern(&type);
248 error += !init_pattern_str(&type, ptr);
249 break;
250 case 'u': flags |= LW_UNNAMED; break;
251 case 's': flags |= LW_SHORT; /* fall through */
252 case 'c': flags &= ~LW_TABLE; break;
253 case 'm': error = ((mflag = enum2int(ptr,0,enum_match,"-m")) < 0);
254 break;
255 case 'S': n = strlen(ptr);
256 if (n == 0 || cstrncmp(ptr, "name", n) == 0)
257 cmp = cstrpppcmp;
258 else if (cstrncmp(ptr, "type", n) == 0)
259 cmp = worldtypecmp;
260 else if (cstrncmp(ptr, "host", n) == 0)
261 cmp = worldhostcmp;
262 else if (cstrncmp(ptr, "port", n) == 0)
263 cmp = worldportcmp;
264 else if (cstrncmp(ptr, "character", n) == 0)
265 cmp = worldcharcmp;
266 else if (cstrncmp(ptr, "-", n) == 0)
267 cmp = NULL;
268 else { eprintf("invalid sort criterion"); error++; }
269 break;
270 default: return shareval(val_zero);
271 }
272 }
273 if (error) return shareval(val_zero);
274 init_pattern_mflag(&type, mflag, 'T');
275 Stringstriptrail(args);
276 if (args->len > offset) {
277 namep = &name;
278 error += !init_pattern(namep, args->data + offset, mflag);
279 }
280 if (error) return shareval(val_zero);
281 result = list_worlds(namep, type.str?&type:NULL, tfout, flags, cmp);
282 free_pattern(&name);
283 free_pattern(&type);
284 return newint(result);
285 }
286
list_worlds(const Pattern * name,const Pattern * type,TFILE * file,int flags,int (* cmp)(const void *,const void *))287 static int list_worlds(const Pattern *name, const Pattern *type, TFILE *file,
288 int flags, int (*cmp)(const void *, const void *))
289 {
290 World *p;
291 Vector worlds = vector_init(128);
292 int first = 1, i;
293 int need, width, width_name, width_type, width_host, width_port;
294 STATIC_BUFFER(buf);
295
296 if (flags & LW_TABLE) {
297 width = (columns < 80 ? 80 : columns) - 1;
298 width_name = width / 5;
299 width_type = width / 7;
300 width_host = width / 3;
301 width_port = 6;
302 tfprintf(file, "%-*s %-*s %*s %-*s %s",
303 width_name, "NAME", width_type, "TYPE", width_host, "HOST",
304 width_port, "PORT", "CHARACTER");
305 }
306
307 /* collect matching worlds */
308 for (p = defaultworld; p || first; p = first ? hworld : p->next, first=0)
309 {
310 if (!p || (!(flags & LW_UNNAMED) && p->flags & WORLD_TEMP)) continue;
311 if (name && !patmatch(name, NULL, p->name)) continue;
312 if (type && !patmatch(type, NULL, p->type ? p->type : "")) continue;
313 vector_add(&worlds, p);
314 }
315
316 if (cmp)
317 vector_sort(&worlds, cmp);
318
319 Stringtrunc(buf, 0);
320 for (i = 0; i < worlds.size; i++) {
321 p = worlds.ptrs[i];
322 if (flags & LW_SHORT) {
323 tfputs(p->name, file);
324 } else if (flags & LW_TABLE) {
325 tfprintf(file, "%-*.*s %-*.*s %*.*s %-*.*s %s",
326 width_name, width_name, p->name,
327 width_type, width_type, p->type,
328 width_host, width_host, p->host,
329 width_port, width_port, p->port,
330 p->character);
331 } else {
332 if (p->myhost) need = 9;
333 else if (p->flags & ~WORLD_TEMP) need = 8;
334 else if (p->mfile) need = 7;
335 else if (p->character || p->pass) need = 6;
336 else if (p->host || p->port) need = 4;
337 else need = 2;
338
339 Stringcpy(buf, "/test addworld(");
340 Sappendf(buf, "\"%q\"", '"', p->name);
341 Sappendf(buf, ", \"%q\"", '"', p->type);
342
343 if (need < 3) goto listworld_tail;
344 Sappendf(buf, ", \"%q\"", '"', p->host);
345
346 if (need < 4) goto listworld_tail;
347 Sappendf(buf, ", \"%q\"", '"', p->port);
348
349 if (need < 5) goto listworld_tail;
350 Sappendf(buf, ", \"%q\"", '"', p->character);
351
352 if (need < 6) goto listworld_tail;
353 Sappendf(buf, ", \"%q\"", '"', p->pass);
354
355 if (need < 7) goto listworld_tail;
356 Sappendf(buf, ", \"%q\"", '"', p->mfile);
357
358 if (need < 8) goto listworld_tail;
359 Sappendf(buf, ", \"%s%s%s\"",
360 (p->flags & WORLD_NOPROXY) ? "p" : "",
361 (p->flags & WORLD_SSL) ? "x" : "",
362 (p->flags & WORLD_ECHO) ? "e" : "");
363
364 if (need < 9) goto listworld_tail;
365 Sappendf(buf, ", \"%q\"", '"', p->myhost);
366
367 listworld_tail:
368 Stringadd(buf, ')');
369 tfputs(buf->data, file);
370 }
371 }
372 vector_free(&worlds);
373 return worlds.size;
374 }
375
handle_saveworld_command(String * args,int offset)376 struct Value *handle_saveworld_command(String *args, int offset)
377 {
378 TFILE *file;
379 char opt;
380 const char *mode = "w";
381 char *name;
382 int result;
383
384 if (restriction >= RESTRICT_FILE) {
385 eprintf("restricted");
386 return shareval(val_zero);
387 }
388
389 startopt(CS(args), "a");
390 while ((opt = nextopt(NULL, NULL, NULL, &offset))) {
391 if (opt != 'a') return shareval(val_zero);
392 mode = "a";
393 }
394 if ((name = tfname(args->data + offset, "WORLDFILE")) == NULL)
395 return shareval(val_zero);
396 if ((file = tfopen(name, mode)) == NULL) {
397 operror(args->data + offset);
398 return shareval(val_zero);
399 }
400 oprintf("%% %sing world definitions to %s", *mode == 'a' ? "Append" :
401 "Writ", file->name);
402 result = list_worlds(NULL, NULL, file, 0, NULL);
403 tfclose(file);
404 return newint(result);
405 }
406
find_world(const char * name)407 World *find_world(const char *name)
408 {
409 World *p;
410
411 if (!name || !*name) return hworld;
412 for (p=hworld; p && (!p->name || cstrcmp(name, p->name) != 0); p = p->next);
413 return p;
414 }
415
416 /* Perform (*func)(world) on every world */
mapworld(void (* func)(World * world))417 void mapworld(void (*func)(World *world))
418 {
419 World *w;
420
421 for (w = hworld; w; w = w->next)
422 (*func)(w);
423 }
424
425