]> jfr.im git - irc/quakenet/newserv.git/blame - helpmod2/hed.c
helpmod2: Fix null pointer dereference.
[irc/quakenet/newserv.git] / helpmod2 / hed.c
CommitLineData
f9d450b2 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
14int 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
27helpmod_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
90helpmod_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);
9779bda3
GB
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 }
f9d450b2 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
110helpmod_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
130int 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
141int 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 */
153helpmod_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
162void 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 sprintf(&buffer[j], "%d%n", editor->line + 1, &nread);
202 j+=nread;
203 break;
204 case '$':
205 sprintf(&buffer[j], "%d%n", hed_line_count(editor) + 0, &nread);
206 j+=nread;
207 break;
208 case '-':
209 case '^':
210 sprintf(&buffer[j], "%d%n", editor->line + 0, &nread);
211 j+=nread;
212 break;
213 case '+':
214 sprintf(&buffer[j], "%d%n", editor->line + 2, &nread);
215 j+=nread;
216 break;
217 default:
218 buffer[j++] = argv[0][i];
219 replaces++;
220 }
221 }
222 buffer[j] = '\0';
223 }
224
225 /* parse the command */
226 if (strchr(buffer, ',') != NULL)
227 {
228 if (sscanf(buffer, "%d,%d%c%d%n", &start, &stop, &command, &target, &nread) == 4 && (nread == strlen(argv[0])));
229 else if (sscanf(buffer, "%d,%d%c%n", &start, &stop, &command, &nread) == 3 && (nread == strlen(argv[0])));
230 else
231 {
232 editor->last_error = HED_ERR_SYNTAX_ERROR;
233 goto ERROR;
234 }
235 }
236 else if (strnumcount(buffer) > 0)
237 {
238 if (sscanf(buffer, "%d%n", &start, &nread) == 1 && nread == strlen(argv[0]))
239 command = 'p';
240 else if (sscanf(buffer, "%d%c%n", &start, &command, &nread) == 2 && nread == strlen(argv[0]));
241 else if (sscanf(buffer, "%d%c%d%n", &start, &command, &target, &nread) == 3 && nread == strlen(argv[0]));
242 else
243 {
244 editor->last_error = HED_ERR_SYNTAX_ERROR;
245 goto ERROR;
246 }
247 stop = start;
248 }
249 else
250 {
251 if (sscanf(buffer, "%c%n", &command, &nread) == 1 && nread == strlen(argv[0]));
252 else
253 {
254 editor->last_error = HED_ERR_SYNTAX_ERROR;
255 goto ERROR;
256 }
257 }
258
259 /* Change to internal presentation */
260 start--;
261 stop--;
262 target--;
263
264 /* Handle the command */
265 switch (command)
266 {
267 case 'a': /* Append */
268 if (hed_line_count(editor) == HED_BUFFER_LINES)
269 {
270 editor->last_error = HED_ERR_BUFFER_FULL;
271 goto ERROR;
272 }
273 if (start < 0 || stop > hed_line_count(editor) || start > stop)
274 {
275 editor->last_error = HED_ERR_INVALID_ARGUMENT;
276 goto ERROR;
277 }
278 editor->state = HED_INPUT_APPEND;
279 break;
280 case 'i': /* Insert */
281 if (hed_line_count(editor) == HED_BUFFER_LINES)
282 {
283 editor->last_error = HED_ERR_BUFFER_FULL;
284 goto ERROR;
285 }
286 if (start < 0 || stop >= hed_line_count(editor) || start > stop)
287 {
288 editor->last_error = HED_ERR_INVALID_ARGUMENT;
289 goto ERROR;
290 }
291 editor->state = HED_INPUT_INSERT;
292 break;
293 case 'c': /* Replace */
294 if (start < 0 || stop >= hed_line_count(editor) || start > stop)
295 {
296 editor->last_error = HED_ERR_INVALID_ARGUMENT;
297 goto ERROR;
298 }
299
300 hed_del_line(editor, start);
301 editor->state = HED_INPUT_INSERT;
302
303 break;
304 case 'd': /* Delete */
305 if (start < 0 || stop >= hed_line_count(editor) || start > stop)
306 {
307 editor->last_error = HED_ERR_INVALID_ARGUMENT;
308 goto ERROR;
309 }
310
311 for (i = 0;i <= stop - start;i++)
312 hed_del_line(editor, start);
313
314 break;
315 case 'e': /* Open file for editing */
316 case 'E': /* Uncondititional e */
317 editor->last_error = HED_ERR_COMMAND_NOT_SUPPORTED;
318 goto ERROR;
319 break;
320 case 'f': /* View (or set) the filename */
321 helpmod_reply(sender, returntype, "Ged: %s", editor->filename);
322 break;
323 case 'h': /* Last error */
324 helpmod_reply(sender, returntype, "Ged: %s", hed_error_text(editor->last_error));
325 break;
326 case 'H':
327 editor->flags ^= HED_FLAG_VERBOSE_ERRORS;
328 break;
329 case 'j': /* Join lines */
330 if ((start < 0 || start >= hed_line_count(editor)) ||
331 (stop < 0 || stop >= hed_line_count(editor)) ||
332 start == stop)
333 {
334 editor->last_error = HED_ERR_INVALID_ARGUMENT;
335 goto ERROR;
336 }
337
338 if (strlen(hed_get_line(editor, start)) + strlen(hed_get_line(editor, stop)) > 384 - 2)
339 {
340 editor->last_error = HED_ERR_LINE_TOO_LONG;
341 goto ERROR;
342 }
343
344 strcat(hed_get_line(editor, start), hed_get_line(editor, stop));
345 hed_del_line(editor, stop);
346
347 break;
348 case 'l': /* Print */
349 case 'p':
350 case 'n': /* Print with line numbers */
351 if (start < 0 || stop >= hed_line_count(editor))
352 {
353 editor->last_error = HED_ERR_INVALID_ARGUMENT;
354 goto ERROR;
355 }
356
357 for (i=start;i <= stop;i++)
358 if (command == 'n')
359 helpmod_reply(sender, returntype, "Ged: %-8d %s", i+1, hed_get_line(editor, i));
360 else
361 helpmod_reply(sender, returntype, "Ged: %s", hed_get_line(editor, i));
362 break;
363 case 'm': /* Move */
364 if (start < 0 || stop >= hed_line_count(editor) || start > stop)
365 {
366 editor->last_error = HED_ERR_INVALID_ARGUMENT;
367 goto ERROR;
368 }
369 if (target < 0 || target >= hed_line_count(editor) - (stop - start) ||
370 (target >= start && target <= stop))
371 {
372 editor->last_error = HED_ERR_INVALID_ARGUMENT;
373 goto ERROR;
374 }
375 if (target > start)
376 target-= (stop - start);
377
378 for (i = 0;i <= stop - start;i++)
379 hed_del_line(editor, start);
380
381 hed_paste(editor, target);
382 break;
383 case 'q': /* Quit */
384 if (editor->flags & HED_FLAG_UNSAVED)
385 {
386 editor->flags &=~HED_FLAG_UNSAVED;
387 editor->last_error = HED_ERR_UNSAVED;
388 goto ERROR;
389 }
390 else
391 {
392 hed_close(editor);
393 return;
394 }
395 return;
396 case 'Q': /* Unconditional quit */
397 hed_close(editor);
398 return;
399 case 'r': /* Read from file */
400 editor->last_error = HED_ERR_COMMAND_NOT_SUPPORTED;
401 goto ERROR;
402 break;
403 case 'w': /* Write file */
404 hed_write(editor);
405 editor->flags &=~HED_FLAG_UNSAVED;
406 helpmod_reply(sender, returntype, "Ged: %d", hed_byte_count(editor));
407 break;
408 case 'x': /* Paste */
409 if (start < 0 || start >= hed_line_count(editor))
410 {
411 editor->last_error = HED_ERR_INVALID_ARGUMENT;
412 goto ERROR;
413 }
414 if (editor->clipboard == NULL)
415 {
416 editor->last_error = HED_ERR_CLIPBOARD_EMPTY;
417 goto ERROR;
418 }
419 hed_paste(editor, start);
420 break;
421 case '=': /* Current line */
422 helpmod_reply(sender, returntype, "Ged: %d", editor->line + 1);
423 break;
424 default:
425 editor->last_error = HED_ERR_UNKNOWN_COMMAND;
426 goto ERROR;
427 }
428
429 editor->line = start;
430 }
431 else
432 { /* HED_INPUT */
660b35f8 433 char *str;
434
435 if (argc == 0)
436 ostr[0] = '\0';
437
f9d450b2 438 /* return to command mode */
660b35f8 439 if (!strcmp(ostr, "."))
f9d450b2 440 {
441 editor->state = HED_COMMAND;
442 return;
443 }
444
445 if (hed_line_count(editor) >= HED_BUFFER_LINES)
446 {
447 editor->last_error = HED_ERR_BUFFER_FULL;
448 editor->state = HED_COMMAND;
449 goto ERROR;
450 }
451
452 if (strlen(ostr) > 384 - 2)
453 {
454 editor->last_error = HED_ERR_LINE_TOO_LONG;
455 goto ERROR;
456 }
457
458 switch (editor->state)
459 {
460 case HED_INPUT_INSERT:
461 str = hed_add_line(editor, editor->line);
462 strcpy(str, ostr);
463 break;
464 case HED_INPUT_APPEND:
465 /* the last line is handled specially */
466 if (editor->line == HED_BUFFER_LINES - 1)
467 editor->line--;
468 str = hed_add_line(editor, editor->line + 1);
469 strcpy(str, ostr);
470 break;
471 case HED_COMMAND:
472 default:
473 break;
474 }
475
476 editor->line++;
477 }
478 editor->last_error = HED_ERR_NO_ERROR;
479 return;
480ERROR:
481 if (editor->flags & HED_FLAG_VERBOSE_ERRORS)
482 helpmod_reply(sender, returntype, "Ged: %s", hed_error_text(editor->last_error));
483 else
484 helpmod_reply(sender, returntype, "Ged: ?");
485}
486
487const char *hed_error_text(hed_error err)
488{
489 switch (err)
490 {
491 case HED_ERR_NO_ERROR:
492 return "No error";
493 case HED_ERR_NO_COMMAND:
494 return "No command given";
495 case HED_ERR_SYNTAX_ERROR:
496 return "Syntax error";
497 case HED_ERR_UNKNOWN_COMMAND:
498 return "Unknown command";
499 case HED_ERR_COMMAND_NOT_SUPPORTED:
500 return "Unsupported command";
501 case HED_ERR_INVALID_ARGUMENT:
502 return "Invalid argument";
503 case HED_ERR_LINE_TOO_LONG:
504 return "Line exceeds 384 characters";
505 case HED_ERR_BUFFER_FULL:
506 return "Buffer full";
507 case HED_ERR_CLIPBOARD_EMPTY:
508 return "Nothing to paste";
509 case HED_ERR_UNSAVED:
510 return "File unsaved, q again to quit";
511 default:
512 return "Error, please contact strutsi";
513 }
514}
515
516char* hed_add_line(helpmod_editor *editor, int position)
517{
518 hed_line **pos, *tmp;
519 hed_clear_clipboard(editor);
520
521 assert(position >= 0 && position < HED_BUFFER_LINES);
522 assert(hed_line_count(editor) < HED_BUFFER_LINES);
523 assert(editor->free_lines != NULL);
524
525 editor->flags |= HED_FLAG_UNSAVED;
526
527 for (pos = &editor->start;*pos != NULL && position;pos = &(*pos)->next, position--);
528
529 tmp = editor->free_lines;
530 editor->free_lines = editor->free_lines->next;
531
532 tmp->next = *pos;
533 tmp->line[0] = '\0';
534
535 *pos = tmp;
536
537 return tmp->line;
538}
539
540void hed_del_line(helpmod_editor *editor, int position)
541{
542 hed_line **ptr, **ptr_free, *tmp;
543 assert(position >= 0 && position < hed_line_count(editor));
544
545 editor->flags |= HED_FLAG_UNSAVED;
546
547 for (ptr = &editor->start;position;ptr = &(*ptr)->next, position--);
548
549 tmp = *ptr;
550 *ptr = (*ptr)->next;
551
552 for (ptr_free = &editor->clipboard;*ptr_free;ptr_free = &(*ptr_free)->next);
553
554 tmp->next = NULL;
555 *ptr_free = tmp;
556}
557
558char* hed_get_line(helpmod_editor *editor, int position)
559{
560 hed_line *ptr;
561 assert(position >= 0 && position < hed_line_count(editor));
562
563 for (ptr = editor->start;position--;ptr = ptr->next);
564 return ptr->line;
565}
566
567void hed_clear_clipboard(helpmod_editor *editor)
568{
569 hed_line **ptr;
570 for (ptr = &editor->clipboard;*ptr != NULL;ptr = &(*ptr)->next);
571 (*ptr) = editor->free_lines;
572 editor->free_lines = editor->clipboard;
573 editor->clipboard = NULL;
574}
575
576void hed_paste(helpmod_editor *editor, int position)
577{
578 hed_line **ptr, **ptr_clipboard;
579
580 assert(position >= 0 && position <= hed_line_count(editor));
581 editor->flags |= HED_FLAG_UNSAVED;
582
583 for (ptr = &editor->start;position;ptr = &(*ptr)->next, position--);
584 for (ptr_clipboard = &editor->clipboard;*ptr_clipboard != NULL;ptr_clipboard = &(*ptr_clipboard)->next);
585
586 *ptr_clipboard = *ptr;
587 *ptr = editor->clipboard;
588
589 editor->clipboard = NULL;
590}