]> jfr.im git - irc/quakenet/snircd.git/blame - ircd/convert-conf.c
Should be unsigned long for A
[irc/quakenet/snircd.git] / ircd / convert-conf.c
CommitLineData
189935b1 1/* convert-conf.c - Convert ircu2.10.11 ircd.conf to ircu2.10.12 format.
2 * Copyright 2005 Michael Poole
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17 * USA.
18 */
19
37d25209 20#include <ctype.h> /* tolower() */
189935b1 21#include <stdio.h> /* *printf(), fgets() */
22#include <stdlib.h> /* free(), strtol() */
23#include <string.h> /* strlen(), memcpy(), strchr(), strspn() */
24
25#define MAX_FIELDS 5
26
27const char *admin_names[] = { "location", "contact", "contact", 0 },
28 *connect_names[] = { "host", "password", "name", "#port", "class", 0 },
29 *crule_names[] = { "server", "", "rule", 0 },
30 *general_names[] = { "name", "vhost", "description", "", "#numeric", 0 },
31 *motd_names[] = { "host", "file", 0 },
32 *class_names[] = { "name", "#pingfreq", "#connectfreq", "#maxlinks", "#sendq", 0 },
37d25209 33 *removed_features[] = { "VIRTUAL_HOST", "TIMESEC", "OPERS_SEE_IN_SECRET_CHANNELS", "LOCOP_SEE_IN_SECRET_CHANNELS", "HIS_STATS_h", "HIS_DESYNCS", "AUTOHIDE", 0 };
189935b1 34char orig_line[512], line[512], dbuf[512];
35char *fields[MAX_FIELDS + 1];
36unsigned int nfields;
37unsigned int lineno;
38
39/*** GENERIC SUPPORT CODE ***/
40
41static int split_line(char *input, char **output)
42{
43 size_t quoted = 0, jj;
44 char *dest = dbuf, ch;
45
46 nfields = 1;
47 output[0] = dest;
48 while (*input != '\0' && *input != '#') switch (ch = *input++) {
49 case ':':
50 if (quoted)
51 *dest++ = ch;
52 else {
53 *dest++ = '\0';
54 if (nfields >= MAX_FIELDS)
55 return nfields;
56 output[nfields++] = dest;
57 }
58 break;
59 case '\\':
60 switch (ch = *input++) {
61 case 'b': *dest++ = '\b'; break;
62 case 'f': *dest++ = '\f'; break;
63 case 'n': *dest++ = '\n'; break;
64 case 'r': *dest++ = '\r'; break;
65 case 't': *dest++ = '\t'; break;
66 case 'v': *dest++ = '\v'; break;
67 default: *dest++ = ch; break;
68 }
69 break;
70 case '"': quoted = !quoted; break;
71 default: *dest++ = ch; break;
72 }
73
74 *dest = '\0';
75 for (jj = nfields; jj < MAX_FIELDS; ++jj)
76 output[jj] = dest;
77 return nfields;
78}
79
80static void simple_line(const char *block, const char **names, const char *extra)
81{
82 size_t ii;
83
84 /* Print the current line and start the new block. */
85 fprintf(stdout, "# %s\n%s {\n", orig_line, block);
86
87 /* Iterate over fields in input line, formatting each. */
88 for (ii = 0; ii < nfields && names[ii]; ++ii) {
89 if (!fields[ii][0] || !names[ii][0])
90 continue;
91 else if (names[ii][0] == '#')
92 fprintf(stdout, "\t%s = %s;\n", names[ii] + 1, fields[ii]);
93 else
94 fprintf(stdout, "\t%s = \"%s\";\n", names[ii], fields[ii]);
95 }
96
97 /* Close the new block (including any fixed-form text). */
98 if (extra)
99 fprintf(stdout, "\t%s\n", extra);
100 fputs("};\n", stdout);
101}
102
37d25209 103#define dupstring(TARGET, SOURCE) do { free(TARGET); if (SOURCE) { size_t len = strlen(SOURCE) + 1; (TARGET) = malloc(len); memcpy((TARGET), (SOURCE), len); } else (TARGET) = 0; } while(0)
189935b1 104
105/*** MANAGING LISTS OF STRINGS ***/
106
107struct string_list {
108 struct string_list *next;
109 char *origin;
110 char *extra;
111 char value[1];
112};
113
37d25209 114/** Find or insert the element from \a list that contains \a value.
115 * If an element of \a list already contains \a value, return it.
116 * Otherwise, append a new element to \a list containing \a value and
117 * return it.
118 * @param[in,out] list A list of strings.
119 * @param[in] value A string to search for.
120 * @return A string list element from \a list containing \a value.
121 */
189935b1 122static struct string_list *string_get(struct string_list **list, const char *value)
123{
124 struct string_list *curr;
125 size_t len = strlen(value), ii;
126
127 while ((curr = *list)) {
128 for (ii = 0; tolower(curr->value[ii]) == tolower(value[ii]) && ii < len; ++ii) ;
129 if (curr->value[ii] == '\0' && value[ii] == '\0')
130 return curr;
131 list = &curr->next;
132 }
133
134 *list = calloc(1, sizeof(**list) + len);
135 memcpy((*list)->value, value, len);
136 return *list;
137}
138
139/*** SERVER CONNECTION RELATED CODE ***/
140
141struct connect {
142 char *host;
143 char *password;
144 char *port;
145 char *class;
146 char *hub;
147 char *maximum;
148 struct connect *next;
149 struct string_list *origins;
150 char name[1];
151} *connects;
152
153static struct connect *get_connect(const char *name)
154{
155 struct connect *conn;
156 size_t ii, nlen;
157
158 /* Look for a pre-existing connection with the same name. */
159 nlen = strlen(name);
160 for (conn = connects; conn; conn = conn->next)
161 {
37d25209 162 for (ii = 0; tolower(name[ii]) == tolower(conn->name[ii]) && ii < nlen; ++ii) ;
189935b1 163 if (conn->name[ii] == '\0' && name[ii] == '\0')
164 break;
165 }
166
167 /* If none was found, create a new one. */
168 if (!conn)
169 {
170 conn = calloc(1, sizeof(*conn) + nlen);
171 for (ii = 0; ii < nlen; ++ii)
37d25209 172 conn->name[ii] = name[ii];
189935b1 173 conn->next = connects;
174 connects = conn;
175 }
176
177 /* Return the connection. */
178 return conn;
179}
180
181static void do_connect(void)
182{
183 struct connect *conn = get_connect(fields[2]);
184 dupstring(conn->host, fields[0]);
185 dupstring(conn->password, fields[1]);
186 dupstring(conn->port, fields[3]);
187 dupstring(conn->class, fields[4]);
188 string_get(&conn->origins, orig_line);
189}
190
191static void do_hub(void)
192{
193 struct connect *conn = get_connect(fields[2]);
194 dupstring(conn->hub, fields[0]);
195 dupstring(conn->maximum, fields[3]);
196 string_get(&conn->origins, orig_line);
197}
198
199static void do_leaf(void)
200{
201 struct connect *conn = get_connect(fields[2]);
202 free(conn->hub);
203 conn->hub = 0;
204 string_get(&conn->origins, orig_line);
205}
206
207static void finish_connects(void)
208{
209 struct connect *conn;
210 struct string_list *sl;
211
212 for (conn = connects; conn; conn = conn->next)
213 {
214 for (sl = conn->origins; sl; sl = sl->next)
215 fprintf(stdout, "# %s\n", sl->value);
37d25209 216 if (conn->name == NULL
217 || conn->host == NULL
218 || conn->password == NULL
219 || conn->class == NULL)
220 {
221 fprintf(stderr, "H:line missing C:line for %s\n",sl->value);
222 continue;
223 }
224
189935b1 225 fprintf(stdout,
226 "Connect {\n\tname =\"%s\";\n\thost = \"%s\";\n"
227 "\tpassword = \"%s\";\n\tclass = \"%s\";\n",
228 conn->name, conn->host, conn->password, conn->class);
229 if (conn->port && conn->port[0] != '\0')
230 fprintf(stdout, "\tport = %s;\n", conn->port);
231 else
232 fprintf(stdout,
233 "# Every Connect block should have a port number.\n"
234 "# To prevent autoconnects, set autoconnect = no.\n"
235 "#\tport = 4400;\n"
236 "\tautoconnect = no;\n");
237 if (conn->maximum && conn->maximum[0] != '\0')
238 fprintf(stdout, "\tmaxhops = %s;\n", conn->maximum);
239 if (conn->hub && conn->hub[0] != '\0')
240 fprintf(stdout, "\thub = \"%s\";\n", conn->hub);
241 fprintf(stdout, "};\n\n");
242
243 }
244}
245
246/*** FEATURE MANAGEMENT CODE ***/
247
248struct feature {
249 struct string_list *values;
250 struct string_list *origins;
251 struct feature *next;
252 char name[1];
253} *features;
254
255struct remapped_feature {
256 const char *name;
257 const char *privilege;
258 int flags; /* 2 = global, 1 = local */
259 struct feature *feature;
260} remapped_features[] = {
261 /* Specially handled privileges: If you change the index of
262 * anything with NULL privilege, change the code in
263 * finish_operators() to match!
264 */
265 { "CRYPT_OPER_PASSWORD", NULL, 0, 0 }, /* default: true */
266 { "OPER_KILL", NULL, 2, 0 }, /* default: true */
267 { "LOCAL_KILL_ONLY", NULL, 2, 0 }, /* default: false */
268 /* remapped features that affect all opers */
269 { "OPER_NO_CHAN_LIMIT", "chan_limit", 3, 0 },
270 { "OPER_MODE_LCHAN", "mode_lchan", 3, 0 },
271 { "OPER_WALK_THROUGH_LMODES", "walk_lchan", 3, 0 },
272 { "NO_OPER_DEOP_LCHAN", "deop_lchan", 3, 0 },
273 { "SHOW_INVISIBLE_USERS", "show_invis", 3, 0 },
274 { "SHOW_ALL_INVISIBLE_USERS", "show_all_invis", 3, 0 },
275 { "UNLIMIT_OPER_QUERY", "unlimit_query", 3, 0 },
276 /* remapped features affecting only global opers */
277 { "OPER_REHASH", "rehash", 2, 0 },
278 { "OPER_RESTART", "restart", 2, 0 },
279 { "OPER_DIE", "die", 2, 0 },
280 { "OPER_GLINE", "gline", 2, 0 },
281 { "OPER_LGLINE", "local_gline", 2, 0 },
282 { "OPER_JUPE", "jupe", 2, 0 },
283 { "OPER_LJUPE", "local_jupe", 2, 0 },
284 { "OPER_OPMODE", "opmode", 2, 0 },
285 { "OPER_LOPMODE", "local_opmode", 2, 0 },
286 { "OPER_FORCE_OPMODE", "force_opmode", 2, 0 },
287 { "OPER_FORCE_LOPMODE", "force_local_opmode", 2, 0 },
288 { "OPER_BADCHAN", "badchan", 2, 0 },
289 { "OPER_LBADCHAN", "local_badchan", 2, 0 },
290 { "OPER_SET", "set", 2, 0 },
291 { "OPER_WIDE_GLINE", "wide_gline", 2, 0 },
292 /* remapped features affecting only local opers */
293 { "LOCOP_KILL", "kill", 1, 0 },
294 { "LOCOP_REHASH", "rehash", 1, 0 },
295 { "LOCOP_RESTART", "restart", 1, 0 },
296 { "LOCOP_DIE", "die", 1, 0 },
297 { "LOCOP_LGLINE", "local_gline", 1, 0 },
298 { "LOCOP_LJUPE", "local_jupe", 1, 0 },
299 { "LOCOP_LOPMODE", "local_opmode", 1, 0 },
300 { "LOCOP_FORCE_LOPMODE", "force_local_opmode", 1, 0 },
301 { "LOCOP_LBADCHAN", "local_badchan", 1, 0 },
302 { "LOCOP_WIDE_GLINE", "wide_gline", 1, 0 },
303 { 0, 0, 0, 0 }
304};
305
306static void do_feature(void)
307{
308 struct feature *feat;
309 size_t ii;
310
311 ii = strlen(fields[0]);
312 feat = calloc(1, sizeof(*feat) + ii);
313 while (ii-- > 0)
37d25209 314 feat->name[ii] = fields[0][ii];
189935b1 315 feat->next = features;
316 features = feat;
317 string_get(&feat->origins, orig_line);
318 for (ii = 1; fields[ii] && fields[ii][0]; ++ii)
319 string_get(&feat->values, fields[ii]);
320}
321
322static void finish_features(void)
323{
324 struct remapped_feature *rmf;
325 struct string_list *sl;
326 struct feature *feat;
327 size_t ii;
328
37d25209 329 fputs("Features {\n", stdout);
330 fputs("\t\"OPLEVELS\" = \"FALSE\";\n", stdout);
331 fputs("\t\"ZANNELS\" = \"FALSE\";\n", stdout);
189935b1 332
333 for (feat = features; feat; feat = feat->next) {
37d25209 334 /* Display the original feature line we are talking about. */
335 for (sl = feat->origins; sl; sl = sl->next)
336 fprintf(stdout, "# %s\n", sl->value);
337
189935b1 338 /* See if the feature was remapped to an oper privilege. */
339 for (rmf = remapped_features; rmf->name; rmf++)
340 if (0 == strcmp(feat->name, rmf->name))
341 break;
342 if (rmf->name) {
343 rmf->feature = feat;
344 fprintf(stdout, "# Above feature mapped to an oper privilege.\n");
345 continue;
346 }
347
348 /* Was it removed? */
349 for (ii = 0; removed_features[ii]; ++ii)
350 if (0 == strcmp(feat->name, removed_features[ii]))
351 break;
352 if (removed_features[ii]) {
353 fprintf(stdout, "# Above feature no longer exists.\n");
354 continue;
355 }
356
9f8856e9 357 /* If it had no value before, drop it now since the lexer does
358 * not accept empty strings and the grammar does not accept
359 * empty stringlists.*/
360 if (!feat->values) {
361 fprintf(stdout, "# Above feature had no value.\n");
362 continue;
363 }
364
189935b1 365 /* Wasn't remapped, wasn't removed: print it out. */
366 fprintf(stdout, "\t\"%s\" =", feat->name);
367 for (sl = feat->values; sl; sl = sl->next)
368 fprintf(stdout, " \"%s\"", sl->value);
369 fprintf(stdout, ";\n");
370 }
371 fputs("};\n\n", stdout);
372
373}
374
375/*** OPERATOR BLOCKS ***/
376
377struct operator {
378 char *name;
379 char *host;
380 char *password;
381 char *class;
382 char *origin;
383 int is_local;
384 struct operator *next;
385} *operators;
386
387static void do_operator(int is_local)
388{
389 struct operator *oper;
390
391 oper = calloc(1, sizeof(*oper));
392 dupstring(oper->host, fields[0]);
393 dupstring(oper->password, fields[1]);
394 dupstring(oper->name, fields[2]);
395 dupstring(oper->class, fields[4]);
396 dupstring(oper->origin, orig_line);
397 oper->is_local = is_local;
398 oper->next = operators;
399 operators = oper;
400}
401
402static void finish_operators(void)
403{
404 struct remapped_feature *remap;
405 struct operator *oper;
406 struct feature *feat;
407 char *pw_salt = "";
408 int global_kill = 0, mask = 0;
409 size_t ii;
410
411 if ((feat = remapped_features[0].feature) && feat->values
412 && 0 == strcmp(feat->values->value, "FALSE"))
413 pw_salt = "$PLAIN$";
414
415 if ((feat = remapped_features[1].feature) && feat->values
416 && 0 == strcmp(feat->values->value, "FALSE"))
417 global_kill = 1;
418 else if ((feat = remapped_features[2].feature) && feat->values
419 && 0 == strcmp(feat->values->value, "FALSE"))
420 global_kill = 2;
421
422 for (oper = operators; oper; oper = oper->next) {
423 fprintf(stdout, "# %s\nOperator {\n\tname = \"%s\";\n"
424 "\thost = \"%s\";\n\tpassword = \"%s%s\";\n"
425 "\tclass = \"%s\";\n",
426 oper->origin, oper->name, oper->host, pw_salt,
427 oper->password, oper->class);
428 if (oper->is_local) {
429 fputs("\tlocal = yes;\n", stdout);
430 mask = 1;
431 } else {
432 fputs("\tlocal = no;\n", stdout);
433 if (global_kill == 1)
434 fputs("\tkill = no;\n\tlocal_kill = no;\n", stdout);
435 else if (global_kill == 2)
436 fputs("\tkill = no;\n\tlocal_kill = yes;\n", stdout);
437 mask = 2;
438 }
439 for (ii = 0; (remap = &remapped_features[ii++])->name; ) {
440 if (!remap->feature || !remap->privilege
37d25209 441 || !remap->feature->values || !(remap->flags & mask))
189935b1 442 continue;
443 fprintf(stdout, "\t%s = %s;\n", remap->privilege,
444 strcmp(remap->feature->values->value, "TRUE") ? "no" : "yes");
445 }
446 fputs("};\n\n", stdout);
447 }
448}
449
450/*** OTHER CONFIG TRANSFORMS ***/
451
452static void do_kill(void)
453{
454 const char *host = fields[0], *reason = fields[1], *user = fields[2];
455
456 if (!memcmp(host, "$R", 3)) {
457 fprintf(stderr, "Empty realname K: line at line %u.\n", lineno);
458 return;
459 }
460
461 /* Print the current line and start the new block. */
462 fprintf(stdout, "# %s\nKill {\n", orig_line);
463
464 /* Translate the user-matching portions. */
465 if (host[0] == '$' && host[1] == 'R') {
466 /* Realname kill, possibly with a username */
467 fprintf(stdout, "\trealname = \"%s\";\n", host + 2);
468 if (user[0] != '\0' && (user[0] != '*' || user[1] != '\0'))
469 fprintf(stdout, "\thost = \"%s@*\";\n", user);
470 } else {
471 /* Normal host or IP-based kill */
472 if (user[0] != '\0' && (user[0] != '*' || user[1] != '\0'))
473 fprintf(stdout, "\thost = \"%s@%s\";\n", user, host);
474 else
475 fprintf(stdout, "\thost = \"%s\";\n", host);
476 }
477
478 /* Translate the reason section. */
479 if (reason[0] == '!')
480 fprintf(stdout, "\tfile = \"%s\";\n", reason + 1);
481 else
482 fprintf(stdout, "\treason = \"%s\";\n", reason);
483
484 /* Close the block. */
485 fprintf(stdout, "};\n");
486}
487
488static void do_port(void)
489{
490 const char *ipmask = fields[0], *iface = fields[1], *flags = fields[2], *port = fields[3];
491
492 /* Print the current line and start the new block. */
493 fprintf(stdout, "# %s\nPort {\n", orig_line);
494
495 /* Print the easy fields. */
496 fprintf(stdout, "\tport = %s;\n", port);
497 if (iface && iface[0] != '\0')
498 fprintf(stdout, "\tvhost = \"%s\";\n", iface);
499 if (ipmask && ipmask[0] != '\0')
500 fprintf(stdout, "\tmask = \"%s\";\n", ipmask);
501
502 /* Translate flag field. */
503 while (*flags) switch (*flags++) {
504 case 'C': case 'c': /* client port is default state */; break;
505 case 'S': case 's': fprintf(stdout, "\tserver = yes;\n"); break;
506 case 'H': case 'h': fprintf(stdout, "\thidden = yes;\n"); break;
507 }
508
509 /* Close the block. */
510 fprintf(stdout, "};\n");
511}
512
513struct string_list *quarantines;
514
515static void do_quarantine(void)
516{
517 struct string_list *q;
518 q = string_get(&quarantines, fields[0]);
519 dupstring(q->origin, orig_line);
520 dupstring(q->extra, fields[1]);
521}
522
523static void finish_quarantines(void)
524{
525 struct string_list *sl;
526
527 if (quarantines)
528 {
529 fputs("Quarantine {\n", stdout);
530 for (sl = quarantines; sl; sl = sl->next)
531 fprintf(stdout, "# %s\n\t\"%s\" = \"%s\";\n", sl->origin, sl->value, sl->extra);
532 fputs("};\n\n", stdout);
533 }
534}
535
536static void do_uworld(void)
537{
538 fprintf(stdout, "# %s\n", orig_line);
539 if (fields[0] && fields[0][0])
540 fprintf(stdout, "Uworld { name = \"%s\"; };\n", fields[0]);
541 if (fields[1] && fields[1][0])
542 fprintf(stdout, "Jupe { nick = \"%s\"; };\n", fields[1]);
543}
544
545static void emit_client(const char *mask, const char *passwd, const char *class, long maxlinks, int is_ip)
546{
547 char *delim;
548 size_t len;
549
550 delim = strchr(mask, '@');
551 if (delim) {
552 *delim++ = '\0';
553 if (is_ip) {
554 len = strspn(delim, "0123456789.*");
555 if (delim[len]) {
556 fprintf(stderr, "Invalid IP mask on line %u.\n", lineno);
557 return;
558 }
559 fprintf(stdout, "Client {\n\tusername = \"%s\";\n\tip = \"%s\";\n", mask, delim);
560 } else {
561 fprintf(stdout, "Client {\n\tusername =\"%s\";\n\thost = \"%s\";\n", mask, delim);
562 }
563 } else if (is_ip) {
564 len = strspn(mask, "0123456789.*");
565 if (mask[len])
566 return;
567 fprintf(stdout, "Client {\n\tip = \"%s\";\n", mask);
568 } else {
569 if (!strchr(mask, '.') && !strchr(mask, '*'))
570 return;
571 fprintf(stdout, "Client {\n\thost = \"%s\";\n", mask);
572 }
573
574 if (passwd)
575 fprintf(stdout, "\tpassword = \"%s\";\n", passwd);
576
577 if (maxlinks >= 0)
578 fprintf(stdout, "\tmaxlinks = %ld;\n", maxlinks);
579
580 fprintf(stdout, "\tclass = \"%s\";\n};\n", class);
581}
582
583static void do_client(void)
584{
585 char *passwd = NULL, *delim;
586 long maxlinks;
587
588 /* Print the current line. */
589 fprintf(stdout, "# %s\n", orig_line);
590
591 /* See if the password is really a maxlinks count. */
592 maxlinks = strtol(fields[1], &delim, 10);
593 if (fields[1][0] == '\0')
594 maxlinks = -1;
595 else if (maxlinks < 0 || maxlinks > 99 || *delim != '\0')
596 passwd = fields[1];
597
598 /* Translate the IP and host mask fields into blocks. */
599 emit_client(fields[0], passwd, fields[4], maxlinks, 1);
600 emit_client(fields[2], passwd, fields[4], maxlinks, 0);
601}
602
603int main(int argc, char *argv[])
604{
605 FILE *ifile;
606
607 if (argc < 2)
608 ifile = stdin;
609 else if (!(ifile = fopen(argv[1], "rt"))) {
610 fprintf(stderr, "Unable to open file %s for input.\n", argv[1]);
611 return 1;
612 }
613
614 for (lineno = 1; fgets(line, sizeof(line), ifile); ++lineno) {
615 /* Read line and pass comments through. */
616 size_t len = strlen(line);
617 if (line[0] == '#') {
618 fputs(line, stdout);
619 continue;
620 }
37d25209 621 /* Strip trailing whitespace. */
622 while (len > 0 && isspace(line[len-1]))
189935b1 623 line[--len] = '\0';
37d25209 624 /* Pass blank lines through. */
189935b1 625 if (len == 0) {
626 fputc('\n', stdout);
627 continue;
628 }
629 /* Skip but report invalid lines. */
630 if (line[1] != ':') {
631 fprintf(stdout, "# %s\n", line);
632 fprintf(stderr, "Invalid input line %d.\n", lineno);
633 continue;
634 }
635 /* Copy the original line into a reusable variable. */
636 strcpy(orig_line, line);
637 /* Split line into fields. */
638 nfields = split_line(line + 2, fields);
639
640 /* Process the input line. */
641 switch (line[0]) {
642 case 'A': case 'a': simple_line("Admin", admin_names, NULL); break;
643 case 'C': case 'c': do_connect(); break;
644 case 'D': simple_line("CRule", crule_names, "all = yes;"); break;
645 case 'd': simple_line("CRule", crule_names, NULL); break;
646 case 'F': case 'f': do_feature(); break;
647 case 'H': case 'h': do_hub(); break;
648 case 'I': case 'i': do_client(); break;
649 case 'K': case 'k': do_kill(); break;
650 case 'L': case 'l': do_leaf(); break;
651 case 'M': case 'm': simple_line("General", general_names, NULL); break;
652 case 'O': do_operator(0); break;
653 case 'o': do_operator(1); break;
654 case 'P': case 'p': do_port(); break;
655 case 'Q': case 'q': do_quarantine(); break;
656 case 'T': case 't': simple_line("Motd", motd_names, NULL); break;
657 case 'U': case 'u': do_uworld(); break;
658 case 'Y': case 'y': simple_line("Class", class_names, NULL); break;
659 default:
660 fprintf(stderr, "Unknown line %u with leading character '%c'.\n", lineno, line[0]);
661 break;
662 }
663 }
664
665 fclose(ifile);
666
667 fputs("\n# The following lines were intentionally moved and rearranged."
668 "\n# Our apologies for any inconvenience this may cause."
669 "\n\n", stdout);
670 finish_connects();
671 finish_quarantines();
672 finish_features();
673 finish_operators();
674
675 return 0;
676}