]> jfr.im git - irc/quakenet/newserv.git/blob - helpmod2/hed.c
GLINES: fix null pointer deref in trustgline / trustungline
[irc/quakenet/newserv.git] / helpmod2 / hed.c
1 #include <string.h>
2 #include <assert.h>
3 #include <stdio.h>
4 #include <ctype.h>
5
6 #include "../localuser/localuserchannel.h"
7
8 #include "hed.h"
9 #include "huser.h"
10 #include "hchannel.h"
11 #include "helpmod.h"
12 #include "hgen.h"
13
14 int hed_is_valid_filename(const char *fname)
15 {
16 int i;
17 if (strlen(fname) < 1 || strlen(fname) > (64 - 1))
18 return 0;
19
20 for (i=0;i < 64 && fname[i] != '\0';i++)
21 if (!isalnum(fname[i]))
22 return 0;
23
24 return 1;
25 }
26
27 helpmod_editor *hed_open(huser *husr, const char *fname)
28 {
29 helpmod_editor *editor;
30 hed_line *ptr;
31 char fname_buffer[128];
32 FILE *file;
33 int i;
34
35 if (hed_get(fname) != NULL)
36 return hed_get(fname);
37
38 if (!hed_is_valid_filename(fname))
39 return NULL;
40
41 sprintf(fname_buffer, HELPMOD_TEXT_DIR"/%s" ,fname);
42 file = fopen(fname_buffer, "rwt");
43 if (file == NULL)
44 return NULL;
45
46 editor = (helpmod_editor*)malloc(sizeof (helpmod_editor));
47
48 editor->user = husr;
49 strcpy(editor->filename, fname);
50 editor->line = 0;
51 editor->flags = 0;
52
53 editor->state = HED_COMMAND;
54 editor->last_error = HED_ERR_NO_ERROR;
55
56 editor->start = NULL;
57 editor->free_lines = &editor->buffers[0];
58 editor->clipboard = NULL;
59
60 for (ptr = editor->free_lines,i = 1;i < HED_BUFFER_LINES;i++,ptr = ptr->next)
61 {
62 ptr->next = &editor->buffers[i];
63 ptr->line[0] = '\0';
64 }
65 ptr->next = NULL;
66
67 for(i = 0;!feof(file) && i < HED_BUFFER_LINES;i++)
68 {
69 char *str = hed_add_line(editor, i);
70 if (!fgets(str, 384, file))
71 { /* If the input ran out, the last line added has to be removed */
72 hed_del_line(editor, i);
73 hed_clear_clipboard(editor);
74 break;
75 }
76 strchr(str, '\n')[0] = '\0';
77 }
78
79 fclose(file);
80
81 /* Clear flags */
82 editor->flags = HED_FLAGS_DEFAULT;
83
84 editor->next = helpmod_editors;
85 helpmod_editors = editor;
86
87 return editor;
88 }
89
90 helpmod_editor *hed_write(helpmod_editor *editor)
91 {
92 FILE *file;
93 char fname_buffer[128];
94 hed_line *ptr;
95
96 sprintf(fname_buffer, HELPMOD_TEXT_DIR"/%s" ,editor->filename);
97 if ((file = fopen(fname_buffer, "wt")) == NULL)
98 {
99 Error("helpmod", ERR_ERROR, "hed: could not open file: %s", fname_buffer);
100 return editor;
101 }
102
103 for (ptr = editor->start;ptr != NULL;ptr = ptr->next)
104 fprintf(file, "%s\n", ptr->line);
105
106 fclose(file);
107 return editor;
108 }
109
110 helpmod_editor *hed_close(helpmod_editor *editor)
111 {
112 helpmod_editor **ptr = &helpmod_editors;
113
114 if (editor == NULL)
115 return NULL;
116
117 for (;*ptr;ptr = &(*ptr)->next)
118 if (*ptr == editor)
119 {
120 helpmod_editor *tmp = editor->next;
121 editor->user->editor = NULL;
122
123 free (editor);
124 *ptr = tmp;
125 break;
126 }
127 return NULL;
128 }
129
130 int hed_byte_count(helpmod_editor *editor)
131 {
132 hed_line *ptr = editor->start;
133 int count = 0;
134
135 for (;ptr && ptr->line[0] != '\0';ptr = ptr->next)
136 count += strlen(ptr->line);
137
138 return count;
139 }
140
141 int hed_line_count(helpmod_editor *editor)
142 {
143 hed_line *ptr = editor->start;
144 int count = 0;
145
146 for (;ptr && ptr != NULL;ptr = ptr->next)
147 count++;
148
149 return count;
150 }
151
152 /* Get by filename */
153 helpmod_editor *hed_get(const char *fname)
154 {
155 helpmod_editor *tmp = helpmod_editors;
156 for (;tmp != NULL;tmp = tmp->next)
157 if (!strcmp(tmp->filename, fname))
158 return tmp;
159 return NULL;
160 }
161
162 void hed_command (huser *sender, channel* returntype, char* ostr, int argc, char *argv[])
163 {
164 helpmod_editor *editor = sender->editor;
165 assert (editor != NULL);
166
167 if (editor->state == HED_COMMAND)
168 {
169 int i, start = editor->line + 1, stop = editor->line + 1, target = editor->line + 1, nread;
170 char command;
171 char buffer[128];
172
173 if (argc == 0)
174 {
175 editor->last_error = HED_ERR_NO_COMMAND;
176 goto ERROR;
177 }
178
179 if (strlen(argv[0]) > 64)
180 {
181 editor->last_error = HED_ERR_SYNTAX_ERROR;
182 goto ERROR;
183 }
184
185 if (!strcmp(argv[0], "wq"))
186 {
187 hed_write(editor);
188 helpmod_reply(sender, returntype, "Ged: %d", hed_byte_count(editor));
189 hed_close(editor);
190 return;
191 }
192
193 { /* Handle replacements */
194 int replaces = 3, j;
195 for (i=0,j=0;argv[0][i] != '\0' && replaces;i++)
196 {
197 replaces--;
198 switch (argv[0][i])
199 {
200 case '.':
201 j+=sprintf(&buffer[j], "%d", editor->line + 1);
202 break;
203 case '$':
204 j+=sprintf(&buffer[j], "%d", hed_line_count(editor) + 0);
205 break;
206 case '-':
207 case '^':
208 j+=sprintf(&buffer[j], "%d", editor->line + 0);
209 break;
210 case '+':
211 j+=sprintf(&buffer[j], "%d", editor->line + 2);
212 break;
213 default:
214 buffer[j++] = argv[0][i];
215 replaces++;
216 }
217 }
218 buffer[j] = '\0';
219 }
220
221 /* parse the command */
222 if (strchr(buffer, ',') != NULL)
223 {
224 if (sscanf(buffer, "%d,%d%c%d%n", &start, &stop, &command, &target, &nread) == 4 && (nread == strlen(argv[0])));
225 else if (sscanf(buffer, "%d,%d%c%n", &start, &stop, &command, &nread) == 3 && (nread == strlen(argv[0])));
226 else
227 {
228 editor->last_error = HED_ERR_SYNTAX_ERROR;
229 goto ERROR;
230 }
231 }
232 else if (strnumcount(buffer) > 0)
233 {
234 if (sscanf(buffer, "%d%n", &start, &nread) == 1 && nread == strlen(argv[0]))
235 command = 'p';
236 else if (sscanf(buffer, "%d%c%n", &start, &command, &nread) == 2 && nread == strlen(argv[0]));
237 else if (sscanf(buffer, "%d%c%d%n", &start, &command, &target, &nread) == 3 && nread == strlen(argv[0]));
238 else
239 {
240 editor->last_error = HED_ERR_SYNTAX_ERROR;
241 goto ERROR;
242 }
243 stop = start;
244 }
245 else
246 {
247 if (sscanf(buffer, "%c%n", &command, &nread) == 1 && nread == strlen(argv[0]));
248 else
249 {
250 editor->last_error = HED_ERR_SYNTAX_ERROR;
251 goto ERROR;
252 }
253 }
254
255 /* Change to internal presentation */
256 start--;
257 stop--;
258 target--;
259
260 /* Handle the command */
261 switch (command)
262 {
263 case 'a': /* Append */
264 if (hed_line_count(editor) == HED_BUFFER_LINES)
265 {
266 editor->last_error = HED_ERR_BUFFER_FULL;
267 goto ERROR;
268 }
269 if (start < 0 || stop > hed_line_count(editor) || start > stop)
270 {
271 editor->last_error = HED_ERR_INVALID_ARGUMENT;
272 goto ERROR;
273 }
274 editor->state = HED_INPUT_APPEND;
275 break;
276 case 'i': /* Insert */
277 if (hed_line_count(editor) == HED_BUFFER_LINES)
278 {
279 editor->last_error = HED_ERR_BUFFER_FULL;
280 goto ERROR;
281 }
282 if (start < 0 || stop >= hed_line_count(editor) || start > stop)
283 {
284 editor->last_error = HED_ERR_INVALID_ARGUMENT;
285 goto ERROR;
286 }
287 editor->state = HED_INPUT_INSERT;
288 break;
289 case 'c': /* Replace */
290 if (start < 0 || stop >= hed_line_count(editor) || start > stop)
291 {
292 editor->last_error = HED_ERR_INVALID_ARGUMENT;
293 goto ERROR;
294 }
295
296 hed_del_line(editor, start);
297 editor->state = HED_INPUT_INSERT;
298
299 break;
300 case 'd': /* Delete */
301 if (start < 0 || stop >= hed_line_count(editor) || start > stop)
302 {
303 editor->last_error = HED_ERR_INVALID_ARGUMENT;
304 goto ERROR;
305 }
306
307 for (i = 0;i <= stop - start;i++)
308 hed_del_line(editor, start);
309
310 break;
311 case 'e': /* Open file for editing */
312 case 'E': /* Uncondititional e */
313 editor->last_error = HED_ERR_COMMAND_NOT_SUPPORTED;
314 goto ERROR;
315 break;
316 case 'f': /* View (or set) the filename */
317 helpmod_reply(sender, returntype, "Ged: %s", editor->filename);
318 break;
319 case 'h': /* Last error */
320 helpmod_reply(sender, returntype, "Ged: %s", hed_error_text(editor->last_error));
321 break;
322 case 'H':
323 editor->flags ^= HED_FLAG_VERBOSE_ERRORS;
324 break;
325 case 'j': /* Join lines */
326 if ((start < 0 || start >= hed_line_count(editor)) ||
327 (stop < 0 || stop >= hed_line_count(editor)) ||
328 start == stop)
329 {
330 editor->last_error = HED_ERR_INVALID_ARGUMENT;
331 goto ERROR;
332 }
333
334 if (strlen(hed_get_line(editor, start)) + strlen(hed_get_line(editor, stop)) > 384 - 2)
335 {
336 editor->last_error = HED_ERR_LINE_TOO_LONG;
337 goto ERROR;
338 }
339
340 strcat(hed_get_line(editor, start), hed_get_line(editor, stop));
341 hed_del_line(editor, stop);
342
343 break;
344 case 'l': /* Print */
345 case 'p':
346 case 'n': /* Print with line numbers */
347 if (start < 0 || stop >= hed_line_count(editor))
348 {
349 editor->last_error = HED_ERR_INVALID_ARGUMENT;
350 goto ERROR;
351 }
352
353 for (i=start;i <= stop;i++)
354 if (command == 'n')
355 helpmod_reply(sender, returntype, "Ged: %-8d %s", i+1, hed_get_line(editor, i));
356 else
357 helpmod_reply(sender, returntype, "Ged: %s", hed_get_line(editor, i));
358 break;
359 case 'm': /* Move */
360 if (start < 0 || stop >= hed_line_count(editor) || start > stop)
361 {
362 editor->last_error = HED_ERR_INVALID_ARGUMENT;
363 goto ERROR;
364 }
365 if (target < 0 || target >= hed_line_count(editor) - (stop - start) ||
366 (target >= start && target <= stop))
367 {
368 editor->last_error = HED_ERR_INVALID_ARGUMENT;
369 goto ERROR;
370 }
371 if (target > start)
372 target-= (stop - start);
373
374 for (i = 0;i <= stop - start;i++)
375 hed_del_line(editor, start);
376
377 hed_paste(editor, target);
378 break;
379 case 'q': /* Quit */
380 if (editor->flags & HED_FLAG_UNSAVED)
381 {
382 editor->flags &=~HED_FLAG_UNSAVED;
383 editor->last_error = HED_ERR_UNSAVED;
384 goto ERROR;
385 }
386 else
387 {
388 hed_close(editor);
389 return;
390 }
391 return;
392 case 'Q': /* Unconditional quit */
393 hed_close(editor);
394 return;
395 case 'r': /* Read from file */
396 editor->last_error = HED_ERR_COMMAND_NOT_SUPPORTED;
397 goto ERROR;
398 break;
399 case 'w': /* Write file */
400 hed_write(editor);
401 editor->flags &=~HED_FLAG_UNSAVED;
402 helpmod_reply(sender, returntype, "Ged: %d", hed_byte_count(editor));
403 break;
404 case 'x': /* Paste */
405 if (start < 0 || start >= hed_line_count(editor))
406 {
407 editor->last_error = HED_ERR_INVALID_ARGUMENT;
408 goto ERROR;
409 }
410 if (editor->clipboard == NULL)
411 {
412 editor->last_error = HED_ERR_CLIPBOARD_EMPTY;
413 goto ERROR;
414 }
415 hed_paste(editor, start);
416 break;
417 case '=': /* Current line */
418 helpmod_reply(sender, returntype, "Ged: %d", editor->line + 1);
419 break;
420 default:
421 editor->last_error = HED_ERR_UNKNOWN_COMMAND;
422 goto ERROR;
423 }
424
425 editor->line = start;
426 }
427 else
428 { /* HED_INPUT */
429 char *str;
430
431 if (argc == 0)
432 ostr[0] = '\0';
433
434 /* return to command mode */
435 if (!strcmp(ostr, "."))
436 {
437 editor->state = HED_COMMAND;
438 return;
439 }
440
441 if (hed_line_count(editor) >= HED_BUFFER_LINES)
442 {
443 editor->last_error = HED_ERR_BUFFER_FULL;
444 editor->state = HED_COMMAND;
445 goto ERROR;
446 }
447
448 if (strlen(ostr) > 384 - 2)
449 {
450 editor->last_error = HED_ERR_LINE_TOO_LONG;
451 goto ERROR;
452 }
453
454 switch (editor->state)
455 {
456 case HED_INPUT_INSERT:
457 str = hed_add_line(editor, editor->line);
458 strcpy(str, ostr);
459 break;
460 case HED_INPUT_APPEND:
461 /* the last line is handled specially */
462 if (editor->line == HED_BUFFER_LINES - 1)
463 editor->line--;
464 str = hed_add_line(editor, editor->line + 1);
465 strcpy(str, ostr);
466 break;
467 case HED_COMMAND:
468 default:
469 break;
470 }
471
472 editor->line++;
473 }
474 editor->last_error = HED_ERR_NO_ERROR;
475 return;
476 ERROR:
477 if (editor->flags & HED_FLAG_VERBOSE_ERRORS)
478 helpmod_reply(sender, returntype, "Ged: %s", hed_error_text(editor->last_error));
479 else
480 helpmod_reply(sender, returntype, "Ged: ?");
481 }
482
483 const char *hed_error_text(hed_error err)
484 {
485 switch (err)
486 {
487 case HED_ERR_NO_ERROR:
488 return "No error";
489 case HED_ERR_NO_COMMAND:
490 return "No command given";
491 case HED_ERR_SYNTAX_ERROR:
492 return "Syntax error";
493 case HED_ERR_UNKNOWN_COMMAND:
494 return "Unknown command";
495 case HED_ERR_COMMAND_NOT_SUPPORTED:
496 return "Unsupported command";
497 case HED_ERR_INVALID_ARGUMENT:
498 return "Invalid argument";
499 case HED_ERR_LINE_TOO_LONG:
500 return "Line exceeds 384 characters";
501 case HED_ERR_BUFFER_FULL:
502 return "Buffer full";
503 case HED_ERR_CLIPBOARD_EMPTY:
504 return "Nothing to paste";
505 case HED_ERR_UNSAVED:
506 return "File unsaved, q again to quit";
507 default:
508 return "Error, please contact strutsi";
509 }
510 }
511
512 char* hed_add_line(helpmod_editor *editor, int position)
513 {
514 hed_line **pos, *tmp;
515 hed_clear_clipboard(editor);
516
517 assert(position >= 0 && position < HED_BUFFER_LINES);
518 assert(hed_line_count(editor) < HED_BUFFER_LINES);
519 assert(editor->free_lines != NULL);
520
521 editor->flags |= HED_FLAG_UNSAVED;
522
523 for (pos = &editor->start;*pos != NULL && position;pos = &(*pos)->next, position--);
524
525 tmp = editor->free_lines;
526 editor->free_lines = editor->free_lines->next;
527
528 tmp->next = *pos;
529 tmp->line[0] = '\0';
530
531 *pos = tmp;
532
533 return tmp->line;
534 }
535
536 void hed_del_line(helpmod_editor *editor, int position)
537 {
538 hed_line **ptr, **ptr_free, *tmp;
539 assert(position >= 0 && position < hed_line_count(editor));
540
541 editor->flags |= HED_FLAG_UNSAVED;
542
543 for (ptr = &editor->start;position;ptr = &(*ptr)->next, position--);
544
545 tmp = *ptr;
546 *ptr = (*ptr)->next;
547
548 for (ptr_free = &editor->clipboard;*ptr_free;ptr_free = &(*ptr_free)->next);
549
550 tmp->next = NULL;
551 *ptr_free = tmp;
552 }
553
554 char* hed_get_line(helpmod_editor *editor, int position)
555 {
556 hed_line *ptr;
557 assert(position >= 0 && position < hed_line_count(editor));
558
559 for (ptr = editor->start;position--;ptr = ptr->next);
560 return ptr->line;
561 }
562
563 void hed_clear_clipboard(helpmod_editor *editor)
564 {
565 hed_line **ptr;
566 for (ptr = &editor->clipboard;*ptr != NULL;ptr = &(*ptr)->next);
567 (*ptr) = editor->free_lines;
568 editor->free_lines = editor->clipboard;
569 editor->clipboard = NULL;
570 }
571
572 void hed_paste(helpmod_editor *editor, int position)
573 {
574 hed_line **ptr, **ptr_clipboard;
575
576 assert(position >= 0 && position <= hed_line_count(editor));
577 editor->flags |= HED_FLAG_UNSAVED;
578
579 for (ptr = &editor->start;position;ptr = &(*ptr)->next, position--);
580 for (ptr_clipboard = &editor->clipboard;*ptr_clipboard != NULL;ptr_clipboard = &(*ptr_clipboard)->next);
581
582 *ptr_clipboard = *ptr;
583 *ptr = editor->clipboard;
584
585 editor->clipboard = NULL;
586 }