1 /*
2  * NAME:	valid()
3  * DESCRIPTION:	check if a path is valid
4  */
valid(string file,int type)5 private string valid(string file, int type)
6 {
7     object player;
8 
9     if (object_name(this_object()) == MASTER) {
10 	return file;
11     }
12 
13     player = this_player();
14     if (player == 0 || interactive(player) == 0) {
15 	return 0;
16     }
17     file = (type == 0) ? player->valid_read(file) : player->valid_write(file);
18     if (player == 0) {
19 	return 0;
20     }
21     if (!stringp(file)) {
22 	write("Bad file name.\n");
23 	return 0;
24     } else if (file != "" &&
25 	       (file[0] == '/' || sscanf(file, "%*s ") != 0 ||
26 	        sscanf(file, "%*s..") != 0)) {
27 	error("Illegal path: " + file);
28     }
29     return file;
30 }
31 
32 /*
33  * NAME:	file_size()
34  * DESCRIPTION:	get the size of a file
35  */
file_size(string file)36 static int file_size(string file)
37 {
38     int *sizes;
39 
40     ARGCHECK(file, file_size, 1);
41     file = valid(file, 0);
42     if (file == 0) {
43 	return -1;
44     }
45 
46     sizes = ::get_dir(file)[1];
47     if (sizeof(sizes) != 1) {
48 	return -1;
49     }
50 
51     return sizes[0];
52 }
53 
54 /*
55  * NAME:	log_file()
56  * DESCRIPTION:	log something
57  */
log_file(string file,string str)58 static void log_file(string file, string str)
59 {
60     int *sizes;
61 
62     ARGCHECK(file, log_file, 1);
63     ARGCHECK(str, log_file, 2);
64 
65     if (sscanf(file, "%*s/") != 0 || strlen(file) > 30) {
66 	error("Illegal file name to log_file(" + file + ")");
67     }
68     file = "/log/" + file;
69     sizes = ::get_dir(file)[1];
70     if (sizeof(sizes) == 1 && sizes[0] >= LOG_FILE_SIZE) {
71 	::remove_file(file + ".old");
72 	::rename_file(file, file + ".old");
73     }
74     ::write_file(file, str);
75 }
76 
77 /*
78  * NAME:	get_dir()
79  * DESCRIPTION:	get directory info
80  */
get_dir(string file)81 static string *get_dir(string file)
82 {
83     int len;
84 
85     ARGCHECK(file, get_dir, 1);
86     file = valid(file, 0);
87     if (file == 0) {
88 	return ({ });
89     }
90 
91     len = strlen(file);
92     if (file == "" || file[len - 1] == '/') {
93 	file += "*";
94     } else if (len > 1 && file[len - 2 ..] == "/.") {
95 	file[len - 1] = '*';
96     }
97     return ::get_dir(file)[0];
98 }
99 
100 /*
101  * NAME:	mkdir()
102  * DESCRIPTION:	make a directory
103  */
mkdir(string file)104 static int mkdir(string file)
105 {
106     ARGCHECK(file, mkdir, 1);
107     file = valid(file, 0);
108     return (file != 0 && ::make_dir(file) != 0);
109 }
110 
111 /*
112  * NAME:	make_dir()
113  * DESCRIPTION:	make a directory
114  */
make_dir(string file)115 static int make_dir(string file)
116 {
117     return mkdir(file);
118 }
119 
120 /*
121  * NAME:	read_bytes()
122  * DESCRIPTION:	read bytes from a file
123  */
read_bytes(string file,int start,int size)124 static varargs string read_bytes(string file, int start, int size)
125 {
126     ARGCHECK(file, read_bytes, 1);
127     file = valid(file, 0);
128     if (file == 0) {
129 	return 0;
130     }
131 
132     return ::read_file(file, start, size);
133 }
134 
135 /*
136  * NAME:	read_lines()
137  * DESCRIPTION:	return a line range of a file
138  */
read_lines(string file,int first,int num)139 private string *read_lines(string file, int first, int num)
140 {
141     int line, offset, size, *saved;
142     string str, *lines;
143 
144     if (first < 0 || num < 0) {
145 	return 0;
146     }
147 
148     if (first == 0) {
149 	first = 1;
150     }
151     line = 1;
152     if (this_user()) {
153 	saved = this_user()->query_file_offset(file);
154 	if (saved != 0) {
155 	    line = saved[0];
156 	    if (line > 2 * first) {
157 		line = 1;
158 	    } else {
159 		offset = saved[1];
160 	    }
161 	}
162     }
163 
164     for (;;) {
165 	if (line <= first) {
166 	    str = ::read_file(file, offset, FILE_CHUNK);
167 	    if (str == 0) {
168 		return 0;
169 	    }
170 	    lines = explode("\n" + str + "\n", "\n");
171 	    size = sizeof(lines) - 1;
172 	    if (line == first) {
173 		if (num == 0 || size < num) {
174 		    if (strlen(str) < FILE_CHUNK) {
175 			return lines;
176 		    }
177 		    error("Line range too large");
178 		}
179 	    }
180 	    if (size == 0) {
181 		if (strlen(str) < FILE_CHUNK) {
182 		    return 0;
183 		}
184 		error("Line too long");
185 	    }
186 	} else {
187 	    offset -= FILE_CHUNK;
188 	    if (offset < 0) {
189 		offset = 0;
190 	    }
191 	    str = ::read_file(file, offset, FILE_CHUNK);
192 	    if (str == 0) {
193 		return 0;
194 	    }
195 	    lines = explode("\n" + str + "\n", "\n");
196 	    size = sizeof(lines) - 1;
197 	    if (offset == 0) {
198 		line = 1;
199 	    } else {
200 		if (size == 0) {
201 		    error("Line too long");
202 		} else {
203 		    --size;
204 		    line -= size;
205 		    if (line <= 0) {
206 			/* ??? */
207 			line = 1;
208 			offset = 0;
209 			continue;
210 		    }
211 		    offset += strlen(lines[0]) + 1;
212 		    lines = lines[1 ..];
213 		}
214 	    }
215 	}
216 
217 	if (line <= first && line + size > first) {
218 	    if (num != 0 && line + size >= first + num) {
219 		first -= line;
220 		if (this_user() != 0) {
221 		    line += first + num;
222 		    offset += strlen(implode(lines[0 .. first + num - 1],
223 				     "\n")) + 1;
224 		    this_user()->set_file_offset(file, line, offset);
225 		}
226 		return lines[first .. first + num - 1] + ({ "" });
227 	    }
228 	    size = first - line;
229 	}
230 	if (line < first) {
231 	    line += size;
232 	    offset += strlen(implode(lines[0 .. size - 1], "\n")) + 1;
233 	}
234     }
235 }
236 
237 /*
238  * NAME:	read_file()
239  * DESCRIPTION:	read a file
240  */
read_file(string file,int first,int num)241 static varargs string read_file(string file, int first, int num)
242 {
243     string *lines;
244 
245     ARGCHECK(file, read_file, 1);
246     file = valid(file, 0);
247     if (file == 0) {
248 	return 0;
249     }
250 
251     if (first == 0 && num == 0) {
252 	return ::read_file(file);
253     }
254 
255     lines = read_lines(file, first, num);
256     if (lines == 0) {
257 	return 0;
258     }
259     return implode(lines, "\n");
260 }
261 
262 /*
263  * NAME:	rm()
264  * DESCRIPTION:	remove a file
265  */
rm(string file)266 static int rm(string file)
267 {
268     ARGCHECK(file, rm, 1);
269     file = valid(file, 1);
270     return (file != 0 && ::remove_file(file));
271 }
272 
273 /*
274  * NAME:	remove_file()
275  * DESCRIPTION:	remove a file
276  */
remove_file(string file)277 static int remove_file(string file)
278 {
279     return rm(file);
280 }
281 
282 /*
283  * NAME:	rmdir()
284  * DESCRIPTION:	remove a directory
285  */
rmdir(string file)286 static int rmdir(string file)
287 {
288     ARGCHECK(file, rmdir, 1);
289     file = valid(file, 1);
290     return (file != 0 && ::remove_dir(file));
291 }
292 
293 /*
294  * NAME:	remove_dir()
295  * DESCRIPTION:	remove a directory
296  */
remove_dir(string file)297 static int remove_dir(string file)
298 {
299     return rmdir(file);
300 }
301 
302 /*
303  * NAME:	rename()
304  * DESCRIPTION:	rename a file
305  */
rename(string from,string to)306 static int rename(string from, string to)
307 {
308     ARGCHECK(from, rename, 1);
309     ARGCHECK(to, rename, 2);
310 
311     from = valid(from, 1);
312     if (from == 0) {
313 	return 0;
314     }
315     to = valid(to, 1);
316     if (to == 0) {
317 	return 0;
318     }
319     return ::rename_file(from, to);
320 }
321 
322 /*
323  * NAME:	rename_file()
324  * DESCRIPTION:	rename a file
325  */
rename_file(string from,string to)326 static int rename_file(string from, string to)
327 {
328     return rename(from, to);
329 }
330 
331 /*
332  * NAME:	restore_object()
333  * DESCRIPTION:	restore an object
334  */
restore_object(string file)335 static int restore_object(string file)
336 {
337     string str, *path;
338 
339     ARGCHECK(file, restore_object, 1);
340 
341     if (this_object() == 0) {
342 	return 0;
343     }
344     if (sscanf(file, "%*s/../") != 0) {
345 	error("Illegal restore file name " + file);
346     }
347     path = explode(object_name(this_object()), "/");
348     switch (path[0]) {
349     case "players":
350 	if (sscanf(file, "players/%s/", str) == 0 || str != path[1] ||
351 	    sscanf(file, "%*s.") != 0) {
352 	    error("Illegal restore file name " + file);
353 	}
354 	break;
355 
356     case "obj":
357     case "room":
358     case "std":
359 	break;
360 
361     default:
362 	error("Illegal use of restore_object()");
363     }
364     return ::restore_object(file + ".o");
365 }
366 
367 /*
368  * NAME:	save_object()
369  * DESCRIPTION:	save an object
370  */
save_object(string file)371 static void save_object(string file)
372 {
373     string str, *path;
374 
375     ARGCHECK(file, save_object, 1);
376 
377     if (this_object() == 0) {
378 	return;
379     }
380     if (sscanf(file, "%*s/../") != 0) {
381 	error("Illegal save file name " + file);
382     }
383     path = explode(object_name(this_object()), "/");
384     switch (path[0]) {
385     case "players":
386 	if (sscanf(file, "players/%s/", str) == 0 || str != path[1] ||
387 	    sscanf(file, "%*s.") != 0) {
388 	    error("Illegal save file name " + file);
389 	}
390 	break;
391 
392     case "obj":
393     case "room":
394     case "std":
395 	break;
396 
397     default:
398 	error("Illegal use of save_object()");
399     }
400     ::save_object(file + ".o");
401 }
402 
403 /*
404  * NAME:	write_bytes()
405  * DESCRIPTION:	write bytes to file
406  */
write_bytes(string file,int start,string str)407 static int write_bytes(string file, int start, string str)
408 {
409     int *sizes;
410 
411     ARGCHECK(file, write_bytes, 1);
412     ARGCHECK(str, write_bytes, 2);
413 
414     file = valid(file, 1);
415     if (file != 0) {
416 	if (start == 0) {
417 	    sizes = ::get_dir(file)[1];
418 	    if (sizeof(sizes) == 1) {
419 		start = -sizes[0];
420 	    }
421 	}
422 	return ::write_file(file, str, start);
423     }
424     return 0;
425 }
426 
427 /*
428  * NAME:	write_file()
429  * DESCRIPTION:	write string to file
430  */
write_file(string file,string str)431 static int write_file(string file, string str)
432 {
433     ARGCHECK(file, write_file, 1);
434     file = valid(file, 1);
435     return (file != 0 && ::write_file(file, str));
436 }
437 
438 /*
439  * NAME:	cat()
440  * DESCRIPTION:	show a file
441  */
cat(string file,int first,int num)442 static varargs int cat(string file, int first, int num)
443 {
444     string *lines;
445 
446     ARGCHECK(file, cat, 1);
447     file = valid(file, 0);
448     if (file == 0) {
449 	return 0;
450     }
451 
452     if (num == 0) {
453 	num = CAT_LINES + 1;
454     }
455     lines = read_lines(file, first, num);
456     if (lines == 0) {
457 	return 0;
458     }
459 
460     if (sizeof(lines) <= CAT_LINES + 1) {
461 	write(implode(lines, "\n"));
462     } else {
463 	write(implode(lines[0 .. CAT_LINES - 1], "\n") +
464 	      "\n***TRUNCATED***\n");
465     }
466     return 1;
467 }
468 
469 /*
470  * NAME:	tail()
471  * DESCRIPTION:	show the tail of a file
472  */
tail(string file)473 static int tail(string file)
474 {
475     int size, *sizes;
476     string str, *lines;
477 
478     ARGCHECK(file, tail, 1);
479     file = valid(file, 0);
480     if (file == 0) {
481 	return 0;
482     }
483 
484     sizes = ::get_dir(file)[1];
485     if (sizeof(sizes) == 1) {
486 	size = TAIL_CHUNK;
487 	for (;;) {
488 	    if (size > sizes[0]) {
489 		size = sizes[0];
490 	    }
491 
492 	    str = ::read_file(file, -size, size);
493 	    if (str == 0 || strlen(str) != size) {
494 		return 0;
495 	    }
496 	    lines = explode("\n" + str + "\n", "\n");
497 
498 	    if (sizeof(lines) >= TAIL_LINES + 1 || size == sizes[0]) {
499 		if ((size=sizeof(lines)) > TAIL_LINES + 1) {
500 		    str = implode(lines[size - TAIL_LINES - 1 ..], "\n");
501 		} else {
502 		    sscanf(str, "%*s\n%s", str);
503 		}
504 		write(str);
505 		return 1;
506 	    }
507 	    size += TAIL_CHUNK;
508 	}
509     }
510     return 0;
511 }
512 
513 /*
514  * NAME:	editor()
515  * DESCRIPTION:	handle an editor command
516  */
editor(string cmd)517 static string editor(string cmd)
518 {
519     string fname;
520 
521     fname = explode(object_name(this_object()), "#")[0];
522     if (fname != EDITOR && fname != CINDENT) {
523 	error("Illegal call to editor()");
524     }
525     if (cmd == 0) {
526 	return ::editor();
527     } else {
528 	return ::editor(cmd);
529     }
530 }
531 
532 /*
533  * NAME:	ed()
534  * DESCRIPTION:	start an editor session
535  */
ed(string file,string exit_func)536 static varargs void ed(string file, string exit_func)
537 {
538     object user, editor;
539 
540     if (this_player() == 0) {
541 	return;
542     }
543     if ((user=interactive(this_object())) == 0) {
544 	error("Tried to start an ed session on a non-interactive player");
545     }
546     if (this_player() != this_object()) {
547 	error("Illegal start of ed");
548     }
549 
550     editor = user->query_editor();
551     if (editor != 0) {
552 	error("Tried to start an ed session, when already active");
553     }
554     rlimits (-1; -1) {
555 	editor = clone_object(EDITOR);
556 	user->set_editor(editor, exit_func);
557     }
558 
559     if (file == 0) {
560 	editor->edit();
561     } else {
562 	editor->edit("e /" + file);
563     }
564 }
565 
566 /*
567  * NAME:	cindent()
568  * DESCRIPTION:	indent an LPC file
569  */
cindent(string file)570 static int cindent(string file)
571 {
572     return clone_object(CINDENT)->indent(file);
573 }
574 
575 /*
576  * NAME:	ls()
577  * DESCRIPTION:	write a directory listing
578  */
ls(string file)579 static int ls(string file)
580 {
581     mixed *dir;
582     string *list, str, dirlist;
583     int *sizes, i, j, sz, max, rows;
584 
585     ARGCHECK(file, ls, 1);
586     file = valid(file, 0);
587     if (file == 0) {
588 	return 0;
589     }
590 
591     i = strlen(file);
592     if (file == "" || file[i - 1] == '/') {
593 	file += "*";
594     } else if (i > 1 && file[i - 2 ..] == "/.") {
595 	file[i - 1] = '*';
596     }
597     dir = ::get_dir(file);
598     list = dir[0];
599     if (sizeof(list) == 0) {
600 	return 0;
601     }
602 
603     for (i = 0, sz = sizeof(list); i < sz; i++) {
604 	j = strlen(list[i]);
605 	if (j > max) {
606 	    max = j;
607 	}
608     }
609     max++;
610     j = 80 / (max + 1);
611     rows = sz / j;
612     if (sz % j > 0) {
613 	rows++;
614     }
615 
616     dirlist = "";
617     sizes = dir[1];
618     for (i = 0; i < rows; i++) {
619 	j = i;
620 	for (;;) {
621 	    str = list[j];
622 	    if (sizes[j] < 0) {
623 		str += "/";
624 	    }
625 	    j += rows;
626 	    if (j >= sz) {
627 		dirlist += str + "\n";
628 		break;
629 	    }
630 	    dirlist += (str + "                                        ")
631 		       [0 .. max];
632 	}
633     }
634     write(dirlist);
635 
636     return 1;
637 }
638